Repository: hiddify/hiddify-app Branch: main Commit: 54bc7eebe871 Files: 610 Total size: 2.9 MB Directory structure: gitextract_h090yf1s/ ├── .gitchangelog.rc ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.yaml │ │ ├── config.yml │ │ └── feature_request.yaml │ ├── auto_translator.py │ ├── change_version.sh │ ├── dependabot.yml │ ├── release_message.md │ ├── sync_translate.sh │ └── workflows/ │ ├── add_signed_microsft.yml │ ├── build.yml │ ├── ci.yml │ ├── release.yml │ ├── stale.yml │ └── winget.yml ├── .gitignore ├── .gitmodules ├── .metadata ├── .prettierrc ├── .release_notes.tpl ├── .stignore ├── .vscode/ │ ├── extensions.json │ ├── launch.json │ └── settings.json ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Dockerfile ├── HISTORY.md ├── LICENSE.md ├── Makefile ├── README.md ├── README_br.md ├── README_cn.md ├── README_fa.md ├── README_ja.md ├── README_ru.md ├── analysis_options.yaml ├── android/ │ ├── .gitignore │ ├── .stignore │ ├── app/ │ │ ├── build.gradle │ │ ├── libs/ │ │ │ └── .gitkeep │ │ └── src/ │ │ ├── debug/ │ │ │ └── AndroidManifest.xml │ │ ├── main/ │ │ │ ├── AndroidManifest.xml │ │ │ ├── aidl/ │ │ │ │ └── com/ │ │ │ │ └── hiddify/ │ │ │ │ └── hiddify/ │ │ │ │ ├── IService.aidl │ │ │ │ └── IServiceCallback.aidl │ │ │ ├── kotlin/ │ │ │ │ └── com/ │ │ │ │ └── hiddify/ │ │ │ │ └── hiddify/ │ │ │ │ ├── ActiveGroupsChannel.kt │ │ │ │ ├── Application.kt │ │ │ │ ├── EventHandler.kt │ │ │ │ ├── GroupsChannel.kt │ │ │ │ ├── LogHandler.kt │ │ │ │ ├── MainActivity.kt │ │ │ │ ├── MethodHandler.kt │ │ │ │ ├── PlatformSettingsHandler.kt │ │ │ │ ├── Settings.kt │ │ │ │ ├── ShortcutActivity.kt │ │ │ │ ├── StatsChannel.kt │ │ │ │ ├── bg/ │ │ │ │ │ ├── AppChangeReceiver.kt │ │ │ │ │ ├── BootReceiver.kt │ │ │ │ │ ├── BoxService.kt │ │ │ │ │ ├── Bugs.kt │ │ │ │ │ ├── DefaultNetworkListener.kt │ │ │ │ │ ├── DefaultNetworkMonitor.kt │ │ │ │ │ ├── LocalResolver.kt │ │ │ │ │ ├── PlatformInterfaceWrapper.kt │ │ │ │ │ ├── ProxyService.kt │ │ │ │ │ ├── ServiceBinder.kt │ │ │ │ │ ├── ServiceConnection.kt │ │ │ │ │ ├── ServiceNotification.kt │ │ │ │ │ ├── TileService.kt │ │ │ │ │ └── VPNService.kt │ │ │ │ ├── constant/ │ │ │ │ │ ├── Action.kt │ │ │ │ │ ├── Alert.kt │ │ │ │ │ ├── PerAppProxyMode.kt │ │ │ │ │ ├── ServiceMode.kt │ │ │ │ │ ├── SettingsKey.kt │ │ │ │ │ ├── Status.kt │ │ │ │ │ └── bugs.kt │ │ │ │ ├── ktx/ │ │ │ │ │ ├── Continuations.kt │ │ │ │ │ └── Wrappers.kt │ │ │ │ └── utils/ │ │ │ │ ├── CommandClient.kt │ │ │ │ ├── GrpcProvider.kt │ │ │ │ └── OutboundMapper.kt │ │ │ ├── protos/ │ │ │ │ ├── extension/ │ │ │ │ │ ├── extension.proto │ │ │ │ │ └── extension_service.proto │ │ │ │ └── v2/ │ │ │ │ ├── config/ │ │ │ │ │ └── route_rule.proto │ │ │ │ ├── hcommon/ │ │ │ │ │ └── common.proto │ │ │ │ ├── hcore/ │ │ │ │ │ ├── hcore.proto │ │ │ │ │ ├── hcore_service.proto │ │ │ │ │ └── tunnelservice/ │ │ │ │ │ ├── tunnel.proto │ │ │ │ │ └── tunnel_service.proto │ │ │ │ ├── hello/ │ │ │ │ │ ├── hello.proto │ │ │ │ │ └── hello_service.proto │ │ │ │ ├── hiddifyoptions/ │ │ │ │ │ └── hiddify_options.proto │ │ │ │ └── profile/ │ │ │ │ ├── profile.proto │ │ │ │ └── profile_service.proto │ │ │ └── res/ │ │ │ ├── drawable/ │ │ │ │ ├── android12splash.xml │ │ │ │ ├── ic_banner_foreground.xml │ │ │ │ ├── ic_launcher_background.xml │ │ │ │ ├── ic_launcher_foreground.xml │ │ │ │ └── launch_background.xml │ │ │ ├── drawable-v21/ │ │ │ │ └── launch_background.xml │ │ │ ├── mipmap-anydpi-v26/ │ │ │ │ ├── ic_banner.xml │ │ │ │ ├── ic_launcher.xml │ │ │ │ └── ic_launcher_round.xml │ │ │ ├── values/ │ │ │ │ ├── colors.xml │ │ │ │ ├── ic_banner_background.xml │ │ │ │ ├── ic_launcher_background.xml │ │ │ │ ├── strings.xml │ │ │ │ └── styles.xml │ │ │ ├── values-night/ │ │ │ │ └── styles.xml │ │ │ ├── values-night-v31/ │ │ │ │ └── styles.xml │ │ │ ├── values-v31/ │ │ │ │ └── styles.xml │ │ │ └── xml/ │ │ │ ├── network_security_config.xml │ │ │ └── shortcuts.xml │ │ └── profile/ │ │ └── AndroidManifest.xml │ ├── build.gradle │ ├── gradle/ │ │ └── wrapper/ │ │ └── gradle-wrapper.properties │ ├── gradle.properties │ └── settings.gradle ├── appcast.xml ├── assets/ │ ├── core/ │ │ └── .gitkeep │ ├── fonts/ │ │ └── emoji_source.txt │ ├── images/ │ │ └── convert_icon.sh │ └── translations/ │ ├── ar.i18n.json │ ├── en.i18n.json │ ├── es.i18n.json │ ├── fa.i18n.json │ ├── fr.i18n.json │ ├── id.i18n.json │ ├── pt-BR.i18n.json │ ├── ru.i18n.json │ ├── tr.i18n.json │ ├── zh-CN.i18n.json │ └── zh-TW.i18n.json ├── build.yaml ├── dependencies.properties ├── devtools_options.yaml ├── ios/ │ ├── .gitignore │ ├── .stignore │ ├── Base.xcconfig │ ├── Flutter/ │ │ ├── AppFrameworkInfo.plist │ │ ├── Debug.xcconfig │ │ └── Release.xcconfig │ ├── Frameworks/ │ │ └── .gitkeep │ ├── HiddifyPacketTunnel/ │ │ ├── HiddifyPacketTunnel.entitlements │ │ ├── Info.plist │ │ ├── Logger.swift │ │ ├── PacketTunnelProvider.swift │ │ ├── PrivacyInfo.xcprivacy │ │ ├── SingBox/ │ │ │ ├── Extension+RunBlocking.swift │ │ │ ├── ExtensionPlatformInterface.swift │ │ │ ├── ExtensionProvider.swift │ │ │ └── SingBox.swift │ │ └── TrafficReader.swift │ ├── Local Packages/ │ │ └── Package.swift │ ├── Podfile │ ├── Runner/ │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets/ │ │ │ ├── AppIcon.appiconset/ │ │ │ │ └── Contents.json │ │ │ ├── LaunchBackground.imageset/ │ │ │ │ └── Contents.json │ │ │ └── LaunchImage.imageset/ │ │ │ ├── Contents.json │ │ │ └── README.md │ │ ├── Base.lproj/ │ │ │ ├── LaunchScreen.storyboard │ │ │ └── Main.storyboard │ │ ├── Extensions/ │ │ │ └── Bundle+Properties.swift │ │ ├── Handlers/ │ │ │ ├── ActiveGroupsEventHandler.swift │ │ │ ├── AlertsEventHandler.swift │ │ │ ├── FileMethodHandler.swift │ │ │ ├── GroupsEventHandler.swift │ │ │ ├── LogsEventHandler.swift │ │ │ ├── MethodHandler.swift │ │ │ ├── PlatformMethodHandler.swift │ │ │ ├── StatsEventHandler.swift │ │ │ └── StatusEventHandler.swift │ │ ├── Info.plist │ │ ├── PrivacyInfo.xcprivacy │ │ ├── Runner-Bridging-Header.h │ │ ├── Runner.entitlements │ │ └── VPN/ │ │ ├── Helpers/ │ │ │ └── Stored.swift │ │ ├── VPNConfig.swift │ │ └── VPNManager.swift │ ├── Runner.xcodeproj/ │ │ ├── project.pbxproj │ │ ├── project.xcworkspace/ │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata/ │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ │ └── xcshareddata/ │ │ └── xcschemes/ │ │ ├── HiddifyPacketTunnel.xcscheme │ │ └── Runner.xcscheme │ ├── Runner.xcworkspace/ │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata/ │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings │ ├── RunnerTests/ │ │ └── RunnerTests.swift │ ├── Shared/ │ │ ├── CommandClient.swift │ │ ├── FilePath.swift │ │ └── Outbound.swift │ ├── exportOptions.plist │ └── packaging/ │ └── ios/ │ └── make_config.yaml ├── lib/ │ ├── bootstrap.dart │ ├── core/ │ │ ├── analytics/ │ │ │ ├── analytics_controller.dart │ │ │ ├── analytics_filter.dart │ │ │ └── analytics_logger.dart │ │ ├── app_info/ │ │ │ └── app_info_provider.dart │ │ ├── db/ │ │ │ ├── converters/ │ │ │ │ └── duration_converter.dart │ │ │ ├── db.dart │ │ │ ├── db.steps.dart │ │ │ ├── provider/ │ │ │ │ └── db_providers.dart │ │ │ └── schemas/ │ │ │ └── db/ │ │ │ ├── drift_schema_v1.json │ │ │ ├── drift_schema_v2.json │ │ │ ├── drift_schema_v3.json │ │ │ ├── drift_schema_v4.json │ │ │ └── drift_schema_v5.json │ │ ├── directories/ │ │ │ └── directories_provider.dart │ │ ├── haptic/ │ │ │ └── haptic_service.dart │ │ ├── http_client/ │ │ │ ├── dio_http_client.dart │ │ │ └── http_client_provider.dart │ │ ├── localization/ │ │ │ ├── locale_extensions.dart │ │ │ ├── locale_preferences.dart │ │ │ └── translations.dart │ │ ├── logger/ │ │ │ ├── custom_logger.dart │ │ │ ├── logger.dart │ │ │ └── logger_controller.dart │ │ ├── model/ │ │ │ ├── app_info_entity.dart │ │ │ ├── constants.dart │ │ │ ├── directories.dart │ │ │ ├── environment.dart │ │ │ ├── failures.dart │ │ │ ├── optional_range.dart │ │ │ └── region.dart │ │ ├── notification/ │ │ │ └── in_app_notification_controller.dart │ │ ├── preferences/ │ │ │ ├── actions_at_closing.dart │ │ │ ├── general_preferences.dart │ │ │ ├── preferences_migration.dart │ │ │ └── preferences_provider.dart │ │ ├── router/ │ │ │ ├── adaptive_layout/ │ │ │ │ ├── my_adaptive_layout.dart │ │ │ │ └── shell_route_action.dart │ │ │ ├── bottom_sheets/ │ │ │ │ ├── bottom_sheets_notifier.dart │ │ │ │ └── widgets/ │ │ │ │ ├── auto_apps_selection_modal.dart │ │ │ │ └── quick_settings_modal.dart │ │ │ ├── deep_linking/ │ │ │ │ ├── my_app_links.dart │ │ │ │ └── url_protocol/ │ │ │ │ ├── README_url_protocol.md │ │ │ │ ├── api.dart │ │ │ │ ├── protocol.dart │ │ │ │ ├── web_url_protocol.dart │ │ │ │ └── windows_protocol.dart │ │ │ ├── dialog/ │ │ │ │ ├── dialog_notifier.dart │ │ │ │ └── widgets/ │ │ │ │ ├── action_at_closing_dialog.dart │ │ │ │ ├── confirmation_dialog.dart │ │ │ │ ├── custom_alert_dialog.dart │ │ │ │ ├── experimental_feature_notice.dart │ │ │ │ ├── free_profile_consent_dialog.dart │ │ │ │ ├── new_version_dialog.dart │ │ │ │ ├── no_active_profile_dialog.dart │ │ │ │ ├── ok_dialog.dart │ │ │ │ ├── proxy_info_dialog.dart │ │ │ │ ├── save_dialog.dart │ │ │ │ ├── setting_checkbox_dialog.dart │ │ │ │ ├── setting_input_dialog.dart │ │ │ │ ├── setting_picker_dialog.dart │ │ │ │ ├── setting_radio_dialog.dart │ │ │ │ ├── setting_slider_dialog.dart │ │ │ │ ├── setting_text_dialog.dart │ │ │ │ ├── sort_profiles_dialog.dart │ │ │ │ ├── unknown_domains_warning_dialog.dart │ │ │ │ ├── warp_license_dialog.dart │ │ │ │ └── window_closing_dialog.dart │ │ │ └── go_router/ │ │ │ ├── go_router_notifier.dart │ │ │ ├── helper/ │ │ │ │ ├── active_breakpoint_notifier.dart │ │ │ │ ├── custom_transition.dart │ │ │ │ └── popup_count_notifier.dart │ │ │ ├── refresh_listenable.dart │ │ │ └── routing_config_notifier.dart │ │ ├── theme/ │ │ │ ├── app_theme.dart │ │ │ ├── app_theme_mode.dart │ │ │ ├── theme_extensions.dart │ │ │ └── theme_preferences.dart │ │ ├── utils/ │ │ │ ├── exception_handler.dart │ │ │ ├── ffi_utils.dart │ │ │ ├── ip_utils.dart │ │ │ ├── json_converters.dart │ │ │ ├── laststeam.dart │ │ │ ├── preferences_utils.dart │ │ │ └── throttler.dart │ │ └── widget/ │ │ ├── adaptive_icon.dart │ │ ├── adaptive_menu.dart │ │ ├── animated_text.dart │ │ ├── animated_visibility.dart │ │ ├── shimmer_skeleton.dart │ │ ├── skeleton_widget.dart │ │ ├── spaced_list_widget.dart │ │ └── tip_card.dart │ ├── features/ │ │ ├── about/ │ │ │ └── widget/ │ │ │ └── about_page.dart │ │ ├── app/ │ │ │ └── widget/ │ │ │ └── app.dart │ │ ├── app_update/ │ │ │ ├── data/ │ │ │ │ ├── app_update_data_providers.dart │ │ │ │ ├── app_update_repository.dart │ │ │ │ └── github_release_parser.dart │ │ │ ├── model/ │ │ │ │ ├── app_update_failure.dart │ │ │ │ └── remote_version_entity.dart │ │ │ └── notifier/ │ │ │ ├── app_update_notifier.dart │ │ │ └── app_update_state.dart │ │ ├── auto_start/ │ │ │ └── notifier/ │ │ │ └── auto_start_notifier.dart │ │ ├── common/ │ │ │ ├── custom_text_scroll.dart │ │ │ ├── general_pref_tiles.dart │ │ │ ├── qr_code_dialog.dart │ │ │ └── qr_code_scanner_screen.dart │ │ ├── connection/ │ │ │ ├── data/ │ │ │ │ ├── connection_data_providers.dart │ │ │ │ └── connection_repository.dart │ │ │ ├── model/ │ │ │ │ ├── connection_failure.dart │ │ │ │ └── connection_status.dart │ │ │ ├── notifier/ │ │ │ │ └── connection_notifier.dart │ │ │ └── widget/ │ │ │ └── connection_wrapper.dart │ │ ├── deep_link/ │ │ │ └── notifier/ │ │ │ └── deep_link_notifier.dart │ │ ├── home/ │ │ │ └── widget/ │ │ │ ├── connection_button.dart │ │ │ ├── empty_profiles_home_body.dart │ │ │ ├── home_page.dart │ │ │ ├── new_con_button.dart │ │ │ └── new_connection_button.dart │ │ ├── intro/ │ │ │ └── widget/ │ │ │ └── intro_page.dart │ │ ├── log/ │ │ │ ├── data/ │ │ │ │ ├── log_data_providers.dart │ │ │ │ ├── log_parser.dart │ │ │ │ ├── log_path_resolver.dart │ │ │ │ └── log_repository.dart │ │ │ ├── model/ │ │ │ │ ├── log_entity.dart │ │ │ │ ├── log_failure.dart │ │ │ │ └── log_level.dart │ │ │ └── overview/ │ │ │ ├── logs_overview_notifier.dart │ │ │ ├── logs_overview_state.dart │ │ │ └── logs_page.dart │ │ ├── per_app_proxy/ │ │ │ ├── data/ │ │ │ │ ├── app_proxy_data_source.dart │ │ │ │ ├── auto_selection_repository.dart │ │ │ │ ├── auto_selection_repository_provider.dart │ │ │ │ └── selected_data_provider.dart │ │ │ ├── model/ │ │ │ │ ├── app_package_info.dart │ │ │ │ ├── per_app_proxy_backup.dart │ │ │ │ ├── per_app_proxy_mode.dart │ │ │ │ └── pkg_flag.dart │ │ │ └── overview/ │ │ │ ├── per_app_proxy_loading_notifier.dart │ │ │ ├── per_app_proxy_notifier.dart │ │ │ ├── per_app_proxy_page.dart │ │ │ └── per_app_proxy_service_notifier.dart │ │ ├── platform_specific/ │ │ │ └── android_quick_settings_tile.dart │ │ ├── profile/ │ │ │ ├── add/ │ │ │ │ ├── add_profile_modal.dart │ │ │ │ ├── model/ │ │ │ │ │ └── free_profiles_model.dart │ │ │ │ └── widgets/ │ │ │ │ ├── fix_btn.dart │ │ │ │ ├── fix_btns.dart │ │ │ │ ├── free_btn.dart │ │ │ │ ├── free_btns.dart │ │ │ │ ├── loading.dart │ │ │ │ ├── nav_bar.dart │ │ │ │ └── widgets.dart │ │ │ ├── data/ │ │ │ │ ├── profile_data_mapper.dart │ │ │ │ ├── profile_data_providers.dart │ │ │ │ ├── profile_data_source.dart │ │ │ │ ├── profile_parser.dart │ │ │ │ ├── profile_path_resolver.dart │ │ │ │ └── profile_repository.dart │ │ │ ├── details/ │ │ │ │ ├── json_editor.dart │ │ │ │ ├── profile_details_notifier.dart │ │ │ │ ├── profile_details_page.dart │ │ │ │ └── profile_details_state.dart │ │ │ ├── model/ │ │ │ │ ├── profile_entity.dart │ │ │ │ ├── profile_failure.dart │ │ │ │ └── profile_sort_enum.dart │ │ │ ├── notifier/ │ │ │ │ ├── active_profile_notifier.dart │ │ │ │ ├── profile_notifier.dart │ │ │ │ └── profiles_update_notifier.dart │ │ │ ├── overview/ │ │ │ │ ├── profiles_modal.dart │ │ │ │ ├── profiles_notifier.dart │ │ │ │ └── profiles_page.dart │ │ │ └── widget/ │ │ │ ├── profile_tile.dart │ │ │ └── profile_tile_main.dart │ │ ├── proxy/ │ │ │ ├── active/ │ │ │ │ ├── active_proxy_card.dart │ │ │ │ ├── active_proxy_delay_indicator.dart │ │ │ │ ├── active_proxy_footer_old.dart │ │ │ │ ├── active_proxy_notifier.dart │ │ │ │ └── ip_widget.dart │ │ │ ├── data/ │ │ │ │ ├── proxy_data_providers.dart │ │ │ │ └── proxy_repository.dart │ │ │ ├── model/ │ │ │ │ ├── ip_info_entity.dart │ │ │ │ ├── proxy_entity.dart │ │ │ │ └── proxy_failure.dart │ │ │ ├── overview/ │ │ │ │ ├── proxies_overview_notifier.dart │ │ │ │ └── proxies_overview_page.dart │ │ │ └── widget/ │ │ │ └── proxy_tile.dart │ │ ├── route_rules/ │ │ │ ├── notifier/ │ │ │ │ ├── android_apps_notifier.dart │ │ │ │ ├── generic_list_notifier.dart │ │ │ │ ├── rule_notifier.dart │ │ │ │ └── rules_notifier.dart │ │ │ ├── overview/ │ │ │ │ ├── android_apps_page.dart │ │ │ │ ├── generic_list_page.dart │ │ │ │ ├── rule_page.dart │ │ │ │ └── rules_page.dart │ │ │ └── widget/ │ │ │ ├── rule_tile.dart │ │ │ ├── setting_checkbox.dart │ │ │ ├── setting_detail_chips.dart │ │ │ ├── setting_divider.dart │ │ │ ├── setting_generic_list.dart │ │ │ ├── setting_radio.dart │ │ │ └── setting_text.dart │ │ ├── settings/ │ │ │ ├── data/ │ │ │ │ ├── battery_optimization_repository.dart │ │ │ │ ├── config_option_data_providers.dart │ │ │ │ └── config_option_repository.dart │ │ │ ├── model/ │ │ │ │ ├── config_option_failure.dart │ │ │ │ └── settings_failure.dart │ │ │ ├── notifier/ │ │ │ │ ├── battery_optimization/ │ │ │ │ │ └── battery_optimizations_notifier.dart │ │ │ │ ├── config_option/ │ │ │ │ │ └── config_option_notifier.dart │ │ │ │ ├── reset_tunnel/ │ │ │ │ │ └── reset_tunnel_notifier.dart │ │ │ │ └── warp_option/ │ │ │ │ └── warp_option_notifier.dart │ │ │ ├── overview/ │ │ │ │ ├── sections/ │ │ │ │ │ ├── dns_options_page.dart │ │ │ │ │ ├── general_page.dart │ │ │ │ │ ├── inbound_options_page.dart │ │ │ │ │ ├── route_options_page.dart │ │ │ │ │ ├── tls_tricks_page.dart │ │ │ │ │ └── warp_options_page.dart │ │ │ │ └── settings_page.dart │ │ │ └── widget/ │ │ │ ├── autocomplete_field.dart │ │ │ └── preference_tile.dart │ │ ├── shortcut/ │ │ │ └── shortcut_wrapper.dart │ │ ├── stats/ │ │ │ ├── data/ │ │ │ │ ├── stats_data_providers.dart │ │ │ │ └── stats_repository.dart │ │ │ ├── model/ │ │ │ │ ├── stats_entity.dart │ │ │ │ └── stats_failure.dart │ │ │ ├── notifier/ │ │ │ │ └── stats_notifier.dart │ │ │ └── widget/ │ │ │ ├── connection_stats_card.dart │ │ │ ├── side_bar_stats_overview.dart │ │ │ └── stats_card.dart │ │ ├── system_tray/ │ │ │ ├── notifier/ │ │ │ │ └── system_tray_notifier.dart │ │ │ └── widget/ │ │ │ └── system_tray_wrapper.dart │ │ └── window/ │ │ ├── notifier/ │ │ │ └── window_notifier.dart │ │ └── widget/ │ │ └── window_wrapper.dart │ ├── gen/ │ │ └── hiddify_core_generated_bindings.dart │ ├── hiddifycore/ │ │ ├── core_interface/ │ │ │ ├── core_interface.dart │ │ │ ├── core_interface_desktop.dart │ │ │ ├── core_interface_mobile.dart │ │ │ ├── core_interface_wrapper.dart │ │ │ ├── core_interface_wrapper_stub.dart │ │ │ └── mtls_channel_cred.dart │ │ ├── generated/ │ │ │ ├── extension/ │ │ │ │ ├── extension.pb.dart │ │ │ │ ├── extension.pbenum.dart │ │ │ │ ├── extension.pbjson.dart │ │ │ │ ├── extension_service.pb.dart │ │ │ │ ├── extension_service.pbenum.dart │ │ │ │ ├── extension_service.pbgrpc.dart │ │ │ │ └── extension_service.pbjson.dart │ │ │ ├── google/ │ │ │ │ └── protobuf/ │ │ │ │ ├── timestamp.pb.dart │ │ │ │ ├── timestamp.pbenum.dart │ │ │ │ └── timestamp.pbjson.dart │ │ │ └── v2/ │ │ │ ├── common/ │ │ │ │ ├── common.pb.dart │ │ │ │ ├── common.pbenum.dart │ │ │ │ └── common.pbjson.dart │ │ │ ├── config/ │ │ │ │ ├── route_rule.pb.dart │ │ │ │ ├── route_rule.pbenum.dart │ │ │ │ └── route_rule.pbjson.dart │ │ │ ├── hcommon/ │ │ │ │ ├── common.pb.dart │ │ │ │ ├── common.pbenum.dart │ │ │ │ └── common.pbjson.dart │ │ │ ├── hcore/ │ │ │ │ ├── hcore.pb.dart │ │ │ │ ├── hcore.pbenum.dart │ │ │ │ ├── hcore.pbjson.dart │ │ │ │ ├── hcore_service.pb.dart │ │ │ │ ├── hcore_service.pbenum.dart │ │ │ │ ├── hcore_service.pbgrpc.dart │ │ │ │ ├── hcore_service.pbjson.dart │ │ │ │ └── tunnelservice/ │ │ │ │ ├── tunnel.pb.dart │ │ │ │ ├── tunnel.pbenum.dart │ │ │ │ ├── tunnel.pbjson.dart │ │ │ │ ├── tunnel_service.pb.dart │ │ │ │ ├── tunnel_service.pbenum.dart │ │ │ │ ├── tunnel_service.pbgrpc.dart │ │ │ │ └── tunnel_service.pbjson.dart │ │ │ ├── hello/ │ │ │ │ ├── hello.pb.dart │ │ │ │ ├── hello.pbenum.dart │ │ │ │ ├── hello.pbjson.dart │ │ │ │ ├── hello_service.pb.dart │ │ │ │ ├── hello_service.pbenum.dart │ │ │ │ ├── hello_service.pbgrpc.dart │ │ │ │ └── hello_service.pbjson.dart │ │ │ ├── hiddifyoptions/ │ │ │ │ ├── hiddify_options.pb.dart │ │ │ │ ├── hiddify_options.pbenum.dart │ │ │ │ └── hiddify_options.pbjson.dart │ │ │ └── profile/ │ │ │ ├── profile.pb.dart │ │ │ ├── profile.pbenum.dart │ │ │ ├── profile.pbjson.dart │ │ │ ├── profile_service.pb.dart │ │ │ ├── profile_service.pbenum.dart │ │ │ ├── profile_service.pbgrpc.dart │ │ │ └── profile_service.pbjson.dart │ │ ├── hiddify_core_service.dart │ │ ├── hiddify_core_service_provider.dart │ │ └── init_signal.dart │ ├── main.dart │ ├── main_prod.dart │ ├── riverpod_observer.dart │ ├── singbox/ │ │ └── model/ │ │ ├── core_status.dart │ │ ├── singbox_config_enum.dart │ │ ├── singbox_config_option.dart │ │ ├── singbox_outbound.dart │ │ ├── singbox_proxy_type.dart │ │ ├── singbox_rule.dart │ │ ├── singbox_stats.dart │ │ └── warp_account.dart │ └── utils/ │ ├── alerts.dart │ ├── async_mutation.dart │ ├── bottom_sheet_page.dart │ ├── callback_debouncer.dart │ ├── custom_loggers.dart │ ├── custom_text_form_field.dart │ ├── date_time_formatter.dart │ ├── link_parsers.dart │ ├── mutation_state.dart │ ├── number_formatters.dart │ ├── placeholders.dart │ ├── platform_utils.dart │ ├── riverpod_utils.dart │ ├── sentry_riverpod_observer.dart │ ├── sentry_utils.dart │ ├── text_utils.dart │ ├── uri_utils.dart │ ├── utils.dart │ └── validators.dart ├── linux/ │ ├── .gitignore │ ├── .stignore │ ├── CMakeLists.txt │ ├── flutter/ │ │ ├── CMakeLists.txt │ │ ├── generated_plugin_registrant.cc │ │ ├── generated_plugin_registrant.h │ │ └── generated_plugins.cmake │ ├── main.cc │ ├── my_application.cc │ ├── my_application.h │ └── packaging/ │ ├── app.hiddify.com.appdata.xml │ ├── appimage/ │ │ ├── AppRun │ │ └── make_config.yaml │ ├── deb/ │ │ └── make_config.yaml │ └── rpm/ │ └── make_config.yaml ├── linux_deps.list ├── macos/ │ ├── .gitignore │ ├── .stignore │ ├── Flutter/ │ │ ├── Flutter-Debug.xcconfig │ │ ├── Flutter-Release.xcconfig │ │ └── GeneratedPluginRegistrant.swift │ ├── Podfile │ ├── Runner/ │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets/ │ │ │ └── AppIcon.appiconset/ │ │ │ └── Contents.json │ │ ├── Base.lproj/ │ │ │ └── MainMenu.xib │ │ ├── Configs/ │ │ │ ├── AppInfo.xcconfig │ │ │ ├── Debug.xcconfig │ │ │ ├── Release.xcconfig │ │ │ └── Warnings.xcconfig │ │ ├── DebugProfile.entitlements │ │ ├── Info.plist │ │ ├── MainFlutterWindow.swift │ │ └── Release.entitlements │ ├── Runner.xcodeproj/ │ │ ├── project.pbxproj │ │ ├── project.xcworkspace/ │ │ │ └── xcshareddata/ │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── swiftpm/ │ │ │ └── Package.resolved │ │ └── xcshareddata/ │ │ └── xcschemes/ │ │ └── Runner.xcscheme │ ├── Runner.xcworkspace/ │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata/ │ │ ├── IDEWorkspaceChecks.plist │ │ └── swiftpm/ │ │ └── Package.resolved │ ├── RunnerTests/ │ │ └── RunnerTests.swift │ └── packaging/ │ ├── dmg/ │ │ └── make_config.yaml │ └── pkg/ │ └── make_config.yaml ├── project.inlang/ │ ├── project_id │ └── settings.json ├── pubspec.yaml ├── scripts/ │ └── package_windows.ps1 ├── snap/ │ └── gui/ │ └── app_icon.desktop ├── test/ │ ├── core/ │ │ └── utils/ │ │ └── ip_utils_test.dart │ ├── drift/ │ │ └── db/ │ │ ├── generated/ │ │ │ ├── schema.dart │ │ │ ├── schema_v1.dart │ │ │ ├── schema_v2.dart │ │ │ ├── schema_v3.dart │ │ │ ├── schema_v4.dart │ │ │ └── schema_v5.dart │ │ └── migration_test.dart │ └── features/ │ └── profile/ │ └── data/ │ └── profile_parser_test.dart ├── test.configs/ │ ├── README.md │ ├── ainita │ ├── dnstt/ │ │ ├── dnstt_raw_config.json │ │ └── readme.md │ ├── fragment │ ├── free_configs │ ├── mahsa │ ├── super_fragment │ ├── warp │ └── warp2 ├── web/ │ ├── drift_worker.js │ ├── index.html │ ├── manifest.json │ └── sqlite3.wasm └── windows/ ├── .gitignore ├── .stignore ├── CMakeLists.txt ├── flutter/ │ ├── CMakeLists.txt │ ├── generated_plugin_registrant.cc │ ├── generated_plugin_registrant.h │ └── generated_plugins.cmake ├── packaging/ │ ├── exe/ │ │ ├── inno_setup.sas │ │ └── make_config.yaml │ └── msix/ │ └── make_config.yaml └── runner/ ├── CMakeLists.txt ├── Runner.rc ├── flutter_window.cpp ├── flutter_window.h ├── main.cpp ├── resource.h ├── runner.exe.manifest ├── utils.cpp ├── utils.h ├── win32_window.cpp └── win32_window.h ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitchangelog.rc ================================================ #output_engine = mustache("markdown") output_engine = mustache(".release_notes.tpl") tag_filter_regexp = r'^v?[0-9]+\.[0-9]+(\.[0-9]+)?$' ignore_regexps = [ r'@minor', r'!minor', r'@cosmetic', r'!cosmetic', r'@refactor', r'!refactor', r'@wip', r'!wip', r'^([cC]hg|[fF]ix|[nN]ew)\s*:\s*[p|P]kg:', r'^([cC]hg|[fF]ix|[nN]ew)\s*:\s*[d|D]ev:', r'^(.{3,3}\s*:)?\s*[fF]irst commit.?\s*$', r'^$', ## ignore commits with empty messages r'release: *', ] ================================================ FILE: .github/ISSUE_TEMPLATE/bug_report.yaml ================================================ --- name: Bug Report description: Report a bug encountered while using Hiddify labels: ["bug"] body: - type: markdown attributes: value: | ## Only English For access to our user forums, please join [our Telegram group](https://t.me/hiddify_board). Please make sure to provide a descriptive, deterministic, and reproducible report. It saves time for both the developers and users who are looking for solutions. Providing as much information as possible, including screenshots and logs, is highly appreciated. This will help us to better understand the issue and respond more effectively. Please DO NOT use this template to ask questions. You can join [our Telegram group](https://t.me/hiddify_board) or [our Website](https://hiddify.com/app) to ask questions. This template is strictly for reporting bugs. - type: checkboxes id: confirm-search attributes: label: Search first description: Please search [existing issues](https://github.com/hiddify/hiddify-app/issues) before reporting. options: - label: I searched and no similar issues were found required: true - type: dropdown id: platform attributes: label: Platform/OS description: What platform are you seeing the problem on? multiple: true options: - Android - Windows - macOS - Linux - Android TV - iOS - HiddifyCli (Core) validations: required: true - type: input id: os attributes: label: "OS version" description: "Please provide the operating system version. (Example: Windows 10)" validations: required: true - type: input id: version attributes: label: Hiddify Version description: "What version of Hiddify are you using?" validations: required: true - type: textarea id: problem attributes: label: What Happened? description: | Please provide as much info as possible. Not doing so may result in your bug not being addressed in a timely manner. validations: required: true - type: textarea id: reproduce attributes: label: Minimal Reproducible Example (MRE) description: | Please tell us the steps to reproduce the bug. A [Minimal Reproducible Example (MRE)](https://stackoverflow.com/help/minimal-reproducible-example) is needed. placeholder: | 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See error validations: required: true - type: textarea id: expected attributes: label: Expected Behavior description: | Please tell us what's the behavior you expect. validations: required: false - type: textarea id: additional attributes: label: Additional Context description: | If applicable, add screenshots, screen recordings or additional context to help explain your problem. validations: required: false - type: textarea id: configs attributes: label: Application Config Options description: Please copy and paste Application config. validations: required: false - type: textarea id: logs attributes: label: Relevant log output description: Please copy and paste any relevant log output. This will be automatically formatted into code. validations: required: false - type: checkboxes id: ask-pr attributes: label: Are you willing to submit a PR? If you know how to fix the bug. description: | If you are not familiar with programming, you can skip this step. If you are a developer and know how to fix the bug, you can submit a PR to fix it. Your contributions are greatly appreciated and play a vital role in helping to improve the project! options: - label: I'm willing to submit a PR (Thank you!) ================================================ FILE: .github/ISSUE_TEMPLATE/config.yml ================================================ blank_issues_enabled: false contact_links: - name: Questions & Help url: https://t.me/hiddify_board about: Ask a question about Hiddify ================================================ FILE: .github/ISSUE_TEMPLATE/feature_request.yaml ================================================ name: Feature Request description: Request a new feature title: '[FR] ' body: - type: markdown attributes: value: | # Only English - type: textarea attributes: label: Feature description description: Please provide a clear and concise description of what you want to happen and what problem will this solve. validations: required: true ================================================ FILE: .github/auto_translator.py ================================================ import i18n import json from deep_translator import GoogleTranslator import sys import os def get_path(lang): base_dir = os.path.abspath('../assets/translations') lang_file = f'strings_{lang}.i18n.json' path = os.path.join(base_dir, lang_file) if path.startswith(base_dir): return path else: raise ValueError('Invalid language file path') def read_translate(lang): pat = get_path(lang) if not os.path.isfile(pat): return {} with open(pat) as f: return json.load(f) def recursive_translate(src, dst, translator): for sk, sv in src.items(): if type(sv) == str: if sk not in dst or not dst[sk]: dst[sk] = translator.translate(sv) print(sk, sv, dst[sk]) if not dst[sk]: del dst[sk] else: if sk not in dst: dst[sk] = {} recursive_translate(sv, dst[sk], translator) if __name__ == "__main__": src = sys.argv[1] dst = sys.argv[2] src_pofile = read_translate(src) dst_pofile = read_translate(dst) translator = GoogleTranslator(source=src, target=dst if dst != 'zh' else "zh-CN") recursive_translate(src_pofile, dst_pofile, translator) with open(os.path.abspath(get_path(dst)), 'w') as df: json.dump(dst_pofile, df, ensure_ascii=False, indent=4) ================================================ FILE: .github/change_version.sh ================================================ #! /bin/bash SED() { [[ "$OSTYPE" == "darwin"* ]] && sed -i '' "$@" || sed -i "$@"; } echo "previous version was $(git describe --tags $(git rev-list --tags --max-count=1))" echo "WARNING: This operation will creates version tag and push to github" if [ "$(curl -o /dev/null -I -s -w "%{http_code}" https://github.com/hiddify/hiddify-core/releases/download/v${CORE_VERSION}/hiddify-core-linux-amd64.tar.gz)" = "404" ]; then echo "Core v${CORE_VERSION} not Found"; exit 3; fi cversion_string=$(grep -e "^version:" pubspec.yaml | cut -d: -f2-) cstr_version=`echo "${cversion_string}" | sed -E -n 's/ *([0-9]+\.[0-9]+\.[0-9]+).*/\1/p'` [ "$cversion_string" == "" ] && { echo "getting old version error"; exit 1 ; } cbuild_number=`echo "${cversion_string}" | sed -E -n 's/.*\+([0-9]+)$/\1/p'` echo "Current Version Name:${cstr_version} Build Number:${cbuild_number}" read -p "new Version? (provide the next x.y.z semver) : " TAG echo $TAG [[ "$TAG" =~ ^[0-9]{1,2}\.[0-9]{1,2}\.[0-9]{1,2}(\.dev)?$ ]] || { echo "Incorrect tag. e.g., 1.2.3 or 1.2.3.dev"; exit 1; } IFS="." read -r -a VERSION_ARRAY <<< "$TAG" VERSION_STR="${VERSION_ARRAY[0]}.${VERSION_ARRAY[1]}.${VERSION_ARRAY[2]}" BUILD_NUMBER=$(( ${VERSION_ARRAY[0]} * 10000 + ${VERSION_ARRAY[1]} * 100 + ${VERSION_ARRAY[2]} )) echo "version: ${VERSION_STR}+${BUILD_NUMBER}" echo "====$cbuild_number" SED "s/^version: .*/version: ${VERSION_STR}\+${BUILD_NUMBER}/g" pubspec.yaml SED "s/^msix_version: .*/msix_version: ${VERSION_ARRAY[0]}.${VERSION_ARRAY[1]}.${VERSION_ARRAY[2]}.0/g" windows/packaging/msix/make_config.yaml SED "s|CURRENT_PROJECT_VERSION = ${cbuild_number}|CURRENT_PROJECT_VERSION = ${BUILD_NUMBER}|g" ios/Runner.xcodeproj/project.pbxproj SED "s/MARKETING_VERSION = ${cstr_version}/MARKETING_VERSION = ${VERSION_STR}/g" ios/Runner.xcodeproj/project.pbxproj git tag ${TAG} > /dev/null gitchangelog > HISTORY.md || { git tag -d ${TAG}; echo "Please run pip install gitchangelog pystache mustache markdown"; exit 2; } git tag -d ${TAG} > /dev/null git add hiddify-core dependencies.properties ios/Runner.xcodeproj/project.pbxproj pubspec.yaml windows/packaging/msix/make_config.yaml HISTORY.md git commit -m "release: version ${TAG}" echo "creating git tag : v${TAG}" git push git tag v${TAG} git push -u origin HEAD --tags echo "Github Actions will detect the new tag and release the new version." ================================================ FILE: .github/dependabot.yml ================================================ # To get started with Dependabot version updates, you'll need to specify which # package ecosystems to update and where the package manifests are located. # Please see the documentation for all configuration options: # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates version: 2 updates: - package-ecosystem: "gitsubmodule" # See documentation for possible values directory: "/" # Location of package manifests schedule: interval: "daily" open-pull-requests-limit: 5 ================================================ FILE: .github/release_message.md ================================================
[![Release Downloads](https://img.shields.io/github/downloads/hiddify/hiddify-next/RELEASE_TAG/total?style=flat-square&logo=github)](https://img.shields.io/github/downloads/hiddify/hiddify-next/RELEASE_TAG/)
**Download based on your OS:**
**بر اساس سیستم عامل خود دانلود کنید:**
OS Download
Android


Windows

macOS (v10.15+)
Linux

**List of all changes:** [ChangeLog](https://github.com/hiddify/hiddify-app/blob/main/HISTORY.md)
================================================ FILE: .github/sync_translate.sh ================================================ # key="FRu3eopQWgsvWmnycBXxv2eWpbUwGOu2" # wget -O ../assets/translations/strings_en.i18n.json "https://localise.biz/api/export/locale/en-US.json?index=id&format=i18next4&key=$key" # wget -O ../assets/translations/strings_fa.i18n.json "https://localise.biz/api/export/locale/fa.json?index=id&format=i18next4&key=$key" # wget -O ../assets/translations/strings_zh.i18n.json "https://localise.biz/api/export/locale/zh.json?index=id&format=i18next4&key=$key" # # # wget -O ../assets/translations/strings_pt.i18n.json "https://localise.biz/api/export/locale/pt.json?index=id&format=i18next4&key=$key" # wget -O ../assets/translations/strings_ru.i18n.json "https://localise.biz/api/export/locale/ru.json?index=id&format=i18next4&key=$key" pip install polib deep-translator python-i18n # python3 auto_translator.py fa en python3 auto_translator.py en fa python3 auto_translator.py en zh-CN # python3 auto_translator.py en pt python3 auto_translator.py en ru python3 auto_translator.py en tr python3 auto_translator.py en es function update_localise(){ lang=$1 pat="../assets/translations/strings_${lang}.i18n.json" # if [[ $lang == 'en' ]];then # pat="../assets/translations/strings.i18n.json" # fi # curl -X POST "https://localise.biz/api/import/json?locale=$lang&key=$LOCALIZ_KEY" \ curl "https://localise.biz/api/import/json?format=i18next4&delete-absent=false&ignore-existing=false&locale=$lang&flag-new=Provisional&key=$LOCALIZ_KEY" \ -H 'Accept: application/json' \ --data-binary @$pat } # update_localise en # update_localise fa # update_localise zh # # # # update_localise pt # update_localise ru ================================================ FILE: .github/workflows/add_signed_microsft.yml ================================================ name: Upload store MSIX to release permissions: contents: write on: release: types: [released] # Run the action when a GitHub release is published # schedule: # - cron: '0 */6 * * *' # Run the action every 6 hours workflow_dispatch: # Manually run the action jobs: upload-store-msix-to-release: runs-on: ubuntu-latest steps: - name: Upload store MSIX to release uses: hiddify/Upload-Microsoft-Store-MSIX-Package-to-GitHub-Release@develop with: store-id: 9pdfnl3qv2s5 token: ${{ secrets.GITHUB_TOKEN }} asset-name-pattern: Hiddify-Windows-Setup-x64 # Optional ================================================ FILE: .github/workflows/build.yml ================================================ name: Build on: workflow_call: inputs: upload-artifact: type: boolean default: true tag-name: type: string default: "draft" channel: type: string default: "dev" env: IS_GITHUB_ACTIONS: 1 CHANNEL: "${{ inputs.channel }}" FLUTTER_VERSION: '3.38.5' NDK_VERSION: r28c UPLOAD_ARTIFACT: "${{ inputs.upload-artifact }}" TAG_NAME: "${{ inputs.tag-name }}" TARGET_NAME_gz: "Hiddify-Linux-x64-AppImage.tar" TARGET_NAME_AppImage: "Hiddify-Linux-x64-AppImage" TARGET_NAME_deb: "Hiddify-Debian-x64" # TARGET_NAME_rpm: "Hiddify-rpm-x64" TARGET_NAME_apk: "Hiddify-Android" TARGET_NAME_aab: "Hiddify-Android" TARGET_NAME_exe: "Hiddify-Windows-Setup-x64" TARGET_NAME_msix: "Hiddify-Windows-x64" TARGET_NAME_zip: "Hiddify-Windows-Portable-x64" TARGET_NAME_dmg: "Hiddify-MacOS" TARGET_NAME_pkg: "Hiddify-MacOS-Installer" TARGET_NAME_ipa: "Hiddify-iOS" jobs: test: outputs: draftBuildCode: ${{ steps.draftBuildCode.outputs.datetime }} runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: subosito/flutter-action@v2.21.0 #issue with 2.13 with: flutter-version: ${{ env.FLUTTER_VERSION }} channel: 'stable' cache: true - name: Prepare run: make linux-amd64-prepare - name: Test run: flutter test - name: make draftBuildCode id: draftBuildCode run: echo "::set-output name=datetime::$(date +'%d.%H.%M')" build: needs: test permissions: write-all continue-on-error: true strategy: fail-fast: false matrix: include: - platform: android-apk os: ubuntu-latest targets: apk - platform: android-aab os: ubuntu-latest targets: aab - platform: windows os: windows-latest aarch: amd64 targets: exe,msix,zip - platform: linux os: ubuntu-22.04 aarch: amd64 targets: deb,gz,AppImage # - platform: linux-amd64 # os: ubuntu-22.04 # aarch: amd64 # targets: deb,gz # - platform: linux-arm64 # os: ubuntu-24.04-arm # aarch: arm64 # targets: deb,gz # - platform: linux-amd64-musl # os: ubuntu-22.04 # aarch: amd64 # targets: deb,gz # - platform: linux-arm64-musl # os: ubuntu-24.04-arm # aarch: arm64 # targets: deb,gz - platform: macos os: macos-15 aarch: universal targets: dmg,pkg # - platform: ios # os: macos-15 # aarch: universal # filename: hiddify-ios # targets: ipa runs-on: ${{ matrix.os }} steps: - name: checkout uses: actions/checkout@v6 - name: Import Apple Codesign Certificates if: ${{ inputs.upload-artifact && startsWith(matrix.os,'macos') }} uses: apple-actions/import-codesign-certs@v6 with: p12-file-base64: "${{ secrets.APPLE_CERTIFICATE_P12 }}" p12-password: "${{ secrets.APPLE_CERTIFICATE_P12_PASSWORD }}" - name: Import Apple Mobile Provisioning Profile if: ${{ inputs.upload-artifact && startsWith(matrix.os,'macos') }} run: | mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles echo "${{secrets.APPLE_MOBILE_PROVISIONING_PROFILES_TARGZ_BASE64}}"|base64 --decode | tar xz -C ~/Library/MobileDevice/Provisioning\ Profiles ls ~/Library/MobileDevice/Provisioning\ Profiles security unlock-keychain -p "" signing_temp.keychain security find-identity -v -p codesigning # ls ~/Library/MobileDevice/Provisioning\ Profiles # echo "${{secrets.NEW_APPLE_MOBILE_PROVISIONING_PROFILES_TARXZ_BASE64}}"|base64 --decode | tar xJ -C ~/Library/MobileDevice/Provisioning\ Profiles # # echo "${{secrets.NEW_APPLE_MOBILE_PROVISIONING_PROFILES_TARGZ_BASE64_2}}"|base64 --decode | tar xz -C ~/Library/MobileDevice/Provisioning\ Profiles # - name: Setup Flutter for arm64 # if: ${{ startsWith(matrix.platform,'linux-arm64') }} # uses: hurelhuyag/flutter-arm64-action@HEAD # with: # channel: 'stable' # flutter-version: ${{ env.FLUTTER_VERSION }} - name: Setup Flutter uses: subosito/flutter-action@v2.21.0 #issue with 2.13 with: flutter-version: ${{ env.FLUTTER_VERSION }} # flutter-version-file: pubspec.yaml channel: 'stable' cache: true - name: Clean up disk space if: startsWith(matrix.platform,'android') run: | df -h sudo rm -rf /usr/share/dotnet df -h sudo rm -rf /opt/ghc df -h sudo rm -rf /opt/hostedtoolcache/CodeQL df -h rm -rf ~/.gradle/caches df -h sudo apt-get autoremove -y df -h sudo apt-get clean df -h tree - name: Setup Java if: startsWith(matrix.platform,'android') uses: actions/setup-java@v5 with: distribution: 'zulu' java-version: 17 # - name: Setup NDK # if: startsWith(matrix.platform,'android') # uses: nttld/setup-ndk@v1 # id: setup-ndk # with: # ndk-version: ${{ env.NDK_VERSION }} # add-to-path: true # link-to-sdk: true - name: Setup Android SDK uses: android-actions/setup-android@v3 if: startsWith(matrix.platform,'android') # - name: Cache Android SDK # uses: actions/cache@v5 # if: startsWith(matrix.platform,'android') # with: # path: | # /usr/local/lib/android/sdk # key: android-sdk-${{ runner.os }}-${{ env.NDK_VERSION }} # restore-keys: | # android-sdk-${{ runner.os }}- - name: Cache Gradle uses: actions/cache@v5 if: startsWith(matrix.platform,'android') with: path: | ~/.gradle/caches ~/.gradle/wrapper key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} restore-keys: | gradle-${{ runner.os }}- - name: Setup dependencies run: | make ${{ matrix.platform }}-install-deps echo "$HOME/.pub-cache/bin" >> $GITHUB_PATH - name: Setup Android Signing Properties if: ${{ inputs.upload-artifact && startsWith(matrix.platform,'android') }} run: | echo "${{ secrets.ANDROID_SIGNING_KEY }}" | base64 --decode > android/key.jks echo "storeFile=$(pwd)/android/key.jks" > android/key.properties echo "storePassword=${{ secrets.ANDROID_SIGNING_STORE_PASSWORD }}" >> android/key.properties echo "keyPassword=${{ secrets.ANDROID_SIGNING_KEY_PASSWORD }}" >> android/key.properties echo "keyAlias=${{ secrets.ANDROID_SIGNING_KEY_ALIAS }}" >> android/key.properties - name: Setup Windows Signing Properties if: ${{ inputs.upload-artifact && startsWith(matrix.platform,'windows') }} run: | [IO.File]::WriteAllBytes("windows\sign.pfx", [Convert]::FromBase64String("${{ secrets.WINDOWS_SIGNING_KEY }}")) (Get-Content "windows\packaging\msix\make_config.yaml") -replace '^certificate_password:.*$', 'certificate_password: ${{ secrets.WINDOWS_SIGNING_PASSWORD }}' | Set-Content "windows\packaging\msix\make_config.yaml" # - name: Temporary disable Permission Handler for windows due to its issue in permission # if: ${{ startsWith(matrix.platform,'windows') }} # run: | # (Get-Content -Path "pubspec.yaml") -notmatch "permission_handler" | Set-Content -Path "pubspec.yaml" # (Get-Content -Path "lib\features\profile\add\add_profile_modal.dart") -notmatch "qr_code_scanner_screen" | Set-Content -Path "lib\features\profile\add\add_profile_modal.dart" # (Get-Content -Path lib\features\profile\add\add_profile_modal.dart) -replace 'await QRCodeScannerScreen\(\).open\(context\);', 'null;' | Set-Content -Path lib\features\profile\add\add_profile_modal.dart # Remove-Item -Path "lib\features\common\qr_code_scanner_screen.dart" - name: Prepare for ${{ matrix.platform }} run: | make ${{ matrix.platform }}-prepare - name: Build ${{ matrix.platform }} env: SENTRY_DSN: ${{ secrets.SENTRY_DSN }} run: | make ${{ matrix.platform }}-release - name: Code Sign if: ${{ inputs.upload-artifact && startsWith(matrix.platform,'windows') }} uses: hiddify/signtool-code-sign-sha256@main with: certificate: '${{ secrets.WINDOWS_SIGNING_KEY }}' cert-password: '${{ secrets.WINDOWS_SIGNING_PASSWORD }}' cert-sha1: '${{ secrets.WINDOWS_SIGNING_SHA1 }}' folder: 'dist' timestamp-server: 'http://timestamp.digicert.com' recursive: true description: 'Hiddify' - name: Copy to out Windows if: matrix.platform == 'windows' shell: pwsh run: | Get-ChildItem -Path dist -Recurse | Select-Object FullName New-Item -ItemType Directory -Path "out" -Force | Out-Null $targets = "${{ matrix.targets }}".Split(',') foreach ($ext in $targets) { $ext = $ext.Trim() $key = "TARGET_NAME_$ext" $targetName = (Get-Item "env:$key").Value $files = Get-ChildItem -Path dist -Recurse -Filter "*.$ext" foreach ($file in $files) { $destination = Join-Path -Path "out" -ChildPath "$targetName.$ext" Move-Item -Path $file.FullName -Destination $destination -Force } } Get-ChildItem -Path out - name: Upload Debug Symbols if: ${{ inputs.upload-artifact && inputs.tag-name != 'draft' }} env: SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} SENTRY_ORG: ${{ secrets.SENTRY_ORG }} SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }} SENTRY_DIST: ${{ matrix.platform == 'android-aab' && 'google-play' || 'general' }} run: | dart pub global activate sentry_dart_plugin dart run sentry_dart_plugin - name: Copy to out Android APK if: matrix.platform == 'android-apk' run: | mkdir out ls -R ./build/app/outputs cp ./build/app/outputs/flutter-apk/*arm64-v8a*.apk out/${TARGET_NAME_apk}-arm64.apk || echo "no arm64 apk" cp ./build/app/outputs/flutter-apk/*armeabi-v7a*.apk out/${TARGET_NAME_apk}-arm7.apk || echo "no arm7 apk" cp ./build/app/outputs/flutter-apk/*x86_64*.apk out/${TARGET_NAME_apk}-x86_64.apk || echo "no x64 apk" cp ./build/app/outputs/flutter-apk/app-release.apk out/${TARGET_NAME_apk}-universal.apk || echo "no universal apk" - name: Copy to out Android AAB if: matrix.platform == 'android-aab' run: | mkdir out ls -R ./build/app/outputs cp ./build/app/outputs/bundle/release/app-release.aab out/hiddify-android-market.aab || echo "no aab" - name: Copy to out unix if: startsWith(matrix.platform,'linux') || matrix.platform == 'macos' || matrix.platform == 'ios' run: | ls -R dist/ mkdir out mkdir tmp_out for EXT in $(echo ${{ matrix.targets }} | tr ',' '\n'); do KEY=TARGET_NAME_${EXT} FILENAME=${!KEY} echo "For $EXT ($KEY) filename is ${FILENAME}" mv dist/*/*.$EXT tmp_out/${FILENAME}.$EXT ls tmp_out [[ "$EXT" != "gz" ]] && chmod +x tmp_out/${FILENAME}.$EXT || echo "Skipping chmod for gz" if [ "${{matrix.platform}}" == "linux" ];then cp ./.github/help/linux/* tmp_out/ else cp ./.github/help/mac-windows/* tmp_out/ fi if [[ "${{matrix.platform}}" == 'ios' ]];then echo mv tmp_out/${FILENAME}.$EXT out/ mv tmp_out/${FILENAME}.$EXT out/ else cd tmp_out # 7z a ${FILENAME}.zip ./ # mv ${FILENAME}.zip ../out/ # [[ $EXT == 'AppImage' ]]&& mv ${FILENAME}.$EXT ../out/ # added for appimage link mv ${FILENAME}.$EXT ../out/ cd .. fi done - name: Upload Artifact if: env.UPLOAD_ARTIFACT == 'true' uses: actions/upload-artifact@v6 with: name: ${{matrix.platform}} path: ./out retention-days: 1 - name: Clean up keychain and provisioning profile if: ${{ always() && startsWith(matrix.os,'macos')}} run: | security delete-keychain $RUNNER_TEMP/app-signing.keychain-db ||echo ok rm ~/Library/MobileDevice/Provisioning\ Profiles/*.mobileprovision ||echo ok update-draft: permissions: write-all if: ${{ inputs.upload-artifact }} needs: [build] runs-on: ubuntu-latest steps: - name: checkout uses: actions/checkout@v6 with: # by default, it uses a depth of 1 # this fetches all history so that we can read each commit fetch-depth: 0 - name: Download Artifact uses: actions/download-artifact@v7 with: merge-multiple: true pattern: "*" path: ./out/ - name: Display Files Structure run: ls -R working-directory: ./out - name: Delete Current Release Assets uses: 8Mi-Tech/delete-release-assets-action@main with: github_token: ${{ secrets.GITHUB_TOKEN }} tag: 'draft' deleteOnlyFromDrafts: false - name: prepare_release_message continue-on-error: true run: | set +e sed 's|RELEASE_TAG|${{ env.TAG_NAME }}|g' ./.github/release_message.md > release.md pip install gitchangelog pystache mustache markdown prelease=$(curl --silent "https://api.github.com/repos/hiddify/hiddify-app/releases/latest" | grep -Po '"tag_name": "\K([^"]*)') echo -e "\n\n
All changes from $current to the latest commit:\n\n">>release.md gitchangelog "${prelease}.." >> release.md 2>&1 || echo "Error in gitchangelog" echo -e "\n\n
">>release.md - name: Create or Update Draft Release uses: softprops/action-gh-release@v2 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: files: ./out/* name: 'draft' tag_name: 'draft' body_path: './release.md' prerelease: true - name: Create or Update Draft Release uses: softprops/action-gh-release@v2 env: GITHUB_TOKEN: ${{ secrets.TOKEN_FOR_HIDDIFY_APP_REPO }} GITHUB_REPOSITORY: hiddify/hiddify-app with: files: ./out/* name: 'draft' tag_name: 'draft' body_path: './release.md' target_commitish: '530bf77839a74d4f13847ba244a20734ac7ab119' prerelease: true repository: hiddify/hiddify-app upload-release: permissions: write-all if: ${{ inputs.upload-artifact && inputs.tag-name != 'draft' }} needs: [build] runs-on: ubuntu-latest steps: - name: checkout uses: actions/checkout@v6 - name: Download Artifact uses: actions/download-artifact@v7 with: merge-multiple: true pattern: "*" path: ./out/ - name: Display Files Structure run: | ls -R ./out ls -R ./.github/ ls -R ./.git/ mv out/hiddify-android-market.aab hiddify-android-market.aab - name: prepare_release_message run: | sed 's|RELEASE_TAG|${{ env.TAG_NAME }}|g' ./.github/release_message.md >> release.md - name: Upload Release uses: softprops/action-gh-release@v2 if: ${{ success() }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: # prerelease: ${{ env.CHANNEL == 'dev' }} prerelease: true tag_name: ${{ env.TAG_NAME }} body_path: './release.md' files: ./out/* - name: Create service_account.json run: echo '${{ secrets.GOOGLE_PLAY_SERVICE_ACCOUNT_JSON }}' > service_account.json - name: Deploy to Google Play Internal Testers uses: r0adkll/upload-google-play@v1 with: serviceAccountJson: service_account.json packageName: app.hiddify.com releaseName: ${{ env.TAG_NAME }} releaseFiles: ./hiddify-android-market.aab track: 'beta' upload-to-testflight: needs: [build] if: ${{ inputs.upload-artifact && inputs.tag-name != 'draft' }} #if: ${{ inputs.upload-artifact }} runs-on: macOS-latest timeout-minutes: 30 steps: - name: Download Artifact uses: actions/download-artifact@v7 with: merge-multiple: true pattern: "*ios*" path: ./out/ - uses: Apple-Actions/import-codesign-certs@v6 with: p12-file-base64: ${{ secrets.APPLE_UPLOAD_CERTIFICATE_P12 }} p12-password: ${{ secrets.APPLE_CERTIFICATE_P12_PASSWORD }} - uses: Apple-Actions/download-provisioning-profiles@v1 with: bundle-id: app.hiddify.com issuer-id: ${{ secrets.APPSTORE_ISSUER_ID }} api-key-id: ${{ secrets.APPSTORE_API_KEY_ID }} api-private-key: ${{ secrets.APPSTORE_API_PRIVATE_KEY }} - uses: Apple-Actions/download-provisioning-profiles@v1 with: bundle-id: app.hiddify.com.SingBoxPacketTunnel issuer-id: ${{ secrets.APPSTORE_ISSUER_ID }} api-key-id: ${{ secrets.APPSTORE_API_KEY_ID }} api-private-key: ${{ secrets.APPSTORE_API_PRIVATE_KEY }} - name: Import Apple Mobile Provisioning Profile run: | mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles echo "${{secrets.APPLE_DIST_PROVISIONING_PROFILES_TARGZ_BASE64}}"|base64 --decode | tar xz -C ~/Library/MobileDevice/Provisioning\ Profiles #echo "${{secrets.NEW_APPLE_STORE_PROVISIONING_PROFILES_TARXZ_BASE64}}"|base64 --decode | tar xJ -C ~/Library/MobileDevice/Provisioning\ Profiles - uses: Apple-Actions/upload-testflight-build@v4 with: app-path: 'out/Hiddify-iOS.ipa' issuer-id: ${{ secrets.APPSTORE_ISSUER_ID }} api-key-id: ${{ secrets.APPSTORE_API_KEY_ID }} api-private-key: ${{ secrets.APPSTORE_API_PRIVATE_KEY }} ================================================ FILE: .github/workflows/ci.yml ================================================ name: CI on: pull_request: paths-ignore: - '**.md' - 'docs/**' - 'test.configs/**' - '.vscode/' - 'appcast.xml' push: branches: - main - dev - android-fix-action-bug - new-design-v2 paths-ignore: - '**.md' - 'docs/**' - '.vscode/' - 'appcast.xml' concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true jobs: run: uses: ./.github/workflows/build.yml secrets: inherit permissions: write-all if: "${{!contains(github.event.head_commit.message, 'release: version')}}" with: upload-artifact: ${{ github.event_name == 'push' }} ================================================ FILE: .github/workflows/release.yml ================================================ name: Release on: push: tags: - 'v[0-9]+.[0-9]+.[0-9]+' - 'v[0-9]+.[0-9]+.[0-9]+.*' concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: build-release: uses: ./.github/workflows/build.yml secrets: inherit permissions: write-all with: upload-artifact: true tag-name: "${{ github.ref_name }}" channel: "${{ github.ref_type == 'tag' && endsWith(github.ref_name, 'dev') && 'dev' || github.ref_type != 'tag' && 'dev' || 'prod' }}" ================================================ FILE: .github/workflows/stale.yml ================================================ name: Mark stale issues and pull requests on: schedule: - cron: "30 8 * * *" jobs: stale: runs-on: ubuntu-latest steps: - uses: actions/stale@v10 with: # stale-issue-message: 'With the release of v4.0.0 (pre-release), most reported issues have been fixed. We’re closing all current issues to start fresh.If you’re still experiencing the problem, please open a new issue and let us know.' days-before-stale: 30 days-before-close: 10 operations-per-run: 60 ================================================ FILE: .github/workflows/winget.yml ================================================ name: Publish to WinGet on: release: types: [released] env: IDENTIFIER: ${{ endsWith(github.event.release.tag_name, 'dev') && 'Hiddify.Next.Beta' || 'Hiddify.Next' }} jobs: publish: runs-on: ubuntu-latest steps: - uses: vedantmgoyal9/winget-releaser@2 with: identifier: ${{ env.IDENTIFIER }} version: ${{ github.event.release.tag_name }} token: ${{ secrets.WINGET_TOKEN }} ================================================ FILE: .gitignore ================================================ # Miscellaneous *.class *.log *.pyc *.swp .sentry-native .DS_Store .atom/ .build/ .buildlog/ .history .svn/ .swiftpm/ migrate_working_dir/ .github/help # IntelliJ related *.iml *.ipr *.iws .idea/ # The .vscode folder contains launch configuration and tasks you configure in # VS Code which you may wish to be included in version control, so this line # is commented out by default. #.vscode/ # Flutter/Dart/Pub related **/doc/api/ **/ios/Flutter/.last_build_id .dart_tool/ .flutter-plugins .flutter-plugins-dependencies .packages .pub-cache/ .pub/ /build/ # generated files **/*.g.dart **/*.freezed.dart **/*.mapper.dart **/*.gen.dart **/*.dll **/*.dylib **/*.xcframework /dist/ /dist_docker/ /assets/core/* !/assets/core/.gitkeep # Symbolication related app.*.symbols # Obfuscation related app.*.map.json # Android Studio will place build artifacts here /android/app/debug /android/app/profile /android/app/release /data .cxx # Windows Self-Signed for msix windows/sign.pfx windows/sign.cer ================================================ FILE: .gitmodules ================================================ [submodule "hiddify-core"] path = hiddify-core url = ssh://git@github.com/hiddify/hiddify-core branch = v3 ================================================ FILE: .metadata ================================================ # This file tracks properties of this Flutter project. # Used by Flutter tool to assess capabilities and perform upgrades etc. # # This file should be version controlled and should not be manually edited. version: revision: "ef1af02aead6fe2414f3aafa5a61087b610e1332" channel: "stable" project_type: app # Tracks metadata for the flutter migrate command migration: platforms: - platform: root create_revision: ef1af02aead6fe2414f3aafa5a61087b610e1332 base_revision: ef1af02aead6fe2414f3aafa5a61087b610e1332 - platform: android create_revision: ef1af02aead6fe2414f3aafa5a61087b610e1332 base_revision: ef1af02aead6fe2414f3aafa5a61087b610e1332 - platform: ios create_revision: ef1af02aead6fe2414f3aafa5a61087b610e1332 base_revision: ef1af02aead6fe2414f3aafa5a61087b610e1332 - platform: linux create_revision: ef1af02aead6fe2414f3aafa5a61087b610e1332 base_revision: ef1af02aead6fe2414f3aafa5a61087b610e1332 - platform: macos create_revision: ef1af02aead6fe2414f3aafa5a61087b610e1332 base_revision: ef1af02aead6fe2414f3aafa5a61087b610e1332 - platform: windows create_revision: ef1af02aead6fe2414f3aafa5a61087b610e1332 base_revision: ef1af02aead6fe2414f3aafa5a61087b610e1332 # User provided section # List of Local paths (relative to this file) that should be # ignored by the migrate tool. # # Files that are not part of the templates will be ignored by default. unmanaged_files: - 'lib/main.dart' - 'ios/Runner.xcodeproj/project.pbxproj' ================================================ FILE: .prettierrc ================================================ { "overrides": [ { "files": ".github/**", "options": { "singleQuote": true } } ] } ================================================ FILE: .release_notes.tpl ================================================ {{#general_title}} # {{{title}}} {{/general_title}} {{#versions}} ## {{{label}}} {{#sections}} #### {{{label}}} {{#commits}} * {{{subject}}} {{#body}} _{{{body}}}_ {{/body}} {{/commits}} {{/sections}} {{/versions}} ================================================ FILE: .stignore ================================================ #include /.stignore #include /android/.stignore #include /ios/.stignore #include /linux/.stignore #include /windows/.stignore #include /macos/.stignore .git .DS_Store .idea .dart_tool .flutter-plugins .flutter-plugins-dependencies .packages .pub-cache .pub build *.log *.iml *.ipr *.iws **/ios/Flutter/.last_build_id /android/app/debug /android/app/profile /android/app/release ================================================ FILE: .vscode/extensions.json ================================================ { "recommendations": [ "dart-code.dart-code", "dart-code.flutter", "github.vscode-github-actions", "golang.go", "redhat.vscode-yaml", "codeium.codeium", "kangping.protobuf" ] } ================================================ FILE: .vscode/launch.json ================================================ { "version": "0.2.0", "configurations": [ { "name": "go Package", "type": "go", "request": "launch", "mode": "auto", "cwd": "./hiddify-core", "program": "./hiddify-core/cmd/main", // "args": ["build","-c","a.txt","-d","b.txt","--full-config"] , // "args": ["temp"] , // "args":["profile","https://raw.githubusercontent.com/hiddify/hiddify-next/refs/heads/main/test.configs/warp"], "args": [ "run", "-c", "https://raw.githubusercontent.com/hiddify/hiddify-next/refs/heads/main/test.configs/warp" ], "buildFlags": "-tags with_clash_api,with_gvisor,with_quic,with_wireguard,with_grpc,with_ech,with_utls,with_reality_server" }, { "name": "Hiddify Dev", "request": "launch", "type": "dart", "flutterMode": "debug", "program": "lib/main.dart", // "args": ["-d","192.168.1.35:36463"] }, { "name": "Hiddify Dev Windows Portable", "request": "launch", "type": "dart", "flutterMode": "debug", "program": "lib/main.dart", "args": [ "-d", "windows", "--dart-define=portable=true" ] }, { "name": "Hiddify Dev Release", "request": "launch", "type": "dart", "flutterMode": "release", "program": "lib/main.dart", }, { "name": "Hiddify Dev Profile", "request": "launch", "type": "dart", "flutterMode": "profile", "program": "lib/main.dart", }, { "name": "Hiddify Prod", "request": "launch", "type": "dart", "flutterMode": "release", "program": "lib/main_prod.dart", }, { "name": "Attach: Design for iPad", "request": "attach", "type": "dart", "program": "lib/main.dart", "args": [ "--app-id", "app.hiddify.com" ], "deviceId": "mac-designed-for-ipad", } ] } ================================================ FILE: .vscode/settings.json ================================================ { "[dart]": { "editor.defaultFormatter": "Dart-Code.dart-code", "editor.formatOnSave": true, "editor.formatOnType": true, "editor.tabSize": 2, "editor.detectIndentation": false, "editor.selectionHighlight": false, "editor.suggest.snippetsPreventQuickSuggestions": false, "editor.suggestSelection": "first", "editor.tabCompletion": "onlySnippets", "editor.wordBasedSuggestions": "off" }, "html.format.wrapLineLength": 250 } ================================================ FILE: CHANGELOG.md ================================================ # Changelog ## [0.16.0.dev] - 2024-2-18 ### New Features and Improvements - Changed App name to **Hiddify** - Changed App icon - Added Mux (**Experimental**) - Added Cloudflare WARP (**Experimental**) - Added connection info - when connected, name of the active node, speed and IP address are shown on home page - delay indicator below connection button shows active node's ping - Added VPN Service (Windows & Linux) (**Experimental**) - VPN Service circumvents need for administrator permission while using TUN - Changed in-app icons (using [Fluent UI System Icons](https://github.com/microsoft/fluentui-system-icons)) - Redesigned navigation flow, separating config options - Added haptic feedback - Added detailed subscription info in profile edit page - Added Chinese Taiwan language. [PR#410](https://github.com/hiddify/hiddify-next/pull/410) by [junlin03](https://github.com/junlin03) and [PR#491](https://github.com/hiddify/hiddify-next/pull/491) by [kouhe3](https://github.com/kouhe3) - Added Japanese Readme. [PR#371](https://github.com/hiddify/hiddify-next/pull/371) by [Ikko Eltociear Ashimine](https://github.com/eltociear) ### Bug Fixes - Fixed TLS Tricks bugs - Fixed logs on iOS. [PR#414](https://github.com/hiddify/hiddify-next/pull/414) by [Amir Mohammadi](https://github.com/amirsaam) and [PR#416](https://github.com/hiddify/hiddify-next/pull/416) by [Ebrahim Tahernejad](https://github.com/EbrahimTahernejad) - Fixed Android service mode - Fixed UI inconsistencies - Fixed Readme download URL. [PR#482](https://github.com/hiddify/hiddify-next/pull/482) by [Ali Afsharzadeh](https://github.com/guoard) ## [0.14.1.dev] - 2024-1-19 ### New Features and Improvements - Redesigned profile options on mobile - Improved configuration parser - Added export config json in iOS - Added iOS URL scheme. [PR#343](https://github.com/hiddify/hiddify-next/pull/343) by [Amir Mohammadi](https://github.com/amirsaam) - Added option to reset VPN profile on iOS ### Bug Fixes - Fixed TLS Tricks causing app crash - Fixed connection status on iOS app relaunch - Fixed iOS connection stats - Fixed infinite subscription traffic - Fixed infinite subscription expiry. [PR#334](https://github.com/hiddify/hiddify-next/pull/334) by [Pavel Volkov](https://github.com/pvolkov) ## [0.14.0.dev] - 2024-1-14 ### New Features and Improvements - Published initial iOS beta version on TestFlight - Thanks to contributions from [GFWFighter](https://github.com/GFWFighter) and [Amir Mohammadi](https://github.com/amirsaam) - iOS version is still in heavy development phase and there are known bugs - Added Spanish language. [PR#314](https://github.com/hiddify/hiddify-next/pull/314) by [AvatarStark](https://github.com/AvatarStark) - Changed Routing Assets page layout, separating assets by type - Improved descriptions for some of the options in settings page ### Bug Fixes - Fixed Deep links on Windows - Fixed minor UI bugs - Fixed subscription profiles with infinite traffic ## [0.13.6] - 2024-1-7 - First stable 0.13.x release. check changes from 0.13.0.dev to 0.13.5.dev for more details. ## [0.13.5.dev] - 2024-1-6 ### New Features and Improvements - Updated sing-box to version 1.7.8 - Improved TLS Fragmentation. [PR#12](https://github.com/hiddify/hiddify-sing-box/pull/12) by [Kyōchikutō | キョウチクトウ](https://github.com/kyochikuto) - Improved v2ray config parser - Added cancel button on new profile modal - Changed default Connection Test URL ### Bug Fixes - Fixed Android service mode - Fixed QR code scanner not scanning deep links ## [0.13.4.dev] - 2024-1-4 ### New Features and Improvements - Added update all subscriptions - Force update all subscription profiles regardless of their interval - Added basic authorization support - Changed app http client, improving experience when fetching profiles, geo assets etc. ### Bug Fixes - Fixed profile auto update service - Fixed localization mistakes in Chinese. [PR#288](https://github.com/hiddify/hiddify-next/pull/288) by [wldjdjsks](https://github.com/huajizhige) ## [0.13.3.dev] - 2024-1-2 ### New Features and Improvements - Added Bypass LAN option (Experimental) - Added Connection from LAN option (Experimental) - Added DNS Routing option - Changed outbound options section to TLS Tricks ### Bug Fixes - Fixed profile edit bug where you were unable to change existing profile's URL - Fixed localization mistakes in Chinese. [PR#287](https://github.com/hiddify/hiddify-next/pull/287) by [Wu Jiahao](https://github.com/wujiahao15) ## [0.13.2.dev] - 2023-12-31 ### Bug Fixes - Fixed db migration bug ## [0.13.1.dev] - 2023-12-31 ### New Features and Improvements - Added experimental feature flag in settings - Added notice dialog when connecting with experimental features ### Bug Fixes - Fixed multiple instance launch on windows - Removed auto connect on desktop which caused bugs on auto launch etc. - Fixed inlang localization setup ## [0.13.0.dev] - 2023-12-29 ### New Features and Improvements - Added desktop shortcuts - Add profile from clipboard by pressing `CTRL+V` (`CMD+V` on macOS) - Close App window by pressing `CTRL+W` (`CMD+W` on macOS) - Quit App by pressing `CTRL+Q` (`CMD+Q` on macOS) - Open settings page by pressing `CMD+,` on macOS - Added Android high refresh rate screen support ### Bug Fixes - Fixed silent start bug where screen would blink - Refactored Window management and system tray, fixing minor bugs - Fixed windows portable release again! ## [0.12.3] - 2023-12-28 ### New Features and Improvements - Added version number in window title on desktop - Added Afghanistan (af) region with default bypass rules ### Bug Fixes - Fixed modal bug where config options were unmodifiable. [PR#267](https://github.com/hiddify/hiddify-next/pull/267) by [在7楼](https://github.com/RayWangQvQ) - Fixed windows portable release ## [0.12.2] - 2023-12-23 ### New Features and Improvements - Updated Sing-box to Version 1.7.6 ### Bug Fixes - Fixed app log file not including stacktrace - Fixed initialization process failing for non-essential dependencies - Fixed analytics preferences requiring app restart ## [0.12.1] - 2023-12-21 ### Bug Fixes - Fixed Android service mode - Fixed [preferences initialization error on Windows and Linux](https://github.com/flutter/flutter/issues/89211) - Fixed incorrect privacy policy URL - Bumped Android compile and target SDK version (34) ## [0.12.0] - 2023-12-20 ### New Features and Improvements - Added TLS Tricks (experimental) - Including TLS fragments and Mixed SNI case. This feature might effect performance and battery life - Added dynamic notification on Android - Active profile name and transfer speed are now shown in notification - Added basic D-pad support for Android TV - Added soffchen to recommended geo assets - Added option to reset Config Options - Improved text input field's accessibility and traversal ### Bug Fixes - Refactored significant portions of the app - Fixed incorrect profile parsing when missing headers - Fixed geo assets bug where assets were deactivated - Changed default memory limit option on desktop, fixing out of memory bug on macOS - Fixed macOS icon - Fixed system tray behavior - Fixed incorrect casing of locale names - Updated sing-box to version 1.7.0 - Fixed Chinese typography bug (thanks to [betaxab](https://github.com/betaxab)) - Fixed localization mistakes in Russian. [PR#189](https://github.com/hiddify/hiddify-next/pull/189) by [jomertix](https://github.com/jomertix) ## [0.11.1] - 2023-11-19 ### Bug Fixes - Fixed Android manifest bug. ## [0.11.0] - 2023-11-19 ### New Features and Improvements - Changed Responsive UI Behavior - Now app is responsive on all platforms with appropriate routing setup. - Added Simplified Service Modes - Choose between VPN(Tun), System Proxy and Proxy only modes. (System Proxy available on desktop) - Added Share Functionality - Share configuration as json(export to clipboard) or share subscription link as QR code. - Redesigned System Tray on Desktop - Options have been simplified and a new mode selector and navigation options are added. - Added Privilege Checks for VPN(TUN) on Desktop - Added Auto Connect on Start - On desktop, app will try to connect to the last used profile on startup. (if last session was not explicitly disconnected by the user) - Added AppCast Update Checker - Checking for new versions of the app will use a more reliable approach on all platforms. - Added Geo Asset Settings - Update geo assets and use recommended providers - Added **winget** Release - Now you're able to install and update Hiddify on Windows using [winget](https://learn.microsoft.com/en-us/windows/package-manager/winget/). - Added Turkish Translations. [PR#173](https://github.com/hiddify/hiddify-next/pull/173) by [Hasan Karlı](https://github.com/hasankarli) - Changed in-app Toasts - Updated Core Sing-box Version to 1.7.0 - Improved Network Reliability While Adding/Updating Subscriptions - Improved QR Code Scanner ### Bug Fixes - Removed **execute config as is** option which caused crashes and confusion for users. - Fixed android service revoke and restart. - Fixed github release update checker. - Fixed translator script. [PR#108](https://github.com/hiddify/hiddify-next/pull/108) by [Hirad Rasoolinejad](https://github.com/Hiiirad) - Fixed localization mistakes in Chinese. [PR#113](https://github.com/hiddify/hiddify-next/pull/113) and [PR#123](https://github.com/hiddify/hiddify-next/pull/123) by [Nyar233](https://github.com/Nyar233) - Fixed localization mistakes in Chinese Readme. [PR#137](https://github.com/hiddify/hiddify-next/pull/137) by [wldjdjsks](https://github.com/huajizhige) - Fixed localization mistakes in Chinese. [PR#138](https://github.com/hiddify/hiddify-next/pull/138) and [PR#165](https://github.com/hiddify/hiddify-next/pull/165) by [wldjdjsks](https://github.com/huajizhige) - Fixed localization mistakes in Russian. [PR#155](https://github.com/hiddify/hiddify-next/pull/155), [PR#162](https://github.com/hiddify/hiddify-next/pull/162) and [PR#169](https://github.com/hiddify/hiddify-next/pull/169) by [solokot](https://github.com/solokot) - Fixed linux build libs command. [PR#161](https://github.com/hiddify/hiddify-next/pull/161) by [Aloxaf](https://github.com/Aloxaf) - Fixed localization mistakes in Russian. [PR#164](https://github.com/hiddify/hiddify-next/pull/164) and [PR#168](https://github.com/hiddify/hiddify-next/pull/168) by [jomertix](https://github.com/jomertix) - Fixed localization mistakes in Chinese. [PR#179](https://github.com/hiddify/hiddify-next/pull/179) by [betaxab](https://github.com/betaxab) - Fixed localization mistakes in Chinese Readme. [PR#172](https://github.com/hiddify/hiddify-next/pull/172) by [Locas](https://github.com/Locas56227) ## [0.10.0] - 2023-10-27 ### New Features and Improvements - Added Basic region-based routing rules - Based on your selected region (Iran, China or Russia), local ip and domains are bypassed. - Redesigned Logs page - Now you're able to pause stream and clear logs. Also logs are delivered more consistently, with less resource consumption. - Added tag of selected outbound of selectors to proxies page - Selected outbound tag of selectors like URLTests are now shown in other selectors as well. - Added color to delay number in proxies page - Memory limit option - Limit sing-box core memory usage. - Revamped theme preferences settings - Added initial iOS implementation. [PR#98](https://github.com/hiddify/hiddify-next/pull/98) by [GFWFighter](https://github.com/GFWFighter) - Added Russian region - Added Terms and Conditions and Privacy policy to about page ### Bug Fixes - Removed reconnection on auto profile updates - Fixed filtering logs by level - Fixed localization mistakes in Russian. [PR#95](https://github.com/hiddify/hiddify-next/pull/95) by [solokot](https://github.com/solokot) - Fixed localization mistakes in Russian. [PR#74](https://github.com/hiddify/hiddify-next/pull/74) by [Elshad Guseynov](https://github.com/lifeindarkside) [0.16.0.dev]: https://github.com/hiddify/hiddify-next/releases/tag/v0.16.0.dev [0.14.1.dev]: https://github.com/hiddify/hiddify-next/releases/tag/v0.14.1.dev [0.14.0.dev]: https://github.com/hiddify/hiddify-next/releases/tag/v0.14.0.dev [0.13.6]: https://github.com/hiddify/hiddify-next/releases/tag/v0.13.6 [0.13.5.dev]: https://github.com/hiddify/hiddify-next/releases/tag/v0.13.5.dev [0.13.4.dev]: https://github.com/hiddify/hiddify-next/releases/tag/v0.13.4.dev [0.13.3.dev]: https://github.com/hiddify/hiddify-next/releases/tag/v0.13.3.dev [0.13.2.dev]: https://github.com/hiddify/hiddify-next/releases/tag/v0.13.2.dev [0.13.1.dev]: https://github.com/hiddify/hiddify-next/releases/tag/v0.13.1.dev [0.13.0.dev]: https://github.com/hiddify/hiddify-next/releases/tag/v0.13.0.dev [0.12.3]: https://github.com/hiddify/hiddify-next/releases/tag/v0.12.3 [0.12.2]: https://github.com/hiddify/hiddify-next/releases/tag/v0.12.2 [0.12.1]: https://github.com/hiddify/hiddify-next/releases/tag/v0.12.1 [0.12.0]: https://github.com/hiddify/hiddify-next/releases/tag/v0.12.0 [0.11.1]: https://github.com/hiddify/hiddify-next/releases/tag/v0.11.1 [0.11.0]: https://github.com/hiddify/hiddify-next/releases/tag/v0.11.0 [0.10.0]: https://github.com/hiddify/hiddify-next/releases/tag/v0.10.0 ================================================ FILE: CODE_OF_CONDUCT.md ================================================ # Contributor Covenant Code of Conduct ## Our Pledge We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. ## Our Standards Examples of behavior that contributes to a positive environment for our community include: * Demonstrating empathy and kindness toward other people * Being respectful of differing opinions, viewpoints, and experiences * Giving and gracefully accepting constructive feedback * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience * Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: * The use of sexualized language or imagery, and sexual attention or advances of any kind * Trolling, insulting or derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or email address, without their explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. ## Scope This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at https://t.me/hiddify. All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the reporter of any incident. ## Enforcement Guidelines Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: ### 1. Correction **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. ### 2. Warning **Community Impact**: A violation through a single incident or series of actions. **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. ### 3. Temporary Ban **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. ### 4. Permanent Ban **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. **Consequence**: A permanent ban from any sort of public interaction within the community. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0, available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity). [homepage]: https://www.contributor-covenant.org For answers to common questions about this code of conduct, see the FAQ at https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations. ================================================ FILE: CONTRIBUTING.md ================================================ # Contributing Every contribution to HiddifyApp is welcome, whether it is reporting a bug, submitting a fix, proposing new features, or just asking a question. To make contributing to HiddifyApp as easy as possible, you will find more details for the development flow in this documentation. [Basic tutorial on how to contribute to HiddifyApp](https://hiddify.com/app/How-to-contribute-to-this-project/) Please note, we have a [Code of Conduct](https://github.com/hiddify/hiddify-app/blob/main/CODE_OF_CONDUCT.md), please follow it in all your interactions with the project. - [Feedback, Issues and Questions](#feedback-issues-and-questions) - [Adding new Features](#adding-new-features) - [Development](#development) - [Working with the Go Code](#working-with-the-go-code) - [Working with the Flutter Code](#working-with-the-flutter-code) - [Setting up the Environment](#setting-up-the-environment) - [Run Release Build on a Device](#run-release-build-on-a-device) - [Release](#release) - [Collaboration and Contact Information](#collaboration-and-contact-information) ## Feedback, Issues and Questions If you encounter any issue, or you have an idea to improve, please: - Search through [existing open and closed GitHub Issues](https://github.com/hiddify/hiddify-app/issues) for the answer first. If you find a relevant topic, please comment on the issue. - If none of the issues are relevant, please add a new [issue](https://github.com/hiddify/hiddify-app/issues/new/choose) following the templates and provide as much relevant information as possible. ## Adding new Features When contributing a complex change to the Hiddify repository, please discuss the change you wish to make within a GitHub issue with the owners of this repository before making the change. ## Development ### Adding Feature / Fix bug in Core: Please follow our [Go Core Development repository](https://github.com/hiddify/hiddify-next-core/main/CONTRIBUTING.m). ### Working with the Flutter Code Hiddify uses [Flutter](https://flutter.dev), make sure that you have the correct version installed before starting development. You can use the following commands to check your installed version: ```shell $ flutter --version # example response Flutter 3.13.4 • channel stable • https://github.com/flutter/flutter.git Framework • revision 367f9ea16b (4 weeks ago) • 2023-09-12 23:27:53 -0500 Engine • revision 9064459a8b Tools • Dart 3.1.2 • DevTools 2.25.0 ``` We recommend using [Visual Studio Code](https://docs.flutter.dev/development/tools/vs-code) extensions for development. #### Setting up the Environment We have extensive use of code generation in the form of [freezed](https://github.com/rrousselGit/freezed), [riverpod](https://github.com/rrousselGit/riverpod), etc. So it's generate these before running the code. Execute the following make commands in order: Assuming you have not built the `hiddify-core` and want to use [existing releases](https://github.com/hiddify/hiddify-next-core/releases), you should run the following command (based on your target platform): - `make windows-prepare` - `make linux-prepare` - `make macos-prepare` - `make ios-prepare` - `make android-prepare` ##### build the `hiddify-core` from source (Optional) If you want to build the `hiddify-core` from source after `make prepare`, use: - `make build-windows-libs` - `make build-linux-libs` - `make build-macos-libs` - `make build-ios-libs` - `make build-android-libs` #### Run Release Build on a Device To run the release build on a device for testing, we have to get the Device ID first by running the following command: ```shell $ flutter devices # example response 3 connected devices: 2211143G (mobile) • 35492ae2 • android-arm64 • Android 13 (API 33) Windows (desktop) • windows • windows-x64 • Microsoft Windows [Version 10.0.22000.2482] Chrome (web) • chrome • web-javascript • Google Chrome 117.0.5938.149 ``` Then we can use one of the listed devices and execute the following command to build and run the app on this device: ```shell flutter run # or flutter run --device-id=35492ae2 ``` ## Release We use [flutter_distributor](https://github.com/leanflutter/flutter_distributor) for packaging. [GitHub action](https://github.com/hiddify/hiddify-app/blob/main/.github/workflows/build.yml) is triggered on every release tag and will create a new GitHub release. After setting up the environment, use the following make commands to build the release version: - `make windows-release` - `make linux-release` - `make macos-release` - `make android-release` - `make ios-release` ## Collaboration and Contact Information We need your collaboration in order to develop this project. If you have experience in these areas, please do not hesitate to contact us. - Flutter Developing - Swift Developing - Go Developing

[![Email](https://img.shields.io/badge/Email-contribute@hiddify.com-005FF9?style=flat-square&logo=mail.ru)](mailto:contribute@hiddify.com) [![Telegram Channel](https://img.shields.io/endpoint?label=Channel&style=flat-square&url=https%3A%2F%2Ftg.sumanjay.workers.dev%2Fhiddify&color=blue)](https://telegram.dog/hiddify) [![Telegram Group](https://img.shields.io/endpoint?color=neon&label=Support%20Group&style=flat-square&url=https%3A%2F%2Ftg.sumanjay.workers.dev%2Fhiddify_board)](https://telegram.dog/hiddify_board) [![Youtube](https://img.shields.io/youtube/channel/views/UCxrmeMvVryNfB4XL35lXQNg?label=Youtube&style=flat-square&logo=youtube)](https://www.youtube.com/@hiddify) [![Twitter](https://img.shields.io/twitter/follow/hiddify_com?color=%231DA1F2&logo=twitter&logoColor=1DA1F2&style=flat-square)](https://twitter.com/intent/follow?screen_name=hiddify_com)
================================================ FILE: Dockerfile ================================================ # ============================================================== # ⚠️ USAGE: TRIGGERED BY MAKEFILE # This file is executed via the 'linux-release-docker' command. # ============================================================== FROM ubuntu:22.04 ENV DEBIAN_FRONTEND=noninteractive COPY linux_deps.list /tmp/linux_deps.list RUN apt-get update && \ DEPS=$(cat /tmp/linux_deps.list | tr -d '\r' | sed 's/#.*//g' | xargs) && \ \ apt-get install -y --no-install-recommends $DEPS && \ \ rm -rf /var/lib/apt/lists/* && \ echo "root ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers RUN wget -O /usr/local/bin/appimagetool "https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage" && \ chmod +x /usr/local/bin/appimagetool RUN mkdir -p /root/develop && \ git clone https://github.com/flutter/flutter.git -b stable /root/develop/flutter ENV PATH="/root/develop/flutter/bin:/root/.pub-cache/bin:${PATH}" RUN git config --global --add safe.directory /root/develop/flutter && \ flutter config --no-analytics && \ dart pub global activate fastforge ENV APPIMAGE_EXTRACT_AND_RUN=1 WORKDIR /app ================================================ FILE: HISTORY.md ================================================ # Changelog ## 4.1.2 (2026-03-05) #### New * Add doc for dnstt. ## v4.1.0 (2026-03-05) #### New * Add dnstt. * Add dnstt. #### Fix * Test. * Set ir flag to shir. * Update linux-appimage-release to copy instead of move Hiddify.AppImage. * Update linux-prepare target to use linux-amd64-libs. * Delete bundled libstdc++ for Arch Linux compatibility in AppImage build. * Resolve shared_preferences conflict between portable and exe formats on Windows. * Remove timezone_to_country package. _This package caused a black screen and prevented the app from running on the Windows (old) platform._ * Install fastforge and update PATH for Windows build. * Disable mux and remove geo-related logic. #### Other * Merge pull request #1995 from veto9292/main. _black screen and running issue on Windows | shared_preferences conflict between portable and exe on Windows | AppImage running on arch etc… | linux build in ci | add .AppImage release next to the tar | replace ir shir flag | add background for dmg format_ * Feat: replate dmg background image and & improve size and position. * Test ci. * Feat: removing country flag from regions text. * Feat: add raw AppImage support for linux. * Feat: implement region detection based on timezone and locale. * Update README_ru.md. * Update README_cn.md. * Update README_ja.md. * Update README_br.md. * Update README_fa.md. * Update README.md. * Centralize the star image. _Added donation support section with links and details._ * Update README_ru.md. * Update README_cn.md. * Update star history chart in README_ja.md. * Update README_br.md. * Fix star history image. * Update README_ru.md. * Update README_cn.md. * Update donation link in README_cn.md. * Update README_ja.md. * Update README_br.md. * Fix donation link and star history chart source. _Updated the donation link and corrected the star history chart image source._ * Update README.md. * Update qrcode reader lib to support 16kb page. * Refactor: update project configuration and clean up unused code. * Merge pull request #1929 from ChabanovX/add-column-exists-check. _Add column exists check_ * Add test in migration that column check works properly. * Add column checks on 4to5 migration. * Rename column check for more explicit one. * Add `column_check` method inside db. * Merge pull request #1986 from salmanmkc/upgrade-github-actions-node24-general. _Upgrade GitHub Actions to latest versions_ * Merge branch 'main' into upgrade-github-actions-node24-general. * Merge pull request #1985 from salmanmkc/upgrade-github-actions-node24. _Upgrade GitHub Actions for Node 24 compatibility_ * Upgrade GitHub Actions for Node 24 compatibility. * Merge pull request #1959 from Enqvy/main. _added tls fragmenting packet option_ * Added manual build (dont meant to be merged) * Feat: add manual Windows build workflow. * Added tls fragmenting packet option. * Merge pull request #1978 from veto9292/sync-slang. _balancer-strategy | remove-mux-geo | sync-slang_ * Feat: update DNS translations for multiple languages. * Feat: add "enableFakeDns" translation for multiple languages. * Feat: add "redirectPort" translations for multiple languages. * Feat: add "directPort" translation to ar. * Feat: implement balancer strategy configuration and UI integration. * Feat: add balancer strategy translations for multiple languages. * Upgrade GitHub Actions to latest versions. ## v4.0.5 (2026-02-20) #### Other * Better support for events. ## v4.0.4 (2026-02-19) #### New * Add endpoint to json editor, * Add more inbounds(TPROXY, REDIRECT DIRECT) disable pause on desktop. * Add ios limits and fix workflow. * Better connection management. * Add up/down and active profile in android notification. * Add HiddifyRPC in android. * Add extra security mode. * Add fragment button. * Add remote config support. * Add better platform management. * Update the ios interface. * Add some more checks. * Add real active tag. * Upgrade flutter and libs. * Add quick setting, organize settings, fix reconnect bug, remove profile tab in mobile, use hiddifynextx forfetch profile in xray mode, update flutter. * Refactor configs to better visualisation. * Update ci. * Upgrade dependencies. * Redesign core interface. * Add detailed ipinfo. * Redisgn ip panel. * Add auto start for mac. * Add support for auto start in macos. * Add org flag. #### Changes * Remove platform widget. * Disable HTTP subscribe link as they create great issue. * Some refactor. #### Fix * Issue in profile editor, better error display. * Issues. * Update build matrix to include Android and Windows targets. * Version issue. * Update step name for Flutter setup in build workflow. * Correct syntax for conditional in Flutter setup for arm64. * Update Flutter setup condition for non-linux-arm64 platforms. * Bug. * Prepare. * Remove unused variables for zip file path in windows release process. * Update app icon type to use stock icon for consistency. * Update AppImage build command to disable appstream and correct file move. * Downgrade `sentry_flutter` to remove jni dependency (avoids forced JDK installation) * Update AppImage post-processing steps and improve Docker volume mounts. * Update build targets for Android to include AAB format and fix windows-install-deps. * Sticky notif. * Update macOS version from 13 to 15 in build workflow. * Replace flutter_distributor with fastforge for macOS and iOS packaging. * Improve sh compatibility. * Change variable assignment from := to = for consistency in Makefile. * Update sed command definition for Windows compatibility. * Workflow. * Workflow. * Persistent data after uninstalling Windows EXE. * Issue in ios and imrpove stablity. * Android bug. * Notification AND VPN MODE in android. * Font json editor. * Ios. * Per app proxy. * Tile service. * Windows build issue. * Typo. * Bugs and improvement. * Select tag bug. * Makefile path issue in Windows OS. * Linux installation. * Ios build. * Bug. * Interface change and warp crash. * Quicktile bug and update to singbox 10.3. * Bug? * Macos. * Macos build. * Emoji in profile and proxies. * No commit message. * Ios. * Ios interface. * Name changing. * Typos for ios. #### Other * Merge branch 'new-design-v2' * Fix arm build. * Refactor: update InboundOptions fields and translations for direct and redirect ports. * Add naive. * Fix. * Update. * Merge pull request #36 from veto9292/new-design-v2. _CI_ * Feat: add supported MIME types for app links on Linux AppImage. * Refactor: rename macOS and iOS install dependencies targets and update activation command. * Speedup ci. * Merge. * Feat: enhance logging in Makefile with color-coded output. * Feat: add post-processing for Windows portable zip release. * Fix CI: "copy to out" for "windows" * Fix CI: Copy to out Windows. * Fix CI: skipping chmod for tar. * Fix CI: NDK version. * Fix CI: linux AppImage build. * Fix CI: linxu AppImage build. * If: REQUIRED_VER in Makefile. * Update: hiddify-core.diff. * Generate: proto files. * Add protoc_plugin activation to linux-install-deps. * Fix CI "Build platform" * Fix CI "Setup dependencies" * Fix connection change issue and more and more optimisation. * Update psiphon add new dns manager, fix ios ipv6 issue. * Add proxy usage bytes, psiphon and more and more. * New balancer approach. * Better proxy,fix ios. * New add amnezia, wiregaurd raw config, wiregaurd noise. * Add inner proxy link, add debounce. * Revert makefile. * Fixes and improvements. * Update ios. * Better android vpn service start and fix bugs. * Update android codes to latest singbox. * Update macos. * Update fultter. * Merge. * Merge pull request #34 from veto9292/small-fix/fastforge-apk-release. _fastforge migration (android, windows, linux) | storage and path | format codebase | upgrade flutter | flutter discontinued package | dart style | restore window state on Desktops | fastforge release-apk (small fix)_ * Merge remote-tracking branch 'h/new-design-v2' into small-fix/fastforge-apk-release. * Remove forgotten test parameter. * Implement window state persistence and position validation - Added `saveWindowState` to persist window size, position, and maximized state. - Added `initWindowState` to restore window bounds with safety checks. - Implemented `checkWindowVisibility` to prevent off-screen startup in multi-monitor setups. * Calling saveWindowState in onWindow (Resized, Moved, Maximize, Unmaximize) methods. * Add window (position, size, maximize) preferences to general_preferences. * Relocate 'window silent start' logic to window_notifier.dart. * Installing 'screen_retriever' package. * Format .dart files. * Fix dart formatter width. * Replace flutter_markdown with flutter_markdown_plus and update version in pubspec.yaml; adjust pubspec.lock accordingly. * Upgrade Flutter and SDK versions; update NDK and Kotlin plugin versions; refresh package dependencies in pubspec.lock. * Added 'Hiddify Dev Windows Portable' config to launch.json for easier testing and development of the Windows Portable version. * Migrate release builds to fastforge and overhaul Linux/Docker systems. _This major update migrates all release pipelines (except macOS/iOS) to fastforge and completely overhauls the Linux build process. ### Migration to fastforge - Migrated all release commands to fastforge for consistency and better management. - Replaced legacy build scripts with fastforge commands for Windows, Linux, and Android. - Android APK/AAB builds now utilize fastforge instead of raw commands. ### Linux Build Refactor - Introduced `linux-release-docker` command. - Updated `linux-release-appimage` with new post-build scripts. - Implemented `Linux Install Deps` script: - Automatically prepares the environment (Ubuntu/WSL). - Clones Flutter SDK from GitHub (stable branch) instead of downloading the archive. - Checks out the specific Flutter version defined in `pubspec.yaml` using `Linux Flutter Sync`. - Installs required dependencies from `linux_deps.list`. - Rewrote AppImage packaging: - Manually constructs the AppImage directory structure. - Injects a custom `AppRun` script. - Implements a self-contained portable data structure: - Creates `hiddify.appimage.home` directory alongside the AppImage. - Stores configuration and user data within this directory to keep the system clean. ### Dockerized Build System - Added `linux-release-docker` for building Linux artifacts without a local Linux environment. - Caches Flutter SDK and Pub packages via Docker volumes to speed up subsequent builds. - Outputs artifacts to `dist_docker` directory on the host machine. ### Windows Improvements - Updated `windows-release-zip` to include the `portable` environment variable for portable builds. ### Other Changes - Added `linux_deps.list` to centralize dependency management. - Added `linux-flutter-sync` to `linux-prepare` command. - Introduced `LINUX_DEPS` variable to extract package list from `linux_deps.list`. - Introduced `REQUIRED_VER` variable to extract Flutter version from `pubspec.yaml`. - Added `linux-flutter-sync` command to ensure the installed Flutter version matches `pubspec.yaml`._ * - Update getDatabaseDirectory to support portable mode based on the `` env var. - Create 'hiddify_portable_data' in the app directory if running in portable mode. - Add 'checkDirectoryAccess' to verify read/write permissions for the portable data folder. - Fallback to the default system path if write access to the portable directory is denied. * - Add environment variable to identify the Windows portable version - This variable is set during the 'windows-release-zip' build process. * On Linux, left-clicking the tray icon opens the context menu instead of triggering `onTrayIconMouseDown`. Added a "Dashboard" menu item as a workaround to allow users to open the app window. * - Ignore MSIX self-signed keys to prevent accidental commit of secrets - Add dist_docker directory (Docker build output) to ignored paths. * - Enable Linux artifact building without requiring a local Linux environment (e.g., WSL or native Ubuntu) - Designed to be executed via the 'linux-release-docker' command in the Makefile. * Add some comments. * Pin Flutter SDK version and register new icon asset - Explicitly define Flutter SDK version in pubspec.yaml for consistency. - Note: This version is critical as it is parsed by Makefile and Dockerfile to setup the build environment. - Add 'ic_launcher_border.png' to the assets list. * Created a dedicated list file for Linux dependencies and integrated it into both Dockerfile and Makefile. * Switched to C++17 to ensure better compatibility with modern libraries and potential future dependencies. * Add some comments. * Update AppImage configuration in make_config.yaml - Remove app_run_file parameter (AppRun is now injected post-build) - Fix icon path - Update AppRun: Add install_integration to setup icons and create local .desktop entry with correct Exec path - Ensure index.theme creation if missing. * Update deb configuration in make_config.yaml - Add comments for supported_mime_type parameter - Add user data cleanup script to postuninstall_scripts - Update postinstall_scripts to set StartupWMClass to app.hiddify.com and remove conflicting hiddify.desktop from .local. * We decided to remove RPM packaging for the following reasons: - Flutter officially supports Debian-based systems only. - Persistent issues with dependency linking. - High maintenance overhead (requires Fedora environment setup). - Successful builds do not guarantee runtime stability on Red Hat-based systems. * Update MSIX config in make_config.yaml - Complete the list of supported languages - Add commented-out parameters for self-signed certificate. * Update exe configuration in make_config.yaml - Update publisher_url from hiddify-next to hiddify-app - Fix app_icon.ico path - Complete list of setup locales - Remove redundant 'executable_name' and 'output_base_file_name' parameters. * Synchronizing main_prod.dart with main.dart. * Removing "distribute_options.yaml" because we are using "fastforge package" command and "distribute_options.yaml" is realated to "fastforge release" command. * Fixing riverpod issue with adding "agreed" parameter to recurcise method (applyConfigOption) and using this paramter instead of reading provider state in next call. * Small fix/ using _prefs instead of ref.read. * "not important" formating text for better understaning changes in next commits. * Renaming db name and import path. * Removing "DbV1" provider and renamign "DbV2" to "Db" * Renaming "db_v2" to "db" and using this "db" as main database by adding onUpgrade and increasing "schemaVersion" to 5 and fixing directory issue by adding "native" parameter to "driftDatabase" method. * Generation new steps.dart for new database. * Removing migration helper for copying data from "db_v1" to "db_v2" * "not important" formating text for better understaning changes in next commit. * Updating "test\drift". auto generated with drift_dev. * Moving "db_v1" schemas to "schemas\db" and generation "schema_v5" * Removing "db_v2" and rename "db_v1" to "db" * Merge branch 'new-design-v2' into remove/geo-assets. * Update release files. * Improve notification AND VPN MODE in android. * Merge pull request #30 from veto9292/remove/geo-assets. _Remove/geo assets_ * Removing "get-geo-assets" target from "Makefile" * Removing "MissingGeoAssets" from "ConnectionFailure" * Moving "GeoAssetType" to "db_v1" * Removing translations related to "geo-assets" * Removing lib\features\geo_asset. * Merge pull request #29 from veto9292/fix/reported-issues-v1. _Fix/reported issues v1_ * Update/test file. auto generated with drift_dev. * Update/ using DbV2 instead of AppDatabase. * Comment geo asset data 'mapper | source' * New/ run db migration from v1 to v2 in bootstrap. * New/ db migration helper from v1 to v2. * Improve/ renaming. add dbV2 provider. * New/creating db_v2 that equals db_v1 v5 schema. downgrade db_v1 to v4 schema. merging tables logics with db logics. * Update/schemas move db schema to new location and regenerate db_v1 schema_v4. * Update/build.yaml add 'db v2' path to drift_dev. changing drift_dev path (schema_dir, db_v1). * Fix/ with the new version of go_router, there is no need to add a navigatorKey to StatefulShellBranch to preserve its state. * Small fix/warnings "unnecessary_underscores" * Fix/ fixing prevent closing branch issue... removing "back_button_interceptor" and "prevent_closing_branch.dart"... upgrading go_router to 16.2.4 and using "PopScope" instead of "back_button_interceptor" package. * Small fix/ add: Warp, Fragment, and Padding icons in Settings option. Change the icon for the 'Padding-Size' option. * Update macos. * Merge pull request #28 from veto9292/chore/upgrade-flutter-version. _Chore/upgrade flutter version_ * Resolve dependency conflicts for Flutter upgrade. _Upgraded development dependencies to support the Flutter SDK migration from version 3.32.5 to 3.35.4 and resolve version solving failures._ * Merge pull request #27 from veto9292/feature/i18n-refactor. _Feature/i18n refactor_ * Small fix/ update pages.profiles.updateSub to updateSubscriptions. "Update connection profiles" => "Update subscriptions" * Refactor(localization): Sync codebase with new i18n structure. _This commit adapts the application's source code to the newly restructured and renamed translation files. It ensures all UI text is correctly referenced and improves how feedback messages are handled. - Updated all translation keys throughout the source code to align with the new nested structure. - Added and corrected translations for user-facing toasts to provide appropriate success and failure feedback (e.g., for import/export actions). - Implemented logic to display newly added translations where they were previously missing._ * Refactor(i18n): Restructure and clean up localization files. _- Restructured keys into a feature-based nested format for better maintainability. - Renamed files from strings_[locale] to [locale].i18n.json to fix tooling warnings. - Removed the unused Sorani Kurdish (ckb) locale._ * New/ auto generated by "dart run slang configure" * Upgrade/ slang(4.4.0 to 4.5.1) slang_flutter(4.4.0 to 4.8.0) * Update workflow. * Merge branch 'new-design-v2' of ssh://github.com/hiddify/hiddify-next-ios into new-design-v2. * Merge pull request #26 from veto9292/fix/reported-issues. _Fix/reported issues_ * Small fix/ profile_parser_test file was updated based on the changes in 'ProfileParser' * Update/ Improved UI/UX and aligned it with the manual section of the 'Add Profile' modal. * Update/ using loadingState instead of (save, update, delete & isBusy). ProfileDetailsPage is now used only for editing. The profile_details_notifier was completely rewritten, and the logic for applying and saving changes has been greatly simplified. * Update/ using Material icons instead of FluentIcons. * Update/ UI improvment. * Delete profile_local_override.dart. * Small fix/ using Material Icons instead of Cupertino Icons. * Update, fix/ fixed 'Free' profiles not updating and made minor changes to align with AddProfileNotifier.addManual. * Update, fix/ using Material icons instead of FluentIcons. Fixed the bottom sheet not closing when showing profileDetails. * Update/ rename method and use url to upsert. * Update/ rename methods. * Small fix. * Update/ moved part of the deleteProfile logic to ProfileDataSource. replaced the abortConnection method with toggleConnection. * Update/ updated AddProfileNotifier methods. added a Tile for isAutoUpdateDisable and to display the auto-update status. made minor changes to the Add button logic. * Update/ prevented error display on HTTP request cancellation. removed commented-out code. separated profile addition methods (addClipboard, addManual). rewrote the add method based on profileRepository changes and removed the renaming logic. canceled the request when AddProfileNotifier is disposed. * Update/ added profileParser and removed httpClient from the profileRepository's input parameters, adding profileParser as the new input parameter. * Update/ removed the local-override logic that was previously added to watchActiveProfile for preserving userOverride. added renaming logic to the insert operation; if a name is duplicated, random numbers are appended to the end (using getByName) so the user can differentiate. added logic to check for a profile's existence before editing. to manage the active profile during deletion, the isActive parameter is received. If the profile being deleted is active, another profile is selected as active after its removal. * Update/ passing the profile object to connect and reconnect. * Update/ parameters of the connect and reconnect methods were changed for simplicity. Commented-out code was removed. applyConfigOption was rewritten to align with the profileOverride changes. * Refactor/ deleted methods include (getByName, addByUrl, updateContent, addByContent, updateSubscription, add, patch, fetch). upsertRemote: Handles both creation and updating of remote profiles from a single entry point. It checks for an existing profile via URL to prevent duplicates. addLocal: Allows for creating profiles directly from local string content. offlineUpdate: Enables offline modifications by re-parsing the profile with new content while preserving existing headers. validateConfig: The logic has been updated to correctly handle and apply configuration overrides, which were previously ignored. removed the 'http_client' dependency from the repository. This responsibility has been moved to the 'profile_parser' to better separate concerns. a previously public field has been made private. the 'setActive' and 'deleteById' methods have been formally defined in the abstract interface. * Refactor/ converts the static 'ProfileParser' into a service class with access to Riverpod's 'ref'. adds 'addRemote', 'addLocal', 'updateRemote', and 'offlineUpdate' methods using 'fpdart'. moves profile download logic from 'profile_repository' to 'ProfileParser' and rewrites it with 'fpdart'. adds error handling for user-cancelled downloads. reads 'useXrayCoreWhenPossible' directly from 'ref' instead of passing all settings. changes the 'parse' method's input to 'ProfileEntity', simplifying the return logic with 'profile.map'. updates the name parser hierarchy to use the link protocol as a fallback. adds logic to disable profile auto-updates via user override. * Remove/ move 'protocol' method from 'link_parsers' to 'profile_parser' * Update/ removed 'toEntry' and 'subInfoPatch' methods from 'ProfileEntity'. added 'toInsertEntry' and 'toUpdateEntry' methods and replaced 'map' with 'switch'. added 'userOverride' and 'populatedHeaders' to the 'toEntity' method and renamed 'testUrl' to 'profileOverride'. * Update/ renamed 'testUrl' to 'profileOverride'. added a default value of 'Duration.zero' to 'updateInterval'. add/ added 'userOverride' and 'populatedHeaders' to the remote and local models. added a 'UserOverride' model with a version structure to ensure backward compatibility in the future. added isAutoUpdateDisable with default(false) to 'userOverride' * Update/ changes to 'ConfigOptionRepository' class: made 'getConfigOptions' private. renamed method 'getFullSingboxConfigOption' to 'fullOptions'. added method 'fullOptionsOverrided', which applies 'profileOverride' to the 'config options' before they're sent. improved error handling with 'fpdart'. * Update/ add 'ConfigOptionFailure' to 'ProfileFailure.invalidConfig' parameters to easily convert 'ConfigOptionFailure' to 'ProfileFailure'. add 'cancelByUser' for when an HTTP request is cancelled by the user(this helps to distinguish the error and prevent its display) * Update/ add 'ConfigOptionFailure' to 'ConnectionFailure.invalidConfigOption' parameters to easily convert 'ConfigOptionFailure' to 'ConnectionFailure' * Update/ schema_v5 test file(auto generated by drift_dev) * Delete/ old drift test files. * Update/ adding columns(userOverride, populatedHeaders) to profile table in db. renaming test_url column to profileOverride. app_database.steps.dart & drift_schema_v5.json is auto generated by drift_dev. * Update/ en, fa translations. add(general.auto, profile.add.disableAutoUpdate, failure.profiles.canceledByUser). update(profile.detailsForm.updateInterval). * Update/ supporting deselected state for Per-app porxy apps in UI. * Update/ bump target/compileSdk to 36; align AGP/Kotlin. * Fix/ deprecated warning. * New/ apps auto selection modal. features: auto update, reset to default, changing update interval, enable or disable auto-selection, perform now. * Update/ using db for managing state. moving and removing providers. adding shareOnGithub to notifier. removing installed apps notifier provider. * Update/ notifying user and disabling auto-selection after a region change. * New/ activating and managing the per-app proxy auto-selection service. * New/ managing loading for asynchronous operations. * New/ this service is initialized in app.dart and is responsible for auto-update functionality for auto-selection feature. It is also in charge of updating the active list in prefs. * Update/ new sort logic with 4 priority. using hooks for getting and managing installed apps. auto sort feature. adding scroll to top feature. adding loading for share to all. adding FAB for auto selection modal. * Fix/ AppProxyMode not found error. * New/ AppProxyDao is for interacting with the AppProxyEntries table. * Update/ change return types from List to Set. removing share method from repository. renaming methods. using AppProxyMode instead of PerAppProxyMode. * New/ appProxyDataSourceProvider. * New/remove/ adding AppPackageInfo as a replacement for InstalledPackageInfo to store installed apps. * New/ per_app_proxy_backup model is used for import & export. * New/ adding AppProxyMode alongside PerAppProxyMode; the difference is the lack of an off state. * New/ PkgFlag, managing bit-flags for selected apps in per-app-proxy. * Update/ import UI and logic for add_profile_modal.dart. * New/update/ moving per_app_proxy_include_list & per_app_proxy_exclude_list to Preferences. adding auto_apps_selection_update_interval & auto_apps_selection_last_update. renaming autoSelectionAppsRegion to autoAppsSelectionRegion. * New/ drift migration test files (generated by drift_dev) * Add/update/fix/remove/ adding AppProxyEntries to db tables, fixing AppDatabase constructor, adding from4To5 and increasing schemaVersion to 5, generating drit_schema_v5 with drift_dev, removing database_connection.dart. * New/ app_database.steps.dart generated by drift_dev. * Move file/ moving drift schema from schemas\ to schemas\app_database. * New/ adding AppProxyEntries to db tables. * Fix/ adding translationsProvider to bootstrap. * Update/ en, fa translations new: autoSelection(performNow, resetToDefault) network.share(alreadyInAuto) update: network(clearSelection) autoSelection(dialogTitle, msg, success, regionNotFound) network.share(emptyList) * Update/ adding database and schema dir to drift_dev in build.yaml. * New/ drift migration test files (generated by drift_dev) * Adding method to RuleEnum for getting index of key. * Updating en, fa translations. Using " instead of ( for autoSelection.dialogTitle. Adding positiveBtnTxt to per-app proxy share dialog. * Improving error management and preventing possible crash for auto_selection_repository. * Changing text "Share" to "Share To All" in per-app proxy. * Displaying a notification dialog before sharing selected apps. * Updation en, fa translations Adding title, dialogTitle, msg to settings.network.share. * Merge branch 'fix/reported-issues' of https://github.com/veto9292/hiddify-next-ios into fix/reported-issues. * Suggesting auto selection apps when changing region. * Improving naming and logic in 'per_app_proxy_page.dart' and adding auto selection, share, import/export to the menu, sorting selected items, using menu for mode selection, setting 'hideSystemApps' with a chip. * Improving naming and logic in 'per_app_proxy_notifier.dart' and adding methods (share, clearSelection, autoSelection, export clipboard/file, import clipboard/file). * Add auto selection data provider and repository To get the list of proxy/bypass applications from GitHub and share the selected list as an issue. * Removing per_app_proxy_data_providers & per_app_proxy_repository Instead, the `installed_apps` package is used. * Using MenuAnchor instead of PopupMenuButton in settings_page. * Updating en & fa translations adding (import, export, share) to general adding (autoSelection, share, import, export) to setting.network. * Adding 'auto_selection_apps_region' to 'general_preferences.dart' * Changing the order of buttons in ConfirmationDialog. * Renaming 'okText' to 'positiveBtnTxt' for confirmation dialog. * Merge pull request #25 from veto9292/fix/reported-issues. _Fix/reported issues_ * Merge branch 'new-design-v2' into fix/reported-issues. * Better log page, upgrade to flutter 3.32.4. * Merge branch 'new-design-v2' of ssh://github.com/hiddify/hiddify-next-ios into new-design-v2. * Merge pull request #24 from veto9292/fix/reported-issues. _Fix/reported issues_ * Merge. * Merge branch 'new-design-v2' of ssh://github.com/hiddify/hiddify-next-ios into new-design-v2. * Merge branch 'new-design-v2' of ssh://github.com/hiddify/hiddify-next-ios into new-design-v2. * Update flutter. * Fixing setSkipTaskbar in macOS and renaming WindowNotifier methods. * Updating window_manager & tray_manager packages. * Remove experimental feature references from settings pages and related files. * Renaming 'inbound_options.dart' to 'inbound_options_page.dart' * Using 'profileDataSourceProvider' instead of 'profileRepositoryProvider' in 'active_profile_notifier.dart' to prevent redundant builds. * Removing 'distinct()' from 'profile_repository.dart' and adding it to 'profile_data_source.dart' to prevent emitting extra events. * Removing 'async' from 'SingConfigOption' and using watch instead of read. * Removing extra file. * Preventing redundant builds in 'routing_config_notifier.dart' when the last profile is deleted and 'Breakpoint' is 'isMobile'. * Fixing SideBarStatsOverview issue. * Removing 'flutter_adaptive_scaffold' and using Flutter SDK. Updating logic related to 'Breakpoint'. * Using 'Breakpoint' instead of Breakpoints in 'flutter_adaptive_scaffold' method. * Using 'Breakpoint' instead of Breakpoints in 'flutter_adaptive_scaffold' method. * Removing 'branchNavKey' and using new 'Breakpoint' logic instead of 'isSmallActive'. * Updating 'ActiveBreakpointNotifier' update logic. * Renaming 'is_small_active' to 'active_breakpoint_notifier'. Adding 'Breakpoint' class for storing breakpoint and helper methods for detecting the active breakpoint. Changing 'ActiveBreakpointNotifier' output to 'Breakpoint' and adding a provider named 'isMobileBreakpoint'. * Commenting out 'PopupCountNotifier'. * Using 'rootNavKey' instead of 'branchNavKey'. * Using 'rootNavKey' instead of 'branchNavKey' and commenting out 'PopupCountNotifier' methods. * Removing flutter_adaptive_scaffold from pubspec. * Updating Flutter to 3.32.5 and upgrading pub. Updating intl to 0.20.2. Solving pub get issue by adding humanizer version 3.0.1 instead of getting from GitHub. * Organizing per app proxy page. * Fix import and use read instead of watch. * Unifying the status bar and system navigation bar color with the scaffold color. * Fixing profile disappearance (when the profile being updated is selected as the active profile) * Improving popup hiding nav bar animation(temporary) * Fixing Segmented Button UI in Quick Setting Modal. * Fixing routing config rebuilding when deleting profile. * Fixing bottom sheet overlapping with keyboard. * Fix warp secure lable for connection button. * Improve warp generation toast, merge warp loading logic in warp_option_notifier.dart. * Improve out animation for bottom sheet when popup is showing. * Fix toast direction, removing context from "in_app_notification_controller", add dismissAll() to _show. * Removing toast handler from "profiles_modal" and "profiles_page", move toast handler for success or failure profiles update to "profiles_update_notifier" * Preventing redundant builds by removing duplicate events from the stream and using "hasAnyProfileProvider" in routing_config_notifier.dart. * Adding ToastificationWrapper for showing toast without context. * Renaming and organizing files and classes related to "Profiles" * Merge pull request #23 from veto9292/fix/reported-issues. _Fix/reported issues_ * Move popup_count_notifier from dialog_notifier to dedicated file. * Rename riverpod_listenable to refresh_listenable. * Remove disabled parameter from SideBarStatsOverview. * Removing unnecessary files. * Remove CustomAlertDialog from alerts.dart (move it to the dialog/widgets) * Using dialog notifier, using new global key. * Fix bottom sheet typo, rename root nav key. * Merge config options with settings feature. * Rewriting all items in settings, adding icons, organizing folder structure and files. * Rename settings_repository to battery_optimization_repository. * Fix imports. * Fix imports. * Fix imports. * Move WarpLicenseDialog.dart to dialog\widgets, fixing import warning dialog_notifier.dart. * Move quick settings modal to botto_sheets\widgets, fixing import warning in bottom_sheets_notifier.dart. * Show toast instead of dialog for warp success generation. * Fix platform_settings_notifier async issue, applying name changes, manage loading state, rewriting platform_settings_tiles and fixing loading. * Rename settings_repository to battery_optimization_repository rename settings_data_providers to battery optimization_provider move files. * Using dialog notifier, moving SettingTextDialog to dialog/widgets. * Using dialog notifier moving SettingsRadioDialog to dialog/widgets. * Using dialog notifier, moving setting_checkbox to dialog/widgets. * Using dialog notifier. * Using dialog notifier. * Using dialog notifier. * Moving proxy-info dialog to the dialog notifier, using goNamed. * Fix bottom sheet typo, using goNamed, using dialog notifier. * Using dialog notifier, add ref to _launchUrlWithCheck method. * Renaming ProfilesOverviewPage to ProfilesPage, fix bottom sheet typo, use context.goNamed, removing autoImplyLeading "becase routing is fixed" * Using dialog notifier. * Using dialog notifier. * Fix imports. * Fix ref and imports. * Using dialog notifier. * Using dialog notifier. * Set return type for dialog. * Renaming LogsOverviewPage to LogsPage, use AppBar instead NestedScrollView. * Add const to constructor, romoving SafeArea to solve UI issue, preventing operations during loading. * Commenting unused widget, fix bottom sheet typo. * Remove showAddProfile and url parameter from HomePage this logic is moved to the redirect in routing-config-notifier. * Using dialog notififer, remove showExperimentalNotice "moved to dialog notifier" * Using dialog notifier, move showExperimenNotice logic to dialog-notifier. * Fix import. * Using dialog notifier, changing icons, dynamic icon for theme mode. * Fix imports, using dialog notifier. * Fix imports, set return type for showing dialog. * Fix imports. * Move new_version_dialog.dart to lib\core\router\dialog\widgets. * Fix ref warning. * Move about_page.dart to lib\features\about\widget. * PreventClosingApp in routing_config_notifier.dart is used for preventing the branch from closing. It transfers the user to Home route after preventing the closing. * Is_small_active.dart IsSmallActive is true when the breakpoint is in the small range, and false otherwise. app.dart Updating the router provider. Assigning a value to IsSmallActive. useEffect has been used for improving performance. * CustomTransition is used for adding animation to pages in routing_config_notifier.dart. The purpose of creating this class is to prevent boilerplate code. * RoutingConfigNotifier returns a RoutingConfig which is used for updating rConfig in GoRouterNotifier. The structure of routes is defined in RoutingConfigNotifier. The redirect method is defined in RoutingConfigNotifier. By listening to other providers, a new list of routes can be returned dynamically. * An instance of RefreshListenable is passed to refreshListenable in GoRouterNotifier. If the notifyListeners method is called, the anonymous redirect function in RoutingConfig is executed. By using ref, a specific provider is listened to, and if it changes, the provider's value is stored globally in a variable, and notifyListeners is called. * GoRouterNotifier creates the GoRouter instance that is passed to MaterialApp.router. It holds a RoutingConfig within itself, which gets its value updated by listening to RoutingConfigNotifier. With changes to RoutingConfig, the app's routes will change dynamically. * Move dialogs from project to lib\core\router\dialog\widgets fix navigation logic for dialogs add translations. * Adding the _show method for managing the number of popups, reducing boilerplate code, and managing context move all dialog to this notifier explicitly specifying a return type. * Fix typo(buttom to bottom), adding the _show method for managing the number of popups, reducing boilerplate code, and managing context. * Fix import, fix Ref warning, remove "error dialog", changing context that are used for displaying toast. * Fix warning/ Ref, import, async. * My_adaptive_layout.dart is a shell for the main navigator which is used inside StatefulShellRoute in go_router shell_route_action.dart, which stores information related to each action in my_adaptive_layout.dart. * Move my_app_links.dart to lib\core\router\deep_linking. * Remove/ app_router.dart, router.dart, routes.dart. * New/ move url_protocol to lib\core\router\deep_linking\url_protocol. * Update/ translations(en, fa) general.save general.close unknownDomains.title unknownDomains.youAreAboutToVisit unknownDomains.thisWebsiteIsNotInOurTrustedList. * Add/ back_button_interceptor package to pubspec, update AndroidManifest (platform-specific setting) * Update go_router and flutter_adaptive_scaffold packages, remove go_router_builder package. * New/ show "qr dialog" for "allow connection from LAN" * Improve/ add comment for network_info_plus package. * Add/ network_info_plus package. * Small fix/ profile tile radius issue. * New/ merge warp license aggrement, seprate warp options logic, move license agreement to connection button and connection repo and more... * New/ add missing warp license to connection_failure. * Small fix/ warnings & rename testUrl to override. * Improve/ add showWarpLicense & showWarpConfig dialog to dialog_notifier. * Add translations/ failure.warp.missingLicense & missingLicenseMsg. * Small fix/ rename testUrl to override. * Fix/ warp license agreeement focus, rename intro page const. * Improve/ focus logic for intro page. * Fix/ adding a timer for syncing status with changes outside the app. * Small fix/ add gap to app bar actions. * Small fix/ add gap to app bar action. * Fix/ fix warnings, fix focus issue, removing extra code related to focus management. * Small fix/ settings_inpu_dialog warnings. * Fix slider focus for settings_inpu_dialog.dart SettingsSliderDialog. * Small fix/ add-profile slider focus. * Fix/ prevent scope switch while overlay is displayed. * Remove/ add_manual_profile_modal.dart. * Fix/ focus issue for adaptive-scaffold, add keyboard const, fix adaptive_root_scaffold warnings. * Small fix/ connection button focus color. * Small fix/ improve home_page logic and ui. * Fix/rewrite intro_page & fix rich text focus, remove _dynamicRootKey, add intro const. * Small fix/ hide adaptive scaffold from intro, fix warnings for routes.dart. * Fix/ merge add profile with manual modal(logic & ui), fix cancel for manual. * Small fix/ config_options_page(async await) * Small fix/ warning(use StringBuffer instead of String) * Small fix/ import warning. * Small fix/ log message. * Package update/ launch_at_startup. * Small fix/ fix waring for config_options_page. * Merge pull request #22 from veto9292/fix/reported-issues. _Fix/reported issues_ * Small fix/ update profile_parser_test. * Small fix/ free profile typo. * Merge pull request #21 from veto9292/fix/reported-issues. _Fix/reported issues_ * Small fix/ neededFeatures(warp to warp_over_proxies) * Fix/ free profile typo. * New/ overriding profile name and options locally and remotely(profile, fragment, name) * Update. * Update core. * Update flutter. * Update pipeline. * Change default connection test url to apple. * Merge branch 'new-design-v2' of https://github.com/hiddify/hiddify-next-ios into new-design-v2. * Merge pull request #20 from veto9292/fix/reported-issues. _Fix/reported issues_ * Small fix/ prefer_const_constructors. * Small fix/ prefer_final_locals. * Small fix/ remove commented imports. * Small fix/ remove unused_import. * Fix/ UI blocking during connection. * Feature/ support for enable-warp in profile headers. * Improve/ sync tray begavior in macOS with other Desktops. * Improve/ add support for custom scheme in flutter. * Setup/ add schemes for Windows. * Setup/ add schemes for Linux. * Setup/ add schemes for macOS. * Setup/ add schemes for iOS. * Setup/ add schemes for Android. * Feature/ ability to delete active profile. * Improve/ show sort profile dialog. * Small fix/ format source code, fix warning. * Small fix/ 'no active profile' dialog boxConstraints. * Moving interrupt handling in proxy reception to core. * Update to fultter 3.29.3. * Add profilename in start. * Merge branch 'new-design-v2' of https://github.com/hiddify/hiddify-next-ios into new-design-v2. * Merge pull request #19 from veto9292/fix/reported-issues. _Fix/reported issues_ * Small fix/ tray manager logic, bootstrap import. * Fix tray_manager, logic rewrite. * Fix proxy_overview_page performance issue, improve UI. * Hide back button for profiles_overview_page. * Improve ux and logic for add_profile_modal by showing region specific message (translation changed) * Custom scheme setup for registration in Windows OS (unpackaged app) * Setup app_links for flutter, merge app_links with go_router. * Setup app_links package for Linux. * Setup app_links package for macOS. * Setup app_links package for Windows OS. * Improve performance. * Add upload to app-draft. * Improve windows luncher. * Merge branch 'new-design-v2' of https://github.com/hiddify/hiddify-next-ios into new-design-v2. * Merge pull request #18 from veto9292/fix/reported-issues. _Fix/reported issues_ * Create custom_text_scroll, fix ButtonSegment over flow. * Fix loading height for add_profile_modal. * Justification is not possible, remove WrapAlignment.spaceBetween. * Remove percent_indicator_premium, fix profile_tile RemainingTrafficIndicator. * Fix per_app_proxy performance issue. * Remove protocol_handler from Windows and macOS. * Fix macos. * Merge branch 'new-design-v2' of https://github.com/hiddify/hiddify-next-ios into new-design-v2. * Merge pull request #17 from veto9292/fix/reported-issues2. _Fix/reported issues2_ * Fix deep link query-parameter. * Fix go router refreshListenable. * Handle deep link with go router. * Remove protocol_handler, deep_link_notifier. * Fix bottom sheets padding. * Fix per app proxy error. * Edit routes location. * Fix canChangeOption. * Fix ChoicePreferenceWidget enable (Enable WARP/Detour Mode) * Hide auto generated back button for settings page. * Return to home page from settings page when back button pressed. * Improve ux, preventing back when user intent to work with slider. * Preventing delete selected profile. * Update deoebdebcies. * Merge pull request #15 from veto9292/fix/reported-issues. _fix reported issues and add en, fa translations_ * Fix reported issues and add en, fa translations. * Improve android stability. * Upgrade flutter to 3.29.1. * Update ios macos. * Merge pull request #14 from veto9292/fix/proto-and-sentry. _Fix/proto and sentry_ * Remove protoc_builder. * Add .sentry-native to .gitignore. * Delete .sentry-native folder. * Fix route_rule.pb.dart import. * Merge pull request #13 from veto9292/feature/route-rule. _Feature/route rule_ * Route rule(not complete) * Add route rule regex validators. * Add riverpod_observer for better debugging. * Config-option file import/export. * Custom okText for confirmationDialog and fix content constraints. * Add package to pubspec.yaml(file_picker, installed_apps, recase) * Fix android. * Move proto to core. * Merge pull request #12 from veto9292/fix/add-profile. _empty region in free config mean all, fix grid crossAxisCount for single item_ * Empty region mean all, fix grid crossAxisCount for single item. * Merge pull request #11 from veto9292/update/add-profile. _update/add_profile_modal_ * Update/add_profile_modal add/free profiles to add_profile_modal remove/http package from pubspec.yaml. * Merge pull request #8 from veto9292/fix/makefile. _fix: makefile path issue in Windows OS_ * Merge pull request #10 from veto9292/feature/route-rule. _add: proto packages & route_rule.proto file_ * Add: proto packages & route_rule.proto file. * Fix windows compile. * Improve ios and windows. * Disallow delete active profile. * Better connection management, add bounce and show connection error. * Make easier access for quick setuo. * Refactor config options. * Update pods. * Update sentry. * Update. * Update sqlite3 dependency. * Update flutter. * Handle profile size. * Support correctly ios. * Update android libs. * Update packages. * Show error incase of fail. * Upgrade flutter. * Some refactor. * Refactor router. * Refactor: routes. * Update. * Merge branch 'ios' into new-design-v2. * Update dialog to platform dialog. * Merge remote-tracking branch 'origin/main' into ios. * Update. * Update. * Merge branch 'fix-latest' into new-design. * Update packages and fix web issues. * V3. * New UI design. * Merge remote-tracking branch 'origin/main' into ios-pv. * Add make with love in intro. * Improve ios UI and integrate it with os. * Update. * Update ios. * Remove 'needed_features' from free_configs. * Update free_configs. * Update cron schedule and add operations limit. * Modify stale.yml for cron schedule and messages. _Updated the stale issue workflow to change the cron schedule and modify the stale issue message and timing._ * Update warp configuration with new server entries. * Update profile title in warp configuration. * Add new warp configurations and psiphon links. * Update title in free_configs for Warp profile. * Remove unused configurations for Insta-Youtube and Ainita. _Removed several configurations related to Insta-Youtube and Ainita.net, including their titles, sublinks, tags, and consent messages._ * Change region from '-' to 'k' in free_configs. _Updated region values from '-' to 'k' for two configurations._ * Add Mahsa profile configuration file. * Add Mahsa configuration profile to free_configs. * Update free_configs. * Update warp configuration with new detour format. * Update free_configs. * Update free_configs. * Create ainita. * Update free_configs. * Chore: update translations with Fink 🐦 * Update super_fragment. * Update super_fragment. * Update super_fragment. * Update super_fragment. * Rename super_fragment.json to super_fragment. * Update super_fragment.json. * Create super_fragment.json. * Update free_configs. * Update free_configs. * Create free_configs. * Update fragment. * Update fragment. * Update fragment. * Create fragment. * Update bug_report.yaml. * Update bug_report.yaml. * Update bug_report.yaml. * Update bug_report.yaml. * Update bug_report.yaml. * Update bug_report.yaml. * Update bug_report.yaml. * Update bug_report.yaml. * Update bug_report.yaml. * Update bug_report.yaml. * Update README_ru.md. * Update README_cn.md. * Update README_br.md. * Update README_ja.md. * Update README_br.md. * Update README.md. * Update README_fa.md. * Update README_ru.md. * Update README_ja.md. * Update README_fa.md. * Update README_cn.md. * Update README_br.md. * Update README.md. * Merge pull request #1475 from simonkimi/main. _Profile Tile InkWell Overflow Fix_ * Fix profile_tile clipBehavior. * Update README.md. * Merge pull request #1464 from kekomenos/patch-1. _Update README.md_ * Update README.md. _ Star History of hiddify/hiddify-app _ * Update README.md. * Create README.md. * Update README_ru.md. * Update README_cn.md. * Update README_ja.md. * Update README_br.md. * Update README_fa.md. * Update README.md. * Update README_br.md. * Update README_ja.md. * Update README_ru.md. * Update README_cn.md. * Update README_fa.md. * Update README_fa.md. * Update README.md. * Update README.md. * Merge pull request #1438 from proninyaroslav/linux-fix-silent. _[Linux] Fix minimize to tray (silent start) option_ * [Linux] Fix minimize to tray if the option is enabled. * Update LICENSE.md. * Update CONTRIBUTING.md. * Update README.md. * Merge pull request #1403 from ldm0206/main. _Update translations zh-CN_ * Chore: update translations with Fink 🐦 ## v2.5.7 (2024-10-03) #### Other * Merge pull request #1382 from TheLastFlame/main. _Remembering window closing action_ * Feat: add action options for closing the application. * Chore: update .gitignore to exclude /data directory. * Refactor: replace HookConsumerWidget with ConsumerWidget and add ThemeModePrefTile. ## v2.5.6 (2024-10-02) #### Fix * Version bug. * Version bug. ## v2.5.5 (2024-09-29) #### Other * Merge pull request #1368 from tarzst/main. _Update RU translations_ * Chore: update translations with Fink 🐦 ## v2.5.2 (2024-09-29) #### Fix * Typo. ## v2.5.1 (2024-09-29) #### Fix * Exception. * Version. ## v2.5.0 (2024-09-28) #### Other * Merge pull request #1335 from laperuz92/patch-1. _Update russian translations_ * Update russian translations. * Merge pull request #1328 from yxiZo/main. _Update translations_ * Chore: update translations with Fink 🐦 * Merge pull request #1322 from andythesilly/main. _set edgeToEdge ui mode_ * Chore: update translations with Fink 🐦 * Set edgeToEdge ui mode. ## v2.3.1 (2024-09-07) #### Fix * Android. #### Other * Merge branch 'main' of https://github.com/hiddify/hiddify-next. * Change name to hiddifypackettunnel. * Fix android build issue. ## v2.3.0 (2024-09-02) #### New * Add brazil region. #### Fix * Black screenn when press back button. #### Other * Hide back icon when no back. * Merge pull request #1277 from sillydillydiddy/main. _Update translations_ * Chore: update translations with Fink 🐦 * Merge pull request #1282 from vedantmgoyal9/patch-1. _Update winget-releaser to latest_ * Update winget.yml. * Update winget.yml. * Update winget-releaser to latest. * Merge pull request #1278 from tensionc/main. _fix: Black screenn when press back button_ * Merge remote-tracking branch 'origin/main' * Update warp. * Update warp. * Revert: Keep button, add judgment. ## v2.2.0 (2024-08-21) #### New * Add several values for dns and url test in auto complete mode. * Add use xray core option. #### Changes * Default tun mode to gvisor. #### Fix * Bug. * Some hard coded items. * Apple bug. * Bug of back button in rtl flutter 3.24. * Naming of links containing &&detour. #### Other * Better auto complete. * Remove hindi. * Update flutter to 3.24. * Merge pull request #1217 from lexxfin/main. _chore: update translations with Fink 🐦_ * Chore: update translations with Fink 🐦 * Merge pull request #1210 from bLueriVerLHR/main. _Fix typo in stats_repository.dart_ * Fix typo in stats_repository.dart. * Chore: update translations with Fink 🐦 * Include app configs before validaing proxies. * Chore: update translations with Fink 🐦 * Change default warp mode to m4. ## v2.1.5 (2024-08-05) #### Fix * Vpn service issue. #### Other * Revert android changes. * Fix warp generation. * Try fix vpn. * Check macos. * Disable mac. * Revert changes to android vpn service. * Better tryicons. ## v2.1.4 (2024-08-05) #### New * Add exit dialog when press close button. * Colorized tray icon. #### Fix * Some bugs. * Bugs in tray icon. * Fr trnalsation. * Translate bug. * Single instance flutter in linux? #### Other * Less retry for ipinfo. * Add french lang. * Test icon linux? * Connection button by proxy status indicator, change connected to connecting when timeout. * Formated export in json editor. * Merge pull request #1174 from Hannisiddiqui/main. _Wanted to add hindi language for indian users_ * Inlang/manage: add languageTag hi. * Inlang/manage: install module. * Chore: update translations with Fink 🐦 * Chore: update translations with Fink 🐦 * Chore: update translations with Fink 🐦 * Chore: update translations with Fink 🐦 * Chore: update translations with Fink 🐦 * Update warp. * Update release_message.md. ## v2.1.1 (2024-08-02) #### New * Add turkey region. #### Other * Add change log. ## v2.1.0 (2024-08-02) #### New * Add invalid config label. #### Fix * Error migrating from v3 to v4 database. * Url test issues, avoid multiple test, and change outbound on test. * Loop in android. #### Other * Merge pull request #1162 from soyangelromero/main. _Update translations EN - ES_ * Chore: update translations with Fink 🐦 * Merge pull request #1154 from MR-TZ-dev/main. _fix: error migrating from v3 to v4 database._ * Better get requests. * Fix; font. ## v2.0.4 (2024-07-31) #### New * Add rich config editor. ## v2.0.3 (2024-07-30) #### Fix * Bug. ## v2.0.2 (2024-07-30) #### Other * Better editor. ## v2.0.1 (2024-07-30) #### Fix * Ios build issue. * Ios bug. * Some bugs in configs and fix core bugs. * Editor text mode bug. #### Other * Update. * Better json editor. * Test. * Use latest macos for build ios. * Test ios. ## v2.0.0 (2024-07-30) #### New * Add json editor and editing configs <3. * Add custom url-test for each repository. #### Fix * Link validator. * Provis? * Ios. #### Other * Update warp. * Fix? * Ios? * Fix ios? * Fix bug. ## v1.9.1 (2024-07-28) #### New * Add auto build for ios. * Add xray type. #### Fix * Add warp issue. #### Other * Revert to sdk 34. * Enable resolve-destination and ipv6 by default. * Merge branch 'main' of hiddify-github:hiddify/hiddify-next. * Merge pull request #1108 from SonzaiEkkusu/main. _Update Translations for Region Indonesia (id)_ * Update Translations for Region Indonesia (id) * Reset direct dns when region change. * Update sdk to 35. * Better xray support. * Merge branch 'main' of hiddify-github:hiddify/hiddify-next. * Merge pull request #1106 from amirsaam/main. _General iOS Maintain_ * General iOS Maintain. ## v1.9.0 (2024-07-25) #### New * Add10000 to all ports for prevenging permission error. add warp from github repository except for china. #### Fix * Fixed problem not launching on Ubuntu 24.04 LTS. #### Other * Merge branch 'main' of hiddify-github:hiddify/hiddify-next. * Merge pull request #1084 from MR-TZ-dev/fix/linuxLaunchFix. _fix: fixed problem not launching on Ubuntu 24.04 LTS._ * Merge pull request #1092 from Metozak/main. _Update translations_ * Chore: update translations with Fink 🐦 * Merge pull request #1097 from SonzaiEkkusu/main. _Add Indonesia region_ * Add Indonesia region. * Add core to 1.9.0. * Upgrade flutter. * Merge pull request #1073 from keyang556/main. _Update translations_ * Chore: update translations with Fink 🐦 * Update warp. * Update warp2. * Update warp2. ## v1.7.0 (2024-07-17) #### New * Add more warp modes, handle both ipv4 and ipv6 in wireguard, add customizable size and more. #### Other * Update warp2. * Create warp2. * Update warp. ## v1.6.3 (2024-07-14) #### Other * Make pre-release by default. ## v1.6.2 (2024-07-14) #### Fix * Qrcode issue and update qrcode lib. * Scanner. #### Other * Renew warp configs on adding. ## v1.6.1 (2024-07-14) #### New * Add knocker parameters. #### Fix * Release issue. * Qrcode issue. #### Other * Android. * Upgrade flutter. * Update flutter and make connection more smooth. * Show info dialog when reconnecting. * Merge pull request #1050 from itispey/dev-test. _Reconnect automatically after changing service-mode_ * Reconnect automatically after changing service-mode. * Merge pull request #1053 from maxyxyd/main. _Update translations_ * Chore: update translations with Fink 🐦 * Chore: update translations with Fink 🐦 * Chore: update translations with Fink 🐦 * Chore: update translations with Fink 🐦 * Merge pull request #1043 from nidghogg/main. _Fixed camera permission handling for QR_ * Merge branch 'main' of https://github.com/nidghogg/hiddify-next. * Fixed Camera permission handling for QR. ## v1.5.0 (2024-07-09) #### Fix * Test bug for geoassets. * Remove geoassets. #### Other * Fix intro page buug. * Merge branch 'main' of hiddify-github:hiddify/hiddify-next. * Merge pull request #1004 from Kianmehrgit/main. _Update translations_ * Chore: update translations with Fink 🐦 * Merge pull request #993 from fodhelper/fix-urltest-fails. _Fix URLTest Fails_ * Update config_option_repository.dart. * Merge pull request #966 from SoranTabesh/main. _Update translations_ * Chore: update translations with Fink 🐦 * Merge pull request #965 from SoranTabesh/patch-1. _Update locale_extensions.dart_ * Update locale_extensions.dart. * Merge pull request #988 from Amirali-Amirifar/fix-changelog-update-years. _Fix CHANGELOG year_ * Fix CHANGELOG year. _see diff._ * Merge pull request #934 from amirsaam/main. _General iOS Maintaining_ * General iOS Maintaining. * Refactor. * Add basic routing options, auto update routing assets,use ruleset, remove geo assets. ## v1.4.0 (2024-06-02) #### Fix * Naming bug. * Bug. * Bug. * Ios. * Lang. * Bug. * Make dark tray icon windows-only. #### Other * Merge pull request #817 from felixhaeberle/main. _Update translations_ * Merge branch 'main' into main. * Chore: update translations with Fink 🐦 * Chore: update translations with Fink 🐦 * Merge pull request #811 from amirsaam/main. _New Bundle + Pods + PrivacyAPIs_ * Privacy Targets. * Privacy Declaration. * New Bundle + Pods. * Merge branch 'android-fix-action-bug' * Downgrade flutter. * Master. * Downgrade to 3.22.0. * Upgrade flutter. * Update to flutter 3.21. * Update. * Downgrade flutter. * Temporary disbale apk build. * Update. * Update. * Test. * Update. * Update. * Upgrade gradle version. * Tmp test. * Add more log. * Update flutter action. * Add debug and ios. * Fix lang update. * Merge pull request #778 from Pikman/main. _Update translations_ * Merge branch 'main' into main. * Fink 🐦: update translations. * Fink 🐦: update translations. * Merge pull request #894 from xmha97/main. _Capitalize the installation folder_ * Update make_config.yaml. * Merge pull request #902 from MrIbrahem/main. _Update ar translations_ * Chore: update translations with Fink 🐦 * Merge pull request #4 from MrIbrahem/patch-1. _Patch 1_ * Update strings_ar.i18n.json. * Update strings_ar.i18n.json. * Update strings_ar.i18n.json. * Update strings_ar.i18n.json. * Update make_config.yaml. * Update settings.json. * Update locale_extensions.dart. * Merge pull request #3 from MrIbrahem/patch-1. _Update strings_ar.i18n.json_ * Update strings_ar.i18n.json. * Update strings_ar.i18n.json. * Merge pull request #2 from MrIbrahem/patch-1. _Create strings_ar.i18n.json_ * Create strings_ar.i18n.json. * Merge pull request #814 from keyang556/main. _Update Traditional Chinese translations_ * Chore: update translations with Fink 🐦 * Chore: update translations with Fink 🐦 * Chore: update translations with Fink 🐦 * Add warp config, update to flutter 1.22. * Fink 🐦: update translations. * Fink 🐦: update translations. * Merge pull request #770 from alkstsgv/main. _Update translations_ * Fink 🐦: update translations. * Test. * Merge pull request #751 from sky96111/main. _fix: make auto dark tray icon windows-only_ * Merge pull request #737 from SoranTabesh/main. _Update translations_ * Fink 🐦: update translations. * Fink 🐦: update translations. * Fink 🐦: update translations. * Fink 🐦: update translations. * Fink 🐦: update translations. * Fink 🐦: update translations. * Fink 🐦: update translations. * Fink 🐦: update translations. * Fink 🐦: update translations. * Fink 🐦: update translations. * Fink 🐦: update translations. * Fink 🐦: update translations. * Fink 🐦: update translations. * Fink 🐦: update translations. * Fink 🐦: update translations. * Fink 🐦: update translations. * Fink 🐦: update translations. * Fink 🐦: update translations. * Fink 🐦: update translations. * Fink 🐦: update translations. * Fink 🐦: update translations. * Rename strings_ckb-KU.i18n.json to strings_ckb-KUR.i18n.json. * Update settings.json. * Fink 🐦: update translations. * Update README_br.md. * Update README_ja.md. * Update README_ru.md. * Update README_cn.md. * Update README_fa.md. * Update README.md. * Update core. * Merge pull request #726 from HSSkyBoy/main. _inlang: update zh-TW transition_ * Update zh-TW transition. * Merge pull request #705 from iamgiko/main. _Update translations_ * Fink 🐦: update translations. * Fink 🐦: update translations. * Update LICENSE.md. * Update LICENSE.md. * Update README_ru.md. * Update README_ja.md. * Update README_cn.md. * Update README_ja.md. * Update README_br.md. * Update README_fa.md. * Update README.md. * Merge pull request #698 from betaxab/p1. _inlang: update translations_ * Inlang: update translations. ## v1.1.1 (2024-03-20) #### New * Add tproxy. #### Fix * Gvisor. * Issue. #### Other * Merge branch 'main' of hiddify-github:hiddify/hiddify-next. * Merge pull request #696 from HSSkyBoy/main. _Update translations (zh_TW and zh_CN)_ * 更新 strings_zh-TW.i18n.json. * 更新 strings_zh-TW.i18n.json. * Update strings_zh-TW.i18n.json. * 更新 strings_zh-TW.i18n.json. * Change APP name. * 更新 strings_zh-TW.i18n.json. * 更新 strings_zh-CN.i18n.json. * Bump version. * Merge pull request #692 from sky96111/main. _Update translations_ * Fink 🐦: update translations. * Merge pull request #686 from sky96111/main. _feat: Use dark tray icon in light theme_ * Feat: dark tray icon in light theme. * Merge pull request #690 from imshahab/m3-dynamic-theme. _Implement Material 3 dynamic theming_ * Implement Material 3 dynamic theming. * Merge pull request #685 from Nyar233/main. _Update translations_ * Fink 🐦: update translations. * Merge pull request #682 from alirezafarvardin/main. _Update translation (Persian)_ * Merge branch 'main' into main. * Update bug_report.yaml. * Update bug_report.yaml. * Update bug_report.yaml. * Update bug_report.yaml. * Update bug_report.yaml. * Update bug_report.yaml. * Fink 🐦: update translations. * Fink 🐦: update translations. ## v1.0.0 (2024-03-18) #### New * Add review. #### Fix * Isssue with singbox 1.8.9. * Ios. * Implementation. * Test, update to singbox 1.8.9. * Bug in warp config. #### Other * Update release_message.md. * Update README.md. * Update README_br.md. * Update README_ja.md. * Update README_cn.md. * Update README_ru.md. * Update README_ru.md. * Update README_fa.md. * Update README_ru.md. * Update README_ru.md. * Update README_cn.md. * Update README_ja.md. * Update README_br.md. * Update README.md. * Update release_message.md. * Update README.md. * Fix postServiceClose implementation. * Merge branch 'main' of hiddify-github:hiddify/hiddify-next. * Fink 🐦: update translations. * Remove mux. * Merge branch 'pr/alikhabazian/668-1' * Resolve qr code permssion issue. * Merge branch 'main' of hiddify-github:hiddify/hiddify-next. * Merge pull request #668 from alikhabazian/newyear. _add image field to ConnectionButton_ * Add useImage feild for change image in every newyear and increase quality. * Add image field to ConnectionButton. * Update. * Merge pull request #653 from alirezafarvardin/main. _Update translations_ * Inlang/manage: add languageTag zh-TW. * Inlang/manage: add languageTag zh-CN. * Inlang/manage: add languageTag tr. * Inlang/manage: add languageTag ru. * Inlang/manage: add languageTag pt-BR. * Inlang/manage: add languageTag id. * Inlang/manage: add languageTag es. * Inlang/manage: remove languageTag es. * Inlang/manage: add languageTag es. * Inlang/manage: remove languageTag zh-CN. * Inlang/manage: remove languageTag id. * Inlang/manage: remove languageTag ru. * Inlang/manage: remove languageTag tr. * Inlang/manage: remove languageTag es. * Inlang/manage: remove languageTag zh-TW. * Inlang/manage: remove languageTag pt-BR. * Merge branch 'hiddify:main' into main. * Fink 🐦: update translations. * Test flight only release. * Add core grpc server. ## v0.17.12 (2024-03-14) #### New * Refactor testflight. * Add installation dependencies for apple. * Add ios build process. * Add Indonesian language. * Add hiddifycli and resolve all false positve from anti virus, auto exit during installation. * Add support for flag emoji in proxy names. * Add version draft. * Auto replace signed exec. * Update to singboc 1.8.7. * Enable tunnel service again, add signing, msix, more log less info. * Add ipwho.is. * Use timezone for location detector. * Add warp fake packet delay, add warp detour, add new ipinfo api. #### Fix * Apple release. * Bug. * No commit message. * Release to ios. * Ios publishing. * Var. * Var. * Vaiable issue. * Potential fix to issue in some windows? * Msix package. * Signing. * Version. * Pub get issue? * Build. * Flag. * Ip flag. * Bug in versioned draft. * Invalid version string for draft. * Action bugs. * Signing. * Signing. * Cert? * Generate Warp Config on iOS. _+ pod install new packages_ * Msix issue. * Appimage fixed. issue: #513. * Name. * Intro page bug and fakepacket delay bug. * Exception reporting on failing in getting ip. * Exception when there is no active profile. * Ip info. * Build bug. * Translation bug, disable signing for pull req. * Bug in change interface listener. * Update download paths in release page. * Logo, add name for hiddify warp sub. #### Other * Update. * Update. * Update. * Update. * Update. * Disable: permission handler for windows. * Disable redundent action run for release. * Merge pull request #647 from hiddify/ios. _Ios_ * Upload versioned. * Update. * Update. * Update ios release. * Upload to appstore in macos. * Test testflight upload. * Fix name issue. * Update. * Update. * Update. * Add provisioning profiles. * Add SingBoxPacketTunnel. * Update. * Update. * Add prereq of ios. * Allow ci build from all branches. * Revert changes for test. * Test disabling system tray. * A test for possible bug in some windows. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Update signing. * Downgrade flutter action. * Merge pull request #621 from hei-hilman/main. _add new translation (Indonesian)_ * Add indonesian translation. * Merge branch 'main' of hiddify-github:hiddify/hiddify-next. * Fix qr code scanner. * Change translation keys. * Change experimental feature notice. * Merge branch 'main' of hiddify-github:hiddify/hiddify-next. * Change sidebar stats. * Add emoji font for flags. * Use software rendering for windows. may resolve white screen? * Update README_br.md. * Update README_ja.md. * Update README_ru.md. * Update README_cn.md. * Refactor config options. * Merge branch 'be1c1bb580ec039044e8057ae1469153008cdb4d' * Add reconnect and animations to connection button. * Add quick settings. * Update release_message.md. * Bump Flutter (v3.19) and dependencies. * Remove redundant logs. * Change db initialization. * Remove draft version. * Make it universal for all os. * Add versioned draft. * Simple debug to bug of some windows. * Update README_fa.md. * Update README.md. * Update README.md. * Update README.md. * Update README.md. * Update README.md. * Update README.md. * Add Config options import. * Disable signing. * Add hide icon from dock on mac. * Add reconnect alert for config options. * Refactor preferences. * Merge pull request #585 from amirsaam/main. * Update msix. * Merge pull request #577 from Aryangh1379/Hiddify-develop. * Merge pull request #572 from Aryangh1379/Hiddify-develop. _Hiddify develop_ * Forgot to remove -x from !#/bin/bash :) * Add AppRun parameters - provided a help menu. * Temporary disable windows service. * Update README.md. * Update README_ru.md. * Update README_cn.md. * Update README_cn.md. * Update README_cn.md. * Update README_ru.md. * Update README_ru.md. * Update README_ja.md. * Update README_fa.md. * Update README_br.md. * Merge pull request #532 from mascenaa/main. _Add README in Portuguese_ * Adding a README in Portuguese. * Merge pull request #556 from Aryangh1379/Hiddify-develop. _AppRun file error fixed. AppImage now is usable_ * AppRun file error fixed. AppImage now is usable. * Merge pull request #549 from pierrot-p/main. _[PT-BR] inlang: Added missing translations_ * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Merge pull request #546 from nya-main/main. _update translations(CN)_ * Manually repair an entry. * Inlang: update translations. * Merge pull request #547 from amirsaam/main. _macOS AppIcon_ * MacOS AppIcon. * Add timezone based region detection. * Url test is only valid for group not a single proxy. * Potentioal fix for crash in some windows. * Bump libcore. * Excluding aab from building draft. * Remove aab draft build. * Change warp options. * Fix Portuguese locale. * Add build test. * Update dependencies. * Change ip error. * Remove extra warp configs. * Update README_ru.md. * Merge pull request #509 from nevazno00/patch-1. _Update README_ru.md_ * Update README_ru.md. * Merge pull request #527 from mascenaa/main. _Add a PT-BR Translate file._ * Add a PT-BR Translate file. * Merge pull request #508 from kouhe3/zh-cn-i18n-patch-2. _strings_zh-CN.i18n.json_ * Merge branch 'main' into zh-cn-i18n-patch-2. * Merge pull request #523 from nya-main/main. _Update translations_ * Merge branch 'main' into main. * Merge pull request #528 from KintaMiao/main. _Update translations_ * Inlang: update translations. * Inlang: update translations. * Fix build. * Add more haptic feedback. * Zh-cn translate. * Detailed subscription info. * Add detailed subscription info. * Fix test. * Add ci test. * Add warp config generator. * Update changelog. * Change ip refresh. * Add auto ip check option. * Change ip error refresh button. * Change mapping. * Fix issue in installing packages in ubuntu. * Fix connection on desktop. * Change interval mapping. * Fix chinese locale. * Add haptic feedback. * Format zh-CN.i18n.json And remove duplicate. * Merge pull request #410 from junlin03/main. _更新繁體中文翻譯_ * Merge branch 'main' into main. * Merge pull request #482 from guoard/fix/downlaod_path. _fix: update download paths in release page_ * Merge pull request #481 from amirsaam/main. _Bugs in config_option_entity.dart_ * Update config_option_entity.dart. * Bugs in config_option_entity.dart. * Change mapping and bug fixes. * Change icons. * Improve ping indicator. * Change service mode choices. * Fix ip widget directionality. * Merge branch 'main' into main. * Inlang: update translations. ## v0.15.8 (2024-02-14) #### New * Update icon. * Add seperated VPN service mode. * Add some example of new warp configs. * Update active profile and its ping, * Change app icon to stable. * Add unavailble mark, fix: windows release bug. #### Changes * Change name to Hiddify. #### Fix * Appimage tunnel service bug. #### Other * Change ip obscure. * Improve accessibility. * Change service error handling. * Fix connection info. * Fix widget build conflict. * Revert build image for appimage to 22.04. * Update README_cn.md. * Update README.md. * Update README.md. * Update README_ja.md. * Update README_fa.md. * Update README_ru.md. * Update README_ru.md. * Update README_cn.md. * Update README_cn.md. * Update README_ja.md. * Update README_fa.md. * Update README.md. * Merge pull request #454 from amirsaam/main. _Update iOS Icons_ * AppIcon Update. * Update iOS Icons. * Add proxy http adapter. * Fix android service mode. * Merge pull request #453 from amirsaam/main. _Update Makefile_ * Update Makefile. * Change routing setup. * Fix connection info ui. * Make ip private for both footer and sidebar. * Make ip anonymous. * Add syncthing ignore files. * Add desktop active proxy info. * Add active proxy delay indicator. * Fix commands. * Change translation fallback strategy. * Update CONTRIBUTING.md. * Merge pull request #439 from amirsaam/main. _Fix iOS Logger_ * Fix iOS Logger. ## v0.15.6 (2024-02-09) #### New * Add macos pkg file and remove zip wrapper. * Add custom AppRun: need forked distributor. * Add intro based on user lang and region. * Hide hidden nodes. * Move running admin service to core. * Display go errors. * Add tunnel service for windows and linux. * Show groups always on top. * Change update time when selected. * Make activated profile always in top. * Add postfix to name if it is not unique. * Add link parser and allow custom configs to be imported. #### Changes * Use our flutter distrobutor version and downgrade windows version (possible fix of build error) #### Fix * Unix copy bug. * Build linux. * Dynamic varible in github action. * Windows? * Windows build? * Typo. * Windows build. * Appimage build and add make req for linux. * Service location bug in linux. * Update of protocol handler. * Bug in logging go erros. * Check core before release. #### Other * Refactor build. * Add debug build for windows. * Rerun windows without cache. * Add ios connection info. * Fix ui inconsistency. * Fix local build commands. * Fix android bugs. * Add connection info footer. * Add ip info fetcher. * Merge pull request #416 from EbrahimTahernejad/patch-1. _Make logger global_ * Use shared instance of logs to log. * Added shared instance for LogsEventHandler. * Merge pull request #414 from amirsaam/main. _Log for iOS by @ Akuma_ * Log for iOS by @ Akuma. * Add ios command client. * Change make flags. * Change the name of tunnel service to HiddifyService. * Make makefile cross platform. * Merge branch 'main' of hiddify-github:hiddify/hiddify-next. * Add cloudflare warp options. * Temporary disable app image build. * Change again to ubuntu 22.04. * Update ci.yml. * Disable tunnel service for mac. * Merge branch 'main' of hiddify-github:hiddify/hiddify-next. * Remove unnecessary dependencies. * Fix mobile routing behavior. * Bump java version. * Update dependencies. * Fix intro page margin. * Send panic to sentry. * Merge branch 'main' of hiddify-github:hiddify/hiddify-next. * Merge pull request #371 from eltociear/add_ja-readme. _Add Japanese README_ * Add Japanese README. * Merge pull request #389 from leic4u/patch-1. _Update README_cn.md_ * Update README_cn.md. * Update README.md. * Add mux to experiments && update translations. ## v0.15.4 (2024-01-26) #### Fix * Fragment bug. ## v0.15.3 (2024-01-26) #### New * Add warp option (experimental) #### Fix * Typo. #### Other * Bump to singbox 1.8.4, and fix bugs. * Refactor makefile. * Update Hiddify Packages. * Update README_ru.md. * Update README_cn.md. * Update README_fa.md. * Update README.md. * Update README_fa.md. * Update to core 0.12.1, singbox 1.8.2. * Update build.yml. * Make 1.1.1.1 as default dns server. * Speed up ping. * Update libcore. ## v0.14.20 (2024-01-21) #### Other * Update feature_request.yaml. * Update bug_report.yaml. ## v0.14.11 (2024-01-20) #### New * Add deb and rpm build. #### Fix * Versioning issue in ios singbox platform extension. * Release bug. * Release bug. * Build linux. * Stats bug in iOS. * Some bugs in ios. #### Other * Merge pull request #351 from amirsaam/main. _use universal bundleid from base.xconfig_ * Use universal bundleid from base.xconfig. * Merge pull request #350 from amirsaam/main. _ios log handler base functionality_ * Log handler base functionality. _+ fix makefile ios-temp-prepare upgrading pub + remove tvOS from libcore local spm +_ * Add mux options. * Add mux. * Update release_message.md. * Update build.yml. * Update changelog. * Update readme. * Fix ios stats. * Add reset tunnel option on ios. * Update build.yml. * Fix release. * Fix release. * FIX: RELEASE BUG. * Fix release bug in macos. * Update build.yml. * Update build.yml. * Update build.yml. * Update dependencies. * Fix not releasing linux packages. * Merge pull request #347 from amirsaam/main. _fix connection bug after bundle update_ * Fix connection bug after bundle update. * Merge pull request #345 from amirsaam/main. _update to latest itunes connect_ * Update to latest itunes connect. * Revert changes for swift package. * Merge pull request #344 from amirsaam/main. _switch to local spm to load libcore_ * Merge branch 'main' of https://github.com/amirsaam/hiddify-next. * Merge branch 'hiddify:main' into main. * Remove tvOS in singbox target. * Remove tvOS, previously added apple vision for ipad. * Handle xcode crash. * Switch to local spm. * Fix make ios. * Bumpcore version to v0.11. * Merge branch 'main' of hiddify-github:hiddify/hiddify-next. * Merge pull request #343 from amirsaam/main. _url scheme + target dependency + general update_ * Merge branch 'hiddify:main' into main. * Add url scheme. * Update Package.resolved. * Target dependancy + pod update, package resolve. * Remove hiddify://import/ from export option. * Merge pull request #342 from amirsaam/main. _getting libcore from spm, adding landscape mode_ * Merge branch 'main' of https://github.com/amirsaam/hiddify-next. * Delete pubspec.lock. * Revert some changers. _This reverts some of commit dde7c2419a12e0f36976f4c0ff8e66d882ec62ac._ * Update pod, getting libcore from spm, adding landscape mode. * Change profile options modal. * Fix infinite sub expire date (#334) _* Fix infinite sub expire date * fix expire * fix build * refactor * make it better readable * Fix infinite sub * Add test for infinite sub ---------_ * Fix fragment bugs. * Merge pull request #340 from amirsaam/main. _fix ui not showing connected after relaunch_ * Fix ui not showing connected after relaunch. * Merge pull request #338 from amirsaam/main. _add push notification entitlement_ * Merge branch 'main' of https://github.com/amirsaam/hiddify-next. * Add config export on ios. * Fix ios connection status on app restart. * Add push notification entitlement. * Fix attempt. * Fix. * Fix ci again. * Fix ci tag matching. * Fix ci. * Change logger to print in console. * Change routing assets order. * Change options description. * Fix ci tag pattern. * Fix ci. * Change workflows. * Add missing es translations. * Inlang: update translations. * Fix infinite sub traffic. * Fix bugs. * Fix modal text alignment. * Merge pull request #329 from amirsaam/newMain. _revert landscape mode_ * Revert landscape mode. * Merge pull request #328 from amirsaam/main. _remove unnecessary force unwrap, libcore linking add landscape mode_ * Remove unnecessary force unwrap, libcore linking add landscape mode, * Change ios method error handling. * Fix bugs. * Remove custom_lint. * Add vscode launch config macos designed for ipad. * Fix ios flutter plugins. * Fix ios core name. * Merge pull request #323 from amirsaam/main. _better fix for vpn connecting_ * Better fix for vpn connecting. * Merge pull request #321 from amirsaam/main. _match exportOptions identifiers with project_ * Fixing not connecting to vpn. * Match exportOptions identifiers with project. * Merge pull request #319 from amirsaam/main. _fix vpn profile not being created + changing run to release_ * Fix vpn profile not being created + changing run to release. * Merge pull request #318 from amirsaam/main. _ios fix?_ * Ios fix? * Fix build. * Fix windows deep link. * Merge pull request #316 from amirsaam/main. _Update pubspec.lock_ * Update pubspec.lock. * Merge pull request #315 from amirsaam/main. _add back cupertino_http_ * Add back cupertino_http. * Update. * Merge branch 'main' of https://github.com/hiddify/hiddify-next. * Update appcast. ## v0.13.6 (2024-01-07) #### Other * Fix build. * Merge branch 'main' of https://github.com/hiddify/hiddify-next. * Fix qr scanner links. * Update changelog. * Update core (sing-box v1.7.8) * Update dependencies. * Remove unused dependencies. * Refactor service wrappers. * Update. * Update ios. * Change default connection test url. * Fix android service mode switch. * Add profile fetch cancel. * Add update all subscriptions. * Fix profile update service. * Inlang: update translations. * Change app http client. * Inlang: update translations. * Fix bugs. * Add experimental flag to new config options. * Add connection from lan option. * Add dns routing option. * Add bypass lan option. * Fix profile edit with new url. * Change tls tricks settings section name. * Fix db migration bug. * Bump version. * Fix minor bugs. * Remove desktop auto connect. * Add experimental feature notice. * Update README.md. * Update README.md. * Fix multi instance on windows. * Inlang: update translations. * Inlang: update translations. * Update README_cn.md. * Update README_cn.md. _Some proxy software such as clash do not have official Chinese names and use English by default._ * Add experimental flag in settings ui. * Fix inlang setup. * Remove robocopy. * Fix build. * Fix windows portable again. * Update changelog. * Add android high refresh rate support. * Refactor desktop window management and tray. * Fix macOS restore from dock. * Add desktop shortcuts. ## v0.12.3 (2023-12-28) #### Changes * Fix inconsistent channel naming. #### Fix * Set codec for stream handlers. * Ios launch bug. #### Other * Add Afghanistan region. * Fix packaging bug. * Fix windows packaging. * Fix bugs. * Change minimum macos version in docs. * Add version to desktop window. * [fix]: SettingsPickerDialog pop exception. * Update ios. * Ios: add /platform event channel. * Feat: Added stats stream for ios. * Feat: Added stats stream for ios. * Update ios. * Update ios. * Update ios. * Merge branch 'main' of https://github.com/hiddify/hiddify-next. * Ios update. ## v0.12.2 (2023-12-23) #### Other * Remove native http client (temporarily) * Update dependencies. * Update core (sing-box v1.7.6) * Fix bugs. * Update core (sing-box v1.7.5) * Change initialization and logging. ## v0.12.1 (2023-12-21) #### Other * Fix preferences initialization error. * Bump android sdk version. * Fix android service mode. * Change privacy policy url. * Update appcast. ## v0.12.0 (2023-12-20) #### New * Add support for hysteria and wg. * Add user-agent like clash sing-box for better compatibility. * Send all releases to beta by default. #### Fix * Bug in windows portable. * Linux parse. #### Other * Add hiddify/iimport schema. * Fix chinese inlang url. * Update README_ru.md. * Update README_cn.md. * Update README.md. * Update README.md. * Update README_ru.md. * Update README_cn.md. * Update README.md. * Update README_fa.md. * Update README_fa.md. * Update README_fa.md. * Update README.md. * Update changelog. * Change memory limit on desktop. * Remove locale name provider. * Fix service mode. * Add android dynamic notification. * Add android stats channel. * Fix translation generator. * Add config option reset. * Add missing routing asset warning. * Fix text input traversal. * Add basic d-pad support. * Fix tray initialization bug. * Update LICENSE.md. * Update LICENSE.md. * Update LICENSE.md. * Update README_fa.md. * Update README_fa.md. * Update README_cn.md. * Update README_ru.md. * Update README.md. * Add preferences migration. * Fix tray behavior. * Fix intro routing bug. * Fix macos build. * Fix macos name. * Fix macos icon. * Update README.md. * Update README.md. * Update README.md. * Update ios. * Update ios. * Update ios. * Merge branch 'main' of https://github.com/hiddify/hiddify-next. * Fix build. * Change entrypoint. * Add TLS trick config options. * Update ios. * Update platform interface. * Merge branch 'main' of https://github.com/hiddify/hiddify-next. * Update dev-i.yml. * Update dev-i.yml. * Update pubspec.yaml. * Create dev-i.yml. * Update changelog. * Update dependencies. * Update core (singbox v1.7) * Fix status mapper. * Refactor. * Refactor logs. * Remove unused clash api. * Update core (singbox 1.7.0-rc.2) * Merge branch 'main' of hiddify-github:hiddify/hiddify-next. * Refactor router. * Refactor profiles. * Update changelog. * Fix chinese typography bug. * Add soffchen geo assets. * Refactor geo assets. * Update dependencies. * Bump ci sdk version. * Bump sdk version. * Update README.md. * Update README_fa.md. * Update README_cn.md. * Update README_ru.md. * Update README_ru.md. * Update README.md. * Update README.md. * Update release_message.md. * Update appcast. * Merge pull request #189 from jomertix/main. _Update ru strings_ * Nothing changed strings_tr.i18n.json. * Nothing changed strings_fa.i18n.json. * Inlang: update translations. * Inlang: update translations. * Update. ## v0.11.1 (2023-11-19) #### Other * Fix android manifest. * Fix documentation. * Update release_message.md. ## v0.11.0 (2023-11-19) #### New * Add ic_launcher for android tv. * Publish dev release to seperate channel. * Add winget. * Add prepare to make file. #### Fix * Build-linux-libs. * Build. * Naming issue. * Winget release. * Action version. * Winget publish. * Build error. * No commit message. * Build. * Bug. * Build. * Distutils. * No commit message. #### Other * Change default direct dns. * Revert "new: add ic_launcher for android tv" _This reverts commit 49bb62c8289d0885a762e01b7f0785690a7d3af7._ * Change ir region rules. * Merge pull request #179 from betaxab/main. _Update translations_ * Inlang: update translations. * Add recommended geo assets. * Add error handling for geo assets. * Fix build. * Add geo assets settings. * Fix bug in strings_tr.i18n.json. * Merge pull request #173 from hasankarli/main. _Update Turkish translations_ * Inlang: update translations. * Merge pull request #169 from solokot/main. _Update Russian translation_ * Merge branch 'main' into main. * Merge pull request #168 from jomertix/main. _inlang: update translations_ * Inlang: update translations. * Merge pull request #172 from Locas56227/main. _Fix and improve Chinese README_ * Fix and improve Chinese README. * Update build.yml. * Update appcast. * Add navigation to system tray. * Update Russian translation. _and partially revert ugly inlang changes_ * Change http adapter. * Add independent dns cache option. * Fix bottom navigation bar accessibility. * Update README_ru.md. * Update README_cn.md. * Update README_fa.md. * Update README.md. * Update release_message.md. * Update README_ru.md. * Update README_cn.md. * Update README_fa.md. * Update README.md. * Merge pull request #165 from huajizhige/main. _Improve translation_ * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Update appcast. * Fix release command. * Add subscription qr code share. * Improve qr code scanner ux. * Fix code typo. * Ci: ignore appcast. * Merge pull request #164 from jomertix/main. _Update ru string_ * Update strings_ru.i18n.json. _Fix translation_ * Update strings_ru.i18n.json. _Fix translate_ * Merge pull request #162 from solokot/main. _Update strings_ru_ * Update strings_ru. _Fix google translate_ * Merge pull request #161 from Aloxaf/Aloxaf-patch-1. _fix: build-linux-libs_ * Add sub link share. * Fix bootstrap bug. * Add proxy tag dialog. * Add export config to clipboard. * Add geoassessts to makefile prepare. * Merge pull request #155 from solokot/main. _Update strings_ru_ * Update strings_ru. * Add service mode and strict route. * Add basic privilege check. * Fix ui bugs. * Remove unnecessary preferences. * Fix config preferences. * Update changelog. * Update core to v0.8.0. * Fix manual update checker. * Fix logs page scroll. * Fix android service restart. * Fix android service revoke. * Change toast. * Fix linting and cleanup. * Remove execute config as is. * Update dependencies. * Update README_cn.md. * Update README_cn.md. * Update README_ru.md. * Update README_cn.md. * Update README_fa.md. * Update README.md. * Merge branch 'main' of hiddify-github:hiddify/hiddify-next. * Merge pull request #138 from huajizhige/main. _inlang: update translations_ * Inlang: update translations. * Update. * Fix. * Fix. * Test. * Merge pull request #137 from huajizhige/patch-1. _Update README_cn.md_ * Update README_cn.md. _Improve translation_ * Update README_cn.md. * Update README_cn.md. * Update README_ru.md. * Update README_cn.md. * Update README_ru.md. * Update README_fa.md. * Update README.md. * Update README.md. * Fix. * Update. * Fix. * Fix build. * Add distutils. * Improve Android TV. * Update. * Add. * Fix appcast url. * Add appcast. * Add auto connect on start. * Merge pull request #123 from Nyar233/main. _Update translations_ * Inlang: update translations. * Update release_message.md. * Update release_message.md. * Merge pull request #115 from solokot/main. _Update Russian: fix google translate_ * Update Russian: fix google translate. * Merge pull request #113 from Nyar233/main. _inlang: update translations_ * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Inlang: update translations. * Change system tray options. * Add core mode. * Add sidebar stats for large screens. * Update dependencies. * Change router for different screen size. * Merge pull request #108 from Hiiirad/patch-1. _Update auto_translator.py to fix the Path Traversal Vulnerability_ * Update auto_translator.py to fix the Path Traversal Vulnerability. ## v0.10.0 (2023-10-27) #### New * Add ios core library to the project. #### Fix * Ipa. #### Other * Remove reconnect on auto profile update. * Update changelog. * Add selected tag for selector outbounds. * Add core debug flag. * Add url test delay color. * Change info logs. * Delete .github/help/linux/آموزش هیدیفای‌نکست فارسی لینوکس.desktop. * Delete .github/help/mac-windows/آموزش هیدیفای‌نکست فارسی.url. * Update dependencies. * Change pre release update checking off. * Add terms and privacy to about page. * Add memory limit option. * Change directory management. * Comment ios. * Change app id. * Merge pull request #98 from GFWFighter/main. _Initial iOS version_ * Merge pull request #1 from GFWFighter/ios. _iOS_ * Merge branch 'main' into ios. * Merge branch 'main' of hiddify-github:hiddify/hiddify-next. * Merge pull request #95 from solokot/main. _Update Russian_ * Update Russian. _Mainly fix google translate of new worlds_ * Change logging. * Change changelog workflow. * Add russia region. * Change theme prefs. * Merge pull request #90 from solokot/main. _Russian translation: fix some mistakes_ * Russian translation: fix some mistakes. * Update README.md. * Update README.md. * Update README.md. * Update README.md. * Update README.md. * Add rules. * Update LICENSE.md. * Inlang: update translations. * Merge pull request #85 from leic4u/main. _Update README_cn.md_ * Update README_cn.md. * Merge branch 'main' of hiddify-github:hiddify/hiddify-next. * Add inlang translations. * Add inlang project and remove localize. * Merge pull request #82 from Iam54r1n4/main. _add inlang translator_ * Update: fr_FR.json. * Add: french language. * Add: inlang translation files(incomplete) * Add: setup inlang translation. * Merge pull request #74 from lifeindarkside/patch-1. _Update strings_ru.i18n.json_ * Update strings_ru.i18n.json. _Update some wrong interpretation_ * Update README_ru.md. * Update README_cn.md. * Update README.md. * First working version. * Underlying VPN Logic. * Initialize Packet Tunnel + Config. * Update Tutorial_For_HiddifyNext_Linux.desktop. * Update Tutorial_For_HiddifyNext.url. * Update آموزش هیدیفای‌نکست فارسی.url. * Update آموزش هیدیفای‌نکست فارسی لینوکس.desktop. * Update README_fa.md. * Update README_fa.md. * Update README_ru.md. * Update README_cn.md. ## v0.9.2 (2023-10-15) #### Other * Fix ndk setup. * Fix android arm bug. ## v0.9.1 (2023-10-15) #### Other * Update README_cn.md. * Update README.md. * Update README_fa.md. * Update README.md. * Update README.md. * Update README_ru.md. * Update README_cn.md. * Update README_fa.md. * Update README.md. * Delete docs/file. * Google play badge. * Delete docs/google-play-badge1.png. * Upload google play badge. * Create file. * Delete docs/google-play-badge.png. * Fix ci. * Change ndk setup. * Update dependencies. * Update README_ru.md. * Update README_cn.md. * Update README_ru.md. * Update README_ru.md. * Change desktop error handling. * Fix android bugs. * Update README.md. * Update README_cn.md. * Update README_ru.md. * Update README_ru.md. * Update README_cn.md. * Update README_fa.md. * Update README.md. * Delete Russian_Flag.png. * Add Russian flag. * Delete docs/file. * Create README_ru.md. * Delete REAMME_ru.md. * Create REAMME_ru.md. * Update README_cn.md. * Update README_fa.md. * Update README_fa.md. * Update README.md. * Update README_cn.md. * Update README_cn.md. * Update README_fa.md. * Update README.md. * Update README.md. * Update file. * Uplaod a badge for google play. * Delete google-play-badge.png. * Add files via upload. * Create file. * Delete docs/google-play-badge.png. * Delete google-play-badge.png. * Add files via upload. * Update README_fa.md. * Update README.md. * Update README.md. * Create Chinese README_cn.md. ## v0.8.12 (2023-10-13) #### Fix * Typo. * Bug. * Release names. #### Other * Update readme. * Update core. * Update README_fa.md. * Update README_fa.md. * Update README.md. * Merge pull request #56 from solokot/main. _Improvement of Russian translation_ * Improvement of Russian translation. _Basically a replacement for machine automatic translation_ * Update README.md. * Update README_fa.md. * Update README_fa.md. * Update README.md. * Update README_fa.md. * Update README.md. * Update README.md. * Update README_fa.md. * Update README_fa.md. * Update README_fa.md. * Update README.md. * Update README_fa.md. * Update README.md. * Update README_fa.md. * Update README_fa.md. * Update README.md. * Update README_fa.md. * Update README_fa.md. * Update README.md. * Update README.md. * Update README_fa.md. * Update README.md. * Update README.md. * Update README_fa.md. * Update README_fa.md. * Update README_fa.md. * Fix bugs. * Change android signing. * Update README.md. * Update readme. * Update contribution guide. * Update README_fa.md. * Update README.md. * Update README_fa.md. * Update README_fa.md. * Update release_message.md. * Update README.md. * Update README_fa.md. * Update README_fa.md. * Update README.md. * Update README_fa.md. * Update release template. ## v0.8.11 (2023-10-08) #### Changes * Remove auto release message. ## v0.8.10 (2023-10-08) #### Fix * Release changelog. ## v0.8.9 (2023-10-08) #### Fix * Missing libs. ## v0.8.8 (2023-10-08) #### Fix * Release bug. ## v0.8.7 (2023-10-08) #### Fix * Release message. ## v0.8.6 (2023-10-08) #### Fix * Windows build. * Build issue. #### Other * Merge branch 'main' of hiddify-github:hiddify/hiddify-next. * Update release_message.md. * Merge branch 'main' of hiddify-github:hiddify/hiddify-next. * Update release_message.md. * Update README.md. * Update README_fa.md. * Delete docs/note. * Add files via upload. * Create note. * Update contribute.md. * Update README.md. * Delete assets/images/google-play-badge.png. * Update README_fa.md. * Update README.md. * Update release_message.md. * Update release_message.md. * Update release_message.md. * Fix build. ## v0.8.5 (2023-10-07) #### Fix * Bug. ## v0.8.4 (2023-10-07) #### Fix * Translate. ## v0.8.3 (2023-10-07) #### Other * Add release message and help. * Merge branch 'main' of hiddify-github:hiddify/hiddify-next. * Fix bugs. * Update release_message.md. * Update release_message.md. * Update README_fa.md. * Update README.md. * Update release_message.md. * Update release_message.md. * Create release_message.md. * Add google play badge to assets. * Update README.md. * Update README_fa.md. * Update README_fa.md. * Update README.md. * Update README.md. * Create contribute.md. * Update README_fa.md. * Update README.md. * Update README_fa.md. * Update README_fa.md. * Update README.md. * Update README.md. * Update README_fa.md. * Add feature request template. * Fix issue template. * Add issue template. ## v0.8.2 (2023-10-07) #### Fix * Hysteria2 and some links. #### Other * Update README_fa.md. * Update README_fa.md. * Update README_fa.md. * Update README.md. * Update README.md. * Update README.md. * Update README.md. * Update README.md. * Update README.md. * Update README.md. * Update README.md. * Update README.md. * Update README.md. * Update README.md. * Update README.md. * Create README_fa.md. * Update README.md. * Update README.md. * Update README.md. _Add some features to the readme_ * Add debug export to clipboard. ## v0.8.1 (2023-10-06) #### New * Add chinese lang. #### Fix * Chinese translation. #### Other * Update core 0.5.1. * Fix floating number sub info header. * Add russian. * Add google play descriptions. * Merge branch 'main' of hiddify-github:hiddify/hiddify-next. ## v0.8.0 (2023-10-05) #### New * Add russian lang. #### Other * Add proxy tag sanitization. * Fix bugs. * Add ignore app update version. * Add new protocols to link parser. * Auto update translations on release. * Update translation. * Remove param in ru translation. ## v0.7.2 (2023-10-04) #### Other * Fix bugs. ## v0.7.1 (2023-10-03) #### Other * Fix log and analytics bugs. ## v0.7.0 (2023-10-03) #### New * Add support of some exception panel with zero usage. #### Other * Fix translation bug. * Improve error handling and presentation. * Add retry for network ops. * Merge branch 'main' of hiddify-github:hiddify/hiddify-next. * Add local profile. * Add auto translator. * Add auto translate. ## v0.6.0 (2023-09-30) #### Other * Fix minor bugs. * Add scheduled profile update. * Update dependencies. * Refactor profile details page. ## v0.5.11 (2023-09-22) #### Other * Fix android bundle abi. * Bump core version. ## v0.5.10 (2023-09-22) #### Other * Fix minor bugs. * Fix profile update bug. ## v0.5.9 (2023-09-22) #### Other * Add build number. ## v0.5.8 (2023-09-22) #### New * Add crashlytics. * Add support for base64 sublink for header from content. * Add profile headers from comments in response! good for hosting in github and show information. * Automated version release. * Send release versions only to play market add pre-release version. #### Changes * Change invalid dns 235.5.5.5 to 8.8.8.8. #### Fix * Improve routing accessibility and logs. * Minor bugs. * Prefs persistence. * Crashlytics. * App update url. * Small profiles. * Makefile vars. * Adaptive icon. * Pre-release. * Typo in adaptive icon. * If .dev is exist in the version do not show update needed. * Keep the link as it is. fix the issue with & * Dependency issue. * Remove extra print. * Bug in get headers from body. * Bug ini ci to google play. * Tag version issue. * Ci bug. * Remove comments. * Bug. #### Other * Fix ci. * Fix false-positive error reports. * Change build setup. * Fix minor bugs. * Refactor app update. * Fix sentry dart plugin upload. * Fix ci debug symbols upload. * Add sentry provider observer. * Ci: add sentry debug info upload. * Update dependencies and general fixes. * Chore: bump agp version. * Ci: fix env. * Ci: add dsn env. * Feat: add region and terms to intro. * Update ci.yml. * Build: add sentry dsn. * Feat: add intro screen. * Feat: add sentry. * Ci: bump macos version. * Feat: update profile when adding preexisting url. * Publish draft even with error. * Update version of core. * Merge branch 'main' of hiddify-github:hiddify/hiddify-next. * Add firebase. * Update translation. * Refactor: version presentation. * Perf: improve header parser. * Feat: remove check for updates in market releases. * Better manage the market release. * Update. * Merge branch 'main' of https://github.com/hiddify/hiddify-next. * Improve accessability. * Fix per-app proxy selection. * Add android per-app proxy. * Add basic flavors. * Update Makefile. * Update ci.yml. * Update ci.yml. * Update Makefile. * Update ci.yml. * Update ci.yml. * Update README.md. * Add accessability semantics. * Add support for fragment in the url. ## v0.1.0 (2023-09-11) #### New * Add signing to android. * Add android universal build also. * Add change in network entitlements. * Change logo icon to next. * Add cache for speed up build process. * Add windows portable version. * Add libclash. * Add as draft release. * Make better ci and building applications. #### Changes * Change windows logo. * Change macos build to flutter_distributor. * Remove x86 builds since flutter does not support. * Add x64 to the name. * Update makefile. #### Fix * Space bugs in some panels. * Aab. * Aab build. * Universal sign. * KeystoreProperties. * Name issue. * Revert package name change. * Name. * Geosite download. * Windows portable bug. * Change name bug. * Setup exe files. * Libclash.so. * Linux AppImage. * Error. * Category in linux. * Add fuse for linux. * Icon. * Linux logo. * Android. * Makefile error. #### Other * Release v0.1.0. * Fix ci build. * Update ci. * Add core version. * Fix barrel file. * Change proxies lifecycle. * Add service restart. * Remove notification service. * Change android notification permission. * Add android service restart. * Change mark new profile active. * Handle unlimited. * Update upload download link stats. * Update Translations. * Remove // TODO add content disposition parsing. * Add content-disposition for profile title. * Update README.md. * Add hysteria2. * Fix android build connection. * Add android connection shortcut. * Add desktop autostart. * Add android boot receiver. * Add android proxy service. * Add android battery optimizations settings. * Change sharedpreferences to unify with android. * Add vclibs. * Update dependencies. * Add submodule. * Fix translation code gen. * Change prefs. * Change default config options. * Remove string casing. * Update ci.yml. * Remove caching. * Change core prefs to use code generation. * Fix custom lint. * Fix general issues. * Remove vclibs. * Update README.md. * Fix blank screen. * Add proxies sort. * Refactor preferences. * Update dependencies. * Add more config options to settings. * Add tun implementation option. * Add android power manager. * Add android quick tile. * Fix macos dependencies. * Fix mac build. * Fix build. * Change system tray icon. * Fix riverpod code generation. * Remove unnecessary config options. * Add accessability semantics. * Update dependencies. * Add config options. * Add pref utilities. * Add stats overview. * Update Translations. * Fix android outbounds view. * Remove unnecessary options. * Change proxies flow. * Add android command client support. * Update README.md. * Add status command receiver. * Add hiddify deeplink. * Add macos deeplink support. * Add singbox deeplink. * Change error prompts. * Fix url parser. * Remove unnecessary prefs. * Update dependencies. * Make AppImage zipped for preserving permission in linux. * Add user agent. * Fix uri launch. * Update ci flutter version. * Fix macos silent start. * Change proxies page. * Fix desktop connection error msg. * Add button tooltips. * Fix logging. * Create dependabot.yml. * Create CODE_OF_CONDUCT.md. * Fix aab. * Add aab file. * Change windows-portable name to HiddifyNext-portable. * Add debug mode. * Add misc settings ui. * Remote unnecessary logs. * Add misc preferences. * Add directory options. * Fix linux build. * Merge branch 'main' of https://github.com/hiddify/hiddify-next. * Fix android builds. * Fix android splash screen. * Update icons. * Fix windows executable build. * Remove unnecessary build steps. * Remove build number from appbar. * Update readme. * Change desktop directories. * Remove desktop notifications. * Force same version code for all platforms. * Add more logs. * Add log timestamp. * Update icons. * Remove drawer branding. * Add download section in readme. * Fix name of universal apk. * Fix order. * Update readme. * Update logging. * Update geo assets url. * Change linux directories. * Update icon. * Update icon. * Update logo for all platforms. * Revert name in macos. * Temporary disable app sandbox. * Change name. * Update Release.entitlements to fix binding issue. * Merge pull request #5 from evstegneych/main. _add macos option_ * Add macos option. * Update ci flutter version. * Update kotlin version. * Update other dependencies. * Update dependencies. * Update flutter version. * Update LICENSE.md. * Add build option for ios but not tested. * Return: build for all. * Fix build for macos. * Fix ci. * Migrate to singbox. * Fix routing. * Fix connection button text casing. * Add update checking. * Add separate page for clash overrides. * Add version number to appbar. * Add more icons. * Add sort limited profiles last. * Fix profile sort icon. * Fix profile traffic ratio. * Add profiles sort option. * Refactor profile addition flow. * Add extra profile metadata. * Fix linux deep link service. * Refactor profile tile. * Add locale based font. * Fix linux startup. * Update dependencies. * Add about page. * Fix linux notifications. * Build all. * Hard coding. * Add png. * Update ci.yml. * Update ci.yml. * Update ci.yml. * Update ci.yml. * Update ci.yml. * Update ci.yml. * Update ci.yml. * Update ci.yml. * Update Makefile. * Update ci.yml. * Update Makefile. * Update ci.yml. * Update ci.yml. * Update Makefile. * Update ci.yml. * Update Makefile. * Fix makefile. * Fix actions. * Fix actions. * Fix actions. * Update build setup. * Merge branch 'main' of https://github.com/hiddify/hiddify-next. * Create LICENSE.md. * Add core submodule. * Remove core libs. * Update Makefile. * Update Makefile. * Remove c install. * Update make. * Merge branch 'main' of https://github.com/hiddify/hiddify-next. * Add Persian font. * Update dependencies. * Update to libclash. * Add cache. * Add: new action. * Add write permission. * Fix. * Modify. * Fix. * Update. * Fix android build. * Fix code gen bug. * Update ci.yml. * Update ci.yml. * Fix CI. * Fix CI. * Merge branch 'main' of https://github.com/hiddify/hiddify-next. * Update ci.yml. * Update ci.yml. * Fix CI. * Update ci.yml. * Update ci.yml. * Add CI. * Add silent start for desktop. * Update readme. * Add headless mode to desktop. * Update dependencies. * Add distribution setup for windows. * Add Farsi(fa) language. * Merge branch 'main' of https://github.com/hiddify/hiddify-next. * Initial commit. * Initial. ================================================ FILE: LICENSE.md ================================================ # Hiddify Extended GNU General Public License v3 The Hiddify project is licensed under GPL v3, with the following additional conditions in accordance with GPL v3 Section 7. Failure to comply with these terms may result in requests to app stores to remove or restrict access to your app. # Additional Conditions to GPL v3: 1. **Source Code Availability:** If you use any part of this code, you must publish your source code on GitHub as a fork of the Hiddify repository and keep it up-to-date with any published app releases. Your repository should be shown as a fork of https://github.com/hiddify/hiddify-app . 2. **Automated Release:** All releases must be made using GitHub Actions. 3. **Attribution:** You must give appropriate credit to Hiddify and https://github.com/hiddify/hiddify-app, link to the original license, and document any changes you have made in Readme. 4. **No Malware:** Adding any malware to the app is strictly prohibited. 5. **Naming and Interface Restrictions:** You are not allowed to publish the app on any app store (e.g., AppStore, Google Play, F-Droid, Microsoft) with a name or user interface that closely resembles Hiddify (e.g., names like Hiddify, Hidy*, Hiddy*, *Ify or similar UI are prohibited). 6. **NonCommercial Use Only:** You may not use this material for commercial purposes, including selling and advertising, without prior written consent. 7. **ShareAlike Requirement:** If you remix, transform, or build upon the material, you must distribute your contributions under this same license as an open-source fork of https://github.com/hiddify/hiddify-app . GNU General Public License ========================== _Version 3, 29 June 2007_ _Copyright © 2007 Free Software Foundation, Inc. <>_ Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. ## Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: **(1)** assert copyright on the software, and **(2)** offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. ## TERMS AND CONDITIONS ### 0. Definitions “This License” refers to version 3 of the GNU General Public License. “Copyright” also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. “The Program” refers to any copyrightable work licensed under this License. Each licensee is addressed as “you”. “Licensees” and “recipients” may be individuals or organizations. To “modify” a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a “modified version” of the earlier work or a work “based on” the earlier work. A “covered work” means either the unmodified Program or a work based on the Program. To “propagate” a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To “convey” a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays “Appropriate Legal Notices” to the extent that it includes a convenient and prominently visible feature that **(1)** displays an appropriate copyright notice, and **(2)** tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. ### 1. Source Code The “source code” for a work means the preferred form of the work for making modifications to it. “Object code” means any non-source form of a work. A “Standard Interface” means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The “System Libraries” of an executable work include anything, other than the work as a whole, that **(a)** is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and **(b)** serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A “Major Component”, in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The “Corresponding Source” for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. ### 2. Basic Permissions All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. ### 3. Protecting Users' Legal Rights From Anti-Circumvention Law No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. ### 4. Conveying Verbatim Copies You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. ### 5. Conveying Modified Source Versions You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: * **a)** The work must carry prominent notices stating that you modified it, and giving a relevant date. * **b)** The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to “keep intact all notices”. * **c)** You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. * **d)** If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an “aggregate” if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. ### 6. Conveying Non-Source Forms You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: * **a)** Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. * **b)** Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either **(1)** a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or **(2)** access to copy the Corresponding Source from a network server at no charge. * **c)** Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. * **d)** Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. * **e)** Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A “User Product” is either **(1)** a “consumer product”, which means any tangible personal property which is normally used for personal, family, or household purposes, or **(2)** anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, “normally used” refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. “Installation Information” for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. ### 7. Additional Terms “Additional permissions” are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: * **a)** Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or * **b)** Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or * **c)** Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or * **d)** Limiting the use for publicity purposes of names of licensors or authors of the material; or * **e)** Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or * **f)** Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered “further restrictions” within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. ### 8. Termination You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated **(a)** provisionally, unless and until the copyright holder explicitly and finally terminates your license, and **(b)** permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. ### 9. Acceptance Not Required for Having Copies You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. ### 10. Automatic Licensing of Downstream Recipients Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An “entity transaction” is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. ### 11. Patents A “contributor” is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's “contributor version”. A contributor's “essential patent claims” are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, “control” includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a “patent license” is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To “grant” such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either **(1)** cause the Corresponding Source to be so available, or **(2)** arrange to deprive yourself of the benefit of the patent license for this particular work, or **(3)** arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. “Knowingly relying” means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is “discriminatory” if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license **(a)** in connection with copies of the covered work conveyed by you (or copies made from those copies), or **(b)** primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. ### 12. No Surrender of Others' Freedom If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. ### 13. Use with the GNU Affero General Public License Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. ### 14. Revised Versions of this License The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License “or any later version” applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. ### 15. Disclaimer of Warranty THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. ### 16. Limitation of Liability IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. ### 17. Interpretation of Sections 15 and 16 If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. _END OF TERMS AND CONDITIONS_ ## How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the “copyright” line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type 'show w'. This is free software, and you are welcome to redistribute it under certain conditions; type 'show c' for details. The hypothetical commands `show w` and `show c` should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an “about box”. You should also get your employer (if you work as a programmer) or school, if any, to sign a “copyright disclaimer” for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see <>. The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read <>. ================================================ FILE: Makefile ================================================ # .ONESHELL: include dependencies.properties # --- Log Colors --- blue := \033[1;34m green := \033[1;92m yellow := \033[1;33m reset := \033[0m # --- Log helpers --- # Usage: $(BLUE) $(DONE) BLUE := echo -e "$(blue) GREEN := echo -e "$(green) YELLOW := echo -e "$(yellow) DONE := $(reset)" MKDIR := mkdir -p RM := rm -rf SEP :=/ ifeq ($(OS),Windows_NT) ifeq ($(IS_GITHUB_ACTIONS),) # MKDIR := -mkdir RM := rmdir /s /q # SEP:=\\ endif endif # Define sed command based on the OS ifeq ($(OS),Windows_NT) # Windows (Assume Git Bash or similar sed is available, or standard syntax) SED := sed -i else ifeq ($(shell uname),Darwin) # macOS SED :=sed -i '' else # Linux SED :=sed -i endif endif BINDIR=hiddify-core$(SEP)bin ANDROID_OUT=android$(SEP)app$(SEP)libs IOS_OUT=ios$(SEP)Frameworks DESKTOP_OUT=hiddify-core$(SEP)bin GEO_ASSETS_DIR=assets$(SEP)core CORE_PRODUCT_NAME=hiddify-core CORE_NAME=hiddify-lib LIB_NAME=hiddify-core ifeq ($(CHANNEL),prod) CORE_URL=https://github.com/hiddify/hiddify-next-core/releases/download/v$(core.version) else CORE_URL=https://github.com/hiddify/hiddify-next-core/releases/download/draft endif ifeq ($(CHANNEL),prod) TARGET=lib/main_prod.dart else TARGET=lib/main.dart endif BUILD_ARGS=--dart-define sentry_dsn=$(SENTRY_DSN) DISTRIBUTOR_ARGS=--skip-clean --build-target $(TARGET) --build-dart-define sentry_dsn=$(SENTRY_DSN) get: flutter pub get gen: dart run build_runner build --delete-conflicting-outputs translate: dart run slang prepare: @echo use the following commands to prepare the library for each platform: @echo make android-prepare @echo make windows-prepare @echo make linux-prepare @echo make macos-prepare @echo make ios-prepare common-prepare: get gen translate windows-prepare: common-prepare windows-libs ios-prepare: common-prepare ios-libs cd ios; pod repo update; pod install;echo "done ios prepare" macos-prepare: common-prepare macos-libs linux-prepare: common-prepare linux-amd64-libs linux-amd64-prepare: common-prepare linux-amd64-libs linux-arm64-prepare: common-prepare linux-arm64-libs linux-amd64-musl-prepare: common-prepare linux-amd64-musl-libs linux-arm64-musl-prepare: common-prepare linux-arm64-musl-libs linux-appimage-prepare:linux-prepare linux-rpm-prepare:linux-prepare linux-deb-prepare:linux-prepare android-prepare:common-prepare android-libs android-apk-prepare:android-prepare android-aab-prepare:android-prepare .PHONY: generate_kotlin_protos generate_kotlin_protos: # Run protoc to generate Kotlin files # protoc \ # --proto_path=hiddify-core/ \ # --java_out=./android/app/src/main/java/ \ # --grpc-java_out=./android/app/src/main/java/ \ # $(shell find hiddify-core/v2 hiddify-core/extension -name "*.proto") rsync -av --delete \ --include='*/' \ --include='*.proto' \ --exclude='*' \ hiddify-core/v2 hiddify-core/extension ./android/app/src/main/protos/ # # Find .proto files and update package declarations # find "./android/app/src/main/java/com/hiddify/hiddify/protos" -type f -name "*.java" | while read -r proto_file; do \ # if grep -q "^package " "$$proto_file"; then \ # $(SED) 's/^package \([\w\.]*\)/package com.hiddify.hiddify.protos.\1/g' "$$proto_file"; \ # fi \ # done generate_go_protoc: make -C hiddify-core -f Makefile protos echo "SED: $(SED)" generate_dart_protoc: mkdir -p lib/hiddifycore/generated protoc --dart_out=grpc:lib/hiddifycore/generated --proto_path=hiddify-core/ $(shell find hiddify-core/v2 hiddify-core/extension -name "*.proto") google/protobuf/timestamp.proto ; \ .PHONY: protos protos: generate_go_protoc generate_kotlin_protos generate_dart_protoc macos-install-deps: brew install create-dmg tree npm install -g appdmg dart pub global activate fastforge ios-install-deps: if [ "$(flutter)" = "true" ]; then \ curl -L -o ~/Downloads/flutter_macos_3.19.3-stable.zip https://storage.googleapis.com/flutter_infra_release/releases/stable/macos/flutter_macos_3.22.3-stable.zip; \ mkdir -p ~/develop; \ cd ~/develop; \ unzip ~/Downloads/flutter_macos_3.22.3-stable.zip; \ export PATH="$$PATH:$$HOME/develop/flutter/bin"; \ echo 'export PATH="$$PATH:$$HOME/develop/flutter/bin"' >> ~/.zshrc; \ export PATH="$PATH:$HOME/develop/flutter/bin"; \ echo 'export PATH="$PATH:$HOME/develop/flutter/bin"' >> ~/.zshrc; \ curl -sSL https://rvm.io/mpapis.asc | gpg --import -; \ curl -sSL https://rvm.io/pkuczynski.asc | gpg --import -; \ curl -sSL https://get.rvm.io | bash -s stable; \ brew install openssl@1.1; \ PKG_CONFIG_PATH=$(brew --prefix openssl@1.1)/lib/pkgconfig rvm install 2.7.5; \ sudo gem install cocoapods -V; \ fi brew install create-dmg tree npm install -g appdmg dart pub global activate fastforge android-install-deps: dart pub global activate fastforge android-apk-install-deps: android-install-deps android-aab-install-deps: android-install-deps # loads the package list from linux_deps.list LINUX_DEPS = $(shell grep -vE '^\s*#|^\s*$$' linux_deps.list) # reads the Flutter version from pubspec.yaml REQUIRED_VER = $(shell sed -n '/environment:/,/flutter:/ s/.*flutter:[[:space:]]*//p' pubspec.yaml | tr -d " '^\"") linux-amd64-install-deps:linux-install-deps linux-amd64-musl-install-deps:linux-install-deps linux-arm64-install-deps:linux-install-deps linux-arm64-musl-install-deps:linux-install-deps linux-install-deps: @$(BLUE)Installing Debian/Ubuntu dependencies...$(DONE) sudo apt-get update -y sudo apt-get install -y $(LINUX_DEPS) # loading fuce kernel module @$(BLUE)Loading fuce kernel module$(DONE) sudo modprobe fuse # tools for appimage @$(BLUE)Installing appimagetool$(DONE) if [ "$$(uname -m)" = "aarch64" ]; then \ wget -O /tmp/appimagetool "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-aarch64.AppImage"; \ else \ wget -O /tmp/appimagetool "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage"; \ fi chmod +x /tmp/appimagetool sudo mv /tmp/appimagetool /usr/local/bin/ # cloning flutter sdk @$(BLUE)Cloning Flutter SDK$(DONE); \ mkdir -p ~/develop; \ cd ~/develop; \ \ if [ ! -d "flutter/.git" ]; then \ $(BLUE)Flutter not found. cloning stable channel$(DONE); \ rm -rf flutter; \ git clone https://github.com/flutter/flutter.git -b stable flutter; \ fi; \ \ git config --global --add safe.directory $$HOME/develop/flutter; \ \ export PATH="$$HOME/develop/flutter/bin:$$PATH"; \ if ! grep -q 'flutter/bin' ~/.bashrc; then \ echo 'export PATH="$$HOME/develop/flutter/bin:$$PATH"' >> ~/.bashrc; \ fi # syncing flutter version $(MAKE) linux-flutter-sync # installing fastforge https://pub.dev/packages/fastforge @$(BLUE)Installing fastforge$(DONE); \ export PATH="$$HOME/develop/flutter/bin:$$HOME/.pub-cache/bin:$$PATH"; \ if ! grep -q '.pub-cache/bin' ~/.bashrc; then \ echo 'export PATH="$$HOME/.pub-cache/bin:$$PATH"' >> ~/.bashrc; \ fi; \ dart pub global activate fastforge; \ dart pub global activate protoc_plugin; \ echo ""; \ echo "============================================================"; \ echo "NOTE: After first setup, use the following command to update the PATH"; \ echo "source ~/.bashrc"; \ echo "============================================================" # syncing 'flutter sdk' version with pubspec.yaml flutter version linux-flutter-sync: @$(BLUE)Syncing Flutter version with pubspec.yaml flutter version$(DONE); \ export PATH="$$HOME/develop/flutter/bin:$$PATH"; \ $(BLUE)Downloading Flutter SDK components...$(DONE); \ flutter --version > /dev/null; \ \ $(BLUE)Checking Flutter version...$(DONE); \ CURRENT_VER=$$(flutter --version | head -n 1 | awk '{print $$2}'); \ $(BLUE)Target: $(REQUIRED_VER) | Current: $$CURRENT_VER$(DONE); \ \ if [ "$$CURRENT_VER" != "$(REQUIRED_VER)" ]; then \ $(BLUE)Version mismatch! switching to $(REQUIRED_VER)...$(DONE); \ cd ~/develop/flutter; \ git fetch --tags; \ git checkout $(REQUIRED_VER); \ $(BLUE)Switched to $(REQUIRED_VER)$(DONE); \ flutter doctor; \ else \ $(GREEN)Flutter SDK is ready.$(DONE); \ fi windows-install-deps: dart pub global activate fastforge # choco install innosetup -y gen_translations: #generating missing translations using google translate cd .github && bash sync_translate.sh make translate android-release: android-apk-release android-aab-release android-apk-release: fastforge package \ --platform android \ --targets apk \ --skip-clean \ --build-target=$(TARGET) \ --build-target-platform=android-arm,android-arm64,android-x64 \ --build-dart-define=sentry_dsn=$(SENTRY_DSN) ls -R build/app/outputs android-aab-release: fastforge package \ --platform android \ --targets aab \ --skip-clean \ --build-target=$(TARGET) \ --build-dart-define=sentry_dsn=$(SENTRY_DSN) \ --build-dart-define=release=google-play windows-release: windows-zip-release windows-exe-release windows-msix-release windows-zip-release: fastforge package \ --platform windows \ --targets zip \ --skip-clean \ --build-target=$(TARGET) \ --build-dart-define=sentry_dsn=$(SENTRY_DSN) \ --build-dart-define=portable=true @FULL_PATH=$$(ls dist/*/*.zip | head -n 1); \ ZIP_DIR=$$(dirname "$$FULL_PATH"); \ ZIP_FILE=$$(basename "$$FULL_PATH"); \ FILE_NAME=$${ZIP_FILE%.*}; \ $(YELLOW)Post-processing Windows portable$(DONE); \ cd "$$ZIP_DIR"; \ $(BLUE)Extracting and Repacking...$(DONE); \ mkdir -p Hiddify; \ unzip -q "$$ZIP_FILE" -d Hiddify/; \ rm "$$ZIP_FILE"; \ tar -a -cf "$$FILE_NAME.zip" Hiddify; \ rm -rf Hiddify; \ $(GREEN)Successful$(DONE) windows-exe-release: fastforge package \ --platform windows \ --targets exe \ --skip-clean \ --build-target=$(TARGET) \ --build-dart-define=sentry_dsn=$(SENTRY_DSN) windows-msix-release: fastforge package \ --platform windows \ --targets msix \ --skip-clean \ --build-target=$(TARGET) \ --build-dart-define=sentry_dsn=$(SENTRY_DSN) linux-release: linux-deb-release linux-appimage-release linux-amd64-release: linux-release linux-arm64-release: linux-release linux-amd64-musl-release: linux-release linux-arm64-musl-release: linux-release linux-deb-release: fastforge package \ --platform linux \ --targets deb \ --skip-clean \ --build-target=$(TARGET) \ --build-dart-define=sentry_dsn=$(SENTRY_DSN) # ============================================================================== # REFERENCE: MANUAL LIBRARY BUNDLING (INJECTION) # ============================================================================== # Use this method only if you need to manually force specific shared libraries # (e.g., libcurl.so.4) into the AppImage bundle. # # IMPLEMENTATION STEPS: # # 1. PRE-BUILD SCRIPT (Add to Makefile before build command): # Create a temporary directory and copy the target library there. # --------------------------------------------------------------------------- # mkdir -p linux/bundled_libs # cp /usr/lib/x86_64-linux-gnu/libcurl.so.4 linux/bundled_libs/ # --------------------------------------------------------------------------- # # 2. CMAKE CONFIGURATION (Add to linux/CMakeLists.txt): # Instruct CMake to include the copied file in the final bundle. # --------------------------------------------------------------------------- # install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/bundled_libs/libcurl.so.4" # DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" # COMPONENT Runtime) # --------------------------------------------------------------------------- # # ! WARNING ! # This approach is generally DISCOURAGED. Manually bundling libraries can lead to # "Dependency Hell," where bundled libs conflict with system libraries or have # their own unresolved dependencies. It increases maintenance cost and may cause # runtime instability. Use only for specific edge cases where standard linking fails. # ============================================================================== linux-appimage-release: fastforge package \ --platform linux \ --targets appimage \ --skip-clean \ --build-target=$(TARGET) \ --build-dart-define=sentry_dsn=$(SENTRY_DSN) @$(YELLOW)Post-processing AppImage$(DONE); \ $(BLUE)Extracting AppImage$(DONE); \ cd dist/* && ./*.AppImage --appimage-extract > /dev/null; \ $(BLUE)Replacing AppRun$(DONE); \ cp ../../linux/packaging/appimage/AppRun squashfs-root/AppRun; \ $(BLUE)Granting permissions$(DONE); \ chmod +x squashfs-root/AppRun; \ $(BLUE)Adding StartupWMClass to hiddify.desktop$(DONE); \ sed -i '/^\[Desktop Entry\]/a StartupWMClass=app.hiddify.com' "squashfs-root/hiddify.desktop"; \ $(BLUE)Removing old AppImage$(DONE); \ rm *.AppImage; \ $(BLUE)Deleting bundled libstdc++ to fix Arch Linux compatibility...$(DONE); \ find squashfs-root/usr/lib -name "libstdc++.so.6" -delete; \ $(BLUE)Rebuilding AppImage$(DONE); \ ARCH=x86_64 appimagetool --no-appstream squashfs-root Hiddify.AppImage > /dev/null; \ $(BLUE)Cleaning up squashfs$(DONE); \ rm -rf squashfs-root; \ $(YELLOW)Creating Portable Package$(DONE); \ PKG_DIR_NAME="hiddify-linux-appimage"; \ $(BLUE)Creating dir: $$PKG_DIR_NAME$(DONE); \ mkdir -p "$$PKG_DIR_NAME"; \ $(BLUE)Moving Hiddify.AppImage$(DONE); \ cp -p "Hiddify.AppImage" "$$PKG_DIR_NAME/Hiddify.AppImage"; \ $(BLUE)Creating Portable Home directory$(DONE); \ mkdir -p "$$PKG_DIR_NAME/Hiddify.AppImage.home"; \ $(BLUE)Compressing to .tar.gz$(DONE); \ tar -czf "$$PKG_DIR_NAME.tar.gz" -C . "$$PKG_DIR_NAME"; \ $(BLUE)Removing intermediate directory$(DONE); \ rm -rf "$$PKG_DIR_NAME"; \ $(GREEN)Successful$(DONE) DOCKER_IMAGE_NAME := hiddify-linux-builder DOCKER_FLUTTER_VOL := hiddify-flutter-sdk-cache DOCKER_PUB_VOL := hiddify-pub-cache ifeq ($(OS),Windows_NT) FIX_OWNERSHIP := echo \"Windows detected: Skipping chown\" else FIX_OWNERSHIP := chown -R $(shell id -u):$(shell id -g) /host/dist_docker endif DOCKER_CMD := \ set -e; \ echo '** Copying source code to container...'; \ mkdir -p /app; \ cp -r /host/. /app/; \ cd /app; \ make linux-flutter-sync; \ make linux-prepare; \ echo '** Building Release (linux-release)...'; \ make linux-release; \ echo '** Copying artifacts to host...'; \ rm -rf /host/dist_docker; \ if [ -d \"dist\" ]; then \ cp -r dist /host/dist_docker; \ echo '** Fixing permissions for dist_docker...'; \ $(FIX_OWNERSHIP); \ else \ echo 'Error: dist folder not found!'; \ exit 1; \ fi; linux-docker-release: @$(BLUE)Cleaning main project to reduce context size$(DONE) flutter clean @$(BLUE)Building docker image (Cached)$(DONE) docker build -t $(DOCKER_IMAGE_NAME) -f Dockerfile . @$(BLUE)Ensuring cache volumes exist$(DONE) docker volume create $(DOCKER_FLUTTER_VOL) || true docker volume create $(DOCKER_PUB_VOL) || true @$(YELLOW)Running build inside container$(DONE) @docker run --rm \ -v "$(CURDIR)://host" \ -v $(DOCKER_FLUTTER_VOL)://root/develop/flutter \ -v $(DOCKER_PUB_VOL)://root/.pub-cache \ -e APPIMAGE_EXTRACT_AND_RUN=1 \ $(DOCKER_IMAGE_NAME) \ //bin/bash -c "$(DOCKER_CMD)" @$(GREEN)Successful. Output is in 'dist_docker' folder.$(DONE) macos-release: fastforge package --platform macos --targets dmg,pkg $(DISTRIBUTOR_ARGS) ios-release: #not tested fastforge package --platform ios --targets ipa --build-export-options-plist ios/exportOptions.plist $(DISTRIBUTOR_ARGS) android-libs: $(MKDIR) $(ANDROID_OUT) || echo Folder already exists. Skipping... curl -L $(CORE_URL)/$(CORE_NAME)-android.tar.gz | tar xz -C $(ANDROID_OUT)/ android-apk-libs: android-libs android-aab-libs: android-libs windows-libs: $(MKDIR) $(DESKTOP_OUT) || echo Folder already exists. Skipping... curl -L $(CORE_URL)/$(CORE_NAME)-windows-amd64.tar.gz | tar xz -C $(DESKTOP_OUT)/ ls $(DESKTOP_OUT) || dir $(DESKTOP_OUT)/ linux-amd64-libs: mkdir -p $(DESKTOP_OUT) curl -L $(CORE_URL)/$(CORE_NAME)-linux-amd64.tar.gz | tar xz -C $(DESKTOP_OUT)/ linux-arm64-libs: mkdir -p $(DESKTOP_OUT) curl -L $(CORE_URL)/$(CORE_NAME)-linux-arm64.tar.gz | tar xz -C $(DESKTOP_OUT)/ linux-amd64-musl-libs: mkdir -p $(DESKTOP_OUT) curl -L $(CORE_URL)/$(CORE_NAME)-linux-amd64-musl.tar.gz | tar xz -C $(DESKTOP_OUT)/ linux-arm64-musl-libs: mkdir -p $(DESKTOP_OUT) curl -L $(CORE_URL)/$(CORE_NAME)-linux-arm64-musl.tar.gz | tar xz -C $(DESKTOP_OUT)/ macos-libs: mkdir -p $(DESKTOP_OUT) curl -L $(CORE_URL)/$(CORE_NAME)-macos.tar.gz | tar xz -C $(DESKTOP_OUT) ios-libs: #not tested mkdir -p $(IOS_OUT) rm -rf $(IOS_OUT)/HiddifyCore.xcframework curl -L $(CORE_URL)/$(CORE_NAME)-ios.tar.gz | tar xz -C "$(IOS_OUT)" get-geo-assets: echo "" # curl -L https://github.com/SagerNet/sing-geoip/releases/latest/download/geoip.db -o $(GEO_ASSETS_DIR)/geoip.db # curl -L https://github.com/SagerNet/sing-geosite/releases/latest/download/geosite.db -o $(GEO_ASSETS_DIR)/geosite.db build-headers: make -C hiddify-core -f Makefile headers && mv $(BINDIR)/$(CORE_NAME)-headers.h $(BINDIR)/hiddify-core.h build-android-libs: make -C hiddify-core -f Makefile android mv $(BINDIR)/$(LIB_NAME).aar $(ANDROID_OUT)/ build-windows-libs: make -C hiddify-core -f Makefile windows-amd64 build-linux-libs: make -C hiddify-core -f Makefile linux-amd64 build-macos-libs: make -C hiddify-core -f Makefile macos build-ios-libs: rm -rf $(IOS_OUT)/HiddifyCore.xcframework make -C hiddify-core -f Makefile ios mv $(BINDIR)/HiddifyCore.xcframework $(IOS_OUT)/HiddifyCore.xcframework release: # Create a new tag for release. @CORE_VERSION=$(core.version) bash -c ".github/change_version.sh " ios-temp-prepare: make ios-prepare flutter build ios-framework cd ios pod install ================================================ FILE: README.md ================================================
[**![Lang_farsi](https://user-images.githubusercontent.com/125398461/234186932-52f1fa82-52c6-417f-8b37-08fe9250a55f.png) فارسی**](README_fa.md) / [**Русский 🇷🇺**](README_ru.md) / [**简体中文 🇨🇳**](README_cn.md) / [**日本語 🇯🇵**](README_ja.md) / [**Portugês-BR 🇧🇷**](README_br.md)


[![GP-Intalls](https://img.shields.io/endpoint?color=green&logo=google-play&logoColor=green&url=https%3A%2F%2Fplay.cuzi.workers.dev%2Fplay%3Fi%3Dapp.hiddify.com%26l%3DGoogle%2520Play%26m%3D%24shortinstalls&style=flat-square)](https://play.google.com/store/apps/details?id=app.hiddify.com) [![Downloads](https://img.shields.io/github/downloads/hiddify/hiddify-next/total?style=flat-square&logo=github)](https://github.com/hiddify/hiddify-app/releases/)[![Last Version](https://img.shields.io/github/release/hiddify/hiddify-next/all.svg?style=flat-square)](https://github.com/hiddify/hiddify-app/releases/)[![Last Release Date](https://img.shields.io/github/release-date/hiddify/hiddify-next.svg?style=flat-square)](https://github.com/hiddify/hiddify-app/releases/)[![commits](https://img.shields.io/github/commit-activity/m/hiddify/hiddify-next?style=flat-square)](https://github.com/hiddify/hiddify-app/) [![Youtube](https://img.shields.io/youtube/channel/views/UCxrmeMvVryNfB4XL35lXQNg?label=Youtube&style=flat-square&logo=youtube)](https://www.youtube.com/@hiddify)[![Telegram Channel](https://img.shields.io/endpoint?label=Channel&style=flat-square&url=https%3A%2F%2Ftg.sumanjay.workers.dev%2Fhiddify&color=blue)](https://telegram.dog/hiddify)[![Telegram Group](https://img.shields.io/endpoint?color=neon&label=Support%20Group&style=flat-square&url=https%3A%2F%2Ftg.sumanjay.workers.dev%2Fhiddify_board)](https://telegram.dog/hiddify_board/5)
## What is Hiddify app?

A multi-platform proxy client based on Sing-box universal proxy tool-chain. Hiddify offers a wide range of capabilities, like automatic node selection, TUN mode, remote profiles etc. Hiddify is ad-free and open-source. With support for a wide range of protocols, it provides a secure and private way for accessing free internet.

English Demo
## 🚀 Main features ✈️ Multi-platform: Android, iOS, Windows, macOS and Linux ⭐ Intuitive and accessible UI 🔍 Delay based node selection 🟡 Wide range of protocols: Vless, Vmess, Reality, TUIC, Hysteria, Wireguard, SSH etc. 🟡 Subscription link and configuration formats: Sing-box, V2ray, Clash, Clash meta 🔄 Automatic subscription update 🔎 Display profile information including remaining days and traffic usage 🛡 Open source, secure and community driven 🌙 Dark and light modes ⚙ Compatible with all proxy management panels ⭐ Appropriate configuration for Iran, China, Russia and other countries 📱 Available on official stores ## 🛍️ Get It On Stores                          ## 📥 Direct Download
OS Download
iOS
Android


Windows

macOS

Linux

## ⚙️ Installation and tutorials **Find tutorial information on our wiki page by clicking on image below.**
[![Tutorials on Wiki](https://github.com/hiddify/hiddify-app/assets/125398461/95bb5cf8-c143-4934-87d6-b9d4c31e035e)](https://hiddify.com/app/)
## 🌎 Translations You can improve existing languages or contribute new ones either by editing the JSON files in `/assets/translations` or [![Translate with Inlang](https://img.shields.io/badge/%20-%20-3ECF8E?logo=i18next&logoColor=white)](https://fink.inlang.com/github.com/hiddify/hiddify-app) by using [Inlang online editor](https://fink.inlang.com/github.com/hiddify/hiddify-app). ## ✏️ Acknowledgements We would like to express our sincere appreciation to the contributors of the following projects, whose robust foundation and innovative features have significantly enhanced the success and functionality of this project. - [Sing-box](https://github.com/SagerNet/sing-box) - [Sing-box for Android](https://github.com/SagerNet/sing-box-for-android) - [Sing-box for Apple](https://github.com/SagerNet/sing-box-for-apple) - [Clash](https://github.com/Dreamacro/clash) - [Clash Meta](https://github.com/MetaCubeX/Clash.Meta) - [FClash](https://github.com/Fclash/Fclash) - [Vazirmatn Font by Saber Rastikerdar](https://github.com/rastikerdar/vazirmatn) - [Others](./pubspec.yaml) ## 🎯 Donation and Support The easiest way to support us is to click on the star (⭐) at the top of this page. We also need financial support for our services. All of our activities are done voluntarily and financial support will be spent on the development of the project. You can view our support addresses [here](https://hiddify.com/donation-and-support/). ## 👩‍🏫 Collaboration and Contact Information Hiddify is a community driven project. If you're interested in contributing, please read the [contribution guidelines](./CONTRIBUTING.md). We would specially appreciate any help we can get in these areas: **Flutter, Go, iOS development (Swift), Android development (Kotlin).**

[![Email](https://img.shields.io/badge/Email-contribute@hiddify.com-005FF9?style=flat-square&logo=mail.ru)](mailto:contribute@hiddify.com) [![Telegram Channel](https://img.shields.io/endpoint?label=Channel&style=flat-square&url=https%3A%2F%2Ftg.sumanjay.workers.dev%2Fhiddify&color=blue)](https://telegram.dog/hiddify) [![Telegram Group](https://img.shields.io/endpoint?color=neon&label=Support%20Group&style=flat-square&url=https%3A%2F%2Ftg.sumanjay.workers.dev%2Fhiddify_board)](https://telegram.dog/hiddify_board) [![Youtube](https://img.shields.io/youtube/channel/views/UCxrmeMvVryNfB4XL35lXQNg?label=Youtube&style=flat-square&logo=youtube)](https://www.youtube.com/@hiddify) [![Twitter](https://img.shields.io/twitter/follow/hiddify_com?color=%231DA1F2&logo=twitter&logoColor=1DA1F2&style=flat-square)](https://twitter.com/intent/follow?screen_name=hiddify_com)

We appreciate all people who are participating in this project. Some people here and many many more outside of Github. It means a lot to us. ♥

Made with Contrib.Rocks

================================================ FILE: README_br.md ================================================
[**![Lang_farsi](https://user-images.githubusercontent.com/125398461/234186932-52f1fa82-52c6-417f-8b37-08fe9250a55f.png) فارسی**](README_fa.md) / [**English 🇺🇸**](README.md) / [**Русский 🇷🇺**](README_ru.md) / [**简体中文 🇨🇳**](README_cn.md) / [**日本語 🇯🇵**](README_ja.md)


[![GP-Intalls](https://img.shields.io/endpoint?color=green&logo=google-play&logoColor=green&url=https%3A%2F%2Fplay.cuzi.workers.dev%2Fplay%3Fi%3Dapp.hiddify.com%26l%3DGoogle%2520Play%26m%3D%24shortinstalls&style=flat-square)](https://play.google.com/store/apps/details?id=app.hiddify.com) [![Downloads](https://img.shields.io/github/downloads/hiddify/hiddify-next/total?style=flat-square&logo=github)](https://github.com/hiddify/hiddify-app/releases/)[![Last Version](https://img.shields.io/github/release/hiddify/hiddify-next/all.svg?style=flat-square)](https://github.com/hiddify/hiddify-app/releases/)[![Last Release Date](https://img.shields.io/github/release-date/hiddify/hiddify-next.svg?style=flat-square)](https://github.com/hiddify/hiddify-app/releases/)[![commits](https://img.shields.io/github/commit-activity/m/hiddify/hiddify-next?style=flat-square)](https://github.com/hiddify/hiddify-app/) [![Youtube](https://img.shields.io/youtube/channel/views/UCxrmeMvVryNfB4XL35lXQNg?label=Youtube&style=flat-square&logo=youtube)](https://www.youtube.com/@hiddify)[![Telegram Channel](https://img.shields.io/endpoint?label=Channel&style=flat-square&url=https%3A%2F%2Ftg.sumanjay.workers.dev%2Fhiddify&color=blue)](https://telegram.dog/hiddify)[![Telegram Group](https://img.shields.io/endpoint?color=neon&label=Support%20Group&style=flat-square&url=https%3A%2F%2Ftg.sumanjay.workers.dev%2Fhiddify_board)](https://telegram.dog/hiddify_board/5)
## O que é Hiddify app?

Um cliente de proxy multiplataforma baseado na ferramenta de proxy universal Sing-box. O Hiddify oferece uma ampla gama de recursos, como seleção automática de nós, modo TUN, perfis remotos, etc. O Hiddify é livre de anúncios e de código aberto. Com suporte para uma ampla variedade de protocolos, ele oferece uma maneira segura e privada de acessar a internet gratuitamente.

English Demo
## 🚀 Principais recursos ✈️ Multiplataforma: Android, iOS, Windows, macOS e Linux ⭐ Interface intuitiva e acessível 🔍 Seleção de nós baseada em atraso 🟡 Amplas opções de protocolos: Vless, Vmess, Reality, TUIC, Hysteria, Wireguard, SSH, etc. 🟡 Links de assinatura e formatos de configuração: Sing-box, V2ray, Clash, Clash meta 🔄 Atualização automática de assinatura 🔎 Exibição de informações do perfil, incluindo dias restantes e uso de tráfego 🛡 Código aberto, seguro e impulsionado pela comunidade 🌙 Modos escuro e claro ⚙ Compatível com todos os painéis de gerenciamento de proxy ⭐ Configuração adequada para Irã, China, Rússia e outros países 📱 Disponível nas lojas oficiais ## 🛍️ Obtenha nas lojas                            ## 📥 Download direto
Sistema Operacional Download
iOS
Android


Windows

macOS

Linux

## ⚙️ Instalação e tutoriais **Encontre informações de tutoriais em nossa página wiki clicando na imagem abaixo.**
[![Tutorials on Wiki](https://github.com/hiddify/hiddify-app/assets/125398461/95bb5cf8-c143-4934-87d6-b9d4c31e035e)](https://hiddify.com/app/)
## 🌎 Tradução Melhore os idiomas existentes ou adicione novos editando manualmente os arquivos JSON localizados em `/assets/translations` ou [![Translate with Inlang](https://img.shields.io/badge/%20-%20-3ECF8E?logo=i18next&logoColor=white)](https://fink.inlang.com/github.com/hiddify/hiddify-app) usando o [editor online Inlang](https://fink.inlang.com/editor/github.com/hiddify/hiddify-app). ## ✏️ Agradecimentos Gostaríamos de expressar nossa sincera gratidão aos contribuidores dos seguintes projetos, cuja base sólida e recursos inovadores têm melhorado significativamente o sucesso e a funcionalidade deste projeto. - [Sing-box](https://github.com/SagerNet/sing-box) - [Sing-box for Android](https://github.com/SagerNet/sing-box-for-android) - [Sing-box for Apple](https://github.com/SagerNet/sing-box-for-apple) - [Clash](https://github.com/Dreamacro/clash) - [Clash Meta](https://github.com/MetaCubeX/Clash.Meta) - [FClash](https://github.com/Fclash/Fclash) - [Vazirmatn Font by Saber Rastikerdar](https://github.com/rastikerdar/vazirmatn) - [Others](./pubspec.yaml) ## 🎯 Doação e Suporte A maneira mais fácil de nos apoiar é clicar na estrela (⭐) no topo desta página. Também precisamos de apoio financeiro para nossos serviços. Todas as nossas atividades são realizadas voluntariamente e o apoio financeiro será utilizado no desenvolvimento do projeto. Você pode ver nossos links de suporte [aqui](https://hiddify.com/donation-and-support/). ## 👩‍🏫 Colaboração e Informações de Contato Hiddify é um projeto impulsionado pela comunidade. Se você estiver interessado em contribuir, por favor, leia as [diretrizes de contribuição](./CONTRIBUTING.md). Agradeceríamos especialmente qualquer ajuda que pudermos obter nessas áreas: **Flutter, Go, desenvolvimento iOS (Swift), desenvolvimento Android (Kotlin).**

[![Email](https://img.shields.io/badge/Email-contribute@hiddify.com-005FF9?style=flat-square&logo=mail.ru)](mailto:contribute@hiddify.com) [![Telegram Channel](https://img.shields.io/endpoint?label=Channel&style=flat-square&url=https%3A%2F%2Ftg.sumanjay.workers.dev%2Fhiddify&color=blue)](https://telegram.dog/hiddify) [![Telegram Group](https://img.shields.io/endpoint?color=neon&label=Support%20Group&style=flat-square&url=https%3A%2F%2Ftg.sumanjay.workers.dev%2Fhiddify_board)](https://telegram.dog/hiddify_board) [![Youtube](https://img.shields.io/youtube/channel/views/UCxrmeMvVryNfB4XL35lXQNg?label=Youtube&style=flat-square&logo=youtube)](https://www.youtube.com/@hiddify) [![Twitter](https://img.shields.io/twitter/follow/hiddify_com?color=%231DA1F2&logo=twitter&logoColor=1DA1F2&style=flat-square)](https://twitter.com/intent/follow?screen_name=hiddify_com)

Agradecemos a todas as pessoas que estão participando deste projeto. Algumas pessoas aqui e muitas outras fora do Github. Isso significa muito para nós. ♥

Feito com Contrib.Rocks

================================================ FILE: README_cn.md ================================================
[**![Lang_farsi](https://user-images.githubusercontent.com/125398461/234186932-52f1fa82-52c6-417f-8b37-08fe9250a55f.png) فارسی**](README_fa.md) / [**Русский 🇷🇺**](README_ru.md) / [**日本語 🇯🇵**](README_ja.md) / [**Portugês-BR 🇧🇷**](README_br.md) / [**English 🇺🇸**](README.md)


[![GP-Intalls](https://img.shields.io/endpoint?color=green&logo=google-play&logoColor=green&url=https%3A%2F%2Fplay.cuzi.workers.dev%2Fplay%3Fi%3Dapp.hiddify.com%26l%3DGoogle%2520Play%26m%3D%24shortinstalls&style=flat-square)](https://play.google.com/store/apps/details?id=app.hiddify.com) [![Downloads](https://img.shields.io/github/downloads/hiddify/hiddify-next/total?style=flat-square&logo=github)](https://github.com/hiddify/hiddify-app/releases/)[![Last Version](https://img.shields.io/github/release/hiddify/hiddify-next/all.svg?style=flat-square)](https://github.com/hiddify/hiddify-app/releases/)[![Last Release Date](https://img.shields.io/github/release-date/hiddify/hiddify-next.svg?style=flat-square)](https://github.com/hiddify/hiddify-app/releases/)[![commits](https://img.shields.io/github/commit-activity/m/hiddify/hiddify-next?style=flat-square)](https://github.com/hiddify/hiddify-app/) [![Youtube](https://img.shields.io/youtube/channel/views/UCxrmeMvVryNfB4XL35lXQNg?label=Youtube&style=flat-square&logo=youtube)](https://www.youtube.com/@hiddify)[![Telegram Channel](https://img.shields.io/endpoint?label=Channel&style=flat-square&url=https%3A%2F%2Ftg.sumanjay.workers.dev%2Fhiddify&color=blue)](https://telegram.dog/hiddify)[![Telegram Group](https://img.shields.io/endpoint?color=neon&label=Support%20Group&style=flat-square&url=https%3A%2F%2Ftg.sumanjay.workers.dev%2Fhiddify_board)](https://telegram.dog/hiddify_board/5)
## Hiddify app 是什么?

一款基于 Sing-box 通用代理工具的跨平台代理客户端。Hiddify 提供了较全面的代理功能,例如自动选择节点、TUN 模式、使用远程配置文件等。Hiddify 无广告,并且代码开源。它为大家自由访问互联网提供了一个支持多种协议的、安全且私密的工具。

English Demo
## 🚀 主要特性 ✈️ 多平台客户端:Android、iOS、Windows、macOS 和 Linux ⭐ 简单易用的用户界面 🔍 基于延迟自动选择节点 🟡 全面的协议支持:**Vless、Vmess、Reality、TUIC、Wireguard、Hysteria、SSH** 🟡 多种订阅链接和配置文件格式支持: **Sing-box、V2ray、Clash、Clash meta** 🔄 支持自动更新订阅 🔎 可显示包含了剩余天数和流量使用情况的配置文件信息 🛡 开源、安全且由社区驱动 🌙 深色和浅色模式 ⚙ 兼容所有的代理管理面板 ⭐ 适用于伊朗、中国、俄罗斯或其他国家的配置 📱 官方商店有售 ## 🛍️ 在商店购买                            ## 📥 直接下载
操作系统 下载链接
iOS
Android


Windows

macOS

Linux

## ⚙️ 安装和教程 **请单击下面的图片,在我们的维基页面上找到相关信息。**
[![Wiki上的所有教程](https://github.com/hiddify/hiddify-app/assets/125398461/95bb5cf8-c143-4934-87d6-b9d4c31e035e)](https://hiddify.com/app/)
## 🌎 翻译 您可以通过手动编辑位于 `/assets/translations` 中的 JSON 文件,或使用 [Inlang 在线编辑器](https://fink.inlang.com/editor/github.com/hiddify/hiddify-app)来改进现有语言或添加新语言。 ## ✏️ 致谢 我们谨向以下项目的贡献者表示诚挚的谢意,这些项目打下的坚实基础和开发的创新功能,显着增强了本项目的功能,为本项目的开发带来了成功。 - [Sing-box](https://github.com/SagerNet/sing-box) - [Sing-box for Android](https://github.com/SagerNet/sing-box-for-android) - [Sing-box for Apple](https://github.com/SagerNet/sing-box-for-apple) - [Clash](https://github.com/Dreamacro/clash) - [Clash Meta](https://github.com/MetaCubeX/Clash.Meta) - [FClash](https://github.com/Fclash/Fclash) - [字体 Vazirmatn Font by Saber Rastikerdar](https://github.com/rastikerdar/vazirmatn) - [其他](./pubspec.yaml) ## 🎯 捐赠和支持 支持我们的最简单方法是单击此页面顶部的Star (⭐) 。 我们的服务也需要经济支持。我们所有的活动都是志愿性质的,经济支持将被用于项目的发展。您可以在 [这里](https://hiddify.com/donation-and-support/) 查看我们的支持地址。 ## 👩‍🏫 合作及联系信息 Hiddify 是一个由社区驱动的项目。如果您有兴趣为本项目做出贡献,请阅读 [贡献指南](./CONTRIBUTING.md)。我们将非常感谢您,如果您能够在以下领域提供任何帮助:Flutter、Go、iOS 开发 (Swift)、Android 开发 (Kotlin)。

[![Email](https://img.shields.io/badge/Email-contribute@hiddify.com-005FF9?style=flat-square&logo=mail.ru)](mailto:contribute@hiddify.com) [![Telegram Channel](https://img.shields.io/endpoint?label=Channel&style=flat-square&url=https%3A%2F%2Ftg.sumanjay.workers.dev%2Fhiddify&color=blue)](https://telegram.dog/hiddify) [![Telegram Group](https://img.shields.io/endpoint?color=neon&label=Support%20Group&style=flat-square&url=https%3A%2F%2Ftg.sumanjay.workers.dev%2Fhiddify_board)](https://telegram.dog/hiddify_board) [![Youtube](https://img.shields.io/youtube/channel/views/UCxrmeMvVryNfB4XL35lXQNg?label=Youtube&style=flat-square&logo=youtube)](https://www.youtube.com/@hiddify) [![Twitter](https://img.shields.io/twitter/follow/hiddify_com?color=%231DA1F2&logo=twitter&logoColor=1DA1F2&style=flat-square)](https://twitter.com/intent/follow?screen_name=hiddify_com)

我们非常感谢所有参与此项目的朋友,包括下面列出的这些朋友,以及许许多多没有在 Github 社区的朋友。这对我们来说意义重大。♥

使用 Contrib.Rocks 制作

================================================ FILE: README_fa.md ================================================
[**🇺🇸 English**](README.md) / [**🇨🇳 简体中文**](README_cn.md) / [**🇷🇺 Русский**](README_ru.md) / [**🇯🇵 日本語**](README_ja.md) / [**🇧🇷 Portugês-BR**](README_br.md)


[![GP-Intalls](https://img.shields.io/endpoint?color=green&logo=google-play&logoColor=green&url=https%3A%2F%2Fplay.cuzi.workers.dev%2Fplay%3Fi%3Dapp.hiddify.com%26l%3DGoogle%2520Play%26m%3D%24shortinstalls&style=flat-square)](https://play.google.com/store/apps/details?id=app.hiddify.com) [![Downloads](https://img.shields.io/github/downloads/hiddify/hiddify-next/total?style=flat-square&logo=github)](https://github.com/hiddify/hiddify-app/releases/)[![Last Version](https://img.shields.io/github/release/hiddify/hiddify-next/all.svg?style=flat-square)](https://github.com/hiddify/hiddify-app/releases/)[![Last Release Date](https://img.shields.io/github/release-date/hiddify/hiddify-next.svg?style=flat-square)](https://github.com/hiddify/hiddify-app/releases/)[![commits](https://img.shields.io/github/commit-activity/m/hiddify/hiddify-next?style=flat-square)](https://github.com/hiddify/hiddify-app/) [![Youtube](https://img.shields.io/youtube/channel/views/UCxrmeMvVryNfB4XL35lXQNg?label=Youtube&style=flat-square&logo=youtube)](https://www.youtube.com/@hiddify)[![Telegram Channel](https://img.shields.io/endpoint?label=Channel&style=flat-square&url=https%3A%2F%2Ftg.sumanjay.workers.dev%2Fhiddify&color=blue)](https://telegram.dog/hiddify)[![Telegram Group](https://img.shields.io/endpoint?color=neon&label=Support%20Group&style=flat-square&url=https%3A%2F%2Ftg.sumanjay.workers.dev%2Fhiddify_board)](https://telegram.dog/hiddify_board/5)
## اپ هیدیفای چیست؟ یک کلاینت خودکار مالتی‌پلتفرم مبتنی بر [سینگ‌باکس](https://github.com/SagerNet/sing-box) که به عنوان یک ابزار عمومی برای پروکسی عمل می‌کند. این برنامه طیف گسترده‌ای از قابلیت‌ها را ارائه می‌دهد مثل انتخاب خودکار نود، مود تونل، پروفایل‌های ریموت و غیره. این برنامه رایگان، بدون آگهی و منبع باز است. با پشتیبانی از طیف وسیعی از پروتکل‌ها، این اپلیکیشن یک ابزار امن و مطمئن برای دسترسی به اینترنت رایگان فراهم می‌کند.
Farsi Demo
## 🚀 امکانات اصلی ✈️ پشتیبانی از چند پلتفرم: اندروید، iOS، ویندوز، مک و لینوکس ⭐ استفاده بسیار آسان با رابط کاربری ساده 🔍 انتخاب خودکار بهترین سرور و کانفیگ بر اساس تاخیر 🟡 پشتیبانی از رنج وسیعی از پروتکل‌ها Vless, Vmess, Reality, TUIC, Hysteria, Wireguard, SSH, etc. 🟡 پشتیبانی لینک‌های سابسکریپشن‌ مختلف: سینگ‌باکس، V2ray، کلش، کلش‌متا 🔄 آپدیت خودکار لینک سابسکریپشن و کانفیگ‌ها 🔎 نمایش اطلاعات پروفایل کاربر شامل روز و حجم باقیمانده 🛡 اپن سورس، کاملا امن و کامیونیتی محور 🌙 دارای تم دارک و لایت ⚙ سازگار با تمام پنل‌ها ⭐ کانفیگ متناسب برای ایران، چین، روسیه و‌ سایر کشورها 📱 انتشار در استورهای معتبر ## 🛍️ دریافت از استورها
                          
## 📥 دانلود مستقیم
سیستم عامل دانلود
iOS
اندروید


ویندوز

مک

لینوکس

## ⚙️ نصب و آموزش **برای مطالعه و مشاهده همه مطالب آموزشی در مورد این برنامه، با کلیک روی تصویر زیر به صفحه ویکی پروژه مراجعه کنید.**
[![همه آموزش‌ها در ویکی](https://github.com/hiddify/hiddify-app/assets/125398461/3b9ec2fe-9057-45fc-b19f-a1356e94525f)](https://hiddify.com/fa/app/)
## 🌎 ترجمه‌ها با ویرایش دستی فایل‌های JSON در assets/translations/ یا با استفاده از [![Translate with Inlang](https://img.shields.io/badge/%20-%20-3ECF8E?logo=i18next&logoColor=white)](https://fink.inlang.com/github.com/hiddify/hiddify-app) [ویرایشگر آنلاین Inlang](https://fink.inlang.com/editor/github.com/hiddify/hiddify-app)، زبان‌های موجود را بهبود بدهید و یا زبان‌های جدید اضافه کنید. ## ✏️ سپاسگزاری‌ها مایلیم از دست‌اندرکاران پروژه‌های زیر صمیمانه قدردانی کنیم که پایه قوی و ویژگی‌های نوآورانه آنها موفقیت و عملکرد این پروژه را به میزان قابل توجهی افزایش داده است. - [سینگ‌باکس](https://github.com/SagerNet/sing-box) - [سینگ‌باکس برای اندروید](https://github.com/SagerNet/sing-box-for-android) - [کلش](https://github.com/Dreamacro/clash) - [کلش‌متا](https://github.com/MetaCubeX/Clash.Meta) - [اف‌کلش](https://github.com/Fclash/Fclash) - [فونت وزیرمتن صابر راستی‌کردار](https://github.com/rastikerdar/vazirmatn) - [سایر](./pubspec.yaml) ## 🎯 حمایت از پروژه ساده‌ترین راه حمایت از ما کلیک کردن روی ستاره (⭐) بالای همین صفحه است. ما برای سرویس هایمان به کمک مالی هم نیاز داریم. تمامی فعالیت‌های ما به صورت داوطلبانه انجام می‌شود و حمایت‌های مالی صرف توسعه پروژه می‌شود. اطلاعات و آدرس‌های حمایت‌ از ما را در [این لینک](https://hiddify.com/fa/donation-and-support/) مشاهده فرمایید. ## 👩‍🏫 راه‌های همکاری و ارتباط با ما هیدیفای‌نکست یه پروژه کامیونیتی محور است. اگر به مشارکت در این پروژه علاقه دارید، لطفا راهنمای مشارکت در این پروژه را مطالعه فرمایید. ما به صورت ویژه هر گونه کمکی در این زمینه‌ها را ارج می‌نهیم: **فلاتر، Go، توسعه iOS (سوئیفت)، توسعه اندروید (کاتلین)**.
[![Email](https://img.shields.io/badge/Email-contribute@hiddify.com-005FF9?style=flat-square&logo=mail.ru)](mailto:contribute@hiddify.com) [![Telegram Channel](https://img.shields.io/endpoint?label=Channel&style=flat-square&url=https%3A%2F%2Ftg.sumanjay.workers.dev%2Fhiddify&color=blue)](https://telegram.dog/hiddify) [![Telegram Group](https://img.shields.io/endpoint?color=neon&label=Support%20Group&style=flat-square&url=https%3A%2F%2Ftg.sumanjay.workers.dev%2Fhiddify_board)](https://telegram.dog/hiddify_board) [![Youtube](https://img.shields.io/youtube/channel/views/UCxrmeMvVryNfB4XL35lXQNg?label=Youtube&style=flat-square&logo=youtube)](https://www.youtube.com/@hiddify) [![Twitter](https://img.shields.io/twitter/follow/hiddify_com?color=%231DA1F2&logo=twitter&logoColor=1DA1F2&style=flat-square)](https://twitter.com/intent/follow?screen_name=hiddify_com)

از همه کسانی که در این پروژه مشارکت می‌کنند سپاسگزاریم. بعضی از آن‌ها اینجا هستند و خیلی های دیگه خارج از گیتهاب. همگی خیلی برای ما ارزشمندند. ♥

ساخته شده با Contrib.Rocks

================================================ FILE: README_ja.md ================================================
[**![Lang_farsi](https://user-images.githubusercontent.com/125398461/234186932-52f1fa82-52c6-417f-8b37-08fe9250a55f.png) فارسی**](README_fa.md) / [**Русский 🇷🇺**](README_ru.md) / [**简体中文 🇨🇳**](README_cn.md) / [**English 🇺🇸**](README.md) / [**Portugês-BR 🇧🇷**](README_br.md)


[![GP-Intalls](https://img.shields.io/endpoint?color=green&logo=google-play&logoColor=green&url=https%3A%2F%2Fplay.cuzi.workers.dev%2Fplay%3Fi%3Dapp.hiddify.com%26l%3DGoogle%2520Play%26m%3D%24shortinstalls&style=flat-square)](https://play.google.com/store/apps/details?id=app.hiddify.com) [![Downloads](https://img.shields.io/github/downloads/hiddify/hiddify-next/total?style=flat-square&logo=github)](https://github.com/hiddify/hiddify-app/releases/)[![Last Version](https://img.shields.io/github/release/hiddify/hiddify-next/all.svg?style=flat-square)](https://github.com/hiddify/hiddify-app/releases/)[![Last Release Date](https://img.shields.io/github/release-date/hiddify/hiddify-next.svg?style=flat-square)](https://github.com/hiddify/hiddify-app/releases/)[![commits](https://img.shields.io/github/commit-activity/m/hiddify/hiddify-next?style=flat-square)](https://github.com/hiddify/hiddify-app/) [![Youtube](https://img.shields.io/youtube/channel/views/UCxrmeMvVryNfB4XL35lXQNg?label=Youtube&style=flat-square&logo=youtube)](https://www.youtube.com/@hiddify)[![Telegram Channel](https://img.shields.io/endpoint?label=Channel&style=flat-square&url=https%3A%2F%2Ftg.sumanjay.workers.dev%2Fhiddify&color=blue)](https://telegram.dog/hiddify)[![Telegram Group](https://img.shields.io/endpoint?color=neon&label=Support%20Group&style=flat-square&url=https%3A%2F%2Ftg.sumanjay.workers.dev%2Fhiddify_board)](https://telegram.dog/hiddify_board/5)
## Hiddify app とは?

Sing-box ユニバーサルプロキシツールチェーンに基づくマルチプラットフォームプロキシクライアントです。Hiddify は、自動ノード選択、TUN モード、リモートプロファイルなど、幅広い機能を提供します。Hiddify は無料でオープンソースです。幅広いプロトコルをサポートし、無料インターネットにアクセスするための安全でプライベートな方法を提供します。

English Demo
## 🚀 主な特徴 ✈️ マルチプラットフォーム: Android、iOS、Windows、macOS 及び Linux ⭐ 直感的でアクセシブルな UI 🔍 遅延に基づいたノード選択 🟡 幅広いプロトコル: Vless、Vmess、Reality、TUIC、Hysteria、Wireguard、SSH など。 🟡 サブスクリプションのリンクと設定フォーマット 🔄 サブスクリプションの自動更新 🔎 残り日数やトラフィック使用量などのプロフィール情報を表示 🛡 オープンソース、セキュア、そしてコミュニティドリブン 🌙 ダークモードとライトモード ⚙ 全てのプロキシ管理パネルに対応 ⭐ イラン、中国、ロシア、その他の国に適した構成 📱公式ストアで購入可能 ## 🛍️ 店舗で入手                            ## 📥 直接ダウンロード
OS ダウンロード
iOS
Android


Windows

macOS

Linux

## ⚙️ インストールとチュートリアル **チュートリアル情報は、下の画像をクリックしてウィキのページをご覧ください。**
[![Tutorials on Wiki](https://github.com/hiddify/hiddify-app/assets/125398461/95bb5cf8-c143-4934-87d6-b9d4c31e035e)](https://hiddify.com/app/)
## 🌎 翻訳 インストールとチュートリアル `/assets/translations` にある JSON ファイルを手動で編集するか、[Inlang オンラインエディタ](https://fink.inlang.com/editor/github.com/hiddify/hiddify-app)を使って、既存の言語を改良したり、新しい言語を追加したりすることができます。 ## ✏️ 謝辞 堅牢な基盤と革新的な機能により、このプロジェクトの成功と機能性を大幅に向上させた、以下のプロジェクトのコントリビューターに心から感謝の意を表します。 - [Sing-box](https://github.com/SagerNet/sing-box) - [Sing-box for Android](https://github.com/SagerNet/sing-box-for-android) - [Sing-box for Apple](https://github.com/SagerNet/sing-box-for-apple) - [Clash](https://github.com/Dreamacro/clash) - [Clash Meta](https://github.com/MetaCubeX/Clash.Meta) - [FClash](https://github.com/Fclash/Fclash) - [Saber Rastikerdar による Vazirmatn フォント](https://github.com/rastikerdar/vazirmatn) - [その他](./pubspec.yaml) ## 🎯 寄付とサポート 最も簡単な支援方法は、このページの上部にある star(⭐)をクリックすることです。 また、私たちのサービスには財政的な支援も必要です。私たちの活動はすべて自発的に行われており、経済的支援はプロジェクトの発展に費やされます。私たちのサポートアドレスは[こちら](https://hiddify.com/donation-and-support/)からご覧いただけます。 ## 👩‍🏫 コラボレーションおよび連絡先 Hiddify はコミュニティドリブンのプロジェクトです。コントリビュートすることに興味がある方は、[コントリビューションガイドライン](./CONTRIBUTING.md)をお読みください。私たちは特に以下の分野において、どのような協力でもいただけるとありがたいです: **Flutter、Go、iOS開発(Swift)、Androi d開発(Kotlin)。**

[![Email](https://img.shields.io/badge/Email-contribute@hiddify.com-005FF9?style=flat-square&logo=mail.ru)](mailto:contribute@hiddify.com) [![Telegram Channel](https://img.shields.io/endpoint?label=Channel&style=flat-square&url=https%3A%2F%2Ftg.sumanjay.workers.dev%2Fhiddify&color=blue)](https://telegram.dog/hiddify) [![Telegram Group](https://img.shields.io/endpoint?color=neon&label=Support%20Group&style=flat-square&url=https%3A%2F%2Ftg.sumanjay.workers.dev%2Fhiddify_board)](https://telegram.dog/hiddify_board) [![Youtube](https://img.shields.io/youtube/channel/views/UCxrmeMvVryNfB4XL35lXQNg?label=Youtube&style=flat-square&logo=youtube)](https://www.youtube.com/@hiddify) [![Twitter](https://img.shields.io/twitter/follow/hiddify_com?color=%231DA1F2&logo=twitter&logoColor=1DA1F2&style=flat-square)](https://twitter.com/intent/follow?screen_name=hiddify_com)

このプロジェクトに参加してくれているすべての人に感謝しております。ここにいる人たちもいるし、GitHub の外にもたくさんいます。それは私たちにとって大きな意味があります。 ♥

Contrib.Rocks で作成。

================================================ FILE: README_ru.md ================================================
[**![Lang_farsi](https://user-images.githubusercontent.com/125398461/234186932-52f1fa82-52c6-417f-8b37-08fe9250a55f.png) فارسی**](README_fa.md) / [**简体中文 🇨🇳**](README_cn.md) / [**English 🇺🇸**](README.md) / [**日本語 🇯🇵**](README_ja.md) / [**Portugês-BR 🇧🇷**](README_br.md)


[![GP-Intalls](https://img.shields.io/endpoint?color=green&logo=google-play&logoColor=green&url=https%3A%2F%2Fplay.cuzi.workers.dev%2Fplay%3Fi%3Dapp.hiddify.com%26l%3DGoogle%2520Play%26m%3D%24shortinstalls&style=flat-square)](https://play.google.com/store/apps/details?id=app.hiddify.com) [![Downloads](https://img.shields.io/github/downloads/hiddify/hiddify-next/total?style=flat-square&logo=github)](https://github.com/hiddify/hiddify-app/releases/)[![Last Version](https://img.shields.io/github/release/hiddify/hiddify-next/all.svg?style=flat-square)](https://github.com/hiddify/hiddify-app/releases/)[![Last Release Date](https://img.shields.io/github/release-date/hiddify/hiddify-next.svg?style=flat-square)](https://github.com/hiddify/hiddify-app/releases/)[![commits](https://img.shields.io/github/commit-activity/m/hiddify/hiddify-next?style=flat-square)](https://github.com/hiddify/hiddify-app/) [![Youtube](https://img.shields.io/youtube/channel/views/UCxrmeMvVryNfB4XL35lXQNg?label=Youtube&style=flat-square&logo=youtube)](https://www.youtube.com/@hiddify)[![Telegram Channel](https://img.shields.io/endpoint?label=Channel&style=flat-square&url=https%3A%2F%2Ftg.sumanjay.workers.dev%2Fhiddify&color=blue)](https://telegram.dog/hiddify)[![Telegram Group](https://img.shields.io/endpoint?color=neon&label=Support%20Group&style=flat-square&url=https%3A%2F%2Ftg.sumanjay.workers.dev%2Fhiddify_board)](https://telegram.dog/hiddify_board/5)
## Что такое Hiddify app? Кроссплатформенный прокси-клиент на основе ядра [Sing-box](https://github.com/SagerNet/sing-box). Hiddify предлагает широкий спектр возможностей, таких как автоматический выбор узла, режим TUN, удалённые конфигурации (подписки). Приложение с открытым исходным кодом, без рекламы. Поддерживая широкий спектр протоколов, мы обеспечиваем безопасный и конфиденциальный доступ к свободному интернету.
English Demo
## 🚀 Основные особенности ⭐ Простотой и лаконичный интерфейс ✈️ Кроссплатформенность: Android, Windows, Linux и macOS 🔍 Автоматический выбор профиля с наименьшей задержкой 🟡 Широкий выбор протоколов: Vless, Vmess, Reality, TUIC, Hysteria, Wireguard, SSH и прочие 🟡 Поддержка всех основных форматов подписки: Sing-box, V2ray, Clash, Clash meta 🔄 Автоматическое обновление подписки 🔎 Отображение информации профиля, включая оставшиеся дни и использованный трафик. 🛡 Открытый исходный код, безопасность, разрабатывается сообществом людей 🌙 Темная и светлая темы ⚙ Совместимость со всеми панелями управления прокси. ⭐ Подходящая конфигурация для Ирана, Китая, России и других стран. 📱Доступно в официальных магазинах. ## 🛍️ Приобретите в магазинах                            ## 📥 Прямая загрузка
Операционная система Скачать
iOS
Android


Windows

MacOS

Linux

## ⚙️ Установка и руководства **Соответствующую информацию можно найти на нашей вики-странице, нажав на изображение ниже.**
[![Все уроки на Wiki](https://github.com/hiddify/hiddify-app/assets/125398461/708a4fdf-fcef-4b22-acc6-67e630687f0a)](https://hiddify.com/app/)
## 🌎 Переводы Улучшайте существующие языки или добавляйте новые, вручную редактируя JSON-файлы, расположенные в `/assets/translations`, или используя [Онлайн редактор Inlang](https://fink.inlang.com/editor/github.com/hiddify/hiddify-app) ## ✏️ Благодарности Мы хотели бы выразить нашу искреннюю признательность участникам следующих проектов, чья прочная основа и инновационные функции значительно повысили успех и функциональность этого проекта. - [Sing-box](https://github.com/SagerNet/sing-box) - [Sing-box для Android](https://github.com/SagerNet/sing-box-for-android) - [Sing-box для Apple](https://github.com/SagerNet/sing-box-for-apple) - [Clash](https://github.com/Dreamacro/clash) - [Clash Meta](https://github.com/MetaCubeX/Clash.Meta) - [FClash](https://github.com/Fclash/Fclash) - [Шрифт Vazirmatn от Saber Rastikerdar](https://github.com/rastikerdar/vazirmatn) - [Others](./pubspec.yaml) ## 🎯 Пожертвования и поддержка Самый простой способ поддержать нас — нажать на звездочку (⭐) вверху этой страницы. Нам также нужна финансовая поддержка для наших сервисов. Вся наша деятельность осуществляется на добровольных началах, а финансовая поддержка будет направлена на развитие проекта. Вы можете просмотреть адреса нашей поддержки [здесь](https://hiddify.com/donation-and-support/). ## 👩‍🏫 Сотрудничество и контактная информация Hiddify - это проект, развиваемый сообществом. Если вас интересует возможность внести свой вклад, пожалуйста, ознакомьтесь с [руководством](./CONTRIBUTING.md). Мы бы особенно оценили любую помощь в следующих областях: **Flutter, Go, iOS разработка (Swift), Android разработка (Kotlin).**

[![Email](https://img.shields.io/badge/Email-contribute@hiddify.com-005FF9?style=flat-square&logo=mail.ru)](mailto:contribute@hiddify.com) [![Telegram Канал](https://img.shields.io/endpoint?label=Channel&style=flat-square&url=https%3A%2F%2Ftg.sumanjay.workers.dev%2Fhiddify&color=blue)](https://telegram.dog/hiddify) [![Группа в Telegram](https://img.shields.io/endpoint?color=neon&label=Support%20Group&style=flat-square&url=https%3A%2F%2Ftg.sumanjay.workers.dev%2Fhiddify_board)](https://telegram.dog/hiddify_board) [![Youtube](https://img.shields.io/youtube/channel/views/UCxrmeMvVryNfB4XL35lXQNg?label=Youtube&style=flat-square&logo=youtube)](https://www.youtube.com/@hiddify) [![Twitter](https://img.shields.io/twitter/follow/hiddify_com?color=%231DA1F2&logo=twitter&logoColor=1DA1F2&style=flat-square)](https://twitter.com/intent/follow?screen_name=hiddify_com)

Мы ценим всех людей, которые участвуют в этом проекте. Некоторые люди здесь и многие-многие другие за пределами Github. Это очень много значит для нас. ♥

Сделано с Contrib.Rocks

================================================ FILE: analysis_options.yaml ================================================ include: package:lint/strict.yaml analyzer: plugins: - custom_lint exclude: - "hiddify-core/**" - "**.g.dart" - "lib/gen/**" errors: invalid_annotation_target: ignore formatter: page_width: 120 linter: rules: sort_pub_dependencies: false sort_unnamed_constructors_first: false avoid_classes_with_only_static_members: false custom_lint: rules: # Enable one rule - provider_parameters ================================================ FILE: android/.gitignore ================================================ gradle-wrapper.jar /.gradle /captures/ /gradlew /gradlew.bat /local.properties GeneratedPluginRegistrant.java # Remember to never publicly share your keystore. # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app key.properties **/*.keystore **/*.jks /app/libs/* !/app/libs/.gitkeep /app/.cxx ================================================ FILE: android/.stignore ================================================ gradle-wrapper.jar .gradle captures/ gradlew gradlew.bat local.properties GeneratedPluginRegistrant.java key.properties **.keystore **.jks ================================================ FILE: android/app/build.gradle ================================================ plugins { id "com.android.application" id "org.jetbrains.kotlin.android" id "dev.flutter.flutter-gradle-plugin" id "com.google.protobuf" version "0.9.4" id "com.squareup.wire" version "5.3.1" } wire { kotlin { android = true } protoPath { srcDir 'src/main/protos' } sourcePath { srcDir 'src/main/protos/' include '**' } } def localProperties = new Properties() def localPropertiesFile = rootProject.file('local.properties') if (localPropertiesFile.exists()) { localPropertiesFile.withReader('UTF-8') { reader -> localProperties.load(reader) } } boolean hasKeyStore = false def keystoreProperties = new Properties() def keystorePropertiesFile = rootProject.file('key.properties') if (keystorePropertiesFile.exists()) { keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) hasKeyStore = true } else { println "+++" println "No keystore defined. The app will not be signed." println "Create a android/key.properties file with the following properties:" println "storePassword" println "keyPassword" println "keyAlias" println "storeFile" println "+++" } def flutterVersionCode = localProperties.getProperty('flutter.versionCode')?: '1' def flutterVersionName = localProperties.getProperty('flutter.versionName') ?: '1.0' android { namespace 'com.hiddify.hiddify' testNamespace "test.com.hiddify.hiddify" compileSdkVersion 36 ndkVersion "28.2.13676358" compileOptions { sourceCompatibility JavaVersion.VERSION_17 targetCompatibility JavaVersion.VERSION_17 } kotlinOptions { jvmTarget = '17' } sourceSets { main.java.srcDirs += 'src/main/kotlin' } defaultConfig { applicationId "app.hiddify.com" minSdkVersion flutter.minSdkVersion targetSdkVersion 36 versionCode flutterVersionCode.toInteger() versionName flutterVersionName multiDexEnabled true } splits { abi { enable true reset() //noinspection ChromeOsAbiSupport include "x86_64", "armeabi-v7a", "arm64-v8a" universalApk true } } if (hasKeyStore) { signingConfigs { release { keyAlias keystoreProperties['keyAlias'] keyPassword keystoreProperties['keyPassword'] storeFile file(keystoreProperties['storeFile']) storePassword keystoreProperties['storePassword'] } } } buildTypes { release { if (hasKeyStore) { signingConfig signingConfigs.release } else { signingConfig signingConfigs.debug } ndk { //noinspection ChromeOsAbiSupport abiFilters "x86_64", "armeabi-v7a", "arm64-v8a" debugSymbolLevel 'FULL' } } } buildFeatures { viewBinding true aidl true } } android.applicationVariants.all { variant -> variant.outputs.each { output -> output.versionCodeOverride = android.defaultConfig.versionCode } } flutter { source '../..' } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar']) implementation 'com.google.code.gson:gson:2.11.0' implementation 'androidx.core:core-ktx:1.16.0' implementation 'androidx.appcompat:appcompat:1.7.0' implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.8.7' implementation "androidx.compose.ui:ui:1.7.8" implementation("com.squareup.wire:wire-grpc-client:5.3.1") implementation 'io.grpc:grpc-okhttp:1.64.0' implementation 'io.grpc:grpc-protobuf-lite:1.64.0' implementation 'io.grpc:grpc-stub:1.64.0' // protoPath(project(':hiddify_api')) // implementation project(':hiddify_api') } ================================================ FILE: android/app/libs/.gitkeep ================================================ ================================================ FILE: android/app/src/debug/AndroidManifest.xml ================================================ ================================================ FILE: android/app/src/main/AndroidManifest.xml ================================================ ================================================ FILE: android/app/src/main/aidl/com/hiddify/hiddify/IService.aidl ================================================ package com.hiddify.hiddify; import com.hiddify.hiddify.IServiceCallback; interface IService { int getStatus(); void registerCallback(in IServiceCallback callback); oneway void unregisterCallback(in IServiceCallback callback); } ================================================ FILE: android/app/src/main/aidl/com/hiddify/hiddify/IServiceCallback.aidl ================================================ package com.hiddify.hiddify; interface IServiceCallback { void onServiceStatusChanged(int status); void onServiceAlert(int type, String message); void onServiceWriteLog(String message); void onServiceResetLogs(in List messages); } ================================================ FILE: android/app/src/main/kotlin/com/hiddify/hiddify/ActiveGroupsChannel.kt ================================================ package com.hiddify.hiddify import android.util.Log import com.google.gson.Gson //import com.hiddify.hiddify.utils.CommandClient import com.hiddify.hiddify.utils.ParsedOutboundGroup import io.flutter.embedding.engine.plugins.FlutterPlugin import io.flutter.plugin.common.EventChannel import com.hiddify.core.libbox.OutboundGroup import kotlinx.coroutines.CoroutineScope // //class ActiveGroupsChannel(private val scope: CoroutineScope) : FlutterPlugin, // CommandClient.Handler { // companion object { // const val TAG = "A/ActiveGroupsChannel" // const val CHANNEL = "com.hiddify.app/active-groups" // val gson = Gson() // } // // private val client = // CommandClient(scope, CommandClient.ConnectionType.GroupOnly, this) // // private var channel: EventChannel? = null // private var event: EventChannel.EventSink? = null // // override fun updateGroups(groups: List) { // MainActivity.instance.runOnUiThread { // val parsedGroups = groups.map { group -> ParsedOutboundGroup.fromOutbound(group) } // event?.success(gson.toJson(parsedGroups)) // } // } // // override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { // channel = EventChannel( // flutterPluginBinding.binaryMessenger, // CHANNEL // ) // // channel!!.setStreamHandler(object : EventChannel.StreamHandler { // override fun onListen(arguments: Any?, events: EventChannel.EventSink?) { // event = events // Log.d(TAG, "connecting active groups command client") // client.connect() // } // // override fun onCancel(arguments: Any?) { // event = null // Log.d(TAG, "disconnecting active groups command client") // client.disconnect() // } // }) // } // // override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { // event = null // client.disconnect() // channel?.setStreamHandler(null) // } //} ================================================ FILE: android/app/src/main/kotlin/com/hiddify/hiddify/Application.kt ================================================ package com.hiddify.hiddify import android.app.Application import android.app.NotificationManager import android.content.Context import android.content.Intent import android.content.IntentFilter import android.net.ConnectivityManager import android.net.wifi.WifiManager import android.os.PowerManager import androidx.core.content.getSystemService import com.hiddify.hiddify.bg.AppChangeReceiver import go.Seq import com.hiddify.hiddify.Application as BoxApplication class Application : Application() { override fun attachBaseContext(base: Context?) { super.attachBaseContext(base) application = this } override fun onCreate() { super.onCreate() Seq.setContext(this) registerReceiver(AppChangeReceiver(), IntentFilter().apply { addAction(Intent.ACTION_PACKAGE_ADDED) addDataScheme("package") }) } companion object { lateinit var application: BoxApplication val notification by lazy { application.getSystemService()!! } val connectivity by lazy { application.getSystemService()!! } val packageManager by lazy { application.packageManager } val powerManager by lazy { application.getSystemService()!! } val notificationManager by lazy { application.getSystemService()!! } val wifiManager by lazy { application.getSystemService()!! } } } ================================================ FILE: android/app/src/main/kotlin/com/hiddify/hiddify/EventHandler.kt ================================================ package com.hiddify.hiddify import android.util.Log import androidx.lifecycle.Observer import com.hiddify.hiddify.constant.Alert import com.hiddify.hiddify.constant.Status import io.flutter.embedding.engine.plugins.FlutterPlugin import io.flutter.plugin.common.EventChannel import io.flutter.plugin.common.JSONMethodCodec class EventHandler : FlutterPlugin { companion object { const val TAG = "A/EventHandler" const val SERVICE_STATUS = "com.hiddify.app/service.status" const val SERVICE_ALERTS = "com.hiddify.app/service.alerts" } private var statusChannel: EventChannel? = null private var alertsChannel: EventChannel? = null private var statusObserver: Observer? = null private var alertsObserver: Observer? = null override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { statusChannel = EventChannel(flutterPluginBinding.binaryMessenger, SERVICE_STATUS, JSONMethodCodec.INSTANCE) alertsChannel = EventChannel(flutterPluginBinding.binaryMessenger, SERVICE_ALERTS, JSONMethodCodec.INSTANCE) statusChannel!!.setStreamHandler(object : EventChannel.StreamHandler { override fun onListen(arguments: Any?, events: EventChannel.EventSink?) { statusObserver = Observer { Log.d(TAG, "new status: $it") val map = listOf( Pair("status", it.name) ) .toMap() events?.success(map) } MainActivity.instance.serviceStatus.observeForever(statusObserver!!) } override fun onCancel(arguments: Any?) { if (statusObserver != null) MainActivity.instance.serviceStatus.removeObserver(statusObserver!!) } }) alertsChannel!!.setStreamHandler(object : EventChannel.StreamHandler { override fun onListen(arguments: Any?, events: EventChannel.EventSink?) { alertsObserver = Observer { if (it == null) return@Observer Log.d(TAG, "new alert: $it") val map = listOf( Pair("status", it.status.name), Pair("alert", it.alert?.name), Pair("message", it.message) ) .mapNotNull { p -> p.second?.let { Pair(p.first, p.second) } } .toMap() events?.success(map) } MainActivity.instance.serviceAlerts.observeForever(alertsObserver!!) } override fun onCancel(arguments: Any?) { if (alertsObserver != null) MainActivity.instance.serviceAlerts.removeObserver(alertsObserver!!) } }) } override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { if (statusObserver != null) MainActivity.instance.serviceStatus.removeObserver(statusObserver!!) statusChannel?.setStreamHandler(null) if (alertsObserver != null) MainActivity.instance.serviceAlerts.removeObserver(alertsObserver!!) alertsChannel?.setStreamHandler(null) } } data class ServiceEvent(val status: Status, val alert: Alert? = null, val message: String? = null) ================================================ FILE: android/app/src/main/kotlin/com/hiddify/hiddify/GroupsChannel.kt ================================================ package com.hiddify.hiddify import android.util.Log import com.google.gson.Gson //import com.hiddify.hiddify.utils.CommandClient import com.hiddify.hiddify.utils.ParsedOutboundGroup import io.flutter.embedding.engine.plugins.FlutterPlugin import io.flutter.plugin.common.EventChannel import com.hiddify.core.libbox.OutboundGroup import kotlinx.coroutines.CoroutineScope // //class GroupsChannel(private val scope: CoroutineScope) : FlutterPlugin, CommandClient.Handler { // companion object { // const val TAG = "A/GroupsChannel" // const val CHANNEL = "com.hiddify.app/groups" // val gson = Gson() // } // // private val client = // CommandClient(scope, CommandClient.ConnectionType.Groups, this) // // private var channel: EventChannel? = null // private var event: EventChannel.EventSink? = null // // override fun updateGroups(groups: List) { // MainActivity.instance.runOnUiThread { // val parsedGroups = groups.map { group -> ParsedOutboundGroup.fromOutbound(group) } // event?.success(gson.toJson(parsedGroups)) // } // } // // override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { // channel = EventChannel( // flutterPluginBinding.binaryMessenger, // CHANNEL // ) // // channel!!.setStreamHandler(object : EventChannel.StreamHandler { // override fun onListen(arguments: Any?, events: EventChannel.EventSink?) { // event = events // Log.d(TAG, "connecting groups command client") // client.connect() // } // // override fun onCancel(arguments: Any?) { // event = null // Log.d(TAG, "disconnecting groups command client") // client.disconnect() // } // }) // } // // override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { // event = null // client.disconnect() // channel?.setStreamHandler(null) // } //} ================================================ FILE: android/app/src/main/kotlin/com/hiddify/hiddify/LogHandler.kt ================================================ package com.hiddify.hiddify import android.util.Log import io.flutter.embedding.engine.plugins.FlutterPlugin import io.flutter.plugin.common.EventChannel class LogHandler : FlutterPlugin { companion object { const val TAG = "A/LogHandler" const val SERVICE_LOGS = "com.hiddify.app/service.logs" } private lateinit var logsChannel: EventChannel override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { logsChannel = EventChannel(flutterPluginBinding.binaryMessenger, SERVICE_LOGS) logsChannel.setStreamHandler(object : EventChannel.StreamHandler { override fun onListen(arguments: Any?, events: EventChannel.EventSink?) { val activity = MainActivity.instance events?.success(activity.logList) activity.logCallback = { events?.success(activity.logList) } } override fun onCancel(arguments: Any?) { MainActivity.instance.logCallback = null } }) } override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { } } ================================================ FILE: android/app/src/main/kotlin/com/hiddify/hiddify/MainActivity.kt ================================================ package com.hiddify.hiddify import android.annotation.SuppressLint import android.content.Intent import android.Manifest import android.content.pm.PackageManager import android.net.VpnService import android.os.Build import android.util.Log import androidx.activity.result.contract.ActivityResultContracts import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat import androidx.lifecycle.MutableLiveData import androidx.lifecycle.lifecycleScope import com.hiddify.hiddify.bg.ServiceConnection import com.hiddify.hiddify.bg.ServiceNotification import com.hiddify.hiddify.constant.Alert import com.hiddify.hiddify.constant.ServiceMode import com.hiddify.hiddify.constant.Status import io.flutter.embedding.android.FlutterFragmentActivity import io.flutter.embedding.engine.FlutterEngine import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import java.util.LinkedList class MainActivity : FlutterFragmentActivity(), ServiceConnection.Callback { companion object { private const val TAG = "ANDROID/MyActivity" lateinit var instance: MainActivity const val VPN_PERMISSION_REQUEST_CODE = 1001 const val NOTIFICATION_PERMISSION_REQUEST_CODE = 1010 } private val connection = ServiceConnection(this, this) val logList = LinkedList() var logCallback: ((Boolean) -> Unit)? = null val serviceStatus = MutableLiveData(Status.Stopped) val serviceAlerts = MutableLiveData(null) override fun configureFlutterEngine(flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) instance = this reconnect() flutterEngine.plugins.add(MethodHandler(lifecycleScope)) flutterEngine.plugins.add(PlatformSettingsHandler()) flutterEngine.plugins.add(EventHandler()) flutterEngine.plugins.add(LogHandler()) // flutterEngine.plugins.add(GroupsChannel(lifecycleScope)) // flutterEngine.plugins.add(ActiveGroupsChannel(lifecycleScope)) // flutterEngine.plugins.add(StatsChannel(lifecycleScope)) } fun reconnect() { connection.reconnect() } @SuppressLint("NewApi") fun startService() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && !ServiceNotification.checkPermission()) { notificationPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS) return } startService0() } private fun startService0() { lifecycleScope.launch(Dispatchers.IO) { if (Settings.rebuildServiceMode()) { connection.reconnect() } if (Settings.serviceMode == ServiceMode.VPN) { if (prepare()) { return@launch } } val intent = Intent(Application.application, Settings.serviceClass()) withContext(Dispatchers.Main) { ContextCompat.startForegroundService(this@MainActivity, intent) } Settings.startedByUser = true } } private suspend fun prepare() = withContext(Dispatchers.Main) { try { val intent = VpnService.prepare(this@MainActivity) if (intent != null) { prepareLauncher.launch(intent) true } else { false } } catch (e: Exception) { onServiceAlert(Alert.RequestVPNPermission, e.message) true } } private val notificationPermissionLauncher = registerForActivityResult( ActivityResultContracts.RequestPermission(), ) { isGranted -> if (Settings.dynamicNotification && !isGranted) { onServiceAlert(Alert.RequestNotificationPermission, null) } else { startService0() } } private val prepareLauncher = registerForActivityResult( ActivityResultContracts.StartActivityForResult(), ) { result -> if (result.resultCode == RESULT_OK) { startService0() } else { onServiceAlert(Alert.RequestVPNPermission, null) } } override fun onServiceStatusChanged(status: Status) { serviceStatus.postValue(status) } override fun onServiceAlert(type: Alert, message: String?) { serviceAlerts.postValue(ServiceEvent(Status.Stopped, type, message)) } override fun onDestroy() { connection.disconnect() super.onDestroy() } @SuppressLint("NewApi") private fun grantNotificationPermission() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { ActivityCompat.requestPermissions( this, arrayOf(Manifest.permission.POST_NOTIFICATIONS), NOTIFICATION_PERMISSION_REQUEST_CODE ) } } override fun onRequestPermissionsResult( requestCode: Int, permissions: Array, grantResults: IntArray ) { if (requestCode == NOTIFICATION_PERMISSION_REQUEST_CODE) { if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { startService() } else onServiceAlert(Alert.RequestNotificationPermission, null) } super.onRequestPermissionsResult(requestCode, permissions, grantResults) } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (requestCode == VPN_PERMISSION_REQUEST_CODE) { if (resultCode == RESULT_OK) startService() else onServiceAlert(Alert.RequestVPNPermission, null) } else if (requestCode == NOTIFICATION_PERMISSION_REQUEST_CODE) { if (resultCode == RESULT_OK) startService() else onServiceAlert(Alert.RequestNotificationPermission, null) } } } ================================================ FILE: android/app/src/main/kotlin/com/hiddify/hiddify/MethodHandler.kt ================================================ package com.hiddify.hiddify import android.util.Log import com.hiddify.hiddify.bg.BoxService //import com.hiddify.hiddify.bg.BoxService.Companion.workingDir import com.hiddify.hiddify.constant.Status import io.flutter.embedding.engine.plugins.FlutterPlugin import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel import com.hiddify.core.libbox.Libbox import com.hiddify.core.mobile.Mobile import com.hiddify.core.mobile.SetupOptions import com.hiddify.hiddify.bg.Bugs import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.delay import kotlinx.coroutines.launch import java.io.File class MethodHandler(private val scope: CoroutineScope) : FlutterPlugin, MethodChannel.MethodCallHandler { private var channel: MethodChannel? = null companion object { const val TAG = "A/MethodHandler" const val channelName = "com.hiddify.app/method" enum class Trigger(val method: String) { Setup("setup"), Start("start"), Stop("stop"), Restart("restart"), AddGrpcClientPublicKey("add_grpc_client_public_key"), GetGrpcServerPublicKey("get_grpc_server_public_key"), } } override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { channel = MethodChannel( flutterPluginBinding.binaryMessenger, channelName, ) channel!!.setMethodCallHandler(this) } override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { channel?.setMethodCallHandler(null) } override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { when (call.method) { Trigger.AddGrpcClientPublicKey.method -> { GlobalScope.launch { result.runCatching { val args = call.arguments as Map<*, *> val clientPub = args["clientPublicKey"] as ByteArray // Mobile.addGrpcClientPublicKey(clientPub) Settings.grpcFlutterPublicKey = clientPub success("") } } } Trigger.GetGrpcServerPublicKey.method -> { GlobalScope.launch { result.runCatching { result.success(Mobile.getServerPublicKey()) } } } Trigger.Setup.method -> { GlobalScope.launch { result.runCatching { val args = call.arguments as Map<*, *> Settings.baseDir = args["baseDir"] as String Settings.workingDir = args["workingDir"] as String Settings.tempDir = args["tempDir"] as String Settings.debugMode = args["debug"] as Boolean? ?: false val mode = args["mode"] as Int val grpcPort = args["grpcPort"] as Int Log.d("debugmode","${Settings.debugMode}") runCatching { Mobile.setup( SetupOptions().also { it.basePath = Settings.baseDir it.workingDir = Settings.workingDir it.tempDir = Settings.tempDir it.fixAndroidStack = Bugs.fixAndroidStack it.mode=mode.toLong() it.listen= "127.0.0.1:" + grpcPort it.secret="" it.debug = Settings.debugMode },null) // Libbox.setup(Settings.baseDir, Settings.workingDir, Settings.tempDir, false) Libbox.redirectStderr(File(Settings.workingDir, "stderr2.log").path) success("") }.onFailure { error(it) } } } } Trigger.Start.method -> { scope.launch { result.runCatching { val args = call.arguments as Map<*, *> Settings.activeConfigPath = args["path"] as String? ?: "" Settings.activeProfileName = args["name"] as String? ?: "" Settings.debugMode = args["debug"] as Boolean? ?: false Settings.grpcServiceModePort = args["grpcPort"] as Int val mainActivity = MainActivity.instance // val started = mainActivity.serviceStatus.value == Status.Started // if (started) { // Log.w(TAG, "service is already running") // return@launch success(true) // } Settings.startCoreAfterStartingService = false mainActivity.startService() success(true) } } } Trigger.Stop.method -> { scope.launch { result.runCatching { val mainActivity = MainActivity.instance val started = mainActivity.serviceStatus.value == Status.Started if (!started) { Log.w(TAG, "service is not running") // return@launch success(true) } BoxService.stop() success(true) } } } // Trigger.Restart.method -> { // scope.launch(Dispatchers.IO) { // result.runCatching { // val args = call.arguments as Map<*, *> // Settings.activeConfigPath = args["path"] as String? ?: "" // Settings.activeProfileName = args["name"] as String? ?: "" // val mainActivity = MainActivity.instance // val started = mainActivity.serviceStatus.value == Status.Started // if (!started) return@launch success(true) // val restart = Settings.rebuildServiceMode() // if (restart) { // mainActivity.reconnect() // BoxService.stop() // delay(1000L) // mainActivity.startService() // return@launch success(true) // } // runCatching { // Libbox.newStandaloneCommandClient().serviceReload() // success(true) // }.onFailure { // error(it) // } // } // } // } else -> result.notImplemented() } } } ================================================ FILE: android/app/src/main/kotlin/com/hiddify/hiddify/PlatformSettingsHandler.kt ================================================ package com.hiddify.hiddify import android.Manifest import android.annotation.SuppressLint import android.app.Activity import android.content.Intent import android.content.pm.ApplicationInfo import android.content.pm.PackageManager import android.graphics.Bitmap import android.graphics.Canvas import android.net.Uri import android.os.Build import android.util.Base64 import com.google.gson.Gson import com.google.gson.annotations.SerializedName import com.hiddify.hiddify.Application.Companion.packageManager import io.flutter.embedding.engine.plugins.FlutterPlugin import io.flutter.embedding.engine.plugins.activity.ActivityAware import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel import io.flutter.plugin.common.PluginRegistry import io.flutter.plugin.common.StandardMethodCodec import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import java.io.ByteArrayOutputStream class PlatformSettingsHandler : FlutterPlugin, MethodChannel.MethodCallHandler, ActivityAware, PluginRegistry.ActivityResultListener { private var channel: MethodChannel? = null private var activity: Activity? = null private lateinit var ignoreRequestResult: MethodChannel.Result companion object { const val channelName = "com.hiddify.app/platform" const val REQUEST_IGNORE_BATTERY_OPTIMIZATIONS = 44 val gson = Gson() enum class Trigger(val method: String) { IsIgnoringBatteryOptimizations("is_ignoring_battery_optimizations"), RequestIgnoreBatteryOptimizations("request_ignore_battery_optimizations"), GetInstalledPackages("get_installed_packages"), GetPackagesIcon("get_package_icon"), } } override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { val taskQueue = flutterPluginBinding.binaryMessenger.makeBackgroundTaskQueue() channel = MethodChannel( flutterPluginBinding.binaryMessenger, channelName, StandardMethodCodec.INSTANCE, taskQueue ) channel!!.setMethodCallHandler(this) } override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { channel?.setMethodCallHandler(null) } override fun onAttachedToActivity(binding: ActivityPluginBinding) { activity = binding.activity binding.addActivityResultListener(this) } override fun onDetachedFromActivityForConfigChanges() { activity = null } override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) { activity = binding.activity binding.addActivityResultListener(this) } override fun onDetachedFromActivity() { activity = null } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?): Boolean { if (requestCode == REQUEST_IGNORE_BATTERY_OPTIMIZATIONS) { ignoreRequestResult.success(resultCode == Activity.RESULT_OK) return true } return false } data class AppItem( @SerializedName("package-name") val packageName: String, @SerializedName("name") val name: String, @SerializedName("is-system-app") val isSystemApp: Boolean ) @SuppressLint("BatteryLife") override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { when (call.method) { Trigger.IsIgnoringBatteryOptimizations.method -> { result.runCatching { success( if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { Application.powerManager.isIgnoringBatteryOptimizations(Application.application.packageName) } else { true } ) } } Trigger.RequestIgnoreBatteryOptimizations.method -> { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { return result.success(true) } val intent = Intent( android.provider.Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS, Uri.parse("package:${Application.application.packageName}") ) ignoreRequestResult = result activity?.startActivityForResult(intent, REQUEST_IGNORE_BATTERY_OPTIMIZATIONS) } Trigger.GetInstalledPackages.method -> { GlobalScope.launch { result.runCatching { val flag = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { PackageManager.GET_PERMISSIONS or PackageManager.MATCH_UNINSTALLED_PACKAGES } else { @Suppress("DEPRECATION") PackageManager.GET_PERMISSIONS or PackageManager.GET_UNINSTALLED_PACKAGES } val installedPackages = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { packageManager.getInstalledPackages( PackageManager.PackageInfoFlags.of( flag.toLong() ) ) } else { @Suppress("DEPRECATION") packageManager.getInstalledPackages(flag) } val list = mutableListOf() installedPackages.forEach { if (it.packageName != Application.application.packageName && (it.requestedPermissions?.contains(Manifest.permission.INTERNET) == true || it.packageName == "android") ) { list.add( AppItem( it.packageName, it.applicationInfo?.loadLabel(packageManager).toString(), (it.applicationInfo?.flags?.and(ApplicationInfo.FLAG_SYSTEM) == 1) ) ) } } list.sortBy { it.name } success(gson.toJson(list)) } } } Trigger.GetPackagesIcon.method -> { result.runCatching { val args = call.arguments as Map<*, *> val packageName = args["packageName"] as String val drawable = packageManager.getApplicationIcon(packageName) val bitmap = Bitmap.createBitmap( drawable.intrinsicWidth, drawable.intrinsicHeight, Bitmap.Config.ARGB_8888 ) val canvas = Canvas(bitmap) drawable.setBounds(0, 0, canvas.width, canvas.height) drawable.draw(canvas) val byteArrayOutputStream = ByteArrayOutputStream() bitmap.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream) val base64: String = Base64.encodeToString(byteArrayOutputStream.toByteArray(), Base64.NO_WRAP) success(base64) } } else -> result.notImplemented() } } } ================================================ FILE: android/app/src/main/kotlin/com/hiddify/hiddify/Settings.kt ================================================ package com.hiddify.hiddify import android.content.Context import android.util.Base64 import com.hiddify.hiddify.bg.ProxyService import com.hiddify.hiddify.bg.VPNService import com.hiddify.hiddify.constant.PerAppProxyMode import com.hiddify.hiddify.constant.ServiceMode import com.hiddify.hiddify.constant.SettingsKey import org.json.JSONObject import java.io.ByteArrayInputStream import java.io.File import java.io.ObjectInputStream object Settings { private val preferences by lazy { val context = Application.application.applicationContext context.getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE) } private const val LIST_IDENTIFIER = "VGhpcyBpcyB0aGUgcHJlZml4IGZvciBhIGxpc3Qu" var perAppProxyMode: String get() = preferences.getString(SettingsKey.PER_APP_PROXY_MODE, PerAppProxyMode.OFF)!! set(value) = preferences.edit().putString(SettingsKey.PER_APP_PROXY_MODE, value).apply() val perAppProxyEnabled: Boolean get() = perAppProxyMode != PerAppProxyMode.OFF val perAppProxyList: List get() { val stringValue = if (perAppProxyMode == PerAppProxyMode.INCLUDE) { preferences.getString(SettingsKey.PER_APP_PROXY_INCLUDE_LIST, "")!! } else { preferences.getString(SettingsKey.PER_APP_PROXY_EXCLUDE_LIST, "")!! } if (!stringValue.startsWith(LIST_IDENTIFIER)) { return stringValue.split(";") } return try { decodeListString(stringValue.substring(LIST_IDENTIFIER.length)) } catch (e: java.lang.Exception) { emptyList() } } private fun decodeListString(listString: String): List { val stream = ObjectInputStream(ByteArrayInputStream(Base64.decode(listString, 0))) return stream.readObject() as List } var activeConfigPath: String get() = preferences.getString(SettingsKey.ACTIVE_CONFIG_PATH, "")!! set(value) = preferences.edit().putString(SettingsKey.ACTIVE_CONFIG_PATH, value).apply() var activeProfileName: String get() = preferences.getString(SettingsKey.ACTIVE_PROFILE_NAME, "")!! set(value) = preferences.edit().putString(SettingsKey.ACTIVE_PROFILE_NAME, value).apply() var serviceMode: String get() = preferences.getString(SettingsKey.SERVICE_MODE, ServiceMode.VPN)!! set(value) = preferences.edit().putString(SettingsKey.SERVICE_MODE, value).apply() var configOptions: String get() = preferences.getString(SettingsKey.CONFIG_OPTIONS, "")!! set(value) = preferences.edit().putString(SettingsKey.CONFIG_OPTIONS, value).apply() var debugMode: Boolean get() = preferences.getBoolean(SettingsKey.DEBUG_MODE, false) set(value) = preferences.edit().putBoolean(SettingsKey.DEBUG_MODE, value).apply() var disableMemoryLimit: Boolean get() = preferences.getBoolean(SettingsKey.DISABLE_MEMORY_LIMIT, false) set(value) = preferences.edit().putBoolean(SettingsKey.DISABLE_MEMORY_LIMIT, value).apply() var dynamicNotification: Boolean get() = preferences.getBoolean(SettingsKey.DYNAMIC_NOTIFICATION, true) set(value) = preferences.edit().putBoolean(SettingsKey.DYNAMIC_NOTIFICATION, value).apply() var systemProxyEnabled: Boolean get() = preferences.getBoolean(SettingsKey.SYSTEM_PROXY_ENABLED, true) set(value) = preferences.edit().putBoolean(SettingsKey.SYSTEM_PROXY_ENABLED, value).apply() var startedByUser: Boolean get() = preferences.getBoolean(SettingsKey.STARTED_BY_USER, false) set(value) = preferences.edit().putBoolean(SettingsKey.STARTED_BY_USER, value).apply() fun serviceClass(): Class<*> { return when (serviceMode) { ServiceMode.VPN -> VPNService::class.java else -> ProxyService::class.java } } private var currentServiceMode : String? = null suspend fun rebuildServiceMode(): Boolean { var newMode = ServiceMode.NORMAL try { if (serviceMode == ServiceMode.VPN) { newMode = ServiceMode.VPN } } catch (_: Exception) { } if (currentServiceMode == newMode) { return false } currentServiceMode = newMode return true } private suspend fun needVPNService(): Boolean { val filePath = activeConfigPath if (filePath.isBlank()) return false val content = JSONObject(File(filePath).readText()) val inbounds = content.getJSONArray("inbounds") for (index in 0 until inbounds.length()) { val inbound = inbounds.getJSONObject(index) if (inbound.getString("type") == "tun") { return true } } return false } var workingDir: String get() = preferences.getString(SettingsKey.WORKING_DIR, "./")!! set(value) = preferences.edit().putString(SettingsKey.WORKING_DIR, value).apply() var tempDir: String get() = preferences.getString(SettingsKey.TMP_DIR, "./")!! set(value) = preferences.edit().putString(SettingsKey.TMP_DIR, value).apply() var baseDir: String get() = preferences.getString(SettingsKey.BASE_DIR, "./")!! set(value) = preferences.edit().putString(SettingsKey.BASE_DIR, value).apply() var grpcFlutterPublicKey: ByteArray get() { val encoded = preferences.getString(SettingsKey.GRPC_FLUTTER_PUBLIC_KEY, null) return encoded?.let { Base64.decode(it, Base64.DEFAULT) } ?: ByteArray(0) } set(value) { val encoded = Base64.encodeToString(value, Base64.DEFAULT) preferences.edit().putString(SettingsKey.GRPC_FLUTTER_PUBLIC_KEY, encoded).apply() } var grpcServiceModePort: Int get() = preferences.getInt(SettingsKey.GRPC_PORT, 17078)!! set(value) = preferences.edit().putInt(SettingsKey.GRPC_PORT, value).apply() var startCoreAfterStartingService: Boolean get() = preferences.getBoolean(SettingsKey.START_CORE_ON_STARTING_SERVICE, false) set(value) = preferences.edit().putBoolean(SettingsKey.START_CORE_ON_STARTING_SERVICE, value).apply() } ================================================ FILE: android/app/src/main/kotlin/com/hiddify/hiddify/ShortcutActivity.kt ================================================ package com.hiddify.hiddify import android.app.Activity import android.content.Intent import android.content.pm.ShortcutManager import android.os.Build import android.os.Bundle import androidx.core.content.getSystemService import androidx.core.content.pm.ShortcutInfoCompat import androidx.core.content.pm.ShortcutManagerCompat import androidx.core.graphics.drawable.IconCompat import com.hiddify.hiddify.bg.BoxService import com.hiddify.hiddify.bg.ServiceConnection import com.hiddify.hiddify.constant.Status class ShortcutActivity : Activity(), ServiceConnection.Callback { private val connection = ServiceConnection(this, this, false) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) if (intent.action == Intent.ACTION_CREATE_SHORTCUT) { setResult( RESULT_OK, ShortcutManagerCompat.createShortcutResultIntent( this, ShortcutInfoCompat.Builder(this, "toggle") .setIntent( Intent( this, ShortcutActivity::class.java ).setAction(Intent.ACTION_MAIN) ) .setIcon( IconCompat.createWithResource( this, R.mipmap.ic_launcher ) ) .setShortLabel(getString(R.string.quick_toggle)) .build() ) ) finish() } else { connection.connect() if (Build.VERSION.SDK_INT >= 25) { getSystemService()?.reportShortcutUsed("toggle") } } moveTaskToBack(true) } override fun onServiceStatusChanged(status: Status) { when (status) { Status.Started -> BoxService.stop() Status.Stopped -> BoxService.start() else -> {} } finish() } override fun onDestroy() { connection.disconnect() super.onDestroy() } } ================================================ FILE: android/app/src/main/kotlin/com/hiddify/hiddify/StatsChannel.kt ================================================ package com.hiddify.hiddify import android.util.Log //import com.hiddify.hiddify.utils.CommandClient import io.flutter.embedding.engine.plugins.FlutterPlugin import io.flutter.plugin.common.EventChannel import io.flutter.plugin.common.JSONMethodCodec import com.hiddify.core.libbox.StatusMessage import kotlinx.coroutines.CoroutineScope //class StatsChannel(private val scope: CoroutineScope) : FlutterPlugin, CommandClient.Handler{ // companion object { // const val TAG = "A/StatsChannel" // const val STATS_CHANNEL = "com.hiddify.app/stats" // } // // private val commandClient = // CommandClient(scope, CommandClient.ConnectionType.Status, this) // // private var statsChannel: EventChannel? = null // private var statsEvent: EventChannel.EventSink? = null // // override fun updateStatus(status: StatusMessage) { // MainActivity.instance.runOnUiThread { // val map = listOf( // Pair("connections-in", status.connectionsIn), // Pair("connections-out", status.connectionsOut), // Pair("uplink", status.uplink), // Pair("downlink", status.downlink), // Pair("uplink-total", status.uplinkTotal), // Pair("downlink-total", status.downlinkTotal) // ).associate { it.first to it.second } // statsEvent?.success(map) // } // } // // override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { // statsChannel = EventChannel( // flutterPluginBinding.binaryMessenger, // STATS_CHANNEL, // JSONMethodCodec.INSTANCE // ) // // statsChannel!!.setStreamHandler(object : EventChannel.StreamHandler { // override fun onListen(arguments: Any?, events: EventChannel.EventSink?) { // statsEvent = events // Log.d(TAG, "connecting stats command client") // commandClient.connect() // } // // override fun onCancel(arguments: Any?) { // statsEvent = null // Log.d(TAG, "disconnecting stats command client") // commandClient.disconnect() // } // }) // } // // override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { // statsEvent = null // commandClient.disconnect() // statsChannel?.setStreamHandler(null) // } //} ================================================ FILE: android/app/src/main/kotlin/com/hiddify/hiddify/bg/AppChangeReceiver.kt ================================================ package com.hiddify.hiddify.bg import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import com.hiddify.hiddify.Settings class AppChangeReceiver : BroadcastReceiver() { companion object { private const val TAG = "A/AppChangeReceiver" } override fun onReceive(context: Context, intent: Intent) { checkUpdate(context, intent) } private fun checkUpdate(context: Context, intent: Intent) { // if (!Settings.perAppProxyEnabled) { // return // } // val perAppProxyUpdateOnChange = Settings.perAppProxyUpdateOnChange // if (perAppProxyUpdateOnChange == Settings.PER_APP_PROXY_DISABLED) { // return // } // val packageName = intent.dataString?.substringAfter("package:") // if (packageName.isNullOrBlank()) { // return // } // if ((perAppProxyUpdateOnChange == Settings.PER_APP_PROXY_INCLUDE)) { // Settings.perAppProxyList = Settings.perAppProxyList + packageName // } else { // Settings.perAppProxyList = Settings.perAppProxyList - packageName // } } } ================================================ FILE: android/app/src/main/kotlin/com/hiddify/hiddify/bg/BootReceiver.kt ================================================ package com.hiddify.hiddify.bg import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import com.hiddify.hiddify.MainActivity import com.hiddify.hiddify.Settings import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import kotlinx.coroutines.withContext class BootReceiver : BroadcastReceiver() { @OptIn(DelicateCoroutinesApi::class) override fun onReceive(context: Context, intent: Intent) { when (intent.action) { Intent.ACTION_BOOT_COMPLETED, Intent.ACTION_MY_PACKAGE_REPLACED -> { } else -> return } GlobalScope.launch(Dispatchers.IO) { if (Settings.startedByUser) { withContext(Dispatchers.Main) { Settings.startCoreAfterStartingService=true //H BoxService.start() } } } } } ================================================ FILE: android/app/src/main/kotlin/com/hiddify/hiddify/bg/BoxService.kt ================================================ package com.hiddify.hiddify.bg import android.app.NotificationChannel import android.app.NotificationManager import android.app.PendingIntent import android.app.Service import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.IntentFilter import android.net.Uri import android.os.Build import android.os.IBinder import android.os.ParcelFileDescriptor import android.os.PowerManager import android.util.Log import androidx.annotation.RequiresApi import androidx.core.app.NotificationCompat import androidx.core.content.ContextCompat import androidx.lifecycle.MutableLiveData import com.hiddify.hiddify.Application import com.hiddify.hiddify.R import com.hiddify.hiddify.Settings import com.hiddify.hiddify.constant.Action import com.hiddify.hiddify.constant.Alert import com.hiddify.hiddify.constant.Status import com.hiddify.core.mobile.SetupOptions import go.Seq import com.hiddify.core.libbox.Libbox import com.hiddify.core.mobile.Mobile import com.hiddify.core.libbox.CommandServer import com.hiddify.core.libbox.CommandServerHandler import com.hiddify.core.libbox.Notification import com.hiddify.core.libbox.PlatformInterface import com.hiddify.core.libbox.SystemProxyStatus import com.hiddify.hiddify.BuildConfig import com.hiddify.hiddify.MainActivity import com.hiddify.hiddify.constant.Bugs import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext import java.io.File class BoxService( private val service: Service, private val platformInterface: PlatformInterface ) { companion object { private const val TAG = "A/BoxService" private var initializeOnce = false private lateinit var workingDir: File private fun initialize() { System.setProperty("GODEBUG", "efence=1,stacktraceback=2"); System.setProperty("GOGC", "off"); if (initializeOnce) return val baseDir = Application.application.filesDir baseDir.mkdirs() workingDir = Application.application.getExternalFilesDir(null) ?: return workingDir.mkdirs() val tempDir = Application.application.cacheDir tempDir.mkdirs() Log.d(TAG, "base dir: ${baseDir.path}") Log.d(TAG, "working dir: ${workingDir.path}") Log.d(TAG, "temp dir: ${tempDir.path}") // //Mobile.setup(baseDir.path, workingDir.path, tempDir.path, 2L ,"127.0.0.1:{Setting}","",false,this) // Libbox.setup(baseDir.path, workingDir.path, tempDir.path, false) // Libbox.setup(SetupOptions().also { // it.basePath = baseDir.path // it.workingPath = workingDir.path // it.tempPath = tempDir.path // it.fixAndroidStack = Bugs.fixAndroidStack // // }) Libbox.redirectStderr(File(Settings.workingDir, "stderr.log").path) initializeOnce = true return } fun start() { val intent = runBlocking { withContext(Dispatchers.IO) { Intent(Application.application, Settings.serviceClass()) } } ContextCompat.startForegroundService(Application.application, intent) } fun stop() { Application.application.sendBroadcast( Intent(Action.SERVICE_CLOSE).setPackage( Application.application.packageName ) ) } } var fileDescriptor: ParcelFileDescriptor? = null private val status = MutableLiveData(Status.Stopped) private val binder = ServiceBinder(status) private val notification = ServiceNotification(status, service) // private var boxService: BoxService? = null private var commandServer: CommandServer? = null private var receiverRegistered = false private val receiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { when (intent.action) { Action.SERVICE_CLOSE -> { stopService() } PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED -> { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { serviceUpdateIdleMode() } } } } } private var activeProfileName = "" private suspend fun startService() { try { status.postValue(Status.Starting) Log.d(TAG, "starting service") withContext(Dispatchers.Main) { notification.show(activeProfileName, R.string.status_starting) } val selectedConfigPath = Settings.activeConfigPath if (selectedConfigPath.isBlank()) { stopAndAlert(Alert.EmptyConfiguration) return } activeProfileName = Settings.activeProfileName withContext(Dispatchers.Main) { notification.show(activeProfileName, R.string.status_starting) binder.broadcast { it.onServiceResetLogs(listOf()) } } DefaultNetworkMonitor.start() Libbox.setMemoryLimit(!Settings.disableMemoryLimit) val newService = try { Mobile.setup( SetupOptions().also { it.basePath = Settings.baseDir it.workingDir = Settings.workingDir it.tempDir = Settings.tempDir it.fixAndroidStack = com.hiddify.hiddify.bg.Bugs.fixAndroidStack it.mode=4L//mode.toLong() it.listen= "127.0.0.1:${Settings.grpcServiceModePort}" it.secret="" it.debug = Settings.debugMode },platformInterface) // Libbox.newService(content,platformInterface) } catch (e: Exception) { stopAndAlert(Alert.CreateService, e.message) return } status.postValue(Status.Started) if (Settings.startCoreAfterStartingService){ Mobile.start("","") } // if (delayStart) { // delay(1000L) // } // newService.start() // boxService = newService // commandServer?.setService(boxService) withContext(Dispatchers.Main) { notification.show(activeProfileName, R.string.status_started) } notification.start() } catch (e: Exception) { stopAndAlert(Alert.StartService, e.message) return } } fun serviceReload() { runBlocking { serviceReload0() } } suspend fun serviceReload0() { notification.close() status.postValue(Status.Starting) val pfd = fileDescriptor if (pfd != null) { pfd.close() fileDescriptor = null } // boxService?.apply { // runCatching { // close() // }.onFailure { // writeLog("service: error when closing: $it") // } // Seq.destroyRef(refnum) // } Mobile.stop() // boxService = null startService() } fun getSystemProxyStatus(): SystemProxyStatus { val status = SystemProxyStatus() if (service is VPNService) { status.available = service.systemProxyAvailable status.enabled = service.systemProxyEnabled } return status } fun setSystemProxyEnabled(isEnabled: Boolean) { serviceReload() } @RequiresApi(Build.VERSION_CODES.M) private fun serviceUpdateIdleMode() { if (Application.powerManager.isDeviceIdleMode) { // boxService?.pause() //Mobile.pause() } else { Mobile.wake() // boxService?.wake() } } private fun stopService() { if (status.value == Status.Stopped) return status.value = Status.Stopping if (receiverRegistered) { service.unregisterReceiver(receiver) receiverRegistered = false } notification.close() GlobalScope.launch(Dispatchers.IO) { val pfd = fileDescriptor if (pfd != null) { pfd.close() fileDescriptor = null } // commandServer?.setService(null) // boxService?.apply { // runCatching { // close() // }.onFailure { // writeLog("service: error when closing: $it") // } // //Seq.destroyRef(refnum) // } // boxService = null // Libbox.registerLocalDNSTransport(null) DefaultNetworkMonitor.stop() // commandServer?.apply { // close() // Seq.destroyRef(refnum) // } // commandServer = null Settings.startedByUser = false withContext(Dispatchers.Main) { Mobile.close(4L) status.value = Status.Stopped service.stopSelf() } notification.close() } } private suspend fun stopAndAlert(type: Alert, message: String? = null) { Settings.startedByUser = false withContext(Dispatchers.Main) { if (receiverRegistered) { service.unregisterReceiver(receiver) receiverRegistered = false } notification.close() binder.broadcast { callback -> callback.onServiceAlert(type.ordinal, message) } status.value = Status.Stopped } } @OptIn(DelicateCoroutinesApi::class) @Suppress("SameReturnValue") internal fun onStartCommand(): Int { if (status.value != Status.Stopped) return Service.START_NOT_STICKY status.value = Status.Starting if (!receiverRegistered) { ContextCompat.registerReceiver(service, receiver, IntentFilter().apply { addAction(Action.SERVICE_CLOSE) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED) } }, ContextCompat.RECEIVER_NOT_EXPORTED) receiverRegistered = true } GlobalScope.launch(Dispatchers.IO) { Settings.startedByUser = true initialize() // try { // startCommandServer() // } catch (e: Exception) { // stopAndAlert(Alert.StartCommandServer, e.message) // return@launch // } startService() } return Service.START_NOT_STICKY } fun onBind(intent: Intent): IBinder { return binder } fun onDestroy() { binder.close() } fun onRevoke() { stopService() } internal fun sendNotification(notification: Notification) { return val builder = NotificationCompat.Builder(service, notification.identifier).setShowWhen(false) .setContentTitle(notification.title).setContentText(notification.body) .setOnlyAlertOnce(true).setSmallIcon(R.drawable.ic_launcher_foreground) .setCategory(NotificationCompat.CATEGORY_EVENT) .setPriority(NotificationCompat.PRIORITY_HIGH).setAutoCancel(true) if (!notification.subtitle.isNullOrBlank()) { builder.setContentInfo(notification.subtitle) } if (!notification.openURL.isNullOrBlank()) { builder.setContentIntent( PendingIntent.getActivity( service, 0, Intent( service, MainActivity::class.java, ).apply { setAction(Action.SERVICE).setData(Uri.parse(notification.openURL)) setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT) }, ServiceNotification.flags, ), ) } GlobalScope.launch(Dispatchers.Main) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { Application.notification.createNotificationChannel( NotificationChannel( notification.identifier, notification.typeName, NotificationManager.IMPORTANCE_HIGH, ), ) } Application.notification.notify(notification.typeID, builder.build()) } } fun writeDebugMessage(message: String?) { Log.d("BoxService", message!!) binder.broadcast { it.onServiceWriteLog(message) } } } ================================================ FILE: android/app/src/main/kotlin/com/hiddify/hiddify/bg/Bugs.kt ================================================ package com.hiddify.hiddify.bg import android.os.Build import com.hiddify.hiddify.BuildConfig object Bugs { // TODO: remove launch after fixed // https://github.com/golang/go/issues/68760 val fixAndroidStack = BuildConfig.DEBUG || Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1 || Build.VERSION.SDK_INT >= Build.VERSION_CODES.P } ================================================ FILE: android/app/src/main/kotlin/com/hiddify/hiddify/bg/DefaultNetworkListener.kt ================================================ package com.hiddify.hiddify.bg import android.annotation.TargetApi import android.net.ConnectivityManager import android.net.Network import android.net.NetworkCapabilities import android.net.NetworkRequest import android.os.Build import android.os.Handler import android.os.Looper import com.hiddify.hiddify.Application import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.ObsoleteCoroutinesApi import kotlinx.coroutines.channels.actor import kotlinx.coroutines.runBlocking import java.net.UnknownHostException object DefaultNetworkListener { private sealed class NetworkMessage { class Start(val key: Any, val listener: (Network?) -> Unit) : NetworkMessage() class Get : NetworkMessage() { val response = CompletableDeferred() } class Stop(val key: Any) : NetworkMessage() class Put(val network: Network) : NetworkMessage() class Update(val network: Network) : NetworkMessage() class Lost(val network: Network) : NetworkMessage() } @OptIn(DelicateCoroutinesApi::class, ObsoleteCoroutinesApi::class) private val networkActor = GlobalScope.actor(Dispatchers.Unconfined) { val listeners = mutableMapOf Unit>() var network: Network? = null val pendingRequests = arrayListOf() for (message in channel) { when (message) { is NetworkMessage.Start -> { if (listeners.isEmpty()) register() listeners[message.key] = message.listener if (network != null) message.listener(network) } is NetworkMessage.Get -> { check(listeners.isNotEmpty()) { "Getting network without any listeners is not supported" } if (network == null) { pendingRequests += message } else { message.response.complete( network, ) } } is NetworkMessage.Stop -> if (listeners.isNotEmpty() && // was not empty listeners.remove(message.key) != null && listeners.isEmpty() ) { network = null unregister() } is NetworkMessage.Put -> { network = message.network pendingRequests.forEach { it.response.complete(message.network) } pendingRequests.clear() listeners.values.forEach { it(network) } } is NetworkMessage.Update -> if (network == message.network) { listeners.values.forEach { it( network, ) } } is NetworkMessage.Lost -> if (network == message.network) { network = null listeners.values.forEach { it(null) } } } } } suspend fun start(key: Any, listener: (Network?) -> Unit) = networkActor.send( NetworkMessage.Start( key, listener, ), ) suspend fun get(): Network = if (fallback) { @TargetApi(23) Application.connectivity.activeNetwork ?: error("missing default network") // failed to listen, return current if available } else { NetworkMessage.Get().run { networkActor.send(this) response.await() } } suspend fun stop(key: Any) = networkActor.send(NetworkMessage.Stop(key)) // NB: this runs in ConnectivityThread, and this behavior cannot be changed until API 26 private object Callback : ConnectivityManager.NetworkCallback() { override fun onAvailable(network: Network) = runBlocking { networkActor.send( NetworkMessage.Put( network, ), ) } override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) { // it's a good idea to refresh capabilities runBlocking { networkActor.send(NetworkMessage.Update(network)) } } override fun onLost(network: Network) = runBlocking { networkActor.send( NetworkMessage.Lost( network, ), ) } } private var fallback = false private val request = NetworkRequest.Builder().apply { addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) if (Build.VERSION.SDK_INT == 23) { // workarounds for OEM bugs removeCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) removeCapability(NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL) } }.build() private val mainHandler = Handler(Looper.getMainLooper()) /** * Unfortunately registerDefaultNetworkCallback is going to return VPN interface since Android P DP1: * https://android.googlesource.com/platform/frameworks/base/+/dda156ab0c5d66ad82bdcf76cda07cbc0a9c8a2e * * This makes doing a requestNetwork with REQUEST necessary so that we don't get ALL possible networks that * satisfies default network capabilities but only THE default network. Unfortunately, we need to have * android.permission.CHANGE_NETWORK_STATE to be able to call requestNetwork. * * Source: https://android.googlesource.com/platform/frameworks/base/+/2df4c7d/services/core/java/com/android/server/ConnectivityService.java#887 */ private fun register() { when (Build.VERSION.SDK_INT) { in 31..Int.MAX_VALUE -> @TargetApi(31) { Application.connectivity.registerBestMatchingNetworkCallback( request, Callback, mainHandler, ) } in 28 until 31 -> @TargetApi(28) { // we want REQUEST here instead of LISTEN Application.connectivity.requestNetwork(request, Callback, mainHandler) } in 26 until 28 -> @TargetApi(26) { Application.connectivity.registerDefaultNetworkCallback(Callback, mainHandler) } in 24 until 26 -> @TargetApi(24) { Application.connectivity.registerDefaultNetworkCallback(Callback) } else -> try { fallback = false Application.connectivity.requestNetwork(request, Callback) } catch (e: RuntimeException) { fallback = true // known bug on API 23: https://stackoverflow.com/a/33509180/2245107 } } } private fun unregister() { runCatching { Application.connectivity.unregisterNetworkCallback(Callback) } } } ================================================ FILE: android/app/src/main/kotlin/com/hiddify/hiddify/bg/DefaultNetworkMonitor.kt ================================================ package com.hiddify.hiddify.bg import android.net.Network import android.os.Build import com.hiddify.hiddify.Application import com.hiddify.core.libbox.InterfaceUpdateListener import com.hiddify.hiddify.constant.Bugs import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import java.net.NetworkInterface object DefaultNetworkMonitor { var defaultNetwork: Network? = null private var listener: InterfaceUpdateListener? = null suspend fun start() { DefaultNetworkListener.start(this) { defaultNetwork = it checkDefaultInterfaceUpdate(it) } defaultNetwork = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { Application.connectivity.activeNetwork } else { DefaultNetworkListener.get() } } suspend fun stop() { DefaultNetworkListener.stop(this) } suspend fun require(): Network { val network = defaultNetwork if (network != null) { return network } return DefaultNetworkListener.get() } fun setListener(listener: InterfaceUpdateListener?) { this.listener = listener checkDefaultInterfaceUpdate(defaultNetwork) } private fun checkDefaultInterfaceUpdate(newNetwork: Network?) { val listener = listener ?: return if (newNetwork != null) { val interfaceName = (Application.connectivity.getLinkProperties(newNetwork) ?: return).interfaceName for (times in 0 until 10) { var interfaceIndex: Int try { interfaceIndex = NetworkInterface.getByName(interfaceName).index } catch (e: Exception) { Thread.sleep(100) continue } listener.updateDefaultInterface(interfaceName, interfaceIndex, false, false) } } else { listener.updateDefaultInterface("", -1, false, false) } } } ================================================ FILE: android/app/src/main/kotlin/com/hiddify/hiddify/bg/LocalResolver.kt ================================================ package com.hiddify.hiddify.bg import android.net.DnsResolver import android.os.Build import android.os.CancellationSignal import android.system.ErrnoException import androidx.annotation.RequiresApi import com.hiddify.hiddify.ktx.tryResumeWithException import com.hiddify.core.libbox.ExchangeContext import com.hiddify.core.libbox.LocalDNSTransport import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.asExecutor import kotlinx.coroutines.runBlocking import java.net.InetAddress import java.net.UnknownHostException import kotlin.coroutines.resume import kotlin.coroutines.suspendCoroutine object LocalResolver : LocalDNSTransport { private const val RCODE_NXDOMAIN = 3 override fun raw(): Boolean = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q @RequiresApi(Build.VERSION_CODES.Q) override fun exchange(ctx: ExchangeContext, message: ByteArray) { return runBlocking { val defaultNetwork = DefaultNetworkMonitor.require() suspendCoroutine { continuation -> val signal = CancellationSignal() ctx.onCancel(signal::cancel) val callback = object : DnsResolver.Callback { override fun onAnswer(answer: ByteArray, rcode: Int) { if (rcode == 0) { ctx.rawSuccess(answer) } else { ctx.errorCode(rcode) } continuation.resume(Unit) } override fun onError(error: DnsResolver.DnsException) { when (val cause = error.cause) { is ErrnoException -> { ctx.errnoCode(cause.errno) continuation.resume(Unit) return } } continuation.tryResumeWithException(error) } } DnsResolver.getInstance().rawQuery( defaultNetwork, message, DnsResolver.FLAG_NO_RETRY, Dispatchers.IO.asExecutor(), signal, callback, ) } } } override fun lookup(ctx: ExchangeContext, network: String, domain: String) { return runBlocking { val defaultNetwork = DefaultNetworkMonitor.require() if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { suspendCoroutine { continuation -> val signal = CancellationSignal() ctx.onCancel(signal::cancel) val callback = object : DnsResolver.Callback> { @Suppress("ThrowableNotThrown") override fun onAnswer(answer: Collection, rcode: Int) { if (rcode == 0) { ctx.success( (answer as Collection).mapNotNull { it?.hostAddress } .joinToString("\n"), ) } else { ctx.errorCode(rcode) } continuation.resume(Unit) } override fun onError(error: DnsResolver.DnsException) { when (val cause = error.cause) { is ErrnoException -> { ctx.errnoCode(cause.errno) continuation.resume(Unit) return } } continuation.tryResumeWithException(error) } } val type = when { network.endsWith("4") -> DnsResolver.TYPE_A network.endsWith("6") -> DnsResolver.TYPE_AAAA else -> null } if (type != null) { DnsResolver.getInstance().query( defaultNetwork, domain, type, DnsResolver.FLAG_NO_RETRY, Dispatchers.IO.asExecutor(), signal, callback, ) } else { DnsResolver.getInstance().query( defaultNetwork, domain, DnsResolver.FLAG_NO_RETRY, Dispatchers.IO.asExecutor(), signal, callback, ) } } } else { val answer = try { defaultNetwork.getAllByName(domain) } catch (e: UnknownHostException) { ctx.errorCode(RCODE_NXDOMAIN) return@runBlocking } ctx.success(answer.mapNotNull { it.hostAddress }.joinToString("\n")) } } } } ================================================ FILE: android/app/src/main/kotlin/com/hiddify/hiddify/bg/PlatformInterfaceWrapper.kt ================================================ package com.hiddify.hiddify.bg import android.annotation.SuppressLint import android.content.pm.PackageManager import android.net.NetworkCapabilities import android.os.Build import android.os.Process import android.util.Log import androidx.annotation.RequiresApi import com.hiddify.hiddify.Application import com.hiddify.core.libbox.InterfaceUpdateListener import com.hiddify.core.libbox.Libbox import com.hiddify.core.libbox.NetworkInterfaceIterator import com.hiddify.core.libbox.PlatformInterface import com.hiddify.core.libbox.StringIterator import com.hiddify.core.libbox.TunOptions import com.hiddify.core.libbox.WIFIState import java.net.Inet6Address import java.net.InetSocketAddress import java.net.InterfaceAddress import java.net.NetworkInterface import java.util.Enumeration import com.hiddify.core.libbox.NetworkInterface as LibboxNetworkInterface import android.system.OsConstants import com.hiddify.core.libbox.ConnectionOwner import com.hiddify.core.libbox.LocalDNSTransport import java.security.KeyStore import kotlin.io.encoding.Base64 import kotlin.io.encoding.ExperimentalEncodingApi interface PlatformInterfaceWrapper : PlatformInterface { override fun usePlatformAutoDetectInterfaceControl(): Boolean = true override fun autoDetectInterfaceControl(fd: Int) { } override fun openTun(options: TunOptions): Int { error("invalid argument") } override fun useProcFS(): Boolean = Build.VERSION.SDK_INT < Build.VERSION_CODES.Q @RequiresApi(Build.VERSION_CODES.Q) override fun findConnectionOwner( ipProtocol: Int, sourceAddress: String, sourcePort: Int, destinationAddress: String, destinationPort: Int, ): ConnectionOwner { try { val uid = Application.connectivity.getConnectionOwnerUid( ipProtocol, InetSocketAddress(sourceAddress, sourcePort), InetSocketAddress(destinationAddress, destinationPort), ) // if (uid == Process.INVALID_UID)error("android: connection owner not found") val owner = ConnectionOwner() owner.userId = uid if (uid!=Process.INVALID_UID) { val packages = Application.packageManager.getPackagesForUid(uid) owner.userName = packages?.firstOrNull() ?: "" owner.androidPackageName = owner.userName } return owner } catch (e: Exception) { Log.e("PlatformInterface", "getConnectionOwnerUid", e) e.printStackTrace(System.err) throw e } } override fun startDefaultInterfaceMonitor(listener: InterfaceUpdateListener) { DefaultNetworkMonitor.setListener(listener) } override fun closeDefaultInterfaceMonitor(listener: InterfaceUpdateListener) { DefaultNetworkMonitor.setListener(null) } override fun getInterfaces(): NetworkInterfaceIterator { val networks = Application.connectivity.allNetworks val networkInterfaces = NetworkInterface.getNetworkInterfaces().toList() val interfaces = mutableListOf() for (network in networks) { val boxInterface = LibboxNetworkInterface() val linkProperties = Application.connectivity.getLinkProperties(network) ?: continue val networkCapabilities = Application.connectivity.getNetworkCapabilities(network) ?: continue boxInterface.name = linkProperties.interfaceName val networkInterface = networkInterfaces.find { it.name == boxInterface.name } ?: continue boxInterface.dnsServer = StringArray(linkProperties.dnsServers.mapNotNull { it.hostAddress }.iterator()) boxInterface.type = when { networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> Libbox.InterfaceTypeWIFI networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> Libbox.InterfaceTypeCellular networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> Libbox.InterfaceTypeEthernet else -> Libbox.InterfaceTypeOther } boxInterface.index = networkInterface.index runCatching { boxInterface.mtu = networkInterface.mtu }.onFailure { Log.e( "PlatformInterface", "failed to get mtu for interface ${boxInterface.name}", it, ) } boxInterface.addresses = StringArray( networkInterface.interfaceAddresses.mapTo(mutableListOf()) { it.toPrefix() } .iterator(), ) var dumpFlags = 0 if (networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) { dumpFlags = OsConstants.IFF_UP or OsConstants.IFF_RUNNING } if (networkInterface.isLoopback) { dumpFlags = dumpFlags or OsConstants.IFF_LOOPBACK } if (networkInterface.isPointToPoint) { dumpFlags = dumpFlags or OsConstants.IFF_POINTOPOINT } if (networkInterface.supportsMulticast()) { dumpFlags = dumpFlags or OsConstants.IFF_MULTICAST } boxInterface.flags = dumpFlags boxInterface.metered = !networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED) interfaces.add(boxInterface) } return InterfaceArray(interfaces.iterator()) } override fun underNetworkExtension(): Boolean = false override fun includeAllNetworks(): Boolean = false override fun clearDNSCache() { } override fun readWIFIState(): WIFIState? { @Suppress("DEPRECATION") val wifiInfo = Application.wifiManager.connectionInfo ?: return null var ssid = wifiInfo.ssid if (ssid == "") { return WIFIState("", "") } if (ssid.startsWith("\"") && ssid.endsWith("\"")) { ssid = ssid.substring(1, ssid.length - 1) } return WIFIState(ssid, wifiInfo.bssid) } override fun localDNSTransport(): LocalDNSTransport? = LocalResolver @OptIn(ExperimentalEncodingApi::class) override fun systemCertificates(): StringIterator { val certificates = mutableListOf() val keyStore = KeyStore.getInstance("AndroidCAStore") if (keyStore != null) { keyStore.load(null, null) val aliases = keyStore.aliases() while (aliases.hasMoreElements()) { val cert = keyStore.getCertificate(aliases.nextElement()) certificates.add( "-----BEGIN CERTIFICATE-----\n" + Base64.encode(cert.encoded) + "\n-----END CERTIFICATE-----", ) } } return StringArray(certificates.iterator()) } private class InterfaceArray(private val iterator: Iterator) : NetworkInterfaceIterator { override fun hasNext(): Boolean = iterator.hasNext() override fun next(): LibboxNetworkInterface = iterator.next() } class StringArray(private val iterator: Iterator) : StringIterator { override fun len(): Int { // not used by core return 0 } override fun hasNext(): Boolean = iterator.hasNext() override fun next(): String = iterator.next() } private fun InterfaceAddress.toPrefix(): String = if (address is Inet6Address) { "${Inet6Address.getByAddress(address.address).hostAddress}/$networkPrefixLength" } else { "${address.hostAddress}/$networkPrefixLength" } private val NetworkInterface.flags: Int @SuppressLint("SoonBlockedPrivateApi") get() { val getFlagsMethod = NetworkInterface::class.java.getDeclaredMethod("getFlags") return getFlagsMethod.invoke(this) as Int } } ================================================ FILE: android/app/src/main/kotlin/com/hiddify/hiddify/bg/ProxyService.kt ================================================ package com.hiddify.hiddify.bg import android.app.Service import android.content.Intent import com.hiddify.core.libbox.Notification class ProxyService : Service(), PlatformInterfaceWrapper { private val service = BoxService(this, this) override fun onStartCommand(intent: Intent?, flags: Int, startId: Int) = service.onStartCommand() override fun onBind(intent: Intent) = service.onBind(intent) override fun onDestroy() = service.onDestroy() override fun sendNotification(notification: Notification) = service.sendNotification(notification) } ================================================ FILE: android/app/src/main/kotlin/com/hiddify/hiddify/bg/ServiceBinder.kt ================================================ package com.hiddify.hiddify.bg import android.os.RemoteCallbackList import androidx.lifecycle.MutableLiveData import com.hiddify.hiddify.IService import com.hiddify.hiddify.IServiceCallback import com.hiddify.hiddify.constant.Status import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock class ServiceBinder(private val status: MutableLiveData) : IService.Stub() { private val callbacks = RemoteCallbackList() private val broadcastLock = Mutex() init { status.observeForever { broadcast { callback -> callback.onServiceStatusChanged(it.ordinal) } } } @OptIn(DelicateCoroutinesApi::class) fun broadcast(work: (IServiceCallback) -> Unit) { GlobalScope.launch(Dispatchers.Main) { broadcastLock.withLock { val count = callbacks.beginBroadcast() try { repeat(count) { try { work(callbacks.getBroadcastItem(it)) } catch (_: Exception) { } } } finally { callbacks.finishBroadcast() } } } } override fun getStatus(): Int = (status.value ?: Status.Stopped).ordinal override fun registerCallback(callback: IServiceCallback) { callbacks.register(callback) } override fun unregisterCallback(callback: IServiceCallback?) { callbacks.unregister(callback) } fun close() { callbacks.kill() } } ================================================ FILE: android/app/src/main/kotlin/com/hiddify/hiddify/bg/ServiceConnection.kt ================================================ package com.hiddify.hiddify.bg import com.hiddify.hiddify.IService import com.hiddify.hiddify.IServiceCallback import com.hiddify.hiddify.Settings import com.hiddify.hiddify.constant.Action import com.hiddify.hiddify.constant.Alert import com.hiddify.hiddify.constant.Status import android.content.ComponentName import android.content.Context import android.content.Intent import android.content.ServiceConnection import android.os.IBinder import android.os.RemoteException import android.util.Log import androidx.appcompat.app.AppCompatActivity import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext class ServiceConnection(private val context: Context, callback: Callback, private val register: Boolean = true) : ServiceConnection { companion object { private const val TAG = "ServiceConnection" } private val callback = ServiceCallback(callback) private var service: IService? = null val status get() = service?.status?.let { Status.values()[it] } ?: Status.Stopped fun connect() { val intent = runBlocking { withContext(Dispatchers.IO) { Intent(context, Settings.serviceClass()).setAction(Action.SERVICE) } } context.bindService(intent, this, AppCompatActivity.BIND_AUTO_CREATE) Log.d(TAG, "request connect") } fun disconnect() { try { context.unbindService(this) } catch (_: IllegalArgumentException) { } Log.d(TAG, "request disconnect") } fun reconnect() { try { context.unbindService(this) } catch (_: IllegalArgumentException) { } val intent = runBlocking { withContext(Dispatchers.IO) { Intent(context, Settings.serviceClass()).setAction(Action.SERVICE) } } context.bindService(intent, this, AppCompatActivity.BIND_AUTO_CREATE) Log.d(TAG, "request reconnect") } override fun onServiceConnected(name: ComponentName, binder: IBinder) { val service = IService.Stub.asInterface(binder) this.service = service try { if (register) service.registerCallback(callback) callback.onServiceStatusChanged(service.status) } catch (e: RemoteException) { Log.e(TAG, "initialize service connection", e) } Log.d(TAG, "service connected") } override fun onServiceDisconnected(name: ComponentName?) { try { service?.unregisterCallback(callback) } catch (e: RemoteException) { Log.e(TAG, "cleanup service connection", e) } Log.d(TAG, "service disconnected") } override fun onBindingDied(name: ComponentName?) { reconnect() Log.d(TAG, "service dead") } interface Callback { fun onServiceStatusChanged(status: Status) fun onServiceAlert(type: Alert, message: String?) { } } class ServiceCallback(private val callback: Callback) : IServiceCallback.Stub() { override fun onServiceStatusChanged(status: Int) { callback.onServiceStatusChanged(Status.values()[status]) } override fun onServiceAlert(type: Int, message: String?) { callback.onServiceAlert(Alert.values()[type], message) } override fun onServiceWriteLog(message: String?) { //TODO("Not yet implemented") } override fun onServiceResetLogs(messages: List?) { //TODO("Not yet implemented") } } } ================================================ FILE: android/app/src/main/kotlin/com/hiddify/hiddify/bg/ServiceNotification.kt ================================================ package com.hiddify.hiddify.bg import android.app.NotificationChannel import android.app.NotificationManager import android.app.PendingIntent import android.app.Service import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.IntentFilter import android.os.Build import android.util.Log import androidx.annotation.StringRes import androidx.core.app.NotificationCompat import androidx.core.app.ServiceCompat import androidx.lifecycle.MutableLiveData import com.hiddify.core.api.v2.config.Protocol import com.hiddify.core.api.v2.hcommon.Empty import com.hiddify.core.api.v2.hcore.CoreClient import com.hiddify.core.api.v2.hcore.SystemInfo import com.hiddify.core.api.v2.hello.HelloClient import com.hiddify.core.api.v2.hello.HelloRequest import com.hiddify.hiddify.Application import com.hiddify.hiddify.MainActivity import com.hiddify.hiddify.R import com.hiddify.hiddify.Settings import com.hiddify.hiddify.constant.Action import com.hiddify.hiddify.constant.Status //import com.hiddify.hiddify.utils.CommandClient import com.hiddify.core.libbox.Libbox import com.hiddify.hiddify.Application.Companion.notification import com.hiddify.hiddify.utils.GrpcClientProvider import com.squareup.wire.GrpcClient import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.isActive import kotlinx.coroutines.Job import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.channels.ReceiveChannel import kotlinx.coroutines.channels.SendChannel import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import okhttp3.OkHttpClient import java.io.IOException import kotlinx.coroutines.delay import kotlinx.coroutines.CancellationException class ServiceNotification(private val status: MutableLiveData, private val service: Service) : BroadcastReceiver(){ companion object { private const val notificationId = 1 private const val notificationChannel = "service" var coreClient: CoreClient?=null val flags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) PendingIntent.FLAG_IMMUTABLE else 0 fun checkPermission(): Boolean { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) { return true } return Application.notification.areNotificationsEnabled() } } val streamingCoroutineScope = CoroutineScope(Dispatchers.IO + SupervisorJob()) // // private val commandClient = // CommandClient(GlobalScope, CommandClient.ConnectionType.Status, this) private var receiverRegistered = false private val notificationBuilder by lazy { NotificationCompat.Builder(service, notificationChannel) .setShowWhen(false) .setOngoing(true) .setContentTitle("Hiddify") .setOnlyAlertOnce(true) .setSmallIcon(R.drawable.ic_stat_logo) .setCategory(NotificationCompat.CATEGORY_SERVICE) .setContentIntent( PendingIntent.getActivity( service, 0, Intent( service, MainActivity::class.java ).setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT), flags ) ) .setPriority(NotificationCompat.PRIORITY_LOW).apply { addAction( NotificationCompat.Action.Builder( 0, service.getText(R.string.stop), PendingIntent.getBroadcast( service, 0, Intent(Action.SERVICE_CLOSE).setPackage( Application.application.packageName ), flags ) ).build() ) } } fun show(profileName: String, @StringRes contentTextId: Int) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { Application.notification.createNotificationChannel( NotificationChannel( notificationChannel, "hiddify service", NotificationManager.IMPORTANCE_LOW ) ) } service.startForeground( notificationId, notificationBuilder .setContentTitle(profileName.takeIf { it.isNotBlank() } ?: "Hiddify") .setContentText(service.getString(contentTextId)).build() ) } suspend fun start() { if (Settings.dynamicNotification && checkPermission()) { // commandClient.connect() startListenSystemInfo() withContext(Dispatchers.Main) { registerReceiver() } } } private fun registerReceiver() { service.registerReceiver(this, IntentFilter().apply { addAction(Intent.ACTION_SCREEN_ON) addAction(Intent.ACTION_SCREEN_OFF) }) receiverRegistered = true } fun updateStatus(previous:SystemInfo,status: SystemInfo) { val uplink=status.uplink_total - previous.uplink_total val downlink=status.downlink_total - previous.downlink_total val content = "${Libbox.formatBytes(uplink)}/s ↑\t${Libbox.formatBytes(downlink)}/s ↓ \n${status.current_outbound}" val title = "${status.current_profile}" Application.notificationManager.notify( notificationId, notificationBuilder.setContentTitle(title).setContentText(content).build() ) } override fun onReceive(context: Context, intent: Intent) { when (intent.action) { Intent.ACTION_SCREEN_ON -> { startListenSystemInfo() } Intent.ACTION_SCREEN_OFF -> { stopListenSystemInfo() } } } fun close() { stopListenSystemInfo() ServiceCompat.stopForeground(service, ServiceCompat.STOP_FOREGROUND_REMOVE) if (receiverRegistered) { service.unregisterReceiver(this) receiverRegistered = false } } private var streamingJob: Job? = null fun startListenSystemInfo() { // Cancel any previous stream if still running Log.d("notification","startListenSystemInfo") streamingJob?.cancel() streamingJob = streamingCoroutineScope.launch(Dispatchers.IO) { Log.d("notification", "startListenSystemInfo-launch") val coreClient = GrpcClientProvider.grpcClient.create(CoreClient::class) try { var previous = coreClient.GetSystemInfo().executeBlocking(Empty()) while (isActive) { delay(1_000) // ✅ coroutine-friendly val current = coreClient.GetSystemInfo().executeBlocking(Empty()) updateStatus(previous,current) previous = current } } catch (e: CancellationException) { // coroutine cancelled normally Log.d("notification", "SystemInfo polling cancelled") notification.cancel(notificationId) } catch (e: Exception) { Log.e("notification", "SystemInfo polling failed", e) notification.cancel(notificationId) } } } fun stopListenSystemInfo(){ try { streamingJob?.cancel() }catch (e: Exception){ Log.d("notification", "Exception ${e}") } } } ================================================ FILE: android/app/src/main/kotlin/com/hiddify/hiddify/bg/TileService.kt ================================================ package com.hiddify.hiddify.bg import android.app.KeyguardManager import android.content.Context import android.content.Intent import android.service.quicksettings.Tile import android.service.quicksettings.TileService import android.util.Log import androidx.annotation.RequiresApi import androidx.core.content.ContextCompat import androidx.lifecycle.lifecycleScope import com.hiddify.hiddify.Application import com.hiddify.hiddify.MainActivity import com.hiddify.hiddify.Settings import com.hiddify.hiddify.constant.ServiceMode import com.hiddify.hiddify.constant.Status import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @RequiresApi(24) class TileService : TileService(), ServiceConnection.Callback { private val connection = ServiceConnection(this, this) override fun onServiceStatusChanged(status: Status) { qsTile?.apply { state = when (status) { Status.Started -> Tile.STATE_ACTIVE Status.Stopped -> Tile.STATE_INACTIVE else -> Tile.STATE_UNAVAILABLE } updateTile() } } override fun onStartListening() { super.onStartListening() connection.connect() } override fun onStopListening() { connection.disconnect() super.onStopListening() } private fun toggleService() { when (connection.status) { Status.Stopped -> { Settings.startCoreAfterStartingService = true BoxService.start() qsTile?.apply { state = Tile.STATE_ACTIVE updateTile() } } Status.Started -> { BoxService.stop() qsTile?.apply { state = Tile.STATE_INACTIVE updateTile() } } else -> {} } } override fun onClick() { val keyguardManager = getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager if (keyguardManager.isKeyguardLocked) { unlockAndRun { toggleService() } } else { toggleService() } } } ================================================ FILE: android/app/src/main/kotlin/com/hiddify/hiddify/bg/VPNService.kt ================================================ package com.hiddify.hiddify.bg import android.util.Log import com.hiddify.hiddify.Settings import android.content.Intent import android.content.pm.PackageManager.NameNotFoundException import android.net.ProxyInfo import android.net.VpnService import android.os.Build import android.os.IBinder import android.os.ParcelFileDescriptor import com.hiddify.core.libbox.Notification import com.hiddify.hiddify.constant.PerAppProxyMode import com.hiddify.hiddify.ktx.toIpPrefix import com.hiddify.core.libbox.TunOptions import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext class VPNService : VpnService(), PlatformInterfaceWrapper { companion object { private const val TAG = "A/VPNService" } private val service = BoxService(this, this) override fun onStartCommand(intent: Intent?, flags: Int, startId: Int) = service.onStartCommand() override fun onBind(intent: Intent): IBinder { val binder = super.onBind(intent) if (binder != null) { return binder } return service.onBind(intent) } override fun onDestroy() { service.onDestroy() } override fun onRevoke() { runBlocking { withContext(Dispatchers.Main) { service.onRevoke() } } } override fun autoDetectInterfaceControl(fd: Int) { protect(fd) } var systemProxyAvailable = false var systemProxyEnabled = false fun addIncludePackage(builder: Builder, packageName: String) { if (packageName == this.packageName) { Log.d("VpnService","Cannot include myself: $packageName") return } try { Log.d("VpnService","Including $packageName") builder.addAllowedApplication(packageName) } catch (e: NameNotFoundException) { } } fun addExcludePackage(builder: Builder, packageName: String) { try { Log.d("VpnService","Excluding $packageName") builder.addDisallowedApplication(packageName) } catch (e: NameNotFoundException) { } } override fun openTun(options: TunOptions): Int { var hasPermission = false for (i in 0 until 20) { if (prepare(this) != null) { Log.w("VPN", "android: missing vpn permission") } else { hasPermission = true break } Thread.sleep(50) } if (!hasPermission) { error("android: missing vpn permission") } // service.fileDescriptor?.close() val builder = Builder() .setSession("hiddify") .setMtu(options.mtu) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { builder.setMetered(false) } val inet4Address = options.inet4Address while (inet4Address.hasNext()) { val address = inet4Address.next() builder.addAddress(address.address(), address.prefix()) } val inet6Address = options.inet6Address while (inet6Address.hasNext()) { val address = inet6Address.next() builder.addAddress(address.address(), address.prefix()) } if (options.autoRoute) { builder.addDnsServer(options.dnsServerAddress.value) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { val inet4RouteAddress = options.inet4RouteAddress if (inet4RouteAddress.hasNext()) { while (inet4RouteAddress.hasNext()) { builder.addRoute(inet4RouteAddress.next().toIpPrefix()) } } else { builder.addRoute("0.0.0.0", 0) } val inet6RouteAddress = options.inet6RouteAddress if (inet6RouteAddress.hasNext()) { while (inet6RouteAddress.hasNext()) { builder.addRoute(inet6RouteAddress.next().toIpPrefix()) } } else { builder.addRoute("::", 0) } val inet4RouteExcludeAddress = options.inet4RouteExcludeAddress while (inet4RouteExcludeAddress.hasNext()) { builder.excludeRoute(inet4RouteExcludeAddress.next().toIpPrefix()) } val inet6RouteExcludeAddress = options.inet6RouteExcludeAddress while (inet6RouteExcludeAddress.hasNext()) { builder.excludeRoute(inet6RouteExcludeAddress.next().toIpPrefix()) } } else { val inet4RouteAddress = options.inet4RouteRange if (inet4RouteAddress.hasNext()) { while (inet4RouteAddress.hasNext()) { val address = inet4RouteAddress.next() builder.addRoute(address.address(), address.prefix()) } } val inet6RouteAddress = options.inet6RouteRange if (inet6RouteAddress.hasNext()) { while (inet6RouteAddress.hasNext()) { val address = inet6RouteAddress.next() builder.addRoute(address.address(), address.prefix()) } } } if (Settings.perAppProxyEnabled) { val appList = Settings.perAppProxyList if (Settings.perAppProxyMode == PerAppProxyMode.INCLUDE) { appList.forEach { addIncludePackage(builder,it) } // addIncludePackage(builder,packageName) } else { appList.forEach { addExcludePackage(builder,it) } addExcludePackage(builder,packageName) } } else { val includePackage = options.includePackage if (includePackage.hasNext()) { while (includePackage.hasNext()) { addIncludePackage(builder,includePackage.next()) } // addIncludePackage(builder,packageName) }else { val excludePackage = options.excludePackage if (excludePackage.hasNext()) { while (excludePackage.hasNext()) { addExcludePackage(builder, excludePackage.next()) } } addExcludePackage(builder, packageName) } } } if (options.isHTTPProxyEnabled && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { systemProxyAvailable = true systemProxyEnabled = Settings.systemProxyEnabled if (systemProxyEnabled) builder.setHttpProxy( ProxyInfo.buildDirectProxy( options.httpProxyServer, options.httpProxyServerPort ) ) } else { systemProxyAvailable = false systemProxyEnabled = false } val pfd = builder.establish() ?: error("android: the application is not prepared or is revoked") service.fileDescriptor = pfd return pfd.fd } // override fun writeLog(message: String) = service.writeLog(message) override fun sendNotification(notification: Notification) { // service.sendNotification(notification) } } ================================================ FILE: android/app/src/main/kotlin/com/hiddify/hiddify/constant/Action.kt ================================================ package com.hiddify.hiddify.constant object Action { const val SERVICE = "com.hiddify.app.SERVICE" const val SERVICE_CLOSE = "com.hiddify.app.SERVICE_CLOSE" } ================================================ FILE: android/app/src/main/kotlin/com/hiddify/hiddify/constant/Alert.kt ================================================ package com.hiddify.hiddify.constant enum class Alert { RequestVPNPermission, RequestNotificationPermission, EmptyConfiguration, StartCommandServer, CreateService, StartService } ================================================ FILE: android/app/src/main/kotlin/com/hiddify/hiddify/constant/PerAppProxyMode.kt ================================================ package com.hiddify.hiddify.constant object PerAppProxyMode { const val OFF = "off" const val INCLUDE = "include" const val EXCLUDE = "exclude" } ================================================ FILE: android/app/src/main/kotlin/com/hiddify/hiddify/constant/ServiceMode.kt ================================================ package com.hiddify.hiddify.constant object ServiceMode { const val NORMAL = "proxy" const val VPN = "vpn" } ================================================ FILE: android/app/src/main/kotlin/com/hiddify/hiddify/constant/SettingsKey.kt ================================================ package com.hiddify.hiddify.constant object SettingsKey { private const val KEY_PREFIX = "flutter." const val SERVICE_MODE = "${KEY_PREFIX}service-mode" const val ACTIVE_CONFIG_PATH = "${KEY_PREFIX}active_config_path" const val ACTIVE_PROFILE_NAME = "${KEY_PREFIX}active_profile_name" const val PER_APP_PROXY_MODE = "${KEY_PREFIX}per_app_proxy_mode" const val PER_APP_PROXY_INCLUDE_LIST = "${KEY_PREFIX}per_app_proxy_include_list" const val PER_APP_PROXY_EXCLUDE_LIST = "${KEY_PREFIX}per_app_proxy_exclude_list" const val DEBUG_MODE = "${KEY_PREFIX}debug_mode" const val DISABLE_MEMORY_LIMIT = "${KEY_PREFIX}disable_memory_limit" const val DYNAMIC_NOTIFICATION = "${KEY_PREFIX}dynamic_notification" const val SYSTEM_PROXY_ENABLED = "${KEY_PREFIX}system_proxy_enabled" // cache const val STARTED_BY_USER = "${KEY_PREFIX}started_by_user" const val CONFIG_OPTIONS = "config_options_json" const val START_CORE_ON_STARTING_SERVICE = "${KEY_PREFIX}starting_core_on_starting_service" const val WORKING_DIR = "working_dir" const val BASE_DIR = "base_dir" const val TMP_DIR = "tmp_dir" const val GRPC_PORT = "grpc_port" const val GRPC_FLUTTER_PUBLIC_KEY = "grpc_flutter_public_key" } ================================================ FILE: android/app/src/main/kotlin/com/hiddify/hiddify/constant/Status.kt ================================================ package com.hiddify.hiddify.constant enum class Status { Stopped, Starting, Started, Stopping, } ================================================ FILE: android/app/src/main/kotlin/com/hiddify/hiddify/constant/bugs.kt ================================================ package com.hiddify.hiddify.constant import android.os.Build import com.hiddify.hiddify.BuildConfig object Bugs { // TODO: remove launch after fixed // https://github.com/golang/go/issues/68760 val fixAndroidStack = BuildConfig.DEBUG || Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1 || Build.VERSION.SDK_INT >= Build.VERSION_CODES.P } ================================================ FILE: android/app/src/main/kotlin/com/hiddify/hiddify/ktx/Continuations.kt ================================================ package com.hiddify.hiddify.ktx import kotlin.coroutines.Continuation fun Continuation.tryResume(value: T) { try { resumeWith(Result.success(value)) } catch (ignored: IllegalStateException) { } } fun Continuation.tryResumeWithException(exception: Throwable) { try { resumeWith(Result.failure(exception)) } catch (ignored: IllegalStateException) { } } ================================================ FILE: android/app/src/main/kotlin/com/hiddify/hiddify/ktx/Wrappers.kt ================================================ package com.hiddify.hiddify.ktx import android.net.IpPrefix import android.os.Build import androidx.annotation.RequiresApi import com.hiddify.core.libbox.RoutePrefix import com.hiddify.core.libbox.StringIterator import com.hiddify.core.libbox.StringBox import java.net.InetAddress val StringBox?.unwrap: String get() { if (this == null) return "" return value } fun StringIterator.toList(): List { return mutableListOf().apply { while (hasNext()) { add(next()) } } } @RequiresApi(Build.VERSION_CODES.TIRAMISU) fun RoutePrefix.toIpPrefix() = IpPrefix(InetAddress.getByName(address()), prefix()) ================================================ FILE: android/app/src/main/kotlin/com/hiddify/hiddify/utils/CommandClient.kt ================================================ package com.hiddify.hiddify.utils import com.hiddify.core.libbox.CommandClient import com.hiddify.core.libbox.CommandClientHandler import com.hiddify.core.libbox.CommandClientOptions import com.hiddify.core.libbox.Connections import com.hiddify.core.libbox.Libbox import com.hiddify.core.libbox.OutboundGroup import com.hiddify.core.libbox.OutboundGroupIterator import com.hiddify.core.libbox.StatusMessage import com.hiddify.core.libbox.StringIterator import com.hiddify.hiddify.ktx.toList import go.Seq import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.isActive import kotlinx.coroutines.launch //open class CommandClient( // private val scope: CoroutineScope, // private val connectionType: ConnectionType, // private val handler: Handler //) { // // enum class ConnectionType { // Status, Groups, Log, ClashMode, GroupOnly // } // // interface Handler { // // fun onConnected() {} // fun onDisconnected() {} // fun updateStatus(status: StatusMessage) {} // fun updateGroups(groups: List) {} // fun clearLog() {} // fun appendLog(message: String) {} // fun initializeClashMode(modeList: List, currentMode: String) {} // fun updateClashMode(newMode: String) {} // // } // // // private var commandClient: CommandClient? = null // private val clientHandler = ClientHandler() // fun connect() { // disconnect() // val options = CommandClientOptions() // options.command = when (connectionType) { // ConnectionType.Status -> Libbox.CommandStatus // ConnectionType.Groups -> Libbox.CommandGroup // ConnectionType.Log -> Libbox.CommandLog // ConnectionType.ClashMode -> Libbox.CommandClashMode // ConnectionType.GroupOnly -> Libbox.CommandGroupInfoOnly // } // options.statusInterval = 2 * 1000 * 1000 * 1000 // val commandClient = CommandClient(clientHandler, options) // scope.launch(Dispatchers.IO) { // for (i in 1..10) { // delay(100 + i.toLong() * 50) // try { // commandClient.connect() // } catch (ignored: Exception) { // continue // } // if (!isActive) { // runCatching { // commandClient.disconnect() // } // return@launch // } // this@CommandClient.commandClient = commandClient // return@launch // } // runCatching { // commandClient.disconnect() // } // } // } // // fun disconnect() { // commandClient?.apply { // runCatching { // disconnect() // } // Seq.destroyRef(refnum) // } // commandClient = null // } // // private inner class ClientHandler : CommandClientHandler { // // override fun connected() { // handler.onConnected() // } // // override fun disconnected(message: String?) { // handler.onDisconnected() // } // // override fun writeGroups(message: OutboundGroupIterator?) { // if (message == null) { // return // } // val groups = mutableListOf() // while (message.hasNext()) { // groups.add(message.next()) // } // handler.updateGroups(groups) // } // // override fun clearLogs() { // handler.clearLog() // } // override fun writeLogs(messageList: StringIterator?) { // // if (messageList == null) { // return // } // // // while (messageList.hasNext()) { // handler.appendLog(messageList.next()) // } // // } // // override fun writeStatus(message: StatusMessage?) { // if (message == null) { // return // } // handler.updateStatus(message) // } // // override fun initializeClashMode(modeList: StringIterator, currentMode: String) { // handler.initializeClashMode(modeList.toList(), currentMode) // } // // override fun updateClashMode(newMode: String) { // handler.updateClashMode(newMode) // } // // // // override fun writeConnections(message: Connections?) { // } // // } // //} ================================================ FILE: android/app/src/main/kotlin/com/hiddify/hiddify/utils/GrpcProvider.kt ================================================ package com.hiddify.hiddify.utils /* * Copyright (C) 2019 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import com.hiddify.core.api.v2.hcore.CoreClient import com.hiddify.hiddify.Settings import com.squareup.wire.GrpcClient import io.grpc.CallOptions import io.grpc.ManagedChannelBuilder import java.io.IOException import java.io.InputStream import java.security.GeneralSecurityException import java.security.KeyStore import java.security.cert.CertificateFactory import java.time.Duration import java.util.Arrays import javax.net.ssl.KeyManagerFactory import javax.net.ssl.SSLContext import javax.net.ssl.SSLSocketFactory import javax.net.ssl.TrustManagerFactory import javax.net.ssl.X509TrustManager import okhttp3.OkHttpClient import okhttp3.Protocol import okhttp3.Protocol.HTTP_1_1 import okhttp3.Protocol.HTTP_2 import okio.Buffer import java.util.concurrent.TimeUnit object GrpcClientProvider { private val okHttpClient = OkHttpClient.Builder() .protocols(listOf(Protocol.H2_PRIOR_KNOWLEDGE)) .connectTimeout(10, TimeUnit.SECONDS) .readTimeout(10, TimeUnit.SECONDS) .writeTimeout(10, TimeUnit.SECONDS) .build() val grpcClient: GrpcClient = GrpcClient.Builder() .client(okHttpClient) .baseUrl("http://127.0.0.1:${Settings.grpcServiceModePort}") .build() private fun socketFactoryAndTrustManager(): Pair { val trustManager: X509TrustManager val sslSocketFactory: SSLSocketFactory try { trustManager = trustManagerForCertificates( trustedCertificatesInputStream(), ) val sslContext = SSLContext.getInstance("TLS") sslContext.init(null, arrayOf(trustManager), null) sslSocketFactory = sslContext.socketFactory } catch (e: GeneralSecurityException) { throw RuntimeException(e) } return sslSocketFactory to trustManager } private fun trustedCertificatesInputStream(): InputStream { val myCertificate = "-----BEGIN CERTIFICATE-----\n" + "MIIC/DCCAeSgAwIBAgIBATANBgkqhkiG9w0BAQsFADAvMS0wKwYDVQQDEyRkNWY2\n" + "NTRhNC0zOWJlLTQyYjEtOGNlYi1kYTI3MmJmNTI2ZTQwIBcNMTkwODEyMjEwMzIx\n" + "WhgPMjExOTA3MTkyMTAzMjFaMC8xLTArBgNVBAMTJGQ1ZjY1NGE0LTM5YmUtNDJi\n" + "MS04Y2ViLWRhMjcyYmY1MjZlNDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC\n" + "ggEBAJQzrBa2Zp7lJ8vJ/EWrkGU2BAOublkMl5XI0cbSIfbvuITXgHX7W5sDeEwx\n" + "6ultnUBVg6PmEbLAaZFtqg7gFPaVGbvP4h07FHSjRdf+y8W3QgoBIhc7/zuJiw1h\n" + "CsJ9D7eGl2dnXO6FgdY6ISnPAfxzzrZPCJtKL+Ffm9UnfCA7AYaQQZoymqVTGIsC\n" + "QAekkRkRia7gpUrTvR0hXST18KMcB7QKEv75rL8pEPHirJjyujBh+4VYVpLRDtbc\n" + "QKxCCXcn/zhTsn+4TV/4SgO1IhU+TBv4/iffzLi/aXKEEoPJhgIbMOd5ri1XBsTe\n" + "pGNaBOlYlEm8q8u1E3nGxzmkBtMCAwEAAaMhMB8wHQYDVR0RAQH/BBMwEYIJbG9j\n" + "YWxob3N0hwQKAAICMA0GCSqGSIb3DQEBCwUAA4IBAQAjL/inUHQbYD6bosFDQfyL\n" + "E9LOanO3ewiuZr5Sa4DJ5n8kNPdAO9M9urfmTbOUdvMfrH+fqiEwo6a7NTqT9bGk\n" + "Ewz7/LdpvWIGMpnijLEPTDTur2VmjpjqtawvzbFiHhdzOZk3o6bKbY3qac7CxaaO\n" + "MWZKF+o+YRCXVAJ2NQZLW2D9ee1qOXpK7VA360MFoyfo3cP8z6DDdNJm6gDAK+wI\n" + "1pMCdrdwHuu+ExKKA8za4r6dThVQu5jp6d7GO+2qf9rGkm1idIgjGtsgC+hPmhLb\n" + "7RK0ynU3Ai32elqwTDpD1WGuP2yacSWweh3GG6lG1NNY7n3tsccUWnsZztQ66Oh4\n" + "-----END CERTIFICATE-----" return Buffer() .writeUtf8(myCertificate) .inputStream() } @Throws(GeneralSecurityException::class) private fun trustManagerForCertificates(inputStream: InputStream): X509TrustManager { val certificateFactory = CertificateFactory.getInstance("X.509") val certificates = certificateFactory.generateCertificates(inputStream) if (certificates.isEmpty()) { throw IllegalArgumentException("expected non-empty set of trusted certificates") } // Put the certificates a key store. val password = "password".toCharArray() // Any password will work. val keyStore = newEmptyKeyStore(password) for ((index, certificate) in certificates.withIndex()) { val certificateAlias = index.toString() keyStore.setCertificateEntry(certificateAlias, certificate) } // Use it to build an X509 trust manager. val keyManagerFactory = KeyManagerFactory.getInstance( KeyManagerFactory.getDefaultAlgorithm(), ) keyManagerFactory.init(keyStore, password) val trustManagerFactory = TrustManagerFactory.getInstance( TrustManagerFactory.getDefaultAlgorithm(), ) trustManagerFactory.init(keyStore) val trustManagers = trustManagerFactory.trustManagers if (trustManagers.size != 1 || trustManagers[0] !is X509TrustManager) { throw IllegalStateException( "Unexpected default trust managers:" + Arrays.toString(trustManagers), ) } return trustManagers[0] as X509TrustManager } @Throws(GeneralSecurityException::class) private fun newEmptyKeyStore(password: CharArray): KeyStore { try { val keyStore = KeyStore.getInstance(KeyStore.getDefaultType()) val inputStream: InputStream? = null // By convention, 'null' creates an empty key store. keyStore.load(inputStream, password) return keyStore } catch (e: IOException) { throw AssertionError(e) } } } ================================================ FILE: android/app/src/main/kotlin/com/hiddify/hiddify/utils/OutboundMapper.kt ================================================ package com.hiddify.hiddify.utils import com.google.gson.annotations.SerializedName import com.hiddify.core.libbox.OutboundGroup import com.hiddify.core.libbox.OutboundGroupItem data class ParsedOutboundGroup( @SerializedName("tag") val tag: String, @SerializedName("type") val type: String, @SerializedName("selected") val selected: String, @SerializedName("items") val items: List ) { companion object { fun fromOutbound(group: OutboundGroup): ParsedOutboundGroup { val outboundItems = group.items val items = mutableListOf() while (outboundItems.hasNext()) { items.add(ParsedOutboundGroupItem(outboundItems.next())) } return ParsedOutboundGroup(group.tag, group.type, group.selected, items) } } } data class ParsedOutboundGroupItem( @SerializedName("tag") val tag: String, @SerializedName("type") val type: String, @SerializedName("url-test-delay") val urlTestDelay: Int, ) { constructor(item: OutboundGroupItem) : this(item.tag, item.type, item.urlTestDelay) } ================================================ FILE: android/app/src/main/protos/extension/extension.proto ================================================ syntax = "proto3"; import "v2/hcommon/common.proto"; package extension; option go_package = "github.com/hiddify/hiddify-core/extension"; option java_package = "com.hiddify.core.api.extension"; message ExtensionActionResult { string extension_id = 1; hcommon.ResponseCode code = 2; string message = 3; } message ExtensionList { repeated ExtensionMsg extensions = 1; } message EditExtensionRequest { string extension_id = 1; bool enable = 2; } message ExtensionMsg { string id = 1; string title = 2; string description = 3; bool enable = 4; } message ExtensionRequest { string extension_id = 1; map data = 2; } message SendExtensionDataRequest { string extension_id = 1; string button=2; map data = 3; } message ExtensionResponse { ExtensionResponseType type = 1; string extension_id = 2; string json_ui = 3; } enum ExtensionResponseType { NOTHING = 0; UPDATE_UI = 1; SHOW_DIALOG = 2; END=3; } ================================================ FILE: android/app/src/main/protos/extension/extension_service.proto ================================================ syntax = "proto3"; import "extension/extension.proto"; import "v2/hcommon/common.proto"; package extension; option go_package = "github.com/hiddify/hiddify-core/extension"; option java_package = "com.hiddify.core.api.extension"; service ExtensionHostService { rpc ListExtensions (hcommon.Empty) returns (ExtensionList) {} rpc Connect (ExtensionRequest) returns (stream ExtensionResponse) {} rpc EditExtension (EditExtensionRequest) returns (ExtensionActionResult) {} rpc SubmitForm (SendExtensionDataRequest) returns (ExtensionActionResult) {} rpc Close (ExtensionRequest) returns (ExtensionActionResult) {} rpc GetUI (ExtensionRequest) returns (ExtensionActionResult) {} } ================================================ FILE: android/app/src/main/protos/v2/config/route_rule.proto ================================================ syntax = "proto3"; package config; option go_package = "github.com/hiddify/hiddify-core/v2/config"; option java_package = "com.hiddify.core.api.v2.config"; message RouteRule { repeated Rule rules = 1 [json_name = "rules"]; } message Rule { uint32 list_order = 1 [json_name = "list_order"]; bool enabled = 2 [json_name = "enabled"]; string name = 3 [json_name = "name"]; Outbound outbound = 4 [json_name = "outbound"]; repeated string rule_sets = 5 [json_name = "rule_set"]; repeated string package_names = 6 [json_name = "package_name"]; repeated string process_names = 7 [json_name = "process_name"]; repeated string process_paths = 8 [json_name = "process_path"]; Network network = 9 [json_name = "network"]; repeated string port_ranges = 10 [json_name = "port_range"]; repeated string source_port_ranges = 11 [json_name = "source_port_range"]; repeated Protocol protocols = 12 [json_name = "protocol"]; repeated string ip_cidrs = 13 [json_name = "ip_cidr"]; repeated string source_ip_cidrs = 14 [json_name = "source_ip_cidr"]; repeated string domains = 15 [json_name = "domain"]; repeated string domain_suffixes = 16 [json_name = "domain_suffix"]; repeated string domain_keywords = 17 [json_name = "domain_keyword"]; repeated string domain_regexes = 18 [json_name = "domain_regex"]; } enum Outbound { proxy = 0; direct = 1; direct_with_fragment = 2; block = 3; } enum Network { all = 0; tcp = 1; udp = 2; } enum Protocol { tls = 0; http = 1; quic = 2; stun = 3; dns = 4; bittorrent = 5; } ================================================ FILE: android/app/src/main/protos/v2/hcommon/common.proto ================================================ syntax = "proto3"; package hcommon; option go_package = "github.com/hiddify/hiddify-core/v2/hcommon"; option java_package = "com.hiddify.core.api.v2.hcommon"; message Empty { } enum ResponseCode { OK = 0; FAILED = 1; AUTH_NEED = 2; } message Response { ResponseCode code = 1; string message = 2; } ================================================ FILE: android/app/src/main/protos/v2/hcore/hcore.proto ================================================ syntax = "proto3"; import "v2/hcommon/common.proto"; import "google/protobuf/timestamp.proto"; package hcore; option go_package = "github.com/hiddify/hiddify-core/v2/hcore"; option java_package = "com.hiddify.core.api.v2.hcore"; enum CoreStates { STOPPED = 0; STARTING = 1; STARTED = 2; STOPPING = 3; } enum MessageType { EMPTY=0; EMPTY_CONFIGURATION = 1; START_COMMAND_SERVER = 2; CREATE_SERVICE = 3; START_SERVICE = 4; UNEXPECTED_ERROR = 5; ALREADY_STARTED = 6; ALREADY_STOPPED = 7; INSTANCE_NOT_FOUND = 8; INSTANCE_NOT_STOPPED = 9; INSTANCE_NOT_STARTED = 10; ERROR_BUILDING_CONFIG = 11; ERROR_PARSING_CONFIG = 12; ERROR_READING_CONFIG = 13; ERROR_EXTENSION = 14; } message CoreInfoResponse { CoreStates core_state = 1; MessageType message_type = 2; string message = 3; } message StartRequest { string config_path = 1; string config_content = 2; // Optional if configPath is not provided. bool disable_memory_limit = 3; bool delay_start = 4; bool enable_old_command_server = 5; bool enable_raw_config = 6; string config_name = 7; } enum SetupMode { OLD = 0; GRPC_NORMAL = 1; GRPC_BACKGROUND = 2; GRPC_NORMAL_INSECURE = 3; GRPC_BACKGROUND_INSECURE = 4; } message CloseRequest { SetupMode mode = 1; } // Define the message equivalent of SetupParameters message SetupRequest { string base_path = 1; string working_dir = 2; string temp_dir = 3; int64 flutter_status_port = 4; string listen = 5; string secret = 6; bool debug = 7; SetupMode mode = 8; bool fix_android_stack = 9; } message SystemInfo { int64 memory = 1; int32 goroutines = 2; int32 connections_in = 3; int32 connections_out = 4; bool traffic_available = 5; int64 uplink = 6; int64 downlink = 7; int64 uplink_total = 8; int64 downlink_total = 9; string current_outbound = 10; string current_profile = 11; } message OutboundInfo { string tag = 1; string type = 2; google.protobuf.Timestamp url_test_time = 3; int32 url_test_delay = 4; optional IpInfo ipinfo = 5; bool is_selected = 6; bool is_group = 7; optional string group_selected_tag=13; optional string group_selected_tag_display=14; bool is_secure = 8; bool is_visible = 9; uint32 port = 10; string host = 11; string tag_display = 12; int64 upload = 15; int64 download = 16; } message IpInfo { string ip = 1 [json_name = "ip"]; // The IP address. string country_code = 2 [json_name = "country_code"]; // The country code. string region = 3 [json_name = "region"]; // The region (optional). string city = 4 [json_name = "city"]; // The city (optional). int32 asn = 5 [json_name = "asn"]; // The Autonomous System Number (optional). string org = 6 [json_name = "org"]; // The organization (optional). double latitude = 7 [json_name = "latitude"]; // The latitude (optional). double longitude = 8 [json_name = "longitude"]; // The longitude (optional). string postal_code = 9 [json_name = "postal_code"]; // The postal code (optional). } message OutboundGroup { string tag = 1; string type = 2; string selected=3; bool selectable=4; bool Is_expand=5; repeated OutboundInfo items = 6; } message OutboundGroupList{ repeated OutboundGroup items = 1; } message WarpAccount { string account_id = 1; string access_token = 2; } message WarpWireguardConfig { string private_key = 1 [json_name = "private-key"]; string local_address_ipv4 = 2 [json_name = "local-address-ipv4"]; string local_address_ipv6 = 3 [json_name = "local-address-ipv6"]; string peer_public_key = 4 [json_name = "peer-public-key"]; string client_id=5 [json_name = "client-id"]; } message WarpGenerationResponse { WarpAccount account = 1; string log = 2; WarpWireguardConfig config = 3; } message SystemProxyStatus { bool available = 1; bool enabled = 2; } message ParseRequest { string content = 1; string config_path = 2; string temp_path = 3; bool debug = 4; } message ParseResponse { hcommon.ResponseCode response_code = 1; string content = 2; string message = 3; } message ChangeHiddifySettingsRequest { string hiddify_settings_json = 1; } message GenerateConfigRequest { string path = 1; string temp_path = 2; bool debug = 3; } message GenerateConfigResponse { string config_content = 1; } message SelectOutboundRequest { string group_tag = 1; string outbound_tag = 2; } message UrlTestRequest { string tag = 1; } message GenerateWarpConfigRequest { string license_key = 1; string account_id = 2; string access_token = 3; } message SetSystemProxyEnabledRequest { bool is_enabled = 1; } enum LogLevel { TRACE = 0; DEBUG = 1; INFO = 2; WARNING = 3; ERROR = 4; FATAL = 5; } enum LogType { CORE = 0; SERVICE = 1; CONFIG = 2; } message LogMessage { LogLevel level = 1; LogType type = 2; string message = 3; google.protobuf.Timestamp time = 4; } message LogRequest { LogLevel level = 1; } message StopRequest{ } ================================================ FILE: android/app/src/main/protos/v2/hcore/hcore_service.proto ================================================ syntax = "proto3"; import "v2/hcommon/common.proto"; package hcore; option go_package = "github.com/hiddify/hiddify-core/v2/hcore"; option java_package = "com.hiddify.core.api.v2.hcore"; import "v2/hcore/hcore.proto"; service Core { rpc Start (StartRequest) returns (CoreInfoResponse); rpc CoreInfoListener (hcommon.Empty) returns (stream CoreInfoResponse); rpc OutboundsInfo (hcommon.Empty) returns (stream OutboundGroupList); rpc MainOutboundsInfo (hcommon.Empty) returns (stream OutboundGroupList); rpc GetSystemInfo (hcommon.Empty) returns (SystemInfo); rpc GetSystemInfoStream (hcommon.Empty) returns (stream SystemInfo); rpc Setup (SetupRequest) returns (hcommon.Response); rpc Parse (ParseRequest) returns (ParseResponse); rpc ChangeHiddifySettings (ChangeHiddifySettingsRequest) returns (CoreInfoResponse); //rpc GenerateConfig (GenerateConfigRequest) returns (GenerateConfigResponse); rpc StartService (StartRequest) returns (CoreInfoResponse); rpc Stop (hcommon.Empty) returns (CoreInfoResponse); rpc Restart (StartRequest) returns (CoreInfoResponse); rpc SelectOutbound (SelectOutboundRequest) returns (hcommon.Response); rpc UrlTest (UrlTestRequest) returns (hcommon.Response); rpc UrlTestActive (hcommon.Empty) returns (hcommon.Response); rpc GenerateWarpConfig (GenerateWarpConfigRequest) returns (WarpGenerationResponse); rpc GetSystemProxyStatus (hcommon.Empty) returns (SystemProxyStatus); rpc SetSystemProxyEnabled (SetSystemProxyEnabledRequest) returns (hcommon.Response); rpc LogListener (LogRequest) returns (stream LogMessage); rpc Close (CloseRequest) returns (hcommon.Empty); } ================================================ FILE: android/app/src/main/protos/v2/hcore/tunnelservice/tunnel.proto ================================================ syntax = "proto3"; package tunnelservice; option go_package = "github.com/hiddify/hiddify-core/v2/hcore/tunnelservice"; option java_package = "com.hiddify.core.api.v2.tunnelservice"; message TunnelStartRequest { bool ipv6 = 1; int32 server_port = 2; string server_username=3; string server_password=4; bool strict_route = 5; bool endpoint_independent_nat = 6; string stack = 7; } message TunnelResponse { string message = 1; } ================================================ FILE: android/app/src/main/protos/v2/hcore/tunnelservice/tunnel_service.proto ================================================ syntax = "proto3"; import "v2/hcommon/common.proto"; package tunnelservice; option go_package = "github.com/hiddify/hiddify-core/v2/hcore/tunnelservice"; option java_package = "com.hiddify.core.api.v2.hcore.tunnelservice"; import "v2/hcore/tunnelservice/tunnel.proto"; service TunnelService { rpc Start(TunnelStartRequest) returns (TunnelResponse); rpc Stop(hcommon.Empty) returns (TunnelResponse); rpc Status(hcommon.Empty) returns (TunnelResponse); rpc Exit(hcommon.Empty) returns (TunnelResponse); } ================================================ FILE: android/app/src/main/protos/v2/hello/hello.proto ================================================ syntax = "proto3"; package hello; option go_package = "github.com/hiddify/hiddify-core/v2/hello"; option java_package = "com.hiddify.core.api.v2.hello"; message HelloRequest { string name = 1; } message HelloResponse { string message = 1; } ================================================ FILE: android/app/src/main/protos/v2/hello/hello_service.proto ================================================ syntax = "proto3"; package hello; option go_package = "github.com/hiddify/hiddify-core/v2/hello"; option java_package = "com.hiddify.core.api.v2.hello"; import "v2/hello/hello.proto"; service Hello { rpc SayHello (HelloRequest) returns (HelloResponse); rpc SayHelloStream (stream HelloRequest) returns (stream HelloResponse); } ================================================ FILE: android/app/src/main/protos/v2/hiddifyoptions/hiddify_options.proto ================================================ /** * This file defines various configuration options for the Hiddify application. */ syntax = "proto3"; package hiddifyoptions; option go_package = "github.com/hiddify/hiddify-core/v2/hiddifyoptions"; option java_package = "com.hiddify.core.api.v2.hiddifyoptions"; /** * HiddifyOptions defines the configuration options for the Hiddify application. */ message HiddifyOptions { bool enable_full_config = 1; // Enables full configuration options. string log_level = 2; // Specifies the logging level (e.g., INFO, DEBUG). string log_file = 3; // Path to the log file. bool enable_clash_api = 4; // Indicates whether the Clash API is enabled. uint32 clash_api_port = 5; // Port for the Clash API (using uint32 for compatibility). string web_secret = 6; // Secret key for accessing the Clash API. string region = 7; // Region for the application. bool block_ads = 8; // If true, blocks ads. bool use_xray_core_when_possible = 9; // If true, use XRay core when possible. repeated Rule rules = 10; // List of routing rules for traffic management. WarpOptions warp = 11; // Configuration options for Warp. WarpOptions warp2 = 12; // Additional configuration options for a second Warp instance. MuxOptions mux = 13; // Configuration options for multiplexing. TLSTricks tls_tricks = 14; // Options for TLS tricks. DNSOptions dns_options = 15; // DNS-related options. InboundOptions inbound_options = 16; // Inbound connection options. URLTestOptions url_test_options = 17; // URL test configuration options. RouteOptions route_options = 18; // Routing-related options. } /** * DomainStrategy defines the strategies for IP address preference when resolving domain names. */ enum DomainStrategy { as_is =0; // As it is. prefer_ipv4 = 1; // Prefer IPv4 addresses. prefer_ipv6 = 2; // Prefer IPv6 addresses. ipv4_only = 3; // Only use IPv4 addresses. ipv6_only = 4; // Only use IPv6 addresses. } /** * IntRange defines a range of integers for various configurations. * It includes the starting and ending values of the range. */ message IntRange { int32 from = 1; // Starting value of the range. int32 to = 2; // Ending value of the range. } /** * DNSOptions defines DNS-related configuration options. */ message DNSOptions { string remote_dns_address = 1; // Remote DNS server address. DomainStrategy remote_dns_domain_strategy = 2; // Strategy for resolving domains with remote DNS. string direct_dns_address = 3; // Direct DNS server address. DomainStrategy direct_dns_domain_strategy = 4; // Strategy for resolving domains with direct DNS. bool independent_dns_cache = 5; // If true, enables independent DNS caching. bool enable_fake_dns = 6; // If true, enables fake DNS responses. bool enable_dns_routing = 7; // If true, enables DNS routing. } /** * InboundOptions defines the configuration options for inbound connections. */ message InboundOptions { bool enable_tun = 1; // If true, enables TUN interface. bool enable_tun_service = 2; // If true, enables TUN service. bool set_system_proxy = 3; // If true, sets the system proxy. uint32 mixed_port = 4; // Port for mixed traffic (using uint32 for compatibility). uint32 tproxy_port = 5; // Port for TProxy connections (using uint32 for compatibility). uint32 redirect_port = 10; // Port for TProxy connections (using uint32 for compatibility). uint32 direct_port = 6; // Port for local DNS service (using uint32 for compatibility). uint32 mtu = 7; // Maximum Transmission Unit size (using uint32 for compatibility). bool strict_route = 8; // If true, enforces strict routing. string tun_stack = 9; // Specifies the TUN stack to use. } /** * URLTestOptions defines the configuration options for URL testing. */ message URLTestOptions { string connection_test_url = 1; // URL used for connection testing. int64 url_test_interval = 2; // Interval for URL tests in milliseconds. } /** * RouteOptions defines options related to traffic routing. */ message RouteOptions { bool resolve_destination = 1; // If true, resolves the destination address. DomainStrategy ipv6_mode = 2; // Strategy for handling IPv6 addresses. bool bypass_lan = 3; // If true, bypasses LAN connections. bool allow_connection_from_lan = 4; // If true, allows connections from LAN. } /** * TLSTricks defines options for TLS tricks to obfuscate traffic. */ message TLSTricks { bool enable_fragment = 1; // If true, enables fragmentation of packets. IntRange fragment_size = 2; // Size of fragments to be used. IntRange fragment_sleep = 3; // Sleep time between fragments. bool mixed_sni_case = 4; // If true, enables mixed SNI case for obfuscation. bool enable_padding = 5; // If true, enables padding of packets. IntRange padding_size = 6; // Size of padding to be used. } /** * MuxOptions defines options for multiplexing connections. */ message MuxOptions { bool enable = 1; // If true, enables multiplexing. bool padding = 2; // If true, enables padding for multiplexed connections. int32 max_streams = 3; // Maximum number of streams allowed (using int32). string protocol = 4; // Protocol used for multiplexing. } /** * WarpOptions defines configuration options for Warp. */ message WarpOptions { string id = 1; // Unique identifier for the Warp configuration. bool enable_warp = 2; // If true, enables Warp functionality. string mode = 3; // Operating mode for Warp. WarpWireguardConfig wireguard_config = 5; // Configuration for WireGuard (defined elsewhere). string fake_packets = 6; // Fake packet configuration. IntRange fake_packet_size = 7; // Size of fake packets. IntRange fake_packet_delay = 8; // Delay for sending fake packets. string fake_packet_mode = 9; // Mode for sending fake packets. string clean_ip = 10; // Clean IP address to use. uint32 clean_port = 11; // Port for clean traffic (using uint32 for compatibility). WarpAccount account = 12; // Account details for Warp (defined elsewhere). } /** * WarpAccount defines account details for Warp. */ message WarpAccount { string account_id = 1; // Unique account identifier. string access_token = 2; // Access token for the account. } /** * WarpWireguardConfig defines the configuration details for WireGuard. */ message WarpWireguardConfig { string private_key = 1; // Private key for WireGuard. string local_address_ipv4 = 2; // Local IPv4 address for WireGuard. string local_address_ipv6 = 3; // Local IPv6 address for WireGuard. string peer_public_key = 4; // Peer public key for WireGuard. string client_id = 5; // Client identifier for WireGuard. } /** * Rule defines routing rules for managing traffic. */ message Rule { string rule_set_url = 1; // URL of the rule set. string domains = 2; // List of domains affected by this rule. string ip = 3; // IP address associated with this rule. string port = 4; // Port number associated with this rule. string network = 5; // Network type (e.g., IPv4, IPv6). string protocol = 6; // Protocol type (e.g., TCP, UDP). string outbound = 7; // Outbound traffic handling (e.g., allow, deny). } ================================================ FILE: android/app/src/main/protos/v2/profile/profile.proto ================================================ syntax = "proto3"; package profile; option go_package = "github.com/hiddify/hiddify-core/v2/profile"; option java_package = "com.hiddify.core.api.v2.profile"; import "v2/hiddifyoptions/hiddify_options.proto"; // ProfileEntity defines a profile entity. message ProfileEntity { string id = 1; // Unique identifier for the profile. // bool active = 2; // Indicates if the profile is active. string name = 3; // Name of the profile. string url = 4; // URL associated with the profile. int64 last_update = 5; // Last update time in milliseconds of the profile. ProfileOptions options = 6; // Options associated with the profile. SubscriptionInfo sub_info = 7; // Subscription-related information. hiddifyoptions.HiddifyOptions override_hiddify_options = 8; // Override Hiddify options. } // ProfileOptions defines options for a profile. message ProfileOptions { int64 update_interval = 1; // Update interval in milliseconds. } // SubscriptionInfo defines subscription-related information. message SubscriptionInfo { int64 upload = 1; // Upload speed in bytes. int64 download = 2; // Download speed in bytes. int64 total = 3; // Total data in bytes. int64 expire = 4; // Expiration time in milliseconds of the subscription. string web_page_url = 5; // URL for the web page. string support_url = 6; // URL for support. } ================================================ FILE: android/app/src/main/protos/v2/profile/profile_service.proto ================================================ syntax = "proto3"; package profile; option go_package = "github.com/hiddify/hiddify-core/v2/profile"; option java_package = "com.hiddify.core.api.v2.profile"; /** * This proto file defines the ProfileService with RPC methods * to manage profiles (add, fetch, update, delete, and set active profiles). */ // Import dependencies import "v2/profile/profile.proto"; // Import the ProfileEntity message from another proto file. import "v2/hcommon/common.proto"; // Import the common response codes and messages. /** * ProfileRequest is the request message for fetching or identifying * a profile by ID, name, or URL. */ message ProfileRequest { string id = 1; // The ID of the profile to fetch (Fastest and recommended). string name = 2; // The name of the profile to fetch (if both 'id' and 'url' are empty). string url = 3; // The URL of the profile to fetch (if both 'id' and 'name' are empty). } /** * AddProfileRequest is the request message for adding a profile * via URL or content. */ message AddProfileRequest { string url = 1; // The URL of the profile to add. string content = 2; // The profile content to add (used if 'url' is empty). string name = 3; // The optional name of the profile. bool mark_as_active = 4; // Whether to mark the profile as active. } /** * ProfileResponse is the response message for profile service operations. */ message ProfileResponse { ProfileEntity profile = 1; // The profile entity, populated in successful operations. hcommon.ResponseCode response_code = 2; // The response code indicating success or failure. string message = 3; // A message indicating the result or error, if any. } /** * MultiProfilesResponse is the response message for fetching multi profiles. */ message MultiProfilesResponse { repeated ProfileEntity profiles = 1; // A list of profile entities. hcommon.ResponseCode response_code = 2; // The response code indicating success or failure. string message = 3; // A message indicating the result or error, if any. } /** * ProfileService defines the RPC methods available for managing profiles. */ service ProfileService { /** * GetProfile fetches a profile by ID, name, or URL. */ rpc GetProfile(ProfileRequest) returns (ProfileResponse); /** * UpdateProfile updates an existing profile. */ rpc UpdateProfile(ProfileEntity) returns (ProfileResponse); /** * GetAllProfiles fetches all profiles. */ rpc GetAllProfiles(hcommon.Empty) returns (MultiProfilesResponse); /** * GetActiveProfile retrieves the currently active profile. */ rpc GetActiveProfile(hcommon.Empty) returns (ProfileResponse); /** * SetActiveProfile sets a profile as active, identified by ID, name, or URL. */ rpc SetActiveProfile(ProfileRequest) returns (hcommon.Response); /** * AddProfile adds a new profile using either a URL or the raw profile content. */ rpc AddProfile(AddProfileRequest) returns (ProfileResponse); /** * DeleteProfile deletes a profile identified by ID, name, or URL. */ rpc DeleteProfile(ProfileRequest) returns (hcommon.Response); } ================================================ FILE: android/app/src/main/res/drawable/android12splash.xml ================================================ ================================================ FILE: android/app/src/main/res/drawable/ic_banner_foreground.xml ================================================ ================================================ FILE: android/app/src/main/res/drawable/ic_launcher_background.xml ================================================ ================================================ FILE: android/app/src/main/res/drawable/ic_launcher_foreground.xml ================================================ ================================================ FILE: android/app/src/main/res/drawable/launch_background.xml ================================================ ================================================ FILE: android/app/src/main/res/drawable-v21/launch_background.xml ================================================ ================================================ FILE: android/app/src/main/res/mipmap-anydpi-v26/ic_banner.xml ================================================ ================================================ FILE: android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml ================================================ ================================================ FILE: android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml ================================================ ================================================ FILE: android/app/src/main/res/values/colors.xml ================================================ ================================================ FILE: android/app/src/main/res/values/ic_banner_background.xml ================================================ #F0F3FA ================================================ FILE: android/app/src/main/res/values/ic_launcher_background.xml ================================================ #F0F3FA ================================================ FILE: android/app/src/main/res/values/strings.xml ================================================ Stop Toggle Service starting… Service started ================================================ FILE: android/app/src/main/res/values/styles.xml ================================================ ================================================ FILE: android/app/src/main/res/values-night/styles.xml ================================================ ================================================ FILE: android/app/src/main/res/values-night-v31/styles.xml ================================================ ================================================ FILE: android/app/src/main/res/values-v31/styles.xml ================================================ ================================================ FILE: android/app/src/main/res/xml/network_security_config.xml ================================================ 127.0.0.1 ================================================ FILE: android/app/src/main/res/xml/shortcuts.xml ================================================ ================================================ FILE: android/app/src/profile/AndroidManifest.xml ================================================ ================================================ FILE: android/build.gradle ================================================ allprojects { repositories { mavenCentral() google() } } rootProject.buildDir = '../build' subprojects { project.buildDir = "${rootProject.buildDir}/${project.name}" } subprojects { project.evaluationDependsOn(':app') } tasks.register("clean", Delete) { delete rootProject.buildDir } ================================================ FILE: android/gradle/wrapper/gradle-wrapper.properties ================================================ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists ================================================ FILE: android/gradle.properties ================================================ org.gradle.jvmargs=-Xmx4048m -Dfile.encoding=UTF-8 android.useAndroidX=true android.enableJetifier=true android.defaults.buildfeatures.buildconfig=true android.nonTransitiveRClass=false android.nonFinalResIds=false org.gradle.daemon=true org.gradle.parallel=true org.gradle.configureondemand=true org.gradle.caching=true ================================================ FILE: android/settings.gradle ================================================ pluginManagement { def flutterSdkPath = { def properties = new Properties() file("local.properties").withInputStream { properties.load(it) } def flutterSdkPath = properties.getProperty("flutter.sdk") assert flutterSdkPath != null, "flutter.sdk not set in local.properties" return flutterSdkPath }() includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") repositories { mavenCentral() google() gradlePluginPortal() } } plugins { id "dev.flutter.flutter-plugin-loader" version "1.0.0" id "com.android.application" version '8.6.0' apply false id "org.jetbrains.kotlin.android" version "2.1.0" apply false } include ":app" ================================================ FILE: appcast.xml ================================================ Release Version 0.13.6 Sun, 7 Jan 2024 22:00:00 +0000 Version 0.13.6 Sun, 7 Jan 2024 22:00:00 +0000 Version 0.13.6 Sun, 7 Jan 2024 22:00:00 +0000 Version 0.13.6 Sun, 7 Jan 2024 22:00:00 +0000 Version 0.13.6 Sun, 7 Jan 2024 22:00:00 +0000 ================================================ FILE: assets/core/.gitkeep ================================================ ================================================ FILE: assets/fonts/emoji_source.txt ================================================ https://github.com/hiddify-com/noto-emoji pyftsubset "Emoji3.ttf" --output-file=Emoji.ttf --unicodes=U+1F1E6+1F1E9,U+1F1E6+1F1EA,U+1F1E6+1F1EB,U+1F1E6+1F1EC,U+1F1E6+1F1EE,U+1F1E6+1F1F1,U+1F1E6+1F1F2,U+1F1E6+1F1F4,U+1F1E6+1F1F6,U+1F1E6+1F1F7,U+1F1E6+1F1F8,U+1F1E6+1F1F9,U+1F1E6+1F1FA,U+1F1E6+1F1FC,U+1F1E6+1F1FD,U+1F1E6+1F1FF,U+1F1E7+1F1E6,U+1F1E7+1F1E7,U+1F1E7+1F1E9,U+1F1E7+1F1EA,U+1F1E7+1F1EB,U+1F1E7+1F1EC,U+1F1E7+1F1ED,U+1F1E7+1F1EE,U+1F1E7+1F1EF,U+1F1E7+1F1F1,U+1F1E7+1F1F2,U+1F1E7+1F1F3,U+1F1E7+1F1F4,U+1F1E7+1F1F6,U+1F1E7+1F1F7,U+1F1E7+1F1F8,U+1F1E7+1F1F9,U+1F1E7+1F1FB,U+1F1E7+1F1FC,U+1F1E7+1F1FE,U+1F1E7+1F1FF,U+1F1E8+1F1E6,U+1F1E8+1F1E8,U+1F1E8+1F1E9,U+1F1E8+1F1EB,U+1F1E8+1F1EC,U+1F1E8+1F1ED,U+1F1E8+1F1EE,U+1F1E8+1F1F0,U+1F1E8+1F1F1,U+1F1E8+1F1F2,U+1F1E8+1F1F3,U+1F1E8+1F1F4,U+1F1E8+1F1F7,U+1F1E8+1F1FA,U+1F1E8+1F1FB,U+1F1E8+1F1FC,U+1F1E8+1F1FD,U+1F1E8+1F1FE,U+1F1E8+1F1FF,U+1F1E9+1F1EA,U+1F1E9+1F1EF,U+1F1E9+1F1F0,U+1F1E9+1F1F2,U+1F1E9+1F1F4,U+1F1E9+1F1FF,U+1F1EA+1F1E8,U+1F1EA+1F1EA,U+1F1EA+1F1EC,U+1F1EA+1F1ED,U+1F1EA+1F1F7,U+1F1EA+1F1F8,U+1F1EA+1F1F9,U+1F1EB+1F1EE,U+1F1EB+1F1EF,U+1F1EB+1F1F0,U+1F1EB+1F1F2,U+1F1EB+1F1F4,U+1F1EB+1F1F7,U+1F1EC+1F1E6,U+1F1EC+1F1E7,U+1F1EC+1F1E9,U+1F1EC+1F1EA,U+1F1EC+1F1EB,U+1F1EC+1F1EC,U+1F1EC+1F1ED,U+1F1EC+1F1EE,U+1F1EC+1F1F1,U+1F1EC+1F1F2,U+1F1EC+1F1F3,U+1F1EC+1F1F5,U+1F1EC+1F1F6,U+1F1EC+1F1F7,U+1F1EC+1F1F8,U+1F1EC+1F1F9,U+1F1EC+1F1FA,U+1F1EC+1F1FC,U+1F1EC+1F1FE,U+1F1ED+1F1F0,U+1F1ED+1F1F2,U+1F1ED+1F1F3,U+1F1ED+1F1F7,U+1F1ED+1F1F9,U+1F1ED+1F1FA,U+1F1EE+1F1E9,U+1F1EE+1F1EA,U+1F1EE+1F1F1,U+1F1EE+1F1F2,U+1F1EE+1F1F3,U+1F1EE+1F1F4,U+1F1EE+1F1F6,U+1F1EE+1F1F7,U+1F1EE+1F1F8,U+1F1EE+1F1F9,U+1F1EF+1F1EA,U+1F1EF+1F1F2,U+1F1EF+1F1F4,U+1F1EF+1F1F5,U+1F1F0+1F1EA,U+1F1F0+1F1EC,U+1F1F0+1F1ED,U+1F1F0+1F1EE,U+1F1F0+1F1F2,U+1F1F0+1F1F3,U+1F1F0+1F1F5,U+1F1F0+1F1F7,U+1F1F0+1F1FC,U+1F1F0+1F1FE,U+1F1F0+1F1FF,U+1F1F1+1F1E6,U+1F1F1+1F1E7,U+1F1F1+1F1E8,U+1F1F1+1F1EE,U+1F1F1+1F1F0,U+1F1F1+1F1F7,U+1F1F1+1F1F8,U+1F1F1+1F1F9,U+1F1F1+1F1FA,U+1F1F1+1F1FB,U+1F1F1+1F1FE,U+1F1F2+1F1E6,U+1F1F2+1F1E8,U+1F1F2+1F1E9,U+1F1F2+1F1EA,U+1F1F2+1F1EB,U+1F1F2+1F1EC,U+1F1F2+1F1ED,U+1F1F2+1F1F0,U+1F1F2+1F1F1,U+1F1F2+1F1F2,U+1F1F2+1F1F3,U+1F1F2+1F1F4,U+1F1F2+1F1F5,U+1F1F2+1F1F6,U+1F1F2+1F1F7,U+1F1F2+1F1F8,U+1F1F2+1F1F9,U+1F1F2+1F1FA,U+1F1F2+1F1FB,U+1F1F2+1F1FC,U+1F1F2+1F1FD,U+1F1F2+1F1FE,U+1F1F2+1F1FF,U+1F1F3+1F1E6,U+1F1F3+1F1E8,U+1F1F3+1F1EA,U+1F1F3+1F1EB,U+1F1F3+1F1EC,U+1F1F3+1F1EE,U+1F1F3+1F1F1,U+1F1F3+1F1F4,U+1F1F3+1F1F5,U+1F1F3+1F1F7,U+1F1F3+1F1FA,U+1F1F3+1F1FF,U+1F1F4+1F1F2,U+1F1F5+1F1E6,U+1F1F5+1F1EA,U+1F1F5+1F1EB,U+1F1F5+1F1EC,U+1F1F5+1F1ED,U+1F1F5+1F1F0,U+1F1F5+1F1F1,U+1F1F5+1F1F2,U+1F1F5+1F1F3,U+1F1F5+1F1F7,U+1F1F5+1F1F8,U+1F1F5+1F1F9,U+1F1F5+1F1FC,U+1F1F5+1F1FE,U+1F1F6+1F1E6,U+1F1F7+1F1EA,U+1F1F7+1F1F4,U+1F1F7+1F1F8,U+1F1F7+1F1FA,U+1F1F7+1F1FC,U+1F1F8+1F1E6,U+1F1F8+1F1E7,U+1F1F8+1F1E8,U+1F1F8+1F1E9,U+1F1F8+1F1EA,U+1F1F8+1F1EC,U+1F1F8+1F1ED,U+1F1F8+1F1EE,U+1F1F8+1F1EF,U+1F1F8+1F1F0,U+1F1F8+1F1F1,U+1F1F8+1F1F2,U+1F1F8+1F1F3,U+1F1F8+1F1F4,U+1F1F8+1F1F7,U+1F1F8+1F1F8,U+1F1F8+1F1F9,U+1F1F8+1F1FB,U+1F1F8+1F1FD,U+1F1F8+1F1FE,U+1F1F8+1F1FF,U+1F1F9+1F1E8,U+1F1F9+1F1E9,U+1F1F9+1F1EB,U+1F1F9+1F1EC,U+1F1F9+1F1ED,U+1F1F9+1F1EF,U+1F1F9+1F1F0,U+1F1F9+1F1F1,U+1F1F9+1F1F2,U+1F1F9+1F1F3,U+1F1F9+1F1F4,U+1F1F9+1F1F7,U+1F1F9+1F1F9,U+1F1F9+1F1FB,U+1F1F9+1F1FC,U+1F1F9+1F1FF,U+1F1FA+1F1E6,U+1F1FA+1F1EC,U+1F1FA+1F1F2,U+1F1FA+1F1F8,U+1F1FA+1F1FE,U+1F1FA+1F1FF,U+1F1FB+1F1E6,U+1F1FB+1F1E8,U+1F1FB+1F1EA,U+1F1FB+1F1EC,U+1F1FB+1F1EE,U+1F1FB+1F1F3,U+1F1FB+1F1FA,U+1F1FC+1F1EB,U+1F1FC+1F1F8,U+1F1FE+1F1EA,U+1F1FE+1F1F9,U+1F1FF+1F1E6,U+1F1FF+1F1F2,U+1F1FF+1F1FC ================================================ FILE: assets/images/convert_icon.sh ================================================ in=$1 convert -define icon:auto-resize=128,64,48,32,16 -gravity center $in.png $in.ico ================================================ FILE: assets/translations/ar.i18n.json ================================================ { "common": { "appTitle": "Hiddify", "start": "ابدأ", "version": "الإصدار", "ok": "موافق", "cancel": "إلغاء", "kContinue": "متابعة", "showMore": "عرض المزيد", "showLess": "عرض أقل", "filter": "تصفية", "all": "الكل", "pause": "إيقاف مؤقت", "resume": "استئناف", "clear": "مسح", "close": "إغلاق", "auto": "تلقائي", "manually": "يدوي", "name": "الاسم", "url": "الرابط", "add": "إضافة", "clipboard": "الحافظة", "addToClipboard": "إضافة إلى الحافظة", "scanQr": "مسح رمز QR", "free": "مجاني", "warp": "WARP", "fragment": "Fragment", "help": "مساعدة", "save": "حفظ", "update": "تحديث", "share": "مشاركة", "edit": "تعديل", "delete": "حذف", "discard": "تجاهل", "import": "استيراد", "export": "تصدير", "later": "لاحقًا", "ignore": "تجاهل", "quit": "خروج", "notSet": "غير محدد", "hide": "إخفاء", "exit": "خروج", "reset": "إعادة تعيين", "done": "تم", "search": "بحث", "decline": "رفض", "agree": "أوافق", "empty": "فارغ", "unknown": "غير معروف", "hidden": "مخفي", "timeout": "انتهى الوقت", "sort": "فرز", "dashboard": "لوحة التحكم", "interval": { "day": { "zero": "", "one": "يوم واحد", "two": "يومان", "few": "$n أيام", "many": "$n يومًا", "other": "$n يوم" }, "hour": { "zero": "", "one": "ساعة واحدة", "two": "ساعتان", "few": "$n ساعات", "many": "$n ساعة", "other": "$n ساعة" } }, "msg": { "permission": { "denied": "تم رفض الإذن" }, "export": { "clipboard": { "success": "تمت الإضافة إلى الحافظة بنجاح", "failure": "فشل النسخ إلى الحافظة", "contentTooLarge": "المحتوى كبير جدًا. استخدم تصدير الملف بدلاً من ذلك" }, "file": { "success": "تم إنشاء ملف JSON بنجاح", "failure": "فشل إنشاء الملف" } }, "import": { "confirm": "تأكيد الاستيراد", "success": "تم الاستيراد بنجاح", "failure": "فشل الاستيراد" } } }, "intro": { "banner": "كل ما تحتاجه لإنترنت بلا قيود", "termsAndPolicyCaution(rich)": "بالاستمرار، أنت توافق على ${tap(@:pages.about.termsAndConditions)}", "info(rich)": "صُنع بـ ❤️ بواسطة Hiddify - ${tap_source(مفتوح المصدر)} (${tap_license(الرخصة)})" }, "pages": { "home": { "title": "الرئيسية", "quickSettings": "الإعدادات السريعة" }, "proxies": { "title": "البروكسيات", "sort": "فرز البروكسيات", "testDelay": "اختبار التأخير", "empty": "لا توجد بروكسيات متاحة", "activeProxy": "البروكسي النشط", "unknownIp": "IP غير معروف", "sortOptions": { "unsorted": "الافتراضي", "name": "أبجديًا", "delay": "حسب التأخير" }, "ipInfo": { "address": "عنوان IP", "country": "الدولة", "organization": "المُنظمة" }, "delay": { "result": "التأخير: ${delay} مللي ثانية", "timeout": "انتهى وقت اختبار التأخير", "testing": "التأخير: قيد الاختبار..." } }, "profiles": { "title": "الملفات الشخصية", "add": "إضافة ملف شخصي", "update": "تحديث الملف الشخصي", "viewAllProfiles": "عرض جميع الملفات الشخصية", "activeProfileName": "اسم الملف النشط: \"${name}\".", "nonActiveProfileName": "تحديد \"${name}\" كملف نشط", "freeSubNotFound": "لم يتم العثور على اشتراك مجاني", "freeSubNotFoundForRegion": "لم يتم العثور على اشتراك مجاني لمنطقة \"${region}\"", "failedToLoad": "فشل التحميل", "updateSubscriptions": "تحديث الاشتراكات", "share": { "urlToClipboard": "رابط URL إلى الحافظة", "showUrlQr": "عرض رمز QR للرابط", "jsonToClipboard": "JSON إلى الحافظة" }, "msg": { "save": { "success": "تم حفظ الملف الشخصي بنجاح" }, "invalidUrl": "رابط غير صالح", "add": { "failure": "فشل إضافة الملف الشخصي" }, "update": { "success": "تم تحديث الملف الشخصي بنجاح", "successNamed": "تم تحديث \"${name}\" بنجاح", "failure": "فشل تحديث الملف الشخصي", "failureNamed": "فشل تحديث \"${name}\"" }, "delete": { "success": "تم حذف الملف الشخصي بنجاح" } } }, "profileDetails": { "title": "الملف الشخصي", "lastUpdate": "آخر تحديث", "form": { "nameHint": "اسم الملف الشخصي", "emptyName": "الاسم مطلوب", "invalidUrl": "رابط غير صالح", "urlHint": "رابط الإعدادات الكامل", "disableAutoUpdate": "تعطيل التحديث التلقائي", "autoUpdateInterval": "فاصل التحديث التلقائي", "loading": "جاري إضافة الملف الشخصي..." } }, "logs": { "title": "السجلات", "shareCoreLogs": "مشاركة سجلات النواة", "shareAppLogs": "مشاركة سجلات التطبيق" }, "about": { "title": "حول التطبيق", "notAvailableMsg": "أنت تستخدم أحدث إصدار بالفعل", "checkForUpdate": "التحقق من وجود تحديثات", "openWorkingDir": "فتح مجلد العمل", "sourceCode": "الكود المصدري", "telegramChannel": "قناة التيليجرام", "termsAndConditions": "الشروط والأحكام", "privacyPolicy": "سياسة الخصوصية" }, "settings": { "title": "الإعدادات", "resetTunnel": "إعادة تعيين ملف VPN", "options": { "import": { "clipboard": "استيراد الخيارات من الحافظة", "file": "استيراد الخيارات من ملف" }, "export": { "anonymousToClipboard": "نسخ الخيارات المجهولة إلى الحافظة", "anonymousToFile": "تصدير الخيارات المجهولة إلى ملف", "allToClipboard": "نسخ جميع الخيارات إلى الحافظة", "allToFile": "تصدير جميع الخيارات إلى ملف" }, "reset": "إعادة تعيين الخيارات" }, "general": { "title": "عام", "locale": "اللغة", "themeMode": "السمة", "themeModes": { "system": "سمة النظام الافتراضية", "dark": "الوضع الداكن", "light": "الوضع الفاتح", "black": "الوضع الأسود" }, "enableAnalytics": "تفعيل التحليلات", "enableAnalyticsMsg": "السماح بجمع بيانات التحليل وتقارير الأعطال لتحسين التطبيق", "autoIpCheck": "التحقق التلقائي من IP الاتصال", "dynamicNotification": "عرض السرعة في الإشعار", "hapticFeedback": "ردود الفعل اللمسية", "actionAtClosing": "الإجراء عند الإغلاق", "autoStart": "البدء عند تسجيل الدخول", "silentStart": "البدء في الخلفية", "ignoreBatteryOptimizations": "تجاهل تحسينات البطارية", "ignoreBatteryOptimizationsMsg": "إزالة القيود للحصول على أفضل أداء للـ VPN", "memoryLimit": "حد الذاكرة", "memoryLimitMsg": "قم بتفعيل هذا الخيار إذا كنت تواجه أخطاء نفاد الذاكرة أو تعطل التطبيق بشكل متكرر", "debugMode": "وضع التصحيح", "debugModeMsg": "أعد تشغيل التطبيق لتطبيق هذا التغيير", "logLevel": "مستوى السجل", "connectionTestUrl": "رابط اختبار الاتصال", "urlTestInterval": "فاصل اختبار الرابط", "clashApiPort": "منفذ Clash API", "useXrayCoreWhenPossible": "استخدام xray-core عند الإمكان", "useXrayCoreWhenPossibleMsg": "استخدم xray-core عند تحليل روابط الاشتراك. تحتاج إلى إعادة استيراد الرابط لتفعيل هذا الخيار" }, "routing": { "title": "التوجيه", "perAppProxy": { "title": "بروكسي لكل تطبيق", "hideSysApps": "إخفاء تطبيقات النظام", "options": { "import": { "clipboard": "استيراد التحديد من الحافظة", "file": "استيراد التحديد من ملف", "msg": "سيؤدي الاستيراد إلى استبدال تحديداتك الحالية. هل أنت متأكد من رغبتك في المتابعة؟" }, "export": { "clipboard": "نسخ التحديد إلى الحافظة", "file": "تصدير التحديد إلى ملف" }, "shareToAll": "مشاركة مع الجميع", "clearAllSelections": "مسح جميع التحديدات" }, "modes": { "all": "الكل", "proxy": "بروكسي", "bypass": "تجاوز", "allMsg": "استخدام البروكسي لجميع التطبيقات", "proxyMsg": "استخدام البروكسي للتطبيقات المحددة فقط", "bypassMsg": "عدم استخدام البروكسي للتطبيقات المحددة" }, "autoSelection": { "title": "الاختيار التلقائي", "performNow": "تنفيذ الآن", "resetToDefault": "إعادة التعيين إلى الافتراضي", "autoUpdateInterval": "فاصل التحديث التلقائي", "toast": { "success": "اكتمل الاختيار التلقائي للتطبيقات بنجاح", "failure": "فشل الاختيار التلقائي", "regionNotFound": "لم يتم العثور على اختيار تلقائي لمنطقة \"${region}\"", "alreadyInAuto": "اختياراتك موجودة بالفعل في القائمة التلقائية" }, "dialog": { "title": "الاختيار التلقائي للتطبيقات", "msg": "تم تعطيل ميزة الاختيار التلقائي للبروكسي بسبب تغيير المنطقة إلى \"${region}\"" } } }, "region": "المنطقة", "regions": { "ir": "إيران (ir)", "cn": "الصين (cn)", "ru": "روسيا (ru)", "af": "أفغانستان (af)", "id": "إندونيسيا (id)", "tr": "تركيا (tr)", "br": "البرازيل (br)", "other": "أخرى" }, "balancerStrategy": { "title": "استراتيجية Balancer", "roundRobin": "Round robin", "consistentHash": "Consistent hash", "stickySession": "Sticky session" }, "blockAds": "حظر الإعلانات", "bypassLan": "تجاوز الشبكة المحلية", "resolveDestination": "تحديد وجهة الاتصال", "ipv6Route": "توجيه IPv6", "ipv6Modes": { "disable": "تعطيل", "enable": "تفعيل", "prefer": "مفضل", "only": "فقط" }, "routeRule": { "title": "قواعد التوجيه", "options": { "import": { "clipboard": "استيراد القواعد من الحافظة", "file": "استيراد القواعد من ملف" }, "export": { "clipboard": "نسخ القواعد إلى الحافظة", "file": "حفظ القواعد في ملف" }, "reset": "إعادة تعيين القواعد" }, "deleteRule": "حذف القاعدة", "createRule": "إنشاء قاعدة جديدة", "rule": { "title": "قاعدة", "ruleChanged": "تم تغيير القاعدة", "ruleChangedMsg": "هل تريد حفظ تعديلاتك؟", "onlyTunMode": "متوفر فقط في وضع TUN", "notAvailabeInThisPlatform": "غير متوفر على هذا النظام", "canNotBeEmpty": "لا يمكن أن يكون فارغًا", "validUrlEx": "https://example.com", "validUrl": "\"URL\" صالح مثل\n@:pages.settings.routing.routeRule.rule.validUrlEx", "validProcessNameEx": "Chrome.exe or google chrome or chrome", "validProcessName": "\"اسم العملية\" صالح مثل\n@:pages.settings.routing.routeRule.rule.validProcessNameEx", "validProcessPathEx": "C:\\Pro...\\chrome.exe\n/App.../Google Chrome\n/usr/lib.../chrome", "validProcessPath": "\"مسار العملية\" صالح مثل\n@:pages.settings.routing.routeRule.rule.validProcessPathEx", "validPortRangeEx": "80 or 1-65000", "validPortRange": "\"منفذ\" أو \"نطاق منافذ\" صالح مثل\n@:pages.settings.routing.routeRule.rule.validPortRangeEx", "validIpCidrEx": "8.8.8.8 or 10.0.0.0/24", "validIpCidr": "IP CIDR صالح مثل\n@:pages.settings.routing.routeRule.rule.validIpCidrEx", "validDomainEx": "Google.com or dl.google.com", "validDomain": "\"نطاق\" صالح مثل\n@:pages.settings.routing.routeRule.rule.validDomainEx", "validDomainSuffixEx": ".com or .ir", "validDomainSuffix": "\"لاحقة نطاق\" صالحة مثل\n@:pages.settings.routing.routeRule.rule.validDomainSuffixEx", "tileTitle(map)": { "name": "الاسم", "outbound": "التوجيه عند التطابق", "rule_set": "رابط مجموعة القواعد", "package_name": "أسماء الحزم", "process_name": "أسماء العمليات", "process_path": "مسارات العمليات", "network": "الشبكات", "port_range": "منافذ الوجهة", "source_port_range": "منافذ المصدر", "protocol": "البروتوكول", "ip_cidr": "IP CIDR الوجهة", "source_ip_cidr": "IP CIDR المصدر", "domain": "النطاق", "domain_suffixe": "لاحقة النطاق", "domain_keyword": "كلمة مفتاحية للنطاق", "domain_regex": "تعبير نمطي للنطاق" }, "outbound(map)": { "proxy": "بروكسي", "direct": "مباشر", "direct_with_fragment": "مباشر مع Fragment", "block": "حظر" }, "network(map)": { "all": "الكل", "tcp": "TCP", "udp": "UDP" }, "protocol(map)": { "": "الكل", "tls": "TLS", "http": "HTTP", "quic": "QUIC", "stun": "STUN", "dns": "DNS", "bittorrent": "BitTorrent" } }, "genericList": { "addNew": "إضافة قيمة جديدة", "update": "تحديث القيمة", "clearList": "مسح القائمة", "clearListMsg": "تم حذف جميع العناصر" }, "androidApps": { "pageTitle": "تطبيقات أندرويد", "showSystemApps": "عرض تطبيقات النظام", "hideSystemApps": "إخفاء تطبيقات النظام", "clearSelection": "مسح التحديد", "uninstalled": "غير مثبت" } } }, "dns": { "title": "DNS", "remoteDns": "DNS البعيد", "remoteDnsDomainStrategy": "استراتيجية نطاق DNS البعيد", "directDns": "محلل خادم الصادر (مباشر)", "directDnsDomainStrategy": "استراتيجية النطاق الصادر", "enableDnsRouting": "تمكين توجيه DNS", "enableFakeDns": "تمكين DNS الوهمي", "domainStrategy": { "auto": "تلقائي", "preferIpv6": "تفضيل IPv6", "preferIpv4": "تفضيل IPv4", "ipv4Only": "IPv4 فقط", "ipv6Only": "IPv6 فقط" } }, "inbound": { "title": "الوارد", "serviceMode": "وضع الخدمة", "serviceModes": { "proxy": "خدمة البروكسي فقط", "systemProxy": "تعيين بروكسي النظام", "tun": "VPN", "tunService": "خدمة VPN" }, "shortServiceModes": { "proxy": "بروكسي", "systemProxy": "بروكسي النظام", "tun": "VPN", "tunService": "خدمة VPN" }, "strictRoute": "توجيه صارم", "tunImplementation": "تنفيذ Tun", "tunImplementations": { "mixed": "مختلط", "system": "النظام", "gvisor": "gVisor" }, "mixedPort": "منفذ مختلط", "tproxyPort": "منفذ البروكسي الشفاف", "directPort": "منفذ مباشر", "redirectPort": "منفذ إعادة التوجيه", "allowConnectionFromLan": "مشاركة VPN على الشبكة المحلية" }, "tlsTricks": { "title": "حيل TLS", "enable": "تفعيل fragment", "packets": "حزم التجزئة", "packetsTlsHello": "TLS Hello", "packets1_1": "1-1", "packets1_2": "1-2", "packets1_3": "1-3", "packets1_4": "1-4", "packets1_5": "1-5", "size": "حجم fragment", "sleep": "تأخير fragment", "mixedSniCase": { "enable": "تفعيل الأحرف المختلطة لـ SNI" }, "padding": { "enable": "تفعيل padding", "size": "حجم padding" } }, "warp": { "title": "WARP", "enable": "تفعيل WARP", "generateConfig": "إنشاء تكوين WARP", "configGenerated": "تم إنشاء تكوين WARP", "missingConfig": "تكوين WARP مفقود", "detourMode": "وضع توجيه WARP", "detourModes": { "proxyOverWarp": "توجيه البروكسيات عبر WARP", "warpOverProxy": "توجيه WARP عبر البروكسيات", "proxyOverWarpExplain": "لإلغاء حظر البروكسيات بواسطة WARP", "warpOverProxyExplain": "لأمان إضافي بواسطة WARP" }, "licenseKey": "مفتاح الترخيص", "cleanIp": "IP نظيف", "port": "المنفذ", "noise": { "count": "عدد الضوضاء", "mode": "وضع الضوضاء", "size": "حجم الضوضاء", "delay": "تأخير الضوضاء" } } } }, "components": { "stats": { "connection": "الاتصال", "traffic": "البيانات", "trafficLive": "البيانات الحالية", "trafficTotal": "إجمالي البيانات", "uplink": "الإرسال", "downlink": "الاستقبال", "speed": "السرعة", "totalTransferred": "إجمالي النقل" }, "subscriptionInfo": { "upload": "الرفع", "download": "التنزيل", "total": "إجمالي البيانات", "expireDate": "تاريخ الانتهاء", "expired": "منتهي الصلاحية", "noTraffic": "نفدت الباقة", "remainingTime": "الوقت المتبقي", "remainingDuration": "متبقٍ ${duration} يوم", "remainingDurationNew": "${duration} يوم", "remainingTrafficSemanticLabel": "تم استهلاك ${consumed} من ${total}", "remainingTraffic": "البيانات المتبقية", "remainingUsage": "المتبقي", "profileSite": "المزوّد", "profileSupport": "الدعم" } }, "dialogs": { "sortProfiles": { "title": "فرز حسب", "sort": { "name": "أبجديًا", "lastUpdate": "آخر تحديث" } }, "warpLicense": { "title": "موافقة Cloudflare WARP", "description(rich)": "Cloudflare WARP هو مزود VPN مجاني لـ WireGuard. بتفعيل هذا الخيار، فإنك توافق على ${tos(شروط الخدمة)} و ${privacy(سياسة الخصوصية)} الخاصة بـ Cloudflare WARP." }, "newVersion": { "title": "تحديث متاح", "msg": "إصدار جديد من @:common.appTitle متاح. هل ترغب في التحديث الآن؟", "currentVersion": "الإصدار الحالي: ", "newVersion": "الإصدار الجديد: ", "updateNow": "التحديث الآن" }, "confirmation": { "settings": { "import": { "msg": "سيؤدي هذا إلى استبدال جميع إعداداتك الحالية. هل أنت متأكد؟" } }, "profile": { "delete": { "title": "حذف الملف الشخصي", "msg": "هل أنت متأكد من رغبتك في حذف هذا الملف الشخصي نهائيًا؟" } }, "perAppProxy": { "shareOnGithub": { "title": "تحسين الاختيار التلقائي", "msg": "بمشاركة التطبيقات التي اخترتها، فإنك تساعد في إكمال قائمة \"الاختيار التلقائي\"" }, "import": { "msg": "سيؤدي هذا إلى استبدال جميع تحديداتك الحالية لبروكسي التطبيقات. هل أنت متأكد من رغبتك في المتابعة؟" } }, "routeRule": { "delete": { "title": "حذف القاعدة", "msg": "هل أنت متأكد من رغبتك في حذف قاعدة \"$rulename\"؟" } } }, "experimentalNotice": { "title": "ميزات تجريبية قيد الاستخدام", "msg": "لقد فعّلت ميزات تجريبية قد تؤثر على جودة الاتصال وتسبب أخطاء غير متوقعة. يمكنك دائمًا تغيير هذه الخيارات أو إعادة تعيينها من صفحة الإعدادات.", "disable": "لا تعرض مرة أخرى" }, "noActiveProfile": { "title": "اختر ملفًا شخصيًا", "msg": "للبدء، قم بإضافة ملف اتصال يحتوي على تفاصيل اتصال VPN الخاصة بك.\n\nأليس لديك خادم VPN بعد؟ لا تقلق، اتبع الدليل أدناه لإعداد واحد بسرعة ومجانًا.", "helpBtn": { "label": "أرني كيف", "url": "https://hiddify.com/manager/" } }, "unknownDomainsWarning": { "title": "تحذير من رابط خارجي", "youAreAboutToVisit": "أنت على وشك زيارة:", "thisWebsiteIsNotInOurTrustedList": "هذا الموقع ليس ضمن قائمة المواقع الموثوقة لدينا. يرجى المتابعة بحذر." }, "proxyInfo": { "fullTag": "العلامة الكاملة:", "type": "النوع:", "testTime": "وقت الاختبار:", "testDelay": "تأخير الاختبار:", "isSelected": "محدد:", "isGroup": "مجموعة", "isSecure": "آمن:", "port": "المنفذ:", "host": "المضيف:", "ip": "IP:", "countryCode": "رمز الدولة:", "region": "المنطقة:", "city": "المدينة:", "asn": "ASN:", "organization": "المُنظمة:", "location": "الموقع:", "postalCode": "الرمز البريدي:" }, "windowClosing": { "askEachTime": "السؤال كل مرة", "alertMessage": "هل تريد إخفاء التطبيق أم الخروج منه؟", "remember": "تذكر خياري" } }, "connection": { "tapToConnect": "انقر للاتصال", "connect": "اتصال", "connecting": "جار الاتصال...", "connected": "متصل", "disconnect": "قطع الاتصال", "disconnecting": "جاري قطع الاتصال...", "reconnect": "إعادة الاتصال", "reconnectMsg": "جاري إعادة الاتصال لتطبيق التغييرات...", "secure": "مُؤمَّن بواسطة WARP" }, "errors": { "unexpected": "خطأ غير متوقع", "connection": { "unexpected": "خطأ اتصال غير متوقع", "timeout": "انتهى وقت الاتصال", "badResponse": "استجابة غير صالحة", "connectionError": "خطأ في الاتصال", "badCertificate": "شهادة غير صالحة" }, "profiles": { "unexpected": "خطأ غير متوقع", "notFound": "لم يتم العثور على الملف الشخصي", "invalidConfig": "تكوينات غير صالحة", "invalidUrl": "رابط غير صالح", "canceledByUser": "تم الإلغاء من قبل المستخدم" }, "connectivity": { "unexpected": "فشل غير متوقع", "missingVpnPermission": "إذن الـ VPN مفقود", "missingNotificationPermission": "إذن الإشعارات مفقود", "core": "خطأ في النواة" }, "singbox": { "serviceNotRunning": "الخدمة لا تعمل", "missingPrivilege": "صلاحيات مطلوبة", "missingPrivilegeMsg": "وضع الـ VPN يتطلب صلاحيات المسؤول. يرجى إعادة تشغيل التطبيق كمسؤول أو تغيير وضع الخدمة.", "invalidConfigOptions": "خيارات تكوين غير صالحة", "invalidConfig": "تكوين غير صالح" }, "warp": { "missingLicense": "رخصة WARP مفقودة", "missingLicenseMsg": "الملف الشخصي المحدد يستخدم ميزة WARP. لاستخدام هذه الميزة، يجب الموافقة على شروط رخصة WARP." } } } ================================================ FILE: assets/translations/en.i18n.json ================================================ { "common": { "appTitle": "Hiddify", "start": "Start", "version": "Version", "ok": "OK", "cancel": "Cancel", "kContinue": "Continue", "showMore": "Show more", "showLess": "Show less", "filter": "Filter", "all": "All", "pause": "Pause", "resume": "Resume", "clear": "Clear", "close": "Close", "auto": "Auto", "manually": "Manually", "name": "Name", "url": "URL", "add": "Add", "clipboard": "Clipboard", "addToClipboard": "Add to clipboard", "scanQr": "Scan QR", "free": "Free", "warp": "WARP", "fragment": "Fragment", "help": "Help", "save": "Save", "update": "Update", "share": "Share", "edit": "Edit", "delete": "Delete", "discard": "Discard", "import": "Import", "export": "Export", "later": "Later", "ignore": "Ignore", "quit": "Quit", "notSet": "Not set", "hide": "Hide", "exit": "Exit", "reset": "Reset", "done": "Done", "search": "Search", "decline": "Decline", "agree": "Agree", "empty": "Empty", "unknown": "Unknown", "hidden": "Hidden", "timeout": "Timeout", "sort": "Sort", "dashboard": "Dashboard", "interval": { "day": { "zero": "", "one": "$n day", "other": "$n days" }, "hour": { "zero": "", "one": "$n hour", "other": "$n hours" } }, "msg": { "permission": { "denied": "Permission denied" }, "export": { "clipboard": { "success": "Added to clipboard successfully", "failure": "Failed to copy to clipboard", "contentTooLarge": "Content too large. Use export file instead" }, "file": { "success": "JSON file created successfully", "failure": "Failed to create file" } }, "import": { "confirm": "Confirm import", "success": "Imported successfully", "failure": "Failed to import" } } }, "intro": { "banner": "All you need for an unrestricted internet", "termsAndPolicyCaution(rich)": "By continuing you agree to ${tap(@:pages.about.termsAndConditions)}", "info(rich)": "Made with ❤️ by Hiddify - ${tap_source(Open Source)} (${tap_license(License)})" }, "pages": { "home": { "title": "Home", "quickSettings": "Quick settings" }, "proxies": { "title": "Proxies", "sort": "Sort proxies", "testDelay": "Test delay", "empty": "No proxies available", "activeProxy": "Active proxy", "unknownIp": "Unknown IP", "sortOptions": { "unsorted": "Default", "name": "Alphabetically", "delay": "By delay", "usage": "By usage" }, "ipInfo": { "address": "IP address", "country": "Country", "organization": "Organization" }, "delay": { "result": "Delay: ${delay}ms", "timeout": "Delay test timeout", "testing": "Delay: testing..." } }, "profiles": { "title": "Profiles", "add": "Add profile", "update": "Update profile", "viewAllProfiles": "View all profiles", "activeProfileName": "Active profile name: \"${name}\".", "nonActiveProfileName": "Select \"${name}\" as active profile", "freeSubNotFound": "No free subscription was found", "freeSubNotFoundForRegion": "No free subscription was found for \"${region}\" region.", "failedToLoad": "Failed to load", "updateSubscriptions": "Update subscriptions", "share": { "urlToClipboard": "URL to clipboard", "showUrlQr": "Show URL QR", "jsonToClipboard": "JSON to clipboard" }, "msg": { "save": { "success": "Profile saved successfully" }, "invalidUrl": "Invalid URL", "add": { "failure": "Failed to add profile" }, "update": { "success": "Profile updated successfully", "successNamed": "\"${name}\" updated successfully", "failure": "Failed to update profile", "failureNamed": "Failed to update \"${name}\"" }, "delete": { "success": "Profile deleted successfully" } } }, "profileDetails": { "title": "Profile", "lastUpdate": "Last update", "form": { "nameHint": "Profile name", "emptyName": "Name is required", "invalidUrl": "Invalid URL", "urlHint": "Full config URL", "disableAutoUpdate": "Disable auto update", "autoUpdateInterval": "Auto update interval", "loading": "Adding profile..." } }, "logs": { "title": "Logs", "shareCoreLogs": "Share core logs", "shareAppLogs": "Share app logs" }, "about": { "title": "About", "notAvailableMsg": "Already using the latest version", "checkForUpdate": "Check for update", "openWorkingDir": "Open working directory", "sourceCode": "Source code", "telegramChannel": "Telegram channel", "termsAndConditions": "Terms and conditions", "privacyPolicy": "Privacy policy" }, "settings": { "title": "Settings", "resetTunnel": "Reset VPN profile", "options": { "import": { "clipboard": "Import options from clipboard", "file": "Import options from file" }, "export": { "anonymousToClipboard": "Copy anonymous options to clipboard", "anonymousToFile": "Export anonymous options to file", "allToClipboard": "Copy all options to clipboard", "allToFile": "Export all options to file" }, "reset": "Reset options" }, "general": { "title": "General", "locale": "Language", "themeMode": "Theme mode", "themeModes": { "system": "System default", "dark": "Dark mode", "light": "Light mode", "black": "Black mode" }, "enableAnalytics": "Enable analytics", "enableAnalyticsMsg": "Give permission to collect analytics and send crash reports to improve the app", "autoIpCheck": "Automatically check connection IP", "dynamicNotification": "Display speed in notification", "hapticFeedback": "Haptic feedback", "actionAtClosing": "Action at closing", "autoStart": "Start at login", "silentStart": "Start minimized", "ignoreBatteryOptimizations": "Disable battery optimization", "ignoreBatteryOptimizationsMsg": "Remove restrictions for optimal VPN performance", "memoryLimit": "Memory limit", "memoryLimitMsg": "Enable if you're experiencing out of memory errors or frequent app crashes", "debugMode": "Debug mode", "debugModeMsg": "Restart the app for applying this change", "logLevel": "Log level", "connectionTestUrl": "Connection test URL", "urlTestInterval": "URL test interval", "clashApiPort": "Clash API port", "useXrayCoreWhenPossible": "Use xray-core when possible", "useXrayCoreWhenPossibleMsg": "Use xray-core during parsing sub links. You need to reimport the sub link to enable this option." }, "routing": { "title": "Routing", "perAppProxy": { "title": "Per-app proxy", "hideSysApps": "Hide system apps", "options": { "import": { "clipboard": "Import selection from clipboard", "file": "Import selection from file", "msg": "Importing will replace your current selections. Are you sure you want to continue?" }, "export": { "clipboard": "Copy selection to clipboard", "file": "Export selection to file" }, "shareToAll": "Share to all", "clearAllSelections": "Clear all selections" }, "modes": { "all": "All", "proxy": "Proxy", "bypass": "Bypass", "allMsg": "Proxy all apps", "proxyMsg": "Proxy only selected apps", "bypassMsg": "Do not proxy selected apps" }, "autoSelection": { "title": "Auto selection", "performNow": "Perform now", "resetToDefault": "Reset to default", "autoUpdateInterval": "Auto update interval", "toast": { "success": "Auto apps selection completed successfully", "failure": "Auto selection failed", "regionNotFound": "Auto selection not found, region \"${region}\"", "alreadyInAuto": "Your selections are already in the auto-list" }, "dialog": { "title": "Auto apps selection", "msg": "The auto selection feature for per-app proxy was disabled due to the region change to \"${region}\"" } } }, "region": "Region", "regions": { "ir": "Iran (ir)", "cn": "China (cn)", "ru": "Russia (ru)", "af": "Afghanistan (af)", "id": "Indonesia (id)", "tr": "Türkiye (tr)", "br": "Brazil (br)", "other": "Other" }, "balancerStrategy": { "title": "Balancer strategy", "roundRobin": "Round robin", "consistentHash": "Consistent hash", "stickySession": "Sticky session" }, "blockAds": "Block advertisements", "bypassLan": "Bypass LAN", "resolveDestination": "Resolve destination", "ipv6Route": "IPv6 route", "ipv6Modes": { "disable": "Disable", "enable": "Enable", "prefer": "Preferred", "only": "Exclusive" }, "routeRule": { "title": "Route rules", "options": { "import": { "clipboard": "Import rules from clipboard", "file": "Import rules from file" }, "export": { "clipboard": "Copy rules to clipboard", "file": "Save rules to file" }, "reset": "Reset rules" }, "deleteRule": "Delete rule", "createRule": "Create new rule", "rule": { "title": "Rule", "ruleChanged": "Rule changed", "ruleChangedMsg": "Do you want to save your edits?", "onlyTunMode": "Available only in tun mode", "notAvailabeInThisPlatform": "Not available in this platform", "canNotBeEmpty": "Can not be empty", "validUrlEx": "https://example.com", "validUrl": "Valid \"URL\" like\n@:pages.settings.routing.routeRule.rule.validUrlEx", "validProcessNameEx": "Chrome.exe or google chrome or chrome", "validProcessName": "Valid \"Process Name\" like\n@:pages.settings.routing.routeRule.rule.validProcessNameEx", "validProcessPathEx": "C:\\Pro...\\chrome.exe\n/App.../Google Chrome\n/usr/lib.../chrome", "validProcessPath": "Valid \"Process Path\" like\n@:pages.settings.routing.routeRule.rule.validProcessPathEx", "validPortRangeEx": "80 or 1-65000", "validPortRange": "Valid \"Port\" or \"Port Range\" like\n@:pages.settings.routing.routeRule.rule.validPortRangeEx", "validIpCidrEx": "8.8.8.8 or 10.0.0.0/24", "validIpCidr": "Valid IP CIDR like\n@:pages.settings.routing.routeRule.rule.validIpCidrEx", "validDomainEx": "Google.com or dl.google.com", "validDomain": "Valid \"Domain\" like\n@:pages.settings.routing.routeRule.rule.validDomainEx", "validDomainSuffixEx": ".com or .ir", "validDomainSuffix": "Valid \"Domain Suffix\" like\n@:pages.settings.routing.routeRule.rule.validDomainSuffixEx", "tileTitle(map)": { "name": "Name", "outbound": "Outbound if match", "rule_set": "Rule set URL", "package_name": "Package names", "process_name": "Process names", "process_path": "Process paths", "network": "Networks", "port_range": "Destination ports", "source_port_range": "Source ports", "protocol": "Protocol", "ip_cidr": "Destination IP CIDR", "source_ip_cidr": "Source IP CIDR", "domain": "Domain", "domain_suffixe": "Domain suffix", "domain_keyword": "Domain keyword", "domain_regex": "Domain regex" }, "outbound(map)": { "proxy": "Proxy", "direct": "Direct", "direct_with_fragment": "Direct with fragment", "block": "Block" }, "network(map)": { "all": "All", "tcp": "TCP", "udp": "UDP" }, "protocol(map)": { "": "All", "tls": "TLS", "http": "HTTP", "quic": "QUIC", "stun": "STUN", "dns": "DNS", "bittorrent": "BitTorrent" } }, "genericList": { "addNew": "Add new value", "update": "Update value", "clearList": "Clear list", "clearListMsg": "All items are deleted" }, "androidApps": { "pageTitle": "Android apps", "showSystemApps": "Show system apps", "hideSystemApps": "Hide system apps", "clearSelection": "Clear selection", "uninstalled": "Uninstalled" } } }, "dns": { "title": "DNS", "remoteDns": "Remote DNS", "remoteDnsDomainStrategy": "Remote DNS domain strategy", "directDns": "Outbound server resolver (direct)", "directDnsDomainStrategy": "Outbound domain strategy", "enableDnsRouting": "Enable DNS routing", "enableFakeDns": "Enable fake DNS", "domainStrategy": { "auto": "Auto", "preferIpv6": "Prefer IPv6", "preferIpv4": "Prefer IPv4", "ipv4Only": "IPv4 only", "ipv6Only": "IPv6 only" } }, "inbound": { "title": "Inbound", "serviceMode": "Service mode", "serviceModes": { "proxy": "Proxy service only", "systemProxy": "Set system proxy", "tun": "VPN", "tunService": "VPN service" }, "shortServiceModes": { "proxy": "Proxy", "systemProxy": "System proxy", "tun": "VPN", "tunService": "VPN service" }, "strictRoute": "Strict route", "tunImplementation": "Tun implementation", "tunImplementations": { "mixed": "Mixed", "system": "System", "gvisor": "gVisor" }, "mixedPort": "Mixed port", "tproxyPort": "Transparent proxy port", "directPort": "Direct port", "redirectPort": "Redirect port", "allowConnectionFromLan": "Share VPN in local network" }, "tlsTricks": { "title": "TLS tricks", "enable": "Enable fragment", "packets": "Fragmentation Packets", "packetsTlsHello": "TLS Hello", "packets1_1": "1-1", "packets1_2": "1-2", "packets1_3": "1-3", "packets1_4": "1-4", "packets1_5": "1-5", "size": "Fragment size", "sleep": "Fragment sleep", "mixedSniCase": { "enable": "Enable mixed SNI case" }, "padding": { "enable": "Enable padding", "size": "Padding size" } }, "warp": { "title": "WARP", "enable": "Enable WARP", "generateConfig": "Generate WARP config", "configGenerated": "Warp config is generated", "missingConfig": "Missing WARP config", "detourMode": "Detour mode", "detourModes": { "proxyOverWarp": "Detour proxies through WARP ", "warpOverProxy": "Detour WARP through proxies", "proxyOverWarpExplain": "Unblock proxies by WARP", "warpOverProxyExplain": "Extra security by WARP" }, "licenseKey": "License key", "cleanIp": "Clean IP", "port": "Port", "noise": { "count": "Noise count", "mode": "Noise mode", "size": "Noise size", "delay": "Noise delay" } } } }, "components": { "stats": { "connection": "Connection", "traffic": "Traffic", "trafficLive": "Live traffic", "trafficTotal": "Total traffic", "uplink": "Uplink", "downlink": "Downlink", "speed": "Speed", "totalTransferred": "Total transferred" }, "subscriptionInfo": { "upload": "Upload", "download": "Download", "total": "Total traffic", "expireDate": "Expire date", "expired": "Expired", "noTraffic": "Out of quota", "remainingTime": "Remaining time", "remainingDuration": "${duration} days remaining", "remainingDurationNew": "${duration} days", "remainingTrafficSemanticLabel": "${consumed} of ${total} traffic consumed", "remainingTraffic": "Remaining traffic", "remainingUsage": "Remaining", "profileSite": "Provider", "profileSupport": "Support" } }, "dialogs": { "sortProfiles": { "title": "Sort by", "sort": { "name": "Alphabetically", "lastUpdate": "Recently updated" } }, "warpLicense": { "title": "Cloudflare WARP consent", "description(rich)": "Cloudflare WARP is a free WireGuard VPN provider. By enabling this option you are agreeing to the Cloudflare WARP's ${tos(Terms of service)} and ${privacy(Privacy policy)}." }, "newVersion": { "title": "Update available", "msg": "A new version of @:common.appTitle is available. Would you like to update now?", "currentVersion": "Current version: ", "newVersion": "New version: ", "updateNow": "Update now" }, "confirmation": { "settings": { "import": { "msg": "This will rewrite all config options with provided values. Are you sure?" } }, "profile": { "delete": { "title": "Delete profile", "msg": "Are you sure you want to permanently delete this profile?" } }, "perAppProxy": { "shareOnGithub": { "title": "Improving auto selection", "msg": "By sharing selected apps, you help complete the \"auto selection\" list" }, "import": { "msg": "This will replace all your current per-app proxy selections. Are you sure you want to continue?" } }, "routeRule": { "delete": { "title": "Delete rule", "msg": "Are you sure you want to delete the \"$rulename\" rule?" } } }, "experimentalNotice": { "title": "Experimental features in use", "msg": "You've enabled some experimental features which might affect connection quality and cause unexpected errors. You can always change or reset these options from config options page.", "disable": "Don't show again" }, "noActiveProfile": { "title": "Choose a profile", "msg": "Let's get started by adding a connection profile that includes your VPN connection details.\n\nDon’t have a VPN server yet? No worries—just follow the tutorial below to set one up quickly and for free.", "helpBtn": { "label": "Show me how", "url": "https://hiddify.com/manager/" } }, "unknownDomainsWarning": { "title": "External link warning", "youAreAboutToVisit": "You are about to visit :", "thisWebsiteIsNotInOurTrustedList": "This website is not in our trusted list. Please proceed with caution." }, "proxyInfo": { "fullTag": "Full tag:", "type": "Type:", "testTime": "Test time:", "testDelay": "Test delay:", "isSelected": "Is selected:", "isGroup": "Is group", "isSecure": "Is secure:", "port": "Port:", "host": "Host:", "ip": "IP:", "countryCode": "Country code:", "region": "Region:", "city": "City:", "asn": "ASN:", "organization": "Organization:", "location": "Location:", "postalCode": "Postal code:", "download": "Download:", "upload": "Upload:" }, "windowClosing": { "askEachTime": "Ask each time", "alertMessage": "Hide or exit the application?", "remember": "Remember my choice" } }, "connection": { "tapToConnect": "Tap to connect", "connect": "Connect", "connecting": "Connecting...", "connected": "Connected", "disconnect": "Disconnect", "disconnecting": "Disconnecting...", "reconnect": "Reconnect", "reconnectMsg": "Reconnecting for taking into account the changes...", "secure": "Secured by WARP" }, "errors": { "unexpected": "Unexpected error", "connection": { "unexpected": "Unexpected connection error", "timeout": "Connection timeout", "badResponse": "Bad response", "connectionError": "Connection error", "badCertificate": "Bad certificate" }, "profiles": { "unexpected": "Unexpected error", "notFound": "Profile not found", "invalidConfig": "Invalid configs", "invalidUrl": "Invalid URL", "canceledByUser": "Canceled by user" }, "connectivity": { "unexpected": "Unexpected failure", "missingVpnPermission": "Missing VPN permission", "missingNotificationPermission": "Missing notification permission", "core": "Core error" }, "singbox": { "serviceNotRunning": "Service is not running", "missingPrivilege": "Missing privilege", "missingPrivilegeMsg": "VPN mode requires administrator privileges. Either relaunch the app as administrator or change service mode.", "invalidConfigOptions": "Invalid configuration options", "invalidConfig": "Invalid configuration" }, "warp": { "missingLicense": "Warp license", "missingLicenseMsg": "The selected profile uses the WARP feature; to use this feature, the WARP license must be agreed to." } } } ================================================ FILE: assets/translations/es.i18n.json ================================================ { "common": { "appTitle": "Hiddify", "start": "Comenzar", "version": "Versión", "ok": "Aceptar", "cancel": "Cancelar", "kContinue": "Continuar", "showMore": "Mostrar más", "showLess": "Mostrar menos", "filter": "Filtrar", "all": "Todos", "pause": "Pausar", "resume": "Reanudar", "clear": "Limpiar", "close": "Cerrar", "auto": "Automático", "manually": "Manualmente", "name": "Nombre", "url": "URL", "add": "Añadir", "clipboard": "Portapapeles", "addToClipboard": "Añadir al portapapeles", "scanQr": "Escanear QR", "free": "Gratis", "warp": "WARP", "fragment": "Fragmento", "help": "Ayuda", "save": "Guardar", "update": "Actualizar", "share": "Compartir", "edit": "Editar", "delete": "Eliminar", "discard": "Descartar", "import": "Importar", "export": "Exportar", "later": "Más tarde", "ignore": "Ignorar", "quit": "Salir", "notSet": "No establecido", "hide": "Ocultar", "exit": "Salir", "reset": "Restablecer", "done": "Hecho", "search": "Buscar", "decline": "Rechazar", "agree": "Aceptar", "empty": "Vacío", "unknown": "Desconocido", "hidden": "Oculto", "timeout": "Tiempo agotado", "sort": "Ordenar", "dashboard": "Panel", "interval": { "day": { "zero": "", "one": "$n día", "other": "$n días" }, "hour": { "zero": "", "one": "$n hora", "other": "$n horas" } }, "msg": { "permission": { "denied": "Permiso denegado" }, "export": { "clipboard": { "success": "Añadido al portapapeles con éxito", "failure": "Error al copiar al portapapeles", "contentTooLarge": "Contenido demasiado grande. Use la exportación a archivo en su lugar" }, "file": { "success": "Archivo JSON creado con éxito", "failure": "Error al crear el archivo" } }, "import": { "confirm": "Confirmar importación", "success": "Importado con éxito", "failure": "Error al importar" } } }, "intro": { "banner": "Todo lo que necesitas para un internet sin restricciones", "termsAndPolicyCaution(rich)": "Al continuar, aceptas los ${tap(@:pages.about.termsAndConditions)}", "info(rich)": "Hecho con ❤️ por Hiddify - ${tap_source(Código Abierto)} (${tap_license(Licencia)})" }, "pages": { "home": { "title": "Inicio", "quickSettings": "Ajustes rápidos" }, "proxies": { "title": "Proxies", "sort": "Ordenar proxies", "testDelay": "Probar latencia", "empty": "No hay proxies disponibles", "activeProxy": "Proxy activo", "unknownIp": "IP desconocida", "sortOptions": { "unsorted": "Por defecto", "name": "Alfabéticamente", "delay": "Por latencia" }, "ipInfo": { "address": "Dirección IP", "country": "País", "organization": "Organización" }, "delay": { "result": "Latencia: ${delay}ms", "timeout": "Tiempo de prueba de latencia agotado", "testing": "Latencia: probando..." } }, "profiles": { "title": "Perfiles", "add": "Añadir perfil", "update": "Actualizar perfil", "viewAllProfiles": "Ver todos los perfiles", "activeProfileName": "Nombre del perfil activo: \"${name}\".", "nonActiveProfileName": "Seleccionar \"${name}\" como perfil activo", "freeSubNotFound": "No se encontró ninguna suscripción gratuita", "freeSubNotFoundForRegion": "No se encontró ninguna suscripción gratuita para la región \"${region}\"", "failedToLoad": "Error al cargar", "updateSubscriptions": "Actualizar suscripciones", "share": { "urlToClipboard": "URL al portapapeles", "showUrlQr": "Mostrar QR de la URL", "jsonToClipboard": "JSON al portapapeles" }, "msg": { "save": { "success": "Perfil guardado con éxito" }, "invalidUrl": "URL no válida", "add": { "failure": "Error al añadir el perfil" }, "update": { "success": "Perfil actualizado con éxito", "successNamed": "\"${name}\" actualizado con éxito", "failure": "Error al actualizar el perfil", "failureNamed": "Error al actualizar \"${name}\"" }, "delete": { "success": "Perfil eliminado con éxito" } } }, "profileDetails": { "title": "Perfil", "lastUpdate": "Última actualización", "form": { "nameHint": "Nombre del perfil", "emptyName": "El nombre es obligatorio", "invalidUrl": "URL no válida", "urlHint": "URL de configuración completa", "disableAutoUpdate": "Desactivar actualización automática", "autoUpdateInterval": "Intervalo de actualización automática", "loading": "Añadiendo perfil..." } }, "logs": { "title": "Registros", "shareCoreLogs": "Compartir registros del núcleo", "shareAppLogs": "Compartir registros de la aplicación" }, "about": { "title": "Acerca de", "notAvailableMsg": "Ya estás usando la última versión", "checkForUpdate": "Buscar actualizaciones", "openWorkingDir": "Abrir directorio de trabajo", "sourceCode": "Código fuente", "telegramChannel": "Canal de Telegram", "termsAndConditions": "Términos y condiciones", "privacyPolicy": "Política de privacidad" }, "settings": { "title": "Ajustes", "resetTunnel": "Restablecer perfil de VPN", "options": { "import": { "clipboard": "Importar opciones desde el portapapeles", "file": "Importar opciones desde un archivo" }, "export": { "anonymousToClipboard": "Copiar opciones anónimas al portapapeles", "anonymousToFile": "Exportar opciones anónimas a un archivo", "allToClipboard": "Copiar todas las opciones al portapapeles", "allToFile": "Exportar todas las opciones a un archivo" }, "reset": "Restablecer opciones" }, "general": { "title": "General", "locale": "Idioma", "themeMode": "Tema", "themeModes": { "system": "Por defecto del sistema", "dark": "Modo oscuro", "light": "Modo claro", "black": "Modo negro" }, "enableAnalytics": "Habilitar análisis", "enableAnalyticsMsg": "Dar permiso para recopilar análisis y enviar informes de fallos para mejorar la aplicación", "autoIpCheck": "Comprobar IP de conexión automáticamente", "dynamicNotification": "Mostrar velocidad en la notificación", "hapticFeedback": "Respuesta háptica", "actionAtClosing": "Acción al cerrar", "autoStart": "Iniciar al arrancar", "silentStart": "Iniciar minimizado", "ignoreBatteryOptimizations": "Desactivar optimización de batería", "ignoreBatteryOptimizationsMsg": "Eliminar restricciones para un rendimiento óptimo de la VPN", "memoryLimit": "Límite de memoria", "memoryLimitMsg": "Habilitar si experimentas errores de falta de memoria o fallos frecuentes de la aplicación", "debugMode": "Modo de depuración", "debugModeMsg": "Reinicia la aplicación para aplicar este cambio", "logLevel": "Nivel de registro", "connectionTestUrl": "URL de prueba de conexión", "urlTestInterval": "Intervalo de prueba de URL", "clashApiPort": "Puerto de la API de Clash", "useXrayCoreWhenPossible": "Usar xray-core cuando sea posible", "useXrayCoreWhenPossibleMsg": "Usa xray-core al analizar enlaces de suscripción. Necesitas reimportar el enlace para habilitar esta opción" }, "routing": { "title": "Enrutamiento", "perAppProxy": { "title": "Proxy por aplicación", "hideSysApps": "Ocultar aplicaciones del sistema", "options": { "import": { "clipboard": "Importar selección desde el portapapeles", "file": "Importar selección desde un archivo", "msg": "La importación reemplazará tus selecciones actuales. ¿Estás seguro de que quieres continuar?" }, "export": { "clipboard": "Copiar selección al portapapeles", "file": "Exportar selección a un archivo" }, "shareToAll": "Compartir con todos", "clearAllSelections": "Borrar todas las selecciones" }, "modes": { "all": "Todo", "proxy": "Proxy", "bypass": "Omitir", "allMsg": "Usar proxy en todas las aplicaciones", "proxyMsg": "Usar proxy solo en aplicaciones seleccionadas", "bypassMsg": "No usar proxy en aplicaciones seleccionadas" }, "autoSelection": { "title": "Selección automática", "performNow": "Ejecutar ahora", "resetToDefault": "Restablecer por defecto", "autoUpdateInterval": "Intervalo de actualización automática", "toast": { "success": "Selección automática de aplicaciones completada con éxito", "failure": "Fallo en la selección automática", "regionNotFound": "No se encontró selección automática para la región \"${region}\"", "alreadyInAuto": "Tus selecciones ya están en la lista automática" }, "dialog": { "title": "Selección automática de aplicaciones", "msg": "La función de selección automática para el proxy por aplicación se ha desactivado debido al cambio de región a \"${region}\"" } } }, "region": "Región", "regions": { "ir": "Irán (ir)", "cn": "China (cn)", "ru": "Rusia (ru)", "af": "Afganistán (af)", "id": "Indonesia (id)", "tr": "Turquía (tr)", "br": "Brasil (br)", "other": "Otro" }, "balancerStrategy": { "title": "Estrategia de Balancer", "roundRobin": "Round robin", "consistentHash": "Consistent hash", "stickySession": "Sticky session" }, "blockAds": "Bloquear anuncios", "bypassLan": "Omitir LAN", "resolveDestination": "Resolver destino", "ipv6Route": "Ruta IPv6", "ipv6Modes": { "disable": "Desactivar", "enable": "Activar", "prefer": "Preferido", "only": "Exclusivo" }, "routeRule": { "title": "Reglas de enrutamiento", "options": { "import": { "clipboard": "Importar reglas desde el portapapeles", "file": "Importar reglas desde un archivo" }, "export": { "clipboard": "Copiar reglas al portapapeles", "file": "Guardar reglas en un archivo" }, "reset": "Restablecer reglas" }, "deleteRule": "Eliminar regla", "createRule": "Crear nueva regla", "rule": { "title": "Regla", "ruleChanged": "Regla modificada", "ruleChangedMsg": "¿Quieres guardar tus cambios?", "onlyTunMode": "Disponible solo en modo TUN", "notAvailabeInThisPlatform": "No disponible en esta plataforma", "canNotBeEmpty": "No puede estar vacío", "validUrlEx": "https://example.com", "validUrl": "\"URL\" válida como\n@:pages.settings.routing.routeRule.rule.validUrlEx", "validProcessNameEx": "Chrome.exe or google chrome or chrome", "validProcessName": "\"Nombre de proceso\" válido como\n@:pages.settings.routing.routeRule.rule.validProcessNameEx", "validProcessPathEx": "C:\\Pro...\\chrome.exe\n/App.../Google Chrome\n/usr/lib.../chrome", "validProcessPath": "\"Ruta de proceso\" válida como\n@:pages.settings.routing.routeRule.rule.validProcessPathEx", "validPortRangeEx": "80 or 1-65000", "validPortRange": "\"Puerto\" o \"Rango de puertos\" válido como\n@:pages.settings.routing.routeRule.rule.validPortRangeEx", "validIpCidrEx": "8.8.8.8 or 10.0.0.0/24", "validIpCidr": "IP CIDR válido como\n@:pages.settings.routing.routeRule.rule.validIpCidrEx", "validDomainEx": "Google.com or dl.google.com", "validDomain": "\"Dominio\" válido como\n@:pages.settings.routing.routeRule.rule.validDomainEx", "validDomainSuffixEx": ".com or .ir", "validDomainSuffix": "\"Sufijo de dominio\" válido como\n@:pages.settings.routing.routeRule.rule.validDomainSuffixEx", "tileTitle(map)": { "name": "Nombre", "outbound": "Salida si coincide", "rule_set": "URL del conjunto de reglas", "package_name": "Nombres de paquetes", "process_name": "Nombres de procesos", "process_path": "Rutas de procesos", "network": "Redes", "port_range": "Puertos de destino", "source_port_range": "Puertos de origen", "protocol": "Protocolo", "ip_cidr": "IP CIDR de destino", "source_ip_cidr": "IP CIDR de origen", "domain": "Dominio", "domain_suffixe": "Sufijo de dominio", "domain_keyword": "Palabra clave de dominio", "domain_regex": "Expresión regular de dominio" }, "outbound(map)": { "proxy": "Proxy", "direct": "Directo", "direct_with_fragment": "Directo con fragmento", "block": "Bloquear" }, "network(map)": { "all": "Todo", "tcp": "TCP", "udp": "UDP" }, "protocol(map)": { "": "Todos", "tls": "TLS", "http": "HTTP", "quic": "QUIC", "stun": "STUN", "dns": "DNS", "bittorrent": "BitTorrent" } }, "genericList": { "addNew": "Añadir nuevo valor", "update": "Actualizar valor", "clearList": "Limpiar lista", "clearListMsg": "Todos los elementos han sido eliminados" }, "androidApps": { "pageTitle": "Aplicaciones de Android", "showSystemApps": "Mostrar aplicaciones del sistema", "hideSystemApps": "Ocultar aplicaciones del sistema", "clearSelection": "Borrar selección", "uninstalled": "Desinstalado" } } }, "dns": { "title": "DNS", "remoteDns": "DNS remoto", "remoteDnsDomainStrategy": "Estrategia de dominio de DNS remoto", "directDns": "Resolución del servidor de salida (directo)", "directDnsDomainStrategy": "Estrategia de dominio de salida", "enableDnsRouting": "Habilitar enrutamiento de DNS", "enableFakeDns": "Habilitar DNS falso", "domainStrategy": { "auto": "Automático", "preferIpv6": "Preferir IPv6", "preferIpv4": "Preferir IPv4", "ipv4Only": "Solo IPv4", "ipv6Only": "Solo IPv6" } }, "inbound": { "title": "Entrada", "serviceMode": "Modo de servicio", "serviceModes": { "proxy": "Solo servicio de proxy", "systemProxy": "Establecer proxy del sistema", "tun": "VPN", "tunService": "Servicio VPN" }, "shortServiceModes": { "proxy": "Proxy", "systemProxy": "Proxy del sistema", "tun": "VPN", "tunService": "Servicio VPN" }, "strictRoute": "Ruta estricta", "tunImplementation": "Implementación de TUN", "tunImplementations": { "mixed": "Mixto", "system": "Sistema", "gvisor": "gVisor" }, "mixedPort": "Puerto mixto", "tproxyPort": "Puerto de proxy transparente", "directPort": "Puerto de Directo", "redirectPort": "Puerto de redirección", "allowConnectionFromLan": "Compartir VPN en la red local" }, "tlsTricks": { "title": "Trucos de TLS", "enable": "Habilitar fragmento", "packets": "Paquetes de fragmentación", "packetsTlsHello": "TLS Hello", "packets1_1": "1-1", "packets1_2": "1-2", "packets1_3": "1-3", "packets1_4": "1-4", "packets1_5": "1-5", "size": "Tamaño de fragmento", "sleep": "Retraso de fragmento", "mixedSniCase": { "enable": "Habilitar mayúsculas/minúsculas mixtas en SNI" }, "padding": { "enable": "Habilitar relleno", "size": "Tamaño de relleno" } }, "warp": { "title": "WARP", "enable": "Habilitar WARP", "generateConfig": "Generar configuración de WARP", "configGenerated": "Configuración de WARP generada", "missingConfig": "Falta la configuración de WARP", "detourMode": "Modo de desvío", "detourModes": { "proxyOverWarp": "Desviar proxies a través de WARP", "warpOverProxy": "Desviar WARP a través de proxies", "proxyOverWarpExplain": "Desbloquear proxies con WARP", "warpOverProxyExplain": "Seguridad extra con WARP" }, "licenseKey": "Clave de licencia", "cleanIp": "IP limpia", "port": "Puerto", "noise": { "count": "Cantidad de ruido", "mode": "Modo de ruido", "size": "Tamaño de ruido", "delay": "Retraso de ruido" } } } }, "components": { "stats": { "connection": "Conexión", "traffic": "Tráfico", "trafficLive": "Tráfico en tiempo real", "trafficTotal": "Tráfico total", "uplink": "Subida", "downlink": "Bajada", "speed": "Velocidad", "totalTransferred": "Total transferido" }, "subscriptionInfo": { "upload": "Subida", "download": "Descarga", "total": "Tráfico total", "expireDate": "Fecha de caducidad", "expired": "Caducado", "noTraffic": "Cuota agotada", "remainingTime": "Tiempo restante", "remainingDuration": "${duration} días restantes", "remainingDurationNew": "${duration} días", "remainingTrafficSemanticLabel": "${consumed} de ${total} de tráfico consumido", "remainingTraffic": "Tráfico restante", "remainingUsage": "Restante", "profileSite": "Proveedor", "profileSupport": "Soporte" } }, "dialogs": { "sortProfiles": { "title": "Ordenar por", "sort": { "name": "Alfabéticamente", "lastUpdate": "Última actualización" } }, "warpLicense": { "title": "Consentimiento de Cloudflare WARP", "description(rich)": "Cloudflare WARP es un proveedor de VPN WireGuard gratuito. Al habilitar esta opción, aceptas los ${tos(Términos de servicio)} y la ${privacy(Política de privacidad)} de Cloudflare WARP." }, "newVersion": { "title": "Actualización disponible", "msg": "Hay disponible una nueva versión de @:common.appTitle. ¿Quieres actualizar ahora?", "currentVersion": "Versión actual: ", "newVersion": "Nueva versión: ", "updateNow": "Actualizar ahora" }, "confirmation": { "settings": { "import": { "msg": "Esto reemplazará todas las opciones de configuración con los valores proporcionados. ¿Estás seguro?" } }, "profile": { "delete": { "title": "Eliminar perfil", "msg": "¿Estás seguro de que quieres eliminar este perfil permanentemente?" } }, "perAppProxy": { "shareOnGithub": { "title": "Mejorando la selección automática", "msg": "Al compartir las aplicaciones seleccionadas, ayudas a completar la lista de \"selección automática\"" }, "import": { "msg": "Esto reemplazará todas tus selecciones actuales de proxy por aplicación. ¿Estás seguro de que quieres continuar?" } }, "routeRule": { "delete": { "title": "Eliminar regla", "msg": "¿Estás seguro de que quieres eliminar la regla \"$rulename\"?" } } }, "experimentalNotice": { "title": "Funciones experimentales en uso", "msg": "Has habilitado algunas funciones experimentales que podrían afectar la calidad de la conexión y causar errores inesperados. Siempre puedes cambiar o restablecer estas opciones desde la página de configuración.", "disable": "No volver a mostrar" }, "noActiveProfile": { "title": "Elige un perfil", "msg": "Para empezar, añade un perfil de conexión que incluya los detalles de tu conexión VPN.\n\n¿Aún no tienes un servidor VPN? No te preocupes, sigue el tutorial a continuación para configurar uno rápidamente y de forma gratuita.", "helpBtn": { "label": "Muéstrame cómo", "url": "https://hiddify.com/manager/" } }, "unknownDomainsWarning": { "title": "Advertencia de enlace externo", "youAreAboutToVisit": "Estás a punto de visitar:", "thisWebsiteIsNotInOurTrustedList": "Este sitio web no está en nuestra lista de confianza. Procede con precaución." }, "proxyInfo": { "fullTag": "Etiqueta completa:", "type": "Tipo:", "testTime": "Hora de la prueba:", "testDelay": "Latencia de la prueba:", "isSelected": "Seleccionado:", "isGroup": "Es un grupo", "isSecure": "Es seguro:", "port": "Puerto:", "host": "Host:", "ip": "IP:", "countryCode": "Código de país:", "region": "Región:", "city": "Ciudad:", "asn": "ASN:", "organization": "Organización:", "location": "Ubicación:", "postalCode": "Código postal:" }, "windowClosing": { "askEachTime": "Preguntar cada vez", "alertMessage": "¿Ocultar o salir de la aplicación?", "remember": "Recordar mi elección" } }, "connection": { "tapToConnect": "Toca para conectar", "connect": "Conectar", "connecting": "Conectando...", "connected": "Conectado", "disconnect": "Desconectar", "disconnecting": "Desconectando...", "reconnect": "Reconectar", "reconnectMsg": "Reconectando para aplicar los cambios...", "secure": "Protegido por WARP" }, "errors": { "unexpected": "Error inesperado", "connection": { "unexpected": "Error de conexión inesperado", "timeout": "Tiempo de conexión agotado", "badResponse": "Respuesta incorrecta", "connectionError": "Error de conexión", "badCertificate": "Certificado no válido" }, "profiles": { "unexpected": "Error inesperado", "notFound": "Perfil no encontrado", "invalidConfig": "Configuraciones no válidas", "invalidUrl": "URL no válida", "canceledByUser": "Cancelado por el usuario" }, "connectivity": { "unexpected": "Fallo inesperado", "missingVpnPermission": "Falta el permiso de VPN", "missingNotificationPermission": "Falta el permiso de notificación", "core": "Error del núcleo" }, "singbox": { "serviceNotRunning": "El servicio no se está ejecutando", "missingPrivilege": "Falta privilegio", "missingPrivilegeMsg": "El modo VPN requiere privilegios de administrador. Reinicia la aplicación como administrador o cambia el modo de servicio.", "invalidConfigOptions": "Opciones de configuración no válidas", "invalidConfig": "Configuración no válida" }, "warp": { "missingLicense": "Falta la licencia de WARP", "missingLicenseMsg": "El perfil seleccionado utiliza la función WARP. Para usar esta función, debes aceptar la licencia de WARP." } } } ================================================ FILE: assets/translations/fa.i18n.json ================================================ { "common": { "appTitle": "هیدیفای", "start": "شروع", "version": "نسخه", "ok": "باشه", "cancel": "لغو", "kContinue": "ادامه", "showMore": "نمایش بیشتر", "showLess": "نمایش کمتر", "filter": "فیلتر", "all": "همه", "pause": "مکث", "resume": "ادامه", "clear": "پاک‌سازی", "close": "بستن", "auto": "خودکار", "manually": "دستی", "name": "نام", "url": "URL", "add": "افزودن", "clipboard": "کلیپ‌بورد", "addToClipboard": "افزودن به کلیپ‌بورد", "scanQr": "اسکن QR", "free": "رایگان", "warp": "WARP", "fragment": "Fragment", "help": "راهنما", "save": "ذخیره", "update": "به‌روزرسانی", "share": "اشتراک‌گذاری", "edit": "ویرایش", "delete": "حذف", "discard": "صرف‌نظر", "import": "وارد کردن", "export": "خروجی گرفتن", "later": "بعداً", "ignore": "نادیده گرفتن", "quit": "خروج", "notSet": "تنظیم نشده", "hide": "پنهان کردن", "exit": "خروج", "reset": "بازنشانی", "done": "انجام شد", "search": "جستجو", "decline": "رد کردن", "agree": "موافقم", "empty": "خالی", "unknown": "ناشناخته", "hidden": "پنهان", "timeout": "عدم پاسخ", "sort": "مرتب‌سازی", "dashboard": "داشبورد", "interval": { "day": { "zero": "", "one": "$n روز", "other": "$n روز" }, "hour": { "zero": "", "one": "$n ساعت", "other": "$n ساعت" } }, "msg": { "permission": { "denied": "دسترسی رد شد" }, "export": { "clipboard": { "success": "با موفقیت در کلیپ‌بورد کپی شد", "failure": "کپی در کلیپ‌بورد ناموفق بود", "contentTooLarge": "محتوا بیش از حد بزرگ است. به جای آن از خروجی فایل استفاده کنید" }, "file": { "success": "فایل JSON با موفقیت ایجاد شد", "failure": "ایجاد فایل ناموفق بود" } }, "import": { "confirm": "تأیید ورود اطلاعات", "success": "با موفقیت وارد شد", "failure": "وارد کردن ناموفق بود" } } }, "intro": { "banner": "هرآنچه برای اینترنت بدون محدودیت نیاز دارید", "termsAndPolicyCaution(rich)": "با ادامه، شما با ${tap(@:pages.about.termsAndConditions)} موافقت می‌کنید", "info(rich)": "ساخته شده با ❤️ توسط هیدیفای - ${tap_source(متن‌باز)} (${tap_license(مجوز)})" }, "pages": { "home": { "title": "خانه", "quickSettings": "تنظیمات سریع" }, "proxies": { "title": "پروکسی‌ها", "sort": "مرتب‌سازی پروکسی‌ها", "testDelay": "تست تأخیر", "empty": "هیچ پروکسی موجود نیست", "activeProxy": "پروکسی فعال", "unknownIp": "IP ناشناخته", "sortOptions": { "unsorted": "پیش‌فرض", "name": "بر اساس نام", "delay": "بر اساس تأخیر" }, "ipInfo": { "address": "آدرس IP", "country": "کشور", "organization": "سازمان" }, "delay": { "result": "تأخیر: ${delay} میلی‌ثانیه", "timeout": "تست تأخیر ناموفق بود", "testing": "تأخیر: در حال تست..." } }, "profiles": { "title": "پروفایل‌ها", "add": "افزودن پروفایل", "update": "به‌روزرسانی پروفایل", "viewAllProfiles": "مشاهده همه پروفایل‌ها", "activeProfileName": "نام پروفایل فعال: «${name}»", "nonActiveProfileName": "انتخاب «${name}» به عنوان پروفایل فعال", "freeSubNotFound": "اشتراک رایگان یافت نشد", "freeSubNotFoundForRegion": "اشتراک رایگان برای منطقه «${region}» یافت نشد", "failedToLoad": "بارگذاری ناموفق بود", "updateSubscriptions": "به‌روزرسانی اشتراک‌ها", "share": { "urlToClipboard": "URL به کلیپ‌بورد", "showUrlQr": "نمایش QR کد URL", "jsonToClipboard": "JSON به کلیپ‌بورد" }, "msg": { "save": { "success": "پروفایل با موفقیت ذخیره شد" }, "invalidUrl": "URL نامعتبر است", "add": { "failure": "افزودن پروفایل ناموفق بود" }, "update": { "success": "پروفایل با موفقیت به‌روز شد", "successNamed": "«${name}» با موفقیت به‌روز شد", "failure": "به‌روزرسانی پروفایل ناموفق بود", "failureNamed": "به‌روزرسانی «${name}» ناموفق بود" }, "delete": { "success": "پروفایل با موفقیت حذف شد" } } }, "profileDetails": { "title": "پروفایل", "lastUpdate": "آخرین به‌روزرسانی", "form": { "nameHint": "نام پروفایل", "emptyName": "نام الزامی است", "invalidUrl": "URL نامعتبر است", "urlHint": "URL کامل کانفیگ", "disableAutoUpdate": "غیرفعال کردن به‌روزرسانی خودکار", "autoUpdateInterval": "فاصله زمانی به‌روزرسانی خودکار", "loading": "در حال افزودن پروفایل..." } }, "logs": { "title": "گزارش‌ها", "shareCoreLogs": "اشتراک‌گذاری گزارش‌های هسته", "shareAppLogs": "اشتراک‌گذاری گزارش‌های برنامه" }, "about": { "title": "درباره", "notAvailableMsg": "شما در حال استفاده از آخرین نسخه هستید", "checkForUpdate": "بررسی برای به‌روزرسانی", "openWorkingDir": "باز کردن پوشه کاری", "sourceCode": "کد منبع", "telegramChannel": "کانال تلگرام", "termsAndConditions": "شرایط و ضوابط", "privacyPolicy": "سیاست حفظ حریم خصوصی" }, "settings": { "title": "تنظیمات", "resetTunnel": "بازنشانی پروفایل VPN", "options": { "import": { "clipboard": "وارد کردن تنظیمات از کلیپ‌بورد", "file": "وارد کردن تنظیمات از فایل" }, "export": { "anonymousToClipboard": "کپی تنظیمات ناشناس به کلیپ‌بورد", "anonymousToFile": "خروجی تنظیمات ناشناس به فایل", "allToClipboard": "کپی تمام تنظیمات به کلیپ‌بورد", "allToFile": "خروجی تمام تنظیمات به فایل" }, "reset": "بازنشانی تنظیمات" }, "general": { "title": "عمومی", "locale": "زبان", "themeMode": "حالت پوسته", "themeModes": { "system": "پیش‌فرض سیستم", "dark": "حالت تیره", "light": "حالت روشن", "black": "حالت مشکی" }, "enableAnalytics": "فعال‌سازی آمار", "enableAnalyticsMsg": "اجازه جمع‌آوری آمار و ارسال گزارش‌های خطا برای بهبود برنامه", "autoIpCheck": "بررسی خودکار IP اتصال", "dynamicNotification": "نمایش سرعت در اعلان", "hapticFeedback": "بازخورد لرزشی", "actionAtClosing": "اقدام هنگام بستن", "autoStart": "شروع هنگام ورود به سیستم", "silentStart": "شروع در حالت کمینه", "ignoreBatteryOptimizations": "غیرفعال کردن بهینه‌سازی باتری", "ignoreBatteryOptimizationsMsg": "حذف محدودیت‌ها برای عملکرد بهینه VPN", "memoryLimit": "محدودیت حافظه", "memoryLimitMsg": "در صورت مواجهه با خطای کمبود حافظه یا بسته‌شدن‌های مکرر برنامه، این گزینه را فعال کنید", "debugMode": "حالت اشکال‌زدایی", "debugModeMsg": "برای اعمال این تغییر، برنامه را مجدداً راه‌اندازی کنید", "logLevel": "سطح گزارش‌گیری", "connectionTestUrl": "URL تست اتصال", "urlTestInterval": "فاصله زمانی تست URL", "clashApiPort": "پورت Clash API", "useXrayCoreWhenPossible": "استفاده از xray-core در صورت امکان", "useXrayCoreWhenPossibleMsg": "هنگام پردازش لینک‌های اشتراک از xray-core استفاده شود. برای فعال‌سازی این گزینه باید لینک اشتراک را مجدداً وارد کنید." }, "routing": { "title": "مسیریابی", "perAppProxy": { "title": "پروکسی برای هر برنامه", "hideSysApps": "پنهان کردن برنامه‌های سیستمی", "options": { "import": { "clipboard": "وارد کردن انتخاب‌ها از کلیپ‌بورد", "file": "وارد کردن انتخاب‌ها از فایل", "msg": "وارد کردن، انتخاب‌های فعلی شما را جایگزین خواهد کرد. آیا مطمئن هستید که می‌خواهید ادامه دهید؟" }, "export": { "clipboard": "کپی انتخاب‌ها به کلیپ‌بورد", "file": "خروجی انتخاب‌ها به فایل" }, "shareToAll": "اشتراک‌گذاری با همه", "clearAllSelections": "پاک کردن تمام انتخاب‌ها" }, "modes": { "all": "همه", "proxy": "پروکسی", "bypass": "دور زدن", "allMsg": "پروکسی کردن تمام برنامه‌ها", "proxyMsg": "فقط برنامه‌های انتخاب‌شده پروکسی شوند", "bypassMsg": "برنامه‌های انتخاب‌شده پروکسی نشوند" }, "autoSelection": { "title": "انتخاب خودکار", "performNow": "اکنون انجام بده", "resetToDefault": "بازنشانی به پیش‌فرض", "autoUpdateInterval": "فاصله زمانی به‌روزرسانی خودکار", "toast": { "success": "انتخاب خودکار برنامه‌ها با موفقیت انجام شد", "failure": "انتخاب خودکار ناموفق بود", "regionNotFound": "انتخاب خودکار برای منطقه «${region}» یافت نشد", "alreadyInAuto": "انتخاب‌های شما از قبل در لیست خودکار موجود است" }, "dialog": { "title": "انتخاب خودکار برنامه‌ها", "msg": "قابلیت انتخاب خودکار برای پروکسی هر برنامه به دلیل تغییر منطقه به «${region}» غیرفعال شد" } } }, "region": "منطقه", "regions": { "ir": "ایران (ir)", "cn": "چین (cn)", "ru": "روسیه (ru)", "af": "افغانستان (af)", "id": "اندونزی (id)", "tr": "ترکیه (tr)", "br": "برزیل (br)", "other": "سایر" }, "balancerStrategy": { "title": "استراتژی Balancer", "roundRobin": "Round robin", "consistentHash": "Consistent hash", "stickySession": "Sticky session" }, "blockAds": "مسدودسازی تبلیغات", "bypassLan": "دور زدن LAN", "resolveDestination": "تحلیل آدرس مقصد", "ipv6Route": "مسیریابی IPv6", "ipv6Modes": { "disable": "غیرفعال", "enable": "فعال", "prefer": "ترجیحی", "only": "انحصاری" }, "routeRule": { "title": "قوانین مسیریابی", "options": { "import": { "clipboard": "وارد کردن قوانین از کلیپ‌بورد", "file": "وارد کردن قوانین از فایل" }, "export": { "clipboard": "کپی قوانین در کلیپ‌بورد", "file": "ذخیره قوانین در فایل" }, "reset": "بازنشانی قوانین" }, "deleteRule": "حذف قانون", "createRule": "ایجاد قانون جدید", "rule": { "title": "قانون", "ruleChanged": "قانون تغییر کرد", "ruleChangedMsg": "آیا می‌خواهید ویرایش‌های خود را ذخیره کنید؟", "onlyTunMode": "فقط در حالت TUN در دسترس است", "notAvailabeInThisPlatform": "در این پلتفرم در دسترس نیست", "canNotBeEmpty": "نمی‌تواند خالی باشد", "validUrlEx": "https://example.com", "validUrl": "«URL» معتبر مانند\n@:pages.settings.routing.routeRule.rule.validUrlEx", "validProcessNameEx": "Chrome.exe یا google chrome یا chrome", "validProcessName": "«نام فرآیند» معتبر مانند\n@:pages.settings.routing.routeRule.rule.validProcessNameEx", "validProcessPathEx": "C:\\Pro...\\chrome.exe\n/App.../Google Chrome\n/usr/lib.../chrome", "validProcessPath": "«مسیر فرآیند» معتبر مانند\n@:pages.settings.routing.routeRule.rule.validProcessPathEx", "validPortRangeEx": "80 یا 1-65000", "validPortRange": "«پورت» یا «محدوده پورت» معتبر مانند\n@:pages.settings.routing.routeRule.rule.validPortRangeEx", "validIpCidrEx": "8.8.8.8 یا 10.0.0.0/24", "validIpCidr": "IP CIDR معتبر مانند\n@:pages.settings.routing.routeRule.rule.validIpCidrEx", "validDomainEx": "Google.com یا dl.google.com", "validDomain": "«دامنه» معتبر مانند\n@:pages.settings.routing.routeRule.rule.validDomainEx", "validDomainSuffixEx": ".com یا .ir", "validDomainSuffix": "«پسوند دامنه» معتبر مانند\n@:pages.settings.routing.routeRule.rule.validDomainSuffixEx", "tileTitle(map)": { "name": "نام", "outbound": "خروجی در صورت تطابق", "rule_set": "URL مجموعه قوانین", "package_name": "نام‌های بسته", "process_name": "نام‌های فرآیند", "process_path": "مسیرهای فرآیند", "network": "شبکه‌ها", "port_range": "پورت‌های مقصد", "source_port_range": "پورت‌های مبدأ", "protocol": "پروتکل", "ip_cidr": "IP CIDR مقصد", "source_ip_cidr": "IP CIDR مبدأ", "domain": "دامنه", "domain_suffixe": "پسوند دامنه", "domain_keyword": "کلمه کلیدی دامنه", "domain_regex": "عبارت منظم دامنه" }, "outbound(map)": { "proxy": "پروکسی", "direct": "مستقیم", "direct_with_fragment": "مستقیم با فرگمنت", "block": "مسدود" }, "network(map)": { "all": "همه", "tcp": "TCP", "udp": "UDP" }, "protocol(map)": { "": "همه", "tls": "TLS", "http": "HTTP", "quic": "QUIC", "stun": "STUN", "dns": "DNS", "bittorrent": "BitTorrent" } }, "genericList": { "addNew": "افزودن مقدار جدید", "update": "به‌روزرسانی مقدار", "clearList": "پاک کردن لیست", "clearListMsg": "تمام موارد حذف شدند" }, "androidApps": { "pageTitle": "برنامه‌های اندروید", "showSystemApps": "نمایش برنامه‌های سیستمی", "hideSystemApps": "پنهان کردن برنامه‌های سیستمی", "clearSelection": "پاک کردن انتخاب‌ها", "uninstalled": "نصب نشده" } } }, "dns": { "title": "DNS", "remoteDns": "DNS اصلی (ریموت)", "remoteDnsDomainStrategy": "استراتژی دامنه DNS", "directDns": "DNS پروکسی‌ها (مستقیم)", "directDnsDomainStrategy": "استراتژی دامنه DNS مستقیم", "enableDnsRouting": "فعال‌سازی مسیریابی DNS", "enableFakeDns": "فعال‌سازی DNS جعلی", "domainStrategy": { "auto": "خودکار", "preferIpv6": "ترجیح IPv6", "preferIpv4": "ترجیح IPv4", "ipv4Only": "فقط IPv4", "ipv6Only": "فقط IPv6" } }, "inbound": { "title": "ورودی", "serviceMode": "حالت سرویس", "serviceModes": { "proxy": "فقط سرویس پروکسی", "systemProxy": "تنظیم پروکسی سیستم", "tun": "VPN", "tunService": "سرویس VPN" }, "shortServiceModes": { "proxy": "پروکسی", "systemProxy": "پروکسی سیستم", "tun": "VPN", "tunService": "سرویس VPN" }, "strictRoute": "مسیربندی سخت‌گیرانه", "tunImplementation": "پیاده‌سازی Tun", "tunImplementations": { "mixed": "ترکیبی", "system": "سیستم", "gvisor": "gVisor" }, "mixedPort": "پورت ترکیبی", "tproxyPort": "پورت پروکسی شفاف", "directPort": "پورت مستقیم", "redirectPort": "پورت تغییر مسیر", "allowConnectionFromLan": "اشتراک VPN در شبکه محلی" }, "tlsTricks": { "title": "ترفندهای TLS", "enable": "فعال‌سازی fragment", "packets": "بسته‌های Fragment", "packetsTlsHello": "TLS Hello", "packets1_1": "1-1", "packets1_2": "1-2", "packets1_3": "1-3", "packets1_4": "1-4", "packets1_5": "1-5", "size": "اندازه fragment", "sleep": "تأخیر fragment", "mixedSniCase": { "enable": "فعال‌سازی mixed SNI case" }, "padding": { "enable": "فعال‌سازی padding", "size": "اندازه padding" } }, "warp": { "title": "WARP", "enable": "فعال‌سازی WARP", "generateConfig": "ایجاد کانفیگ WARP", "configGenerated": "کانفیگ Warp ایجاد شد", "missingConfig": "کانفیگ WARP موجود نیست", "detourMode": "حالت مسیریابی WARP", "detourModes": { "proxyOverWarp": "عبور پروکسی‌ها از طریق WARP", "warpOverProxy": "عبور WARP از طریق پروکسی‌ها", "proxyOverWarpExplain": "رفع انسداد پروکسی‌ها با WARP", "warpOverProxyExplain": "امنیت بیشتر با WARP" }, "licenseKey": "کلید لایسنس", "cleanIp": "IP تمیز", "port": "پورت", "noise": { "count": "تعداد نویز", "mode": "حالت نویز", "size": "اندازه نویز", "delay": "تأخیر نویز" } } } }, "components": { "stats": { "connection": "اتصال", "traffic": "ترافیک", "trafficLive": "ترافیک لحظه‌ای", "trafficTotal": "ترافیک کل", "uplink": "ارسال", "downlink": "دریافت", "speed": "سرعت", "totalTransferred": "کل منتقل شده" }, "subscriptionInfo": { "upload": "آپلود", "download": "دانلود", "total": "کل ترافیک", "expireDate": "تاریخ انقضا", "expired": "منقضی شده", "noTraffic": "حجم تمام شده", "remainingTime": "زمان باقی‌مانده", "remainingDuration": "${duration} روز باقی‌مانده", "remainingDurationNew": "${duration} روز", "remainingTrafficSemanticLabel": "${consumed} از ${total} ترافیک مصرف شده", "remainingTraffic": "ترافیک باقی‌مانده", "remainingUsage": "باقی‌مانده", "profileSite": "سرویس‌دهنده", "profileSupport": "پشتیبانی" } }, "dialogs": { "sortProfiles": { "title": "مرتب‌سازی بر اساس", "sort": { "name": "بر اساس نام", "lastUpdate": "آخرین به‌روزرسانی" } }, "warpLicense": { "title": "رضایت‌نامه Cloudflare WARP", "description(rich)": "Cloudflare WARP یک ارائه‌دهنده رایگان WireGuard VPN است. با فعال کردن این گزینه، شما با ${tos(شرایط خدمات)} و ${privacy(سیاست حفظ حریم خصوصی)} Cloudflare WARP موافقت می‌کنید." }, "newVersion": { "title": "به‌روزرسانی موجود است", "msg": "نسخه جدیدی از @:common.appTitle در دسترس است. آیا مایل به به‌روزرسانی هستید؟", "currentVersion": "نسخه فعلی: ", "newVersion": "نسخه جدید: ", "updateNow": "اکنون به‌روزرسانی کن" }, "confirmation": { "settings": { "import": { "msg": "این عمل تمام تنظیمات فعلی شما را بازنویسی خواهد کرد. آیا مطمئن هستید؟" } }, "profile": { "delete": { "title": "حذف پروفایل", "msg": "آیا از حذف دائمی این پروفایل مطمئن هستید؟" } }, "perAppProxy": { "shareOnGithub": { "title": "بهبود انتخاب خودکار", "msg": "با اشتراک‌گذاری برنامه‌های انتخاب‌شده، به تکمیل لیست «انتخاب خودکار» کمک می‌کنید" }, "import": { "msg": "این عمل تمام انتخاب‌های فعلی شما برای پروکسی برنامه‌ها را جایگزین خواهد کرد. آیا مطمئن هستید؟" } }, "routeRule": { "delete": { "title": "حذف قانون", "msg": "آیا از حذف قانون «$rulename» مطمئن هستید؟" } } }, "experimentalNotice": { "title": "ویژگی‌های آزمایشی در حال استفاده", "msg": "شما برخی ویژگی‌های آزمایشی را فعال کرده‌اید که ممکن است بر کیفیت اتصال تأثیر گذاشته و باعث خطاهای غیرمنتظره شوند. همیشه می‌توانید این گزینه‌ها را از صفحه تنظیمات تغییر داده یا بازنشانی کنید.", "disable": "دیگر نمایش نده" }, "noActiveProfile": { "title": "یک پروفایل انتخاب کنید", "msg": "بیایید با افزودن یک پروفایل اتصال که شامل جزئیات اتصال VPN شماست، شروع کنیم.\n\nهنوز سرور VPN ندارید؟ نگران نباشید، با دنبال کردن راهنمای زیر می‌توانید به سرعت و رایگان یکی برای خودتان بسازید.", "helpBtn": { "label": "راهنمایی کن", "url": "https://hiddify.com/manager/" } }, "unknownDomainsWarning": { "title": "هشدار لینک خارجی", "youAreAboutToVisit": "شما در حال بازدید از این آدرس هستید:", "thisWebsiteIsNotInOurTrustedList": "این وب‌سایت در لیست مورد اعتماد ما نیست. لطفاً با احتیاط ادامه دهید." }, "proxyInfo": { "fullTag": "تگ کامل:", "type": "نوع:", "testTime": "زمان تست:", "testDelay": "تأخیر تست:", "isSelected": "انتخاب شده:", "isGroup": "گروه است", "isSecure": "امن است:", "port": "پورت:", "host": "میزبان:", "ip": "IP:", "countryCode": "کد کشور:", "region": "منطقه:", "city": "شهر:", "asn": "ASN:", "organization": "سازمان:", "location": "مکان:", "postalCode": "کد پستی:" }, "windowClosing": { "askEachTime": "هر بار بپرس", "alertMessage": "پنهان کردن یا خروج از برنامه؟", "remember": "انتخابم را به خاطر بسپار" } }, "connection": { "tapToConnect": "برای اتصال ضربه بزنید", "connect": "اتصال", "connecting": "در حال اتصال...", "connected": "متصل شد", "disconnect": "قطع اتصال", "disconnecting": "در حال قطع اتصال...", "reconnect": "اتصال مجدد", "reconnectMsg": "در حال اتصال مجدد برای اعمال تغییرات...", "secure": "ایمن شده با WARP" }, "errors": { "unexpected": "خطای غیرمنتظره", "connection": { "unexpected": "خطای غیرمنتظره در اتصال", "timeout": "اتصال با وقفه زمانی مواجه شد", "badResponse": "پاسخ نامعتبر", "connectionError": "خطای اتصال", "badCertificate": "گواهی‌نامه نامعتبر" }, "profiles": { "unexpected": "خطای غیرمنتظره", "notFound": "پروفایل یافت نشد", "invalidConfig": "کانفیگ‌های نامعتبر", "invalidUrl": "URL نامعتبر", "canceledByUser": "توسط کاربر لغو شد" }, "connectivity": { "unexpected": "شکست غیرمنتظره", "missingVpnPermission": "مجوز VPN وجود ندارد", "missingNotificationPermission": "مجوز اعلان وجود ندارد", "core": "خطای هسته" }, "singbox": { "serviceNotRunning": "سرویس در حال اجرا نیست", "missingPrivilege": "دسترسی وجود ندارد", "missingPrivilegeMsg": "حالت VPN به دسترسی ادمین نیاز دارد. یا برنامه را به عنوان ادمین مجدداً راه‌اندازی کنید یا حالت سرویس را تغییر دهید.", "invalidConfigOptions": "گزینه‌های پیکربندی نامعتبر", "invalidConfig": "پیکربندی نامعتبر" }, "warp": { "missingLicense": "لایسنس Warp", "missingLicenseMsg": "پروفایل انتخاب‌شده از ویژگی WARP استفاده می‌کند؛ برای استفاده از این قابلیت، باید با لایسنس WARP موافقت شود." } } } ================================================ FILE: assets/translations/fr.i18n.json ================================================ { "common": { "appTitle": "Hiddify", "start": "Commencer", "version": "Version", "ok": "OK", "cancel": "Annuler", "kContinue": "Continuer", "showMore": "Afficher plus", "showLess": "Afficher moins", "filter": "Filtrer", "all": "Tous", "pause": "Pause", "resume": "Reprendre", "clear": "Effacer", "close": "Fermer", "auto": "Automatique", "manually": "Manuellement", "name": "Nom", "url": "URL", "add": "Ajouter", "clipboard": "Presse-papiers", "addToClipboard": "Ajouter au presse-papiers", "scanQr": "Scanner le QR code", "free": "Gratuit", "warp": "WARP", "fragment": "Fragment", "help": "Aide", "save": "Enregistrer", "update": "Mettre à jour", "share": "Partager", "edit": "Modifier", "delete": "Supprimer", "discard": "Ignorer", "import": "Importer", "export": "Exporter", "later": "Plus tard", "ignore": "Ignorer", "quit": "Quitter", "notSet": "Non défini", "hide": "Masquer", "exit": "Quitter", "reset": "Réinitialiser", "done": "Terminé", "search": "Rechercher", "decline": "Refuser", "agree": "Accepter", "empty": "Vide", "unknown": "Inconnu", "hidden": "Caché", "timeout": "Délai expiré", "sort": "Trier", "dashboard": "Tableau de bord", "interval": { "day": { "zero": "", "one": "$n jour", "other": "$n jours" }, "hour": { "zero": "", "one": "$n heure", "other": "$n heures" } }, "msg": { "permission": { "denied": "Permission refusée" }, "export": { "clipboard": { "success": "Ajouté au presse-papiers avec succès", "failure": "Échec de la copie dans le presse-papiers", "contentTooLarge": "Contenu trop volumineux. Utilisez plutôt l'exportation de fichier" }, "file": { "success": "Fichier JSON créé avec succès", "failure": "Échec de la création du fichier" } }, "import": { "confirm": "Confirmer l'importation", "success": "Importé avec succès", "failure": "Échec de l'importation" } } }, "intro": { "banner": "Tout ce dont vous avez besoin pour un internet sans restrictions", "termsAndPolicyCaution(rich)": "En continuant, vous acceptez les ${tap(@:pages.about.termsAndConditions)}", "info(rich)": "Fait avec ❤️ par Hiddify - ${tap_source(Open Source)} (${tap_license(Licence)})" }, "pages": { "home": { "title": "Accueil", "quickSettings": "Réglages rapides" }, "proxies": { "title": "Proxys", "sort": "Trier les proxys", "testDelay": "Tester le délai", "empty": "Aucun proxy disponible", "activeProxy": "Proxy actif", "unknownIp": "IP inconnue", "sortOptions": { "unsorted": "Par défaut", "name": "Alphabétiquement", "delay": "Par latence" }, "ipInfo": { "address": "Adresse IP", "country": "Pays", "organization": "Organisation" }, "delay": { "result": "Latence : ${delay} ms", "timeout": "Délai du test de latence expiré", "testing": "Latence : test en cours..." } }, "profiles": { "title": "Profils", "add": "Ajouter un profil", "update": "Mettre à jour le profil", "viewAllProfiles": "Voir tous les profils", "activeProfileName": "Nom du profil actif : \"${name}\".", "nonActiveProfileName": "Sélectionner \"${name}\" comme profil actif", "freeSubNotFound": "Aucun abonnement gratuit trouvé", "freeSubNotFoundForRegion": "Aucun abonnement gratuit trouvé pour la région \"${region}\"", "failedToLoad": "Échec du chargement", "updateSubscriptions": "Mettre à jour les abonnements", "share": { "urlToClipboard": "URL dans le presse-papiers", "showUrlQr": "Afficher le QR code de l'URL", "jsonToClipboard": "JSON dans le presse-papiers" }, "msg": { "save": { "success": "Profil enregistré avec succès" }, "invalidUrl": "URL invalide", "add": { "failure": "Échec de l'ajout du profil" }, "update": { "success": "Profil mis à jour avec succès", "successNamed": "\"${name}\" a été mis à jour avec succès", "failure": "Échec de la mise à jour du profil", "failureNamed": "Échec de la mise à jour de \"${name}\"" }, "delete": { "success": "Profil supprimé avec succès" } } }, "profileDetails": { "title": "Profil", "lastUpdate": "Dernière mise à jour", "form": { "nameHint": "Nom du profil", "emptyName": "Le nom est requis", "invalidUrl": "URL invalide", "urlHint": "URL complète de la configuration", "disableAutoUpdate": "Désactiver la mise à jour automatique", "autoUpdateInterval": "Intervalle de mise à jour automatique", "loading": "Ajout du profil..." } }, "logs": { "title": "Journaux", "shareCoreLogs": "Partager les journaux du noyau", "shareAppLogs": "Partager les journaux de l'application" }, "about": { "title": "À propos", "notAvailableMsg": "Vous utilisez déjà la dernière version", "checkForUpdate": "Vérifier les mises à jour", "openWorkingDir": "Ouvrir le répertoire de travail", "sourceCode": "Code source", "telegramChannel": "Canal Telegram", "termsAndConditions": "Conditions d'utilisation", "privacyPolicy": "Politique de confidentialité" }, "settings": { "title": "Paramètres", "resetTunnel": "Réinitialiser le profil VPN", "options": { "import": { "clipboard": "Importer les options depuis le presse-papiers", "file": "Importer les options depuis un fichier" }, "export": { "anonymousToClipboard": "Copier les options anonymes dans le presse-papiers", "anonymousToFile": "Exporter les options anonymes vers un fichier", "allToClipboard": "Copier toutes les options dans le presse-papiers", "allToFile": "Exporter toutes les options vers un fichier" }, "reset": "Réinitialiser les options" }, "general": { "title": "Général", "locale": "Langue", "themeMode": "Thème", "themeModes": { "system": "Thème du système par défaut", "dark": "Mode sombre", "light": "Mode clair", "black": "Mode noir" }, "enableAnalytics": "Activer les statistiques", "enableAnalyticsMsg": "Autoriser la collecte de statistiques et de rapports d'erreurs pour améliorer l'application", "autoIpCheck": "Vérifier automatiquement l'IP de connexion", "dynamicNotification": "Afficher la vitesse dans la notification", "hapticFeedback": "Retour haptique", "actionAtClosing": "Action à la fermeture", "autoStart": "Lancer au démarrage", "silentStart": "Démarrer réduit", "ignoreBatteryOptimizations": "Désactiver l'optimisation de la batterie", "ignoreBatteryOptimizationsMsg": "Supprimer les restrictions pour une performance VPN optimale", "memoryLimit": "Limite de mémoire", "memoryLimitMsg": "Activer si vous rencontrez des erreurs de mémoire insuffisante ou des plantages fréquents de l'application", "debugMode": "Mode de débogage", "debugModeMsg": "Redémarrez l'application pour appliquer cette modification", "logLevel": "Niveau de journalisation", "connectionTestUrl": "URL de test de connexion", "urlTestInterval": "Intervalle de test de l'URL", "clashApiPort": "Port de l'API Clash", "useXrayCoreWhenPossible": "Utiliser xray-core si possible", "useXrayCoreWhenPossibleMsg": "Utilisez xray-core lors de l'analyse des liens d'abonnement. Vous devez réimporter le lien pour activer cette option" }, "routing": { "title": "Routage", "perAppProxy": { "title": "Proxy par application", "hideSysApps": "Masquer les applications système", "options": { "import": { "clipboard": "Importer la sélection depuis le presse-papiers", "file": "Importer la sélection depuis un fichier", "msg": "L'importation remplacera vos sélections actuelles. Êtes-vous sûr de vouloir continuer ?" }, "export": { "clipboard": "Copier la sélection dans le presse-papiers", "file": "Exporter la sélection vers un fichier" }, "shareToAll": "Partager avec tous", "clearAllSelections": "Effacer toutes les sélections" }, "modes": { "all": "Toutes", "proxy": "Proxy", "bypass": "Contourner", "allMsg": "Utiliser le proxy pour toutes les applications", "proxyMsg": "Utiliser le proxy uniquement pour les applications sélectionnées", "bypassMsg": "Ne pas utiliser le proxy pour les applications sélectionnées" }, "autoSelection": { "title": "Sélection automatique", "performNow": "Exécuter maintenant", "resetToDefault": "Réinitialiser par défaut", "autoUpdateInterval": "Intervalle de mise à jour automatique", "toast": { "success": "Sélection automatique des applications terminée avec succès", "failure": "Échec de la sélection automatique", "regionNotFound": "Sélection automatique non trouvée pour la région \"${region}\"", "alreadyInAuto": "Vos sélections sont déjà dans la liste automatique" }, "dialog": { "title": "Sélection automatique des applications", "msg": "La fonction de sélection automatique pour le proxy par application a été désactivée en raison du changement de région vers \"${region}\"" } } }, "region": "Région", "regions": { "ir": "Iran (ir)", "cn": "Chine (cn)", "ru": "Russie (ru)", "af": "Afghanistan (af)", "id": "Indonésie (id)", "tr": "Turquie (tr)", "br": "Brésil (br)", "other": "Autre" }, "balancerStrategy": { "title": "Stratégie de Balancer", "roundRobin": "Round robin", "consistentHash": "Consistent hash", "stickySession": "Sticky session" }, "blockAds": "Bloquer les publicités", "bypassLan": "Contourner le LAN", "resolveDestination": "Résoudre la destination", "ipv6Route": "Route IPv6", "ipv6Modes": { "disable": "Désactiver", "enable": "Activer", "prefer": "Préféré", "only": "Uniquement" }, "routeRule": { "title": "Règles de routage", "options": { "import": { "clipboard": "Importer les règles depuis le presse-papiers", "file": "Importer les règles depuis un fichier" }, "export": { "clipboard": "Copier les règles dans le presse-papiers", "file": "Enregistrer les règles dans un fichier" }, "reset": "Réinitialiser les règles" }, "deleteRule": "Supprimer la règle", "createRule": "Créer une nouvelle règle", "rule": { "title": "Règle", "ruleChanged": "Règle modifiée", "ruleChangedMsg": "Voulez-vous enregistrer vos modifications ?", "onlyTunMode": "Disponible uniquement en mode TUN", "notAvailabeInThisPlatform": "Non disponible sur cette plateforme", "canNotBeEmpty": "Ne peut pas être vide", "validUrlEx": "https://example.com", "validUrl": "\"URL\" valide comme\n@:pages.settings.routing.routeRule.rule.validUrlEx", "validProcessNameEx": "Chrome.exe ou google chrome ou chrome", "validProcessName": "\"Nom de processus\" valide comme\n@:pages.settings.routing.routeRule.rule.validProcessNameEx", "validProcessPathEx": "C:\\Pro...\\chrome.exe\n/App.../Google Chrome\n/usr/lib.../chrome", "validProcessPath": "\"Chemin de processus\" valide comme\n@:pages.settings.routing.routeRule.rule.validProcessPathEx", "validPortRangeEx": "80 ou 1-65000", "validPortRange": "\"Port\" ou \"Plage de ports\" valide comme\n@:pages.settings.routing.routeRule.rule.validPortRangeEx", "validIpCidrEx": "8.8.8.8 ou 10.0.0.0/24", "validIpCidr": "IP CIDR valide comme\n@:pages.settings.routing.routeRule.rule.validIpCidrEx", "validDomainEx": "Google.com ou dl.google.com", "validDomain": "\"Domaine\" valide comme\n@:pages.settings.routing.routeRule.rule.validDomainEx", "validDomainSuffixEx": ".com ou .ir", "validDomainSuffix": "\"Suffixe de domaine\" valide comme\n@:pages.settings.routing.routeRule.rule.validDomainSuffixEx", "tileTitle(map)": { "name": "Nom", "outbound": "Sortie si correspondance", "rule_set": "URL de l'ensemble de règles", "package_name": "Noms de paquets", "process_name": "Noms de processus", "process_path": "Chemins de processus", "network": "Réseaux", "port_range": "Ports de destination", "source_port_range": "Ports source", "protocol": "Protocole", "ip_cidr": "IP CIDR de destination", "source_ip_cidr": "IP CIDR source", "domain": "Domaine", "domain_suffixe": "Suffixe de domaine", "domain_keyword": "Mot-clé de domaine", "domain_regex": "Expression régulière de domaine" }, "outbound(map)": { "proxy": "Proxy", "direct": "Direct", "direct_with_fragment": "Direct avec fragment", "block": "Bloquer" }, "network(map)": { "all": "Tous", "tcp": "TCP", "udp": "UDP" }, "protocol(map)": { "": "Tous", "tls": "TLS", "http": "HTTP", "quic": "QUIC", "stun": "STUN", "dns": "DNS", "bittorrent": "BitTorrent" } }, "genericList": { "addNew": "Ajouter une nouvelle valeur", "update": "Mettre à jour la valeur", "clearList": "Vider la liste", "clearListMsg": "Tous les éléments ont été supprimés" }, "androidApps": { "pageTitle": "Applications Android", "showSystemApps": "Afficher les applications système", "hideSystemApps": "Masquer les applications système", "clearSelection": "Effacer la sélection", "uninstalled": "Désinstallé" } } }, "dns": { "title": "DNS", "remoteDns": "DNS distant", "remoteDnsDomainStrategy": "Stratégie de domaine DNS distant", "directDns": "Résolveur de serveur sortant (direct)", "directDnsDomainStrategy": "Stratégie de domaine sortant", "enableDnsRouting": "Activer le routage DNS", "enableFakeDns": "Activer le faux DNS", "domainStrategy": { "auto": "Auto", "preferIpv6": "Préférer IPv6", "preferIpv4": "Préférer IPv4", "ipv4Only": "IPv4 uniquement", "ipv6Only": "IPv6 uniquement" } }, "inbound": { "title": "Entrant", "serviceMode": "Mode de service", "serviceModes": { "proxy": "Service proxy uniquement", "systemProxy": "Définir le proxy système", "tun": "VPN", "tunService": "Service VPN" }, "shortServiceModes": { "proxy": "Proxy", "systemProxy": "Proxy système", "tun": "VPN", "tunService": "Service VPN" }, "strictRoute": "Routage strict", "tunImplementation": "Implémentation TUN", "tunImplementations": { "mixed": "Mixte", "system": "Système", "gvisor": "gVisor" }, "mixedPort": "Port mixte", "tproxyPort": "Port de proxy transparent", "directPort": "Port direct local", "redirectPort": "Port de redirection", "allowConnectionFromLan": "Partager le VPN sur le réseau local" }, "tlsTricks": { "title": "Astuces TLS", "enable": "Activer le fragment", "packets": "Paquets de fragmentation", "packetsTlsHello": "TLS Hello", "packets1_1": "1-1", "packets1_2": "1-2", "packets1_3": "1-3", "packets1_4": "1-4", "packets1_5": "1-5", "size": "Taille du fragment", "sleep": "Délai du fragment", "mixedSniCase": { "enable": "Activer la casse mixte pour SNI" }, "padding": { "enable": "Activer le remplissage", "size": "Taille du remplissage" } }, "warp": { "title": "WARP", "enable": "Activer WARP", "generateConfig": "Générer la configuration WARP", "configGenerated": "Configuration WARP générée", "missingConfig": "Configuration WARP manquante", "detourMode": "Mode de détour", "detourModes": { "proxyOverWarp": "Détourner les proxys via WARP", "warpOverProxy": "Détourner WARP via les proxys", "proxyOverWarpExplain": "Débloquer les proxys avec WARP", "warpOverProxyExplain": "Sécurité supplémentaire avec WARP" }, "licenseKey": "Clé de licence", "cleanIp": "IP propre", "port": "Port", "noise": { "count": "Nombre de bruits", "mode": "Mode bruit", "size": "Taille du bruit", "delay": "Délai du bruit" } } } }, "components": { "stats": { "connection": "Connexion", "traffic": "Trafic", "trafficLive": "Trafic en direct", "trafficTotal": "Trafic total", "uplink": "Envoi", "downlink": "Réception", "speed": "Vitesse", "totalTransferred": "Total transféré" }, "subscriptionInfo": { "upload": "Envoi", "download": "Téléchargement", "total": "Trafic total", "expireDate": "Date d'expiration", "expired": "Expiré", "noTraffic": "Quota épuisé", "remainingTime": "Temps restant", "remainingDuration": "${duration} jours restants", "remainingDurationNew": "${duration} jours", "remainingTrafficSemanticLabel": "${consumed} sur ${total} de trafic consommé", "remainingTraffic": "Trafic restant", "remainingUsage": "Restant", "profileSite": "Fournisseur", "profileSupport": "Support" } }, "dialogs": { "sortProfiles": { "title": "Trier par", "sort": { "name": "Alphabétiquement", "lastUpdate": "Dernière mise à jour" } }, "warpLicense": { "title": "Consentement Cloudflare WARP", "description(rich)": "Cloudflare WARP est un fournisseur VPN WireGuard gratuit. En activant cette option, vous acceptez les ${tos(Conditions d'utilisation)} et la ${privacy(Politique de confidentialité)} de Cloudflare WARP." }, "newVersion": { "title": "Mise à jour disponible", "msg": "Une nouvelle version de @:common.appTitle est disponible. Voulez-vous mettre à jour maintenant ?", "currentVersion": "Version actuelle : ", "newVersion": "Nouvelle version : ", "updateNow": "Mettre à jour" }, "confirmation": { "settings": { "import": { "msg": "Cette action remplacera toutes les options de configuration par les valeurs fournies. Êtes-vous sûr ?" } }, "profile": { "delete": { "title": "Supprimer le profil", "msg": "Êtes-vous sûr de vouloir supprimer ce profil définitivement ?" } }, "perAppProxy": { "shareOnGithub": { "title": "Améliorer la sélection automatique", "msg": "En partageant les applications sélectionnées, vous aidez à compléter la liste de \"sélection automatique\"" }, "import": { "msg": "Cette action remplacera toutes vos sélections actuelles de proxy par application. Êtes-vous sûr de vouloir continuer ?" } }, "routeRule": { "delete": { "title": "Supprimer la règle", "msg": "Êtes-vous sûr de vouloir supprimer la règle \"$rulename\" ?" } } }, "experimentalNotice": { "title": "Fonctionnalités expérimentales en cours d'utilisation", "msg": "Vous avez activé des fonctionnalités expérimentales qui pourraient affecter la qualité de la connexion et causer des erreurs inattendues. Vous pouvez toujours modifier ou réinitialiser ces options depuis la page de configuration.", "disable": "Ne plus afficher" }, "noActiveProfile": { "title": "Choisissez un profil", "msg": "Pour commencer, ajoutez un profil de connexion qui inclut les détails de votre connexion VPN.\n\nVous n'avez pas encore de serveur VPN ? Pas de problème, suivez le tutoriel ci-dessous pour en configurer un rapidement et gratuitement.", "helpBtn": { "label": "Montrez-moi comment", "url": "https://hiddify.com/manager/" } }, "unknownDomainsWarning": { "title": "Avertissement de lien externe", "youAreAboutToVisit": "Vous êtes sur le point de visiter :", "thisWebsiteIsNotInOurTrustedList": "Ce site web ne figure pas dans notre liste de confiance. Veuillez procéder avec prudence." }, "proxyInfo": { "fullTag": "Tag complet :", "type": "Type :", "testTime": "Heure du test :", "testDelay": "Délai du test :", "isSelected": "Sélectionné :", "isGroup": "Est un groupe", "isSecure": "Est sécurisé :", "port": "Port :", "host": "Hôte :", "ip": "IP :", "countryCode": "Code pays :", "region": "Région :", "city": "Ville :", "asn": "ASN :", "organization": "Organisation :", "location": "Emplacement :", "postalCode": "Code postal :" }, "windowClosing": { "askEachTime": "Demander à chaque fois", "alertMessage": "Masquer ou quitter l'application ?", "remember": "Mémoriser mon choix" } }, "connection": { "tapToConnect": "Appuyez pour vous connecter", "connect": "Connecter", "connecting": "Connexion en cours...", "connected": "Connecté", "disconnect": "Déconnecter", "disconnecting": "Déconnexion en cours...", "reconnect": "Reconnecter", "reconnectMsg": "Reconnexion pour prendre en compte les changements...", "secure": "Sécurisé par WARP" }, "errors": { "unexpected": "Erreur inattendue", "connection": { "unexpected": "Erreur de connexion inattendue", "timeout": "Délai de connexion expiré", "badResponse": "Mauvaise réponse", "connectionError": "Erreur de connexion", "badCertificate": "Certificat invalide" }, "profiles": { "unexpected": "Erreur inattendue", "notFound": "Profil non trouvé", "invalidConfig": "Configurations invalides", "invalidUrl": "URL invalide", "canceledByUser": "Annulé par l'utilisateur" }, "connectivity": { "unexpected": "Échec inattendu", "missingVpnPermission": "Autorisation VPN manquante", "missingNotificationPermission": "Autorisation de notification manquante", "core": "Erreur du noyau" }, "singbox": { "serviceNotRunning": "Le service n'est pas en cours d'exécution", "missingPrivilege": "Autorisation manquante", "missingPrivilegeMsg": "Le mode VPN nécessite des droits d'administrateur. Veuillez relancer l'application en tant qu'administrateur ou changer le mode de service.", "invalidConfigOptions": "Options de configuration invalides", "invalidConfig": "Configuration invalide" }, "warp": { "missingLicense": "Licence WARP manquante", "missingLicenseMsg": "Le profil sélectionné utilise la fonctionnalité WARP. Pour utiliser cette fonctionnalité, vous devez accepter la licence WARP." } } } ================================================ FILE: assets/translations/id.i18n.json ================================================ { "common": { "appTitle": "Hiddify", "start": "Mulai", "version": "Versi", "ok": "Oke", "cancel": "Batal", "kContinue": "Lanjutkan", "showMore": "Tampilkan lebih banyak", "showLess": "Tampilkan lebih sedikit", "filter": "Filter", "all": "Semua", "pause": "Jeda", "resume": "Lanjutkan", "clear": "Bersihkan", "close": "Tutup", "auto": "Otomatis", "manually": "Manual", "name": "Nama", "url": "URL", "add": "Tambah", "clipboard": "Papan klip", "addToClipboard": "Tambah ke papan klip", "scanQr": "Pindai QR", "free": "Gratis", "warp": "WARP", "fragment": "Fragmen", "help": "Bantuan", "save": "Simpan", "update": "Perbarui", "share": "Bagikan", "edit": "Edit", "delete": "Hapus", "discard": "Buang", "import": "Impor", "export": "Ekspor", "later": "Nanti", "ignore": "Abaikan", "quit": "Keluar", "notSet": "Belum diatur", "hide": "Sembunyikan", "exit": "Keluar", "reset": "Setel ulang", "done": "Selesai", "search": "Cari", "decline": "Tolak", "agree": "Setuju", "empty": "Kosong", "unknown": "Tidak dikenal", "hidden": "Tersembunyi", "timeout": "Waktu habis", "sort": "Urutkan", "dashboard": "Dasbor", "interval": { "day": { "zero": "", "one": "$n hari", "other": "$n hari" }, "hour": { "zero": "", "one": "$n jam", "other": "$n jam" } }, "msg": { "permission": { "denied": "Izin ditolak" }, "export": { "clipboard": { "success": "Berhasil ditambahkan ke papan klip", "failure": "Gagal menyalin ke papan klip", "contentTooLarge": "Konten terlalu besar. Gunakan ekspor file saja" }, "file": { "success": "File JSON berhasil dibuat", "failure": "Gagal membuat file" } }, "import": { "confirm": "Konfirmasi impor", "success": "Berhasil diimpor", "failure": "Gagal mengimpor" } } }, "intro": { "banner": "Semua yang Anda butuhkan untuk internet tanpa batasan", "termsAndPolicyCaution(rich)": "Dengan melanjutkan, Anda menyetujui ${tap(@:pages.about.termsAndConditions)}", "info(rich)": "Dibuat dengan ❤️ oleh Hiddify - ${tap_source(Sumber Terbuka)} (${tap_license(Lisensi)})" }, "pages": { "home": { "title": "Beranda", "quickSettings": "Pengaturan cepat" }, "proxies": { "title": "Proxy", "sort": "Urutkan proksi", "testDelay": "Uji latensi", "empty": "Tidak ada proksi yang tersedia", "activeProxy": "Proksi aktif", "unknownIp": "IP tidak dikenal", "sortOptions": { "unsorted": "Default", "name": "Berdasarkan abjad", "delay": "Berdasarkan latensi" }, "ipInfo": { "address": "Alamat IP", "country": "Negara", "organization": "Organisasi" }, "delay": { "result": "Latensi: ${delay}ms", "timeout": "Waktu tes latensi habis", "testing": "Latensi: sedang menguji..." } }, "profiles": { "title": "Profil", "add": "Tambah profil", "update": "Perbarui profil", "viewAllProfiles": "Lihat semua profil", "activeProfileName": "Nama profil aktif: \"${name}\".", "nonActiveProfileName": "Pilih \"${name}\" sebagai profil aktif", "freeSubNotFound": "Tidak ada langganan gratis yang ditemukan", "freeSubNotFoundForRegion": "Tidak ada langganan gratis yang ditemukan untuk wilayah \"${region}\"", "failedToLoad": "Gagal memuat", "updateSubscriptions": "Perbarui langganan", "share": { "urlToClipboard": "URL ke papan klip", "showUrlQr": "Tampilkan QR URL", "jsonToClipboard": "JSON ke papan klip" }, "msg": { "save": { "success": "Profil berhasil disimpan" }, "invalidUrl": "URL tidak valid", "add": { "failure": "Gagal menambahkan profil" }, "update": { "success": "Profil berhasil diperbarui", "successNamed": "\"${name}\" berhasil diperbarui", "failure": "Gagal memperbarui profil", "failureNamed": "Gagal memperbarui \"${name}\"" }, "delete": { "success": "Profil berhasil dihapus" } } }, "profileDetails": { "title": "Profil", "lastUpdate": "Terakhir diperbarui", "form": { "nameHint": "Nama profil", "emptyName": "Nama wajib diisi", "invalidUrl": "URL tidak valid", "urlHint": "URL konfigurasi lengkap", "disableAutoUpdate": "Nonaktifkan pembaruan otomatis", "autoUpdateInterval": "Interval pembaruan otomatis", "loading": "Menambahkan profil..." } }, "logs": { "title": "Log", "shareCoreLogs": "Bagikan log inti", "shareAppLogs": "Bagikan log aplikasi" }, "about": { "title": "Tentang", "notAvailableMsg": "Anda sudah menggunakan versi terbaru", "checkForUpdate": "Periksa pembaruan", "openWorkingDir": "Buka direktori kerja", "sourceCode": "Kode sumber", "telegramChannel": "Saluran Telegram", "termsAndConditions": "Syarat dan Ketentuan", "privacyPolicy": "Kebijakan Privasi" }, "settings": { "title": "Pengaturan", "resetTunnel": "Setel ulang profil VPN", "options": { "import": { "clipboard": "Impor opsi dari papan klip", "file": "Impor opsi dari file" }, "export": { "anonymousToClipboard": "Salin opsi anonim ke papan klip", "anonymousToFile": "Ekspor opsi anonim ke file", "allToClipboard": "Salin semua opsi ke papan klip", "allToFile": "Ekspor semua opsi ke file" }, "reset": "Setel ulang opsi" }, "general": { "title": "Umum", "locale": "Bahasa", "themeMode": "Tema", "themeModes": { "system": "Default sistem", "dark": "Mode gelap", "light": "Mode terang", "black": "Mode hitam" }, "enableAnalytics": "Aktifkan analitik", "enableAnalyticsMsg": "Berikan izin untuk mengumpulkan analitik dan mengirim laporan kerusakan untuk meningkatkan aplikasi", "autoIpCheck": "Periksa IP koneksi secara otomatis", "dynamicNotification": "Tampilkan kecepatan di notifikasi", "hapticFeedback": "Umpan balik haptik", "actionAtClosing": "Tindakan saat menutup", "autoStart": "Mulai saat masuk", "silentStart": "Mulai diminimalkan", "ignoreBatteryOptimizations": "Nonaktifkan optimasi baterai", "ignoreBatteryOptimizationsMsg": "Hapus batasan untuk kinerja VPN yang optimal", "memoryLimit": "Batas memori", "memoryLimitMsg": "Aktifkan jika Anda mengalami kesalahan kehabisan memori atau aplikasi sering macet", "debugMode": "Mode debug", "debugModeMsg": "Mulai ulang aplikasi untuk menerapkan perubahan ini", "logLevel": "Level log", "connectionTestUrl": "URL uji koneksi", "urlTestInterval": "Interval uji URL", "clashApiPort": "Port API Clash", "useXrayCoreWhenPossible": "Gunakan xray-core jika memungkinkan", "useXrayCoreWhenPossibleMsg": "Gunakan xray-core saat mengurai tautan langganan. Anda perlu mengimpor ulang tautan untuk mengaktifkan opsi ini." }, "routing": { "title": "Perutean", "perAppProxy": { "title": "Proxy per aplikasi", "hideSysApps": "Sembunyikan aplikasi sistem", "options": { "import": { "clipboard": "Impor pilihan dari papan klip", "file": "Impor pilihan dari file", "msg": "Mengimpor akan menggantikan pilihan Anda saat ini. Anda yakin ingin melanjutkan?" }, "export": { "clipboard": "Salin pilihan ke papan klip", "file": "Ekspor pilihan ke file" }, "shareToAll": "Bagikan ke semua", "clearAllSelections": "Hapus semua pilihan" }, "modes": { "all": "Semua", "proxy": "Proxy", "bypass": "Lewati", "allMsg": "Proxy semua aplikasi", "proxyMsg": "Proxy hanya aplikasi yang dipilih", "bypassMsg": "Jangan proxy aplikasi yang dipilih" }, "autoSelection": { "title": "Pilihan otomatis", "performNow": "Lakukan sekarang", "resetToDefault": "Setel ulang ke default", "autoUpdateInterval": "Interval pembaruan otomatis", "toast": { "success": "Pilihan aplikasi otomatis berhasil diselesaikan", "failure": "Pilihan otomatis gagal", "regionNotFound": "Pilihan otomatis tidak ditemukan untuk wilayah \"${region}\"", "alreadyInAuto": "Pilihan Anda sudah ada dalam daftar otomatis" }, "dialog": { "title": "Pilihan aplikasi otomatis", "msg": "Fitur pilihan otomatis untuk proxy per aplikasi dinonaktifkan karena perubahan wilayah menjadi \"${region}\"" } } }, "region": "Wilayah", "regions": { "ir": "Iran (ir)", "cn": "Tiongkok (cn)", "ru": "Rusia (ru)", "af": "Afghanistan (af)", "id": "Indonesia (id)", "tr": "Turki (tr)", "br": "Brasil (br)", "other": "Lainnya" }, "balancerStrategy": { "title": "Strategi Balancer", "roundRobin": "Round robin", "consistentHash": "Consistent hash", "stickySession": "Sticky session" }, "blockAds": "Blokir iklan", "bypassLan": "Lewati LAN", "resolveDestination": "Resolusi tujuan", "ipv6Route": "Rute IPv6", "ipv6Modes": { "disable": "Nonaktifkan", "enable": "Aktifkan", "prefer": "Prioritaskan", "only": "Hanya" }, "routeRule": { "title": "Aturan perutean", "options": { "import": { "clipboard": "Impor aturan dari papan klip", "file": "Impor aturan dari file" }, "export": { "clipboard": "Salin aturan ke papan klip", "file": "Simpan aturan ke file" }, "reset": "Setel ulang aturan" }, "deleteRule": "Hapus aturan", "createRule": "Buat aturan baru", "rule": { "title": "Aturan", "ruleChanged": "Aturan diubah", "ruleChangedMsg": "Apakah Anda ingin menyimpan editan Anda?", "onlyTunMode": "Hanya tersedia dalam mode TUN", "notAvailabeInThisPlatform": "Tidak tersedia di platform ini", "canNotBeEmpty": "Tidak boleh kosong", "validUrlEx": "https://example.com", "validUrl": "\"URL\" yang valid seperti\n@:pages.settings.routing.routeRule.rule.validUrlEx", "validProcessNameEx": "Chrome.exe atau google chrome atau chrome", "validProcessName": "\"Nama Proses\" yang valid seperti\n@:pages.settings.routing.routeRule.rule.validProcessNameEx", "validProcessPathEx": "C:\\Pro...\\chrome.exe\n/App.../Google Chrome\n/usr/lib.../chrome", "validProcessPath": "\"Jalur Proses\" yang valid seperti\n@:pages.settings.routing.routeRule.rule.validProcessPathEx", "validPortRangeEx": "80 atau 1-65000", "validPortRange": "\"Port\" atau \"Rentang Port\" yang valid seperti\n@:pages.settings.routing.routeRule.rule.validPortRangeEx", "validIpCidrEx": "8.8.8.8 atau 10.0.0.0/24", "validIpCidr": "IP CIDR yang valid seperti\n@:pages.settings.routing.routeRule.rule.validIpCidrEx", "validDomainEx": "Google.com atau dl.google.com", "validDomain": "\"Domain\" yang valid seperti\n@:pages.settings.routing.routeRule.rule.validDomainEx", "validDomainSuffixEx": ".com atau .ir", "validDomainSuffix": "\"Sufiks Domain\" yang valid seperti\n@:pages.settings.routing.routeRule.rule.validDomainSuffixEx", "tileTitle(map)": { "name": "Nama", "outbound": "Keluar jika cocok", "rule_set": "URL set aturan", "package_name": "Nama paket", "process_name": "Nama proses", "process_path": "Jalur proses", "network": "Jaringan", "port_range": "Port tujuan", "source_port_range": "Port sumber", "protocol": "Protokol", "ip_cidr": "IP CIDR tujuan", "source_ip_cidr": "IP CIDR sumber", "domain": "Domain", "domain_suffixe": "Sufiks domain", "domain_keyword": "Kata kunci domain", "domain_regex": "Regex domain" }, "outbound(map)": { "proxy": "Proxy", "direct": "Langsung", "direct_with_fragment": "Langsung dengan fragmen", "block": "Blokir" }, "network(map)": { "all": "Semua", "tcp": "TCP", "udp": "UDP" }, "protocol(map)": { "": "Semua", "tls": "TLS", "http": "HTTP", "quic": "QUIC", "stun": "STUN", "dns": "DNS", "bittorrent": "BitTorrent" } }, "genericList": { "addNew": "Tambah nilai baru", "update": "Perbarui nilai", "clearList": "Bersihkan daftar", "clearListMsg": "Semua item dihapus" }, "androidApps": { "pageTitle": "Aplikasi Android", "showSystemApps": "Tampilkan aplikasi sistem", "hideSystemApps": "Sembunyikan aplikasi sistem", "clearSelection": "Hapus pilihan", "uninstalled": "Dihapus" } } }, "dns": { "title": "DNS", "remoteDns": "DNS jarak jauh", "remoteDnsDomainStrategy": "Strategi domain DNS jarak jauh", "directDns": "Penyelesai server keluar (langsung)", "directDnsDomainStrategy": "Strategi domain keluar", "enableDnsRouting": "Aktifkan perutean DNS", "enableFakeDns": "Aktifkan DNS palsu", "domainStrategy": { "auto": "Otomatis", "preferIpv6": "Pilih IPv6", "preferIpv4": "Pilih IPv4", "ipv4Only": "Hanya IPv4", "ipv6Only": "Hanya IPv6" } }, "inbound": { "title": "Masuk", "serviceMode": "Mode layanan", "serviceModes": { "proxy": "Hanya layanan proksi", "systemProxy": "Atur proksi sistem", "tun": "VPN", "tunService": "Layanan VPN" }, "shortServiceModes": { "proxy": "Proxy", "systemProxy": "Proxy sistem", "tun": "VPN", "tunService": "Layanan VPN" }, "strictRoute": "Rute ketat", "tunImplementation": "Implementasi TUN", "tunImplementations": { "mixed": "Campuran", "system": "Sistem", "gvisor": "gVisor" }, "mixedPort": "Port campuran", "tproxyPort": "Port proksi transparan", "directPort": "Port direct", "redirectPort": "Port pengalihan", "allowConnectionFromLan": "Bagikan VPN di jaringan lokal" }, "tlsTricks": { "title": "Trik TLS", "enable": "Aktifkan fragmen", "packets": "Paket Fragmentasi", "packetsTlsHello": "TLS Hello", "packets1_1": "1-1", "packets1_2": "1-2", "packets1_3": "1-3", "packets1_4": "1-4", "packets1_5": "1-5", "size": "Ukuran fragmen", "sleep": "Jeda fragmen", "mixedSniCase": { "enable": "Aktifkan kasus SNI campuran" }, "padding": { "enable": "Aktifkan padding", "size": "Ukuran padding" } }, "warp": { "title": "WARP", "enable": "Aktifkan WARP", "generateConfig": "Buat konfigurasi WARP", "configGenerated": "Konfigurasi WARP dibuat", "missingConfig": "Konfigurasi WARP hilang", "detourMode": "Mode Rute WARP", "detourModes": { "proxyOverWarp": "Alihkan proksi melalui WARP", "warpOverProxy": "Alihkan WARP melalui proksi", "proxyOverWarpExplain": "Buka blokir proksi dengan WARP", "warpOverProxyExplain": "Keamanan ekstra dengan WARP" }, "licenseKey": "Kunci lisensi", "cleanIp": "IP bersih", "port": "Port", "noise": { "count": "Jumlah kebisingan", "mode": "Mode kebisingan", "size": "Ukuran kebisingan", "delay": "Penundaan kebisingan" } } } }, "components": { "stats": { "connection": "Koneksi", "traffic": "Lalu lintas", "trafficLive": "Lalu lintas langsung", "trafficTotal": "Total lalu lintas", "uplink": "Unggah", "downlink": "Unduh", "speed": "Kecepatan", "totalTransferred": "Total ditransfer" }, "subscriptionInfo": { "upload": "Unggah", "download": "Unduh", "total": "Total lalu lintas", "expireDate": "Tanggal kedaluwarsa", "expired": "Kedaluwarsa", "noTraffic": "Kuota habis", "remainingTime": "Sisa waktu", "remainingDuration": "tersisa ${duration} hari", "remainingDurationNew": "${duration} hari", "remainingTrafficSemanticLabel": "${consumed} dari ${total} lalu lintas terpakai", "remainingTraffic": "Sisa lalu lintas", "remainingUsage": "Sisa", "profileSite": "Penyedia", "profileSupport": "Dukungan" } }, "dialogs": { "sortProfiles": { "title": "Urut berdasarkan", "sort": { "name": "Menurut abjad", "lastUpdate": "Terakhir diperbarui" } }, "warpLicense": { "title": "Persetujuan Cloudflare WARP", "description(rich)": "Cloudflare WARP adalah penyedia VPN WireGuard gratis. Dengan mengaktifkan opsi ini, Anda menyetujui ${tos(Ketentuan Layanan)} dan ${privacy(Kebijakan Privasi)} Cloudflare WARP." }, "newVersion": { "title": "Pembaruan tersedia", "msg": "Versi baru @:common.appTitle tersedia. Apakah Anda ingin memperbarui sekarang?", "currentVersion": "Versi saat ini: ", "newVersion": "Versi baru: ", "updateNow": "Perbarui sekarang" }, "confirmation": { "settings": { "import": { "msg": "Ini akan menimpa semua opsi konfigurasi dengan nilai yang diberikan. Anda yakin?" } }, "profile": { "delete": { "title": "Hapus profil", "msg": "Anda yakin ingin menghapus profil ini secara permanen?" } }, "perAppProxy": { "shareOnGithub": { "title": "Meningkatkan pilihan otomatis", "msg": "Dengan membagikan aplikasi yang dipilih, Anda membantu melengkapi daftar \"pilihan otomatis\"" }, "import": { "msg": "Ini akan menggantikan semua pilihan proksi per aplikasi Anda saat ini. Anda yakin ingin melanjutkan?" } }, "routeRule": { "delete": { "title": "Hapus aturan", "msg": "Anda yakin ingin menghapus aturan \"$rulename\"?" } } }, "experimentalNotice": { "title": "Fitur eksperimental sedang digunakan", "msg": "Anda telah mengaktifkan beberapa fitur eksperimental yang mungkin memengaruhi kualitas koneksi dan menyebabkan kesalahan tak terduga. Anda selalu dapat mengubah atau menyetel ulang opsi ini dari halaman konfigurasi.", "disable": "Jangan tampilkan lagi" }, "noActiveProfile": { "title": "Pilih profil", "msg": "Mari kita mulai dengan menambahkan profil koneksi yang berisi detail koneksi VPN Anda.\n\nBelum punya server VPN? Jangan khawatir, ikuti tutorial di bawah ini untuk mengaturnya dengan cepat dan gratis.", "helpBtn": { "label": "Tunjukkan caranya", "url": "https://hiddify.com/manager/" } }, "unknownDomainsWarning": { "title": "Peringatan tautan eksternal", "youAreAboutToVisit": "Anda akan mengunjungi:", "thisWebsiteIsNotInOurTrustedList": "Situs web ini tidak ada dalam daftar tepercaya kami. Harap lanjutkan dengan hati-hati." }, "proxyInfo": { "fullTag": "Tag lengkap:", "type": "Tipe:", "testTime": "Waktu uji:", "testDelay": "Jeda uji:", "isSelected": "Dipilih:", "isGroup": "Grup", "isSecure": "Aman:", "port": "Port:", "host": "Host:", "ip": "IP:", "countryCode": "Kode negara:", "region": "Wilayah:", "city": "Kota:", "asn": "ASN:", "organization": "Organisasi:", "location": "Lokasi:", "postalCode": "Kode pos:" }, "windowClosing": { "askEachTime": "Tanyakan setiap kali", "alertMessage": "Sembunyikan atau keluar dari aplikasi?", "remember": "Ingat pilihan saya" } }, "connection": { "tapToConnect": "Ketuk untuk menyambungkan", "connect": "Sambungkan", "connecting": "Menyambungkan...", "connected": "Tersambung", "disconnect": "Putuskan", "disconnecting": "Memutuskan...", "reconnect": "Sambungkan kembali", "reconnectMsg": "Menyambungkan kembali untuk menerapkan perubahan...", "secure": "Diamankan oleh WARP" }, "errors": { "unexpected": "Kesalahan tak terduga", "connection": { "unexpected": "Kesalahan koneksi tak terduga", "timeout": "Waktu koneksi habis", "badResponse": "Respons buruk", "connectionError": "Kesalahan koneksi", "badCertificate": "Sertifikat rusak" }, "profiles": { "unexpected": "Kesalahan tak terduga", "notFound": "Profil tidak ditemukan", "invalidConfig": "Konfigurasi tidak valid", "invalidUrl": "URL tidak valid", "canceledByUser": "Dibatalkan oleh pengguna" }, "connectivity": { "unexpected": "Kegagalan tak terduga", "missingVpnPermission": "Izin VPN hilang", "missingNotificationPermission": "Izin notifikasi hilang", "core": "Kesalahan inti" }, "singbox": { "serviceNotRunning": "Layanan tidak berjalan", "missingPrivilege": "Izin hilang", "missingPrivilegeMsg": "Mode VPN memerlukan izin administrator. Harap mulai ulang aplikasi sebagai administrator atau ubah mode layanan.", "invalidConfigOptions": "Opsi konfigurasi tidak valid", "invalidConfig": "Konfigurasi tidak valid" }, "warp": { "missingLicense": "Lisensi WARP hilang", "missingLicenseMsg": "Profil yang dipilih menggunakan fitur WARP. Untuk menggunakan fitur ini, Anda harus menyetujui lisensi WARP." } } } ================================================ FILE: assets/translations/pt-BR.i18n.json ================================================ { "common": { "appTitle": "Hiddify", "start": "Começar", "version": "Versão", "ok": "OK", "cancel": "Cancelar", "kContinue": "Continuar", "showMore": "Mostrar mais", "showLess": "Mostrar menos", "filter": "Filtrar", "all": "Todos", "pause": "Pausar", "resume": "Retomar", "clear": "Limpar", "close": "Fechar", "auto": "Automático", "manually": "Manualmente", "name": "Nome", "url": "URL", "add": "Adicionar", "clipboard": "Área de transferência", "addToClipboard": "Adicionar à área de transferência", "scanQr": "Escanear QR", "free": "Grátis", "warp": "WARP", "fragment": "Fragmento", "help": "Ajuda", "save": "Salvar", "update": "Atualizar", "share": "Compartilhar", "edit": "Editar", "delete": "Excluir", "discard": "Descartar", "import": "Importar", "export": "Exportar", "later": "Mais tarde", "ignore": "Ignorar", "quit": "Sair", "notSet": "Não definido", "hide": "Ocultar", "exit": "Sair", "reset": "Redefinir", "done": "Concluído", "search": "Buscar", "decline": "Recusar", "agree": "Aceitar", "empty": "Vazio", "unknown": "Desconhecido", "hidden": "Oculto", "timeout": "Tempo esgotado", "sort": "Ordenar", "dashboard": "Painel", "interval": { "day": { "zero": "", "one": "$n dia", "other": "$n dias" }, "hour": { "zero": "", "one": "$n hora", "other": "$n horas" } }, "msg": { "permission": { "denied": "Permissão negada" }, "export": { "clipboard": { "success": "Adicionado à área de transferência com sucesso", "failure": "Falha ao copiar para a área de transferência", "contentTooLarge": "Conteúdo muito grande. Use a exportação para arquivo" }, "file": { "success": "Arquivo JSON criado com sucesso", "failure": "Falha ao criar o arquivo" } }, "import": { "confirm": "Confirmar importação", "success": "Importado com sucesso", "failure": "Falha ao importar" } } }, "intro": { "banner": "Tudo o que você precisa para uma internet sem restrições", "termsAndPolicyCaution(rich)": "Ao continuar, você concorda com os ${tap(@:pages.about.termsAndConditions)}", "info(rich)": "Feito com ❤️ por Hiddify - ${tap_source(Código Aberto)} (${tap_license(Licença)})" }, "pages": { "home": { "title": "Início", "quickSettings": "Configurações rápidas" }, "proxies": { "title": "Proxies", "sort": "Ordenar proxies", "testDelay": "Testar latência", "empty": "Nenhum proxy disponível", "activeProxy": "Proxy ativo", "unknownIp": "IP desconhecido", "sortOptions": { "unsorted": "Padrão", "name": "Alfabeticamente", "delay": "Por latência" }, "ipInfo": { "address": "Endereço de IP", "country": "País", "organization": "Organização" }, "delay": { "result": "Latência: ${delay}ms", "timeout": "Tempo limite do teste de latência esgotado", "testing": "Latência: testando..." } }, "profiles": { "title": "Perfis", "add": "Adicionar perfil", "update": "Atualizar perfil", "viewAllProfiles": "Ver todos os perfis", "activeProfileName": "Nome do perfil ativo: \"${name}\".", "nonActiveProfileName": "Selecionar \"${name}\" como perfil ativo", "freeSubNotFound": "Nenhuma assinatura gratuita encontrada", "freeSubNotFoundForRegion": "Nenhuma assinatura gratuita encontrada para a região \"${region}\"", "failedToLoad": "Falha ao carregar", "updateSubscriptions": "Atualizar assinaturas", "share": { "urlToClipboard": "URL para a área de transferência", "showUrlQr": "Mostrar QR code da URL", "jsonToClipboard": "JSON para a área de transferência" }, "msg": { "save": { "success": "Perfil salvo com sucesso" }, "invalidUrl": "URL inválida", "add": { "failure": "Falha ao adicionar perfil" }, "update": { "success": "Perfil atualizado com sucesso", "successNamed": "\"${name}\" atualizado com sucesso", "failure": "Falha ao atualizar perfil", "failureNamed": "Falha ao atualizar \"${name}\"" }, "delete": { "success": "Perfil excluído com sucesso" } } }, "profileDetails": { "title": "Perfil", "lastUpdate": "Última atualização", "form": { "nameHint": "Nome do perfil", "emptyName": "O nome é obrigatório", "invalidUrl": "URL inválida", "urlHint": "URL de configuração completa", "disableAutoUpdate": "Desativar atualização automática", "autoUpdateInterval": "Intervalo de atualização automática", "loading": "Adicionando perfil..." } }, "logs": { "title": "Registros", "shareCoreLogs": "Compartilhar registros do núcleo", "shareAppLogs": "Compartilhar registros do aplicativo" }, "about": { "title": "Sobre", "notAvailableMsg": "Você já está usando a versão mais recente", "checkForUpdate": "Verificar atualizações", "openWorkingDir": "Abrir diretório de trabalho", "sourceCode": "Código-fonte", "telegramChannel": "Canal do Telegram", "termsAndConditions": "Termos e Condições", "privacyPolicy": "Política de Privacidade" }, "settings": { "title": "Configurações", "resetTunnel": "Redefinir perfil de VPN", "options": { "import": { "clipboard": "Importar opções da área de transferência", "file": "Importar opções de um arquivo" }, "export": { "anonymousToClipboard": "Copiar opções anônimas para a área de transferência", "anonymousToFile": "Exportar opções anônimas para um arquivo", "allToClipboard": "Copiar todas as opções para a área de transferência", "allToFile": "Exportar todas as opções para um arquivo" }, "reset": "Redefinir opções" }, "general": { "title": "Geral", "locale": "Idioma", "themeMode": "Tema", "themeModes": { "system": "Padrão do sistema", "dark": "Modo escuro", "light": "Modo claro", "black": "Modo preto" }, "enableAnalytics": "Ativar análise de dados", "enableAnalyticsMsg": "Permitir a coleta de dados de análise e relatórios de falhas para melhorar o aplicativo", "autoIpCheck": "Verificar IP da conexão automaticamente", "dynamicNotification": "Exibir velocidade na notificação", "hapticFeedback": "Feedback tátil", "actionAtClosing": "Ação ao fechar", "autoStart": "Iniciar com o sistema", "silentStart": "Iniciar minimizado", "ignoreBatteryOptimizations": "Desativar otimização de bateria", "ignoreBatteryOptimizationsMsg": "Remover restrições para um desempenho ideal da VPN", "memoryLimit": "Limite de memória", "memoryLimitMsg": "Ative se estiver enfrentando erros de falta de memória ou travamentos frequentes do aplicativo", "debugMode": "Modo de depuração", "debugModeMsg": "Reinicie o aplicativo para aplicar esta alteração", "logLevel": "Nível de registro", "connectionTestUrl": "URL de teste de conexão", "urlTestInterval": "Intervalo de teste de URL", "clashApiPort": "Porta da API do Clash", "useXrayCoreWhenPossible": "Usar xray-core quando possível", "useXrayCoreWhenPossibleMsg": "Use o xray-core ao analisar links de assinatura. Você precisa reimportar o link para ativar esta opção" }, "routing": { "title": "Roteamento", "perAppProxy": { "title": "Proxy por aplicativo", "hideSysApps": "Ocultar aplicativos do sistema", "options": { "import": { "clipboard": "Importar seleção da área de transferência", "file": "Importar seleção de um arquivo", "msg": "A importação substituirá suas seleções atuais. Tem certeza de que deseja continuar?" }, "export": { "clipboard": "Copiar seleção para a área de transferência", "file": "Exportar seleção para um arquivo" }, "shareToAll": "Compartilhar com todos", "clearAllSelections": "Limpar todas as seleções" }, "modes": { "all": "Todos", "proxy": "Proxy", "bypass": "Ignorar", "allMsg": "Usar proxy para todos os aplicativos", "proxyMsg": "Usar proxy apenas para aplicativos selecionados", "bypassMsg": "Não usar proxy para aplicativos selecionados" }, "autoSelection": { "title": "Seleção automática", "performNow": "Executar agora", "resetToDefault": "Redefinir para o padrão", "autoUpdateInterval": "Intervalo de atualização automática", "toast": { "success": "Seleção automática de aplicativos concluída com sucesso", "failure": "Falha na seleção automática", "regionNotFound": "Seleção automática não encontrada para a região \"${region}\"", "alreadyInAuto": "Suas seleções já estão na lista automática" }, "dialog": { "title": "Seleção automática de aplicativos", "msg": "A função de seleção automática para o proxy por aplicativo foi desativada devido à mudança de região para \"${region}\"" } } }, "region": "Região", "regions": { "ir": "Irã (ir)", "cn": "China (cn)", "ru": "Rússia (ru)", "af": "Afeganistão (af)", "id": "Indonésia (id)", "tr": "Turquia (tr)", "br": "Brasil (br)", "other": "Outro" }, "balancerStrategy": { "title": "Estratégia de Balancer", "roundRobin": "Round robin", "consistentHash": "Consistent hash", "stickySession": "Sticky session" }, "blockAds": "Bloquear anúncios", "bypassLan": "Ignorar LAN", "resolveDestination": "Resolver destino", "ipv6Route": "Rota IPv6", "ipv6Modes": { "disable": "Desativar", "enable": "Ativar", "prefer": "Preferencial", "only": "Exclusivo" }, "routeRule": { "title": "Regras de Roteamento", "options": { "import": { "clipboard": "Importar regras da área de transferência", "file": "Importar regras de um arquivo" }, "export": { "clipboard": "Copiar regras para a área de transferência", "file": "Salvar regras em um arquivo" }, "reset": "Redefinir regras" }, "deleteRule": "Excluir regra", "createRule": "Criar nova regra", "rule": { "title": "Regra", "ruleChanged": "Regra alterada", "ruleChangedMsg": "Deseja salvar suas edições?", "onlyTunMode": "Disponível apenas no modo TUN", "notAvailabeInThisPlatform": "Não disponível nesta plataforma", "canNotBeEmpty": "Não pode estar vazio", "validUrlEx": "https://example.com", "validUrl": "\"URL\" válido como\n@:pages.settings.routing.routeRule.rule.validUrlEx", "validProcessNameEx": "Chrome.exe ou google chrome ou chrome", "validProcessName": "\"Nome do Processo\" válido como\n@:pages.settings.routing.routeRule.rule.validProcessNameEx", "validProcessPathEx": "C:\\Pro...\\chrome.exe\n/App.../Google Chrome\n/usr/lib.../chrome", "validProcessPath": "\"Caminho do Processo\" válido como\n@:pages.settings.routing.routeRule.rule.validProcessPathEx", "validPortRangeEx": "80 ou 1-65000", "validPortRange": "\"Porta\" ou \"Intervalo de Portas\" válido como\n@:pages.settings.routing.routeRule.rule.validPortRangeEx", "validIpCidrEx": "8.8.8.8 ou 10.0.0.0/24", "validIpCidr": "IP CIDR válido como\n@:pages.settings.routing.routeRule.rule.validIpCidrEx", "validDomainEx": "Google.com ou dl.google.com", "validDomain": "\"Domínio\" válido como\n@:pages.settings.routing.routeRule.rule.validDomainEx", "validDomainSuffixEx": ".com ou .ir", "validDomainSuffix": "\"Sufixo de Domínio\" válido como\n@:pages.settings.routing.routeRule.rule.validDomainSuffixEx", "tileTitle(map)": { "name": "Nome", "outbound": "Saída se corresponder", "rule_set": "URL do conjunto de regras", "package_name": "Nomes dos pacotes", "process_name": "Nomes dos processos", "process_path": "Caminhos dos processos", "network": "Redes", "port_range": "Portas de destino", "source_port_range": "Portas de origem", "protocol": "Protocolo", "ip_cidr": "IP CIDR de destino", "source_ip_cidr": "IP CIDR de origem", "domain": "Domínio", "domain_suffixe": "Sufixo de domínio", "domain_keyword": "Palavra-chave de domínio", "domain_regex": "Expressão regular de domínio" }, "outbound(map)": { "proxy": "Proxy", "direct": "Direto", "direct_with_fragment": "Direto com fragmento", "block": "Bloquear" }, "network(map)": { "all": "Todos", "tcp": "TCP", "udp": "UDP" }, "protocol(map)": { "": "Todos", "tls": "TLS", "http": "HTTP", "quic": "QUIC", "stun": "STUN", "dns": "DNS", "bittorrent": "BitTorrent" } }, "genericList": { "addNew": "Adicionar novo valor", "update": "Atualizar valor", "clearList": "Limpar lista", "clearListMsg": "Todos os itens foram excluídos" }, "androidApps": { "pageTitle": "Aplicativos Android", "showSystemApps": "Mostrar aplicativos do sistema", "hideSystemApps": "Ocultar aplicativos do sistema", "clearSelection": "Limpar seleção", "uninstalled": "Desinstalado" } } }, "dns": { "title": "DNS", "remoteDns": "DNS remoto", "remoteDnsDomainStrategy": "Estratégia de domínio de DNS remoto", "directDns": "Resolvedor de servidor de saída (direto)", "directDnsDomainStrategy": "Estratégia de domínio de saída", "enableDnsRouting": "Ativar roteamento de DNS", "enableFakeDns": "Ativar DNS falso", "domainStrategy": { "auto": "Automático", "preferIpv6": "Preferir IPv6", "preferIpv4": "Preferir IPv4", "ipv4Only": "Apenas IPv4", "ipv6Only": "Apenas IPv6" } }, "inbound": { "title": "Entrada", "serviceMode": "Modo de serviço", "serviceModes": { "proxy": "Apenas serviço de proxy", "systemProxy": "Definir proxy do sistema", "tun": "VPN", "tunService": "Serviço VPN" }, "shortServiceModes": { "proxy": "Proxy", "systemProxy": "Proxy do sistema", "tun": "VPN", "tunService": "Serviço VPN" }, "strictRoute": "Roteamento estrito", "tunImplementation": "Implementação de TUN", "tunImplementations": { "mixed": "Misto", "system": "Sistema", "gvisor": "gVisor" }, "mixedPort": "Porta mista", "tproxyPort": "Porta de proxy transparente", "directPort": "Porta de directo", "redirectPort": "Porta de redirecionamento", "allowConnectionFromLan": "Compartilhar VPN na rede local" }, "tlsTricks": { "title": "Truques de TLS", "enable": "Ativar fragmento", "packets": "Pacotes de Fragmentação", "packetsTlsHello": "TLS Hello", "packets1_1": "1-1", "packets1_2": "1-2", "packets1_3": "1-3", "packets1_4": "1-4", "packets1_5": "1-5", "size": "Tamanho do fragmento", "sleep": "Atraso do fragmento", "mixedSniCase": { "enable": "Ativar maiúsculas/minúsculas mistas no SNI" }, "padding": { "enable": "Ativar preenchimento", "size": "Tamanho do preenchimento" } }, "warp": { "title": "WARP", "enable": "Ativar WARP", "generateConfig": "Gerar configuração WARP", "configGenerated": "Configuração WARP gerada", "missingConfig": "Configuração WARP ausente", "detourMode": "Modo de Roteamento WARP", "detourModes": { "proxyOverWarp": "Desviar proxies através do WARP", "warpOverProxy": "Desviar WARP através de proxies", "proxyOverWarpExplain": "Desbloquear proxies com WARP", "warpOverProxyExplain": "Segurança extra com WARP" }, "licenseKey": "Chave de licença", "cleanIp": "IP limpo", "port": "Porta", "noise": { "count": "Contagem de ruído", "mode": "Modo de ruído", "size": "Tamanho do ruído", "delay": "Atraso do ruído" } } } }, "components": { "stats": { "connection": "Conexão", "traffic": "Tráfego", "trafficLive": "Tráfego ao vivo", "trafficTotal": "Tráfego total", "uplink": "Envio", "downlink": "Recebimento", "speed": "Velocidade", "totalTransferred": "Total transferido" }, "subscriptionInfo": { "upload": "Upload", "download": "Download", "total": "Tráfego total", "expireDate": "Data de validade", "expired": "Expirado", "noTraffic": "Cota esgotada", "remainingTime": "Tempo restante", "remainingDuration": "${duration} dias restantes", "remainingDurationNew": "${duration} dias", "remainingTrafficSemanticLabel": "${consumed} de ${total} de tráfego consumido", "remainingTraffic": "Tráfego restante", "remainingUsage": "Restante", "profileSite": "Provedor", "profileSupport": "Suporte" } }, "dialogs": { "sortProfiles": { "title": "Ordenar por", "sort": { "name": "Alfabeticamente", "lastUpdate": "Última atualização" } }, "warpLicense": { "title": "Consentimento do Cloudflare WARP", "description(rich)": "O Cloudflare WARP é um provedor de VPN WireGuard gratuito. Ao ativar esta opção, você concorda com os ${tos(Termos de Serviço)} e a ${privacy(Política de Privacidade)} do Cloudflare WARP." }, "newVersion": { "title": "Atualização disponível", "msg": "Uma nova versão do @:common.appTitle está disponível. Deseja atualizar agora?", "currentVersion": "Versão atual: ", "newVersion": "Nova versão: ", "updateNow": "Atualizar agora" }, "confirmation": { "settings": { "import": { "msg": "Isso substituirá todas as opções de configuração pelos valores fornecidos. Você tem certeza?" } }, "profile": { "delete": { "title": "Excluir perfil", "msg": "Tem certeza de que deseja excluir este perfil permanentemente?" } }, "perAppProxy": { "shareOnGithub": { "title": "Melhorando a seleção automática", "msg": "Ao compartilhar os aplicativos selecionados, você ajuda a completar a lista de \"seleção automática\"" }, "import": { "msg": "Isso substituirá todas as suas seleções atuais de proxy por aplicativo. Tem certeza de que deseja continuar?" } }, "routeRule": { "delete": { "title": "Excluir regra", "msg": "Tem certeza de que deseja excluir a regra \"$rulename\"?" } } }, "experimentalNotice": { "title": "Recursos experimentais em uso", "msg": "Você ativou alguns recursos experimentais que podem afetar a qualidade da conexão e causar erros inesperados. Você sempre pode alterar ou redefinir essas opções na página de configurações.", "disable": "Não mostrar novamente" }, "noActiveProfile": { "title": "Escolha um perfil", "msg": "Para começar, adicione um perfil de conexão que inclua os detalhes da sua conexão VPN.\n\nAinda não tem um servidor VPN? Não se preocupe, siga o tutorial abaixo para configurar um rapidamente e de graça.", "helpBtn": { "label": "Mostre-me como", "url": "https://hiddify.com/manager/" } }, "unknownDomainsWarning": { "title": "Aviso de link externo", "youAreAboutToVisit": "Você está prestes a visitar:", "thisWebsiteIsNotInOurTrustedList": "Este site não está na nossa lista de confiança. Prossiga com cautela." }, "proxyInfo": { "fullTag": "Tag completa:", "type": "Tipo:", "testTime": "Hora do teste:", "testDelay": "Latência do teste:", "isSelected": "Selecionado:", "isGroup": "É um grupo", "isSecure": "É seguro:", "port": "Porta:", "host": "Host:", "ip": "IP:", "countryCode": "Código do país:", "region": "Região:", "city": "Cidade:", "asn": "ASN:", "organization": "Organização:", "location": "Localização:", "postalCode": "Código postal:" }, "windowClosing": { "askEachTime": "Perguntar sempre", "alertMessage": "Ocultar ou sair do aplicativo?", "remember": "Lembrar minha escolha" } }, "connection": { "tapToConnect": "Toque para conectar", "connect": "Conectar", "connecting": "Conectando...", "connected": "Conectado", "disconnect": "Desconectar", "disconnecting": "Desconectando...", "reconnect": "Reconectar", "reconnectMsg": "Reconectando para aplicar as alterações...", "secure": "Protegido por WARP" }, "errors": { "unexpected": "Erro inesperado", "connection": { "unexpected": "Erro de conexão inesperado", "timeout": "Tempo limite de conexão esgotado", "badResponse": "Resposta inválida", "connectionError": "Erro de conexão", "badCertificate": "Certificado inválido" }, "profiles": { "unexpected": "Erro inesperado", "notFound": "Perfil não encontrado", "invalidConfig": "Configurações inválidas", "invalidUrl": "URL inválida", "canceledByUser": "Cancelado pelo usuário" }, "connectivity": { "unexpected": "Falha inesperada", "missingVpnPermission": "Permissão de VPN ausente", "missingNotificationPermission": "Permissão de notificação ausente", "core": "Erro no núcleo" }, "singbox": { "serviceNotRunning": "O serviço não está em execução", "missingPrivilege": "Permissão ausente", "missingPrivilegeMsg": "O modo VPN requer privilégios de administrador. Reinicie o aplicativo como administrador ou altere o modo de serviço.", "invalidConfigOptions": "Opções de configuração inválidas", "invalidConfig": "Configuração inválida" }, "warp": { "missingLicense": "Licença do WARP ausente", "missingLicenseMsg": "O perfil selecionado usa o recurso WARP. Para usar este recurso, você deve concordar com a licença do WARP." } } } ================================================ FILE: assets/translations/ru.i18n.json ================================================ { "common": { "appTitle": "Hiddify", "start": "Начать", "version": "Версия", "ok": "OK", "cancel": "Отмена", "kContinue": "Продолжить", "showMore": "Показать больше", "showLess": "Показать меньше", "filter": "Фильтр", "all": "Все", "pause": "Пауза", "resume": "Возобновить", "clear": "Очистить", "close": "Закрыть", "auto": "Авто", "manually": "Вручную", "name": "Имя", "url": "URL", "add": "Добавить", "clipboard": "Буфер обмена", "addToClipboard": "Добавить в буфер обмена", "scanQr": "Сканировать QR", "free": "Бесплатно", "warp": "WARP", "fragment": "Фрагмент", "help": "Справка", "save": "Сохранить", "update": "Обновить", "share": "Поделиться", "edit": "Изменить", "delete": "Удалить", "discard": "Отменить", "import": "Импорт", "export": "Экспорт", "later": "Позже", "ignore": "Игнорировать", "quit": "Выход", "notSet": "Не задано", "hide": "Скрыть", "exit": "Выйти", "reset": "Сброс", "done": "Готово", "search": "Поиск", "decline": "Отклонить", "agree": "Согласен", "empty": "Пусто", "unknown": "Неизвестно", "hidden": "Скрытый", "timeout": "Тайм-аут", "sort": "Сортировать", "dashboard": "Панель управления", "interval": { "day": { "zero": "", "one": "$n день", "few": "$n дня", "many": "$n дней", "other": "$n дня" }, "hour": { "zero": "", "one": "$n час", "few": "$n часа", "many": "$n часов", "other": "$n часа" } }, "msg": { "permission": { "denied": "Доступ запрещен" }, "export": { "clipboard": { "success": "Успешно добавлено в буфер обмена", "failure": "Не удалось скопировать в буфер обмена", "contentTooLarge": "Слишком большой контент. Используйте экспорт в файл" }, "file": { "success": "Файл JSON успешно создан", "failure": "Не удалось создать файл" } }, "import": { "confirm": "Подтвердить импорт", "success": "Успешно импортировано", "failure": "Не удалось импортировать" } } }, "intro": { "banner": "Все, что вам нужно для интернета без ограничений", "termsAndPolicyCaution(rich)": "Продолжая, вы соглашаетесь с ${tap(@:pages.about.termsAndConditions)}", "info(rich)": "Сделано с ❤️ Hiddify - ${tap_source(Открытый исходный код)} (${tap_license(Лицензия)})" }, "pages": { "home": { "title": "Главная", "quickSettings": "Быстрые настройки" }, "proxies": { "title": "Прокси", "sort": "Сортировать прокси", "testDelay": "Проверить задержку", "empty": "Нет доступных прокси", "activeProxy": "Активный прокси", "unknownIp": "Неизвестный IP", "sortOptions": { "unsorted": "По умолчанию", "name": "По алфавиту", "delay": "По задержке" }, "ipInfo": { "address": "IP-адрес", "country": "Страна", "organization": "Организация" }, "delay": { "result": "Задержка: ${delay} мс", "timeout": "Тайм-аут теста задержки", "testing": "Задержка: тестирование..." } }, "profiles": { "title": "Профили", "add": "Добавить профиль", "update": "Обновить профиль", "viewAllProfiles": "Посмотреть все профили", "activeProfileName": "Имя активного профиля: \"${name}\".", "nonActiveProfileName": "Выбрать \"${name}\" как активный профиль", "freeSubNotFound": "Бесплатная подписка не найдена", "freeSubNotFoundForRegion": "Бесплатная подписка для региона \"${region}\" не найдена", "failedToLoad": "Не удалось загрузить", "updateSubscriptions": "Обновить подписки", "share": { "urlToClipboard": "URL в буфер обмена", "showUrlQr": "Показать QR-код URL", "jsonToClipboard": "JSON в буфер обмена" }, "msg": { "save": { "success": "Профиль успешно сохранен" }, "invalidUrl": "Неверный URL", "add": { "failure": "Не удалось добавить профиль" }, "update": { "success": "Профиль успешно обновлен", "successNamed": "\"${name}\" успешно обновлен", "failure": "Не удалось обновить профиль", "failureNamed": "Не удалось обновить \"${name}\"" }, "delete": { "success": "Профиль успешно удален" } } }, "profileDetails": { "title": "Профиль", "lastUpdate": "Последнее обновление", "form": { "nameHint": "Имя профиля", "emptyName": "Имя обязательно для заполнения", "invalidUrl": "Неверный URL", "urlHint": "Полный URL конфигурации", "disableAutoUpdate": "Отключить автообновление", "autoUpdateInterval": "Интервал автообновления", "loading": "Добавление профиля..." } }, "logs": { "title": "Логи", "shareCoreLogs": "Поделиться логами ядра", "shareAppLogs": "Поделиться логами приложения" }, "about": { "title": "О программе", "notAvailableMsg": "Вы уже используете последнюю версию", "checkForUpdate": "Проверить обновления", "openWorkingDir": "Открыть рабочую папку", "sourceCode": "Исходный код", "telegramChannel": "Канал в Telegram", "termsAndConditions": "Условия использования", "privacyPolicy": "Политика конфиденциальности" }, "settings": { "title": "Настройки", "resetTunnel": "Сбросить профиль VPN", "options": { "import": { "clipboard": "Импортировать настройки из буфера обмена", "file": "Импортировать настройки из файла" }, "export": { "anonymousToClipboard": "Копировать анонимные настройки в буфер обмена", "anonymousToFile": "Экспортировать анонимные настройки в файл", "allToClipboard": "Копировать все настройки в буфер обмена", "allToFile": "Экспортировать все настройки в файл" }, "reset": "Сбросить настройки" }, "general": { "title": "Общие", "locale": "Язык", "themeMode": "Тема оформления", "themeModes": { "system": "Системная", "dark": "Темная", "light": "Светлая", "black": "Черная" }, "enableAnalytics": "Включить аналитику", "enableAnalyticsMsg": "Разрешить сбор аналитики и отправку отчетов о сбоях для улучшения приложения", "autoIpCheck": "Автоматически проверять IP-адрес", "dynamicNotification": "Отображать скорость в уведомлении", "hapticFeedback": "Тактильный отклик", "actionAtClosing": "Действие при закрытии", "autoStart": "Запускать при входе в систему", "silentStart": "Запускать свернутым", "ignoreBatteryOptimizations": "Отключить оптимизацию батареи", "ignoreBatteryOptimizationsMsg": "Снять ограничения для оптимальной работы VPN", "memoryLimit": "Ограничение памяти", "memoryLimitMsg": "Включите, если вы сталкиваетесь с ошибками нехватки памяти или частыми сбоями приложения", "debugMode": "Режим отладки", "debugModeMsg": "Перезапустите приложение, чтобы применить это изменение", "logLevel": "Уровень логирования", "connectionTestUrl": "URL для теста соединения", "urlTestInterval": "Интервал теста URL", "clashApiPort": "Порт Clash API", "useXrayCoreWhenPossible": "Использовать xray-core, если возможно", "useXrayCoreWhenPossibleMsg": "Использовать xray-core при обработке ссылок на подписку. Необходимо повторно импортировать ссылку, чтобы включить эту опцию." }, "routing": { "title": "Маршрутизация", "perAppProxy": { "title": "Прокси для приложений", "hideSysApps": "Скрыть системные приложения", "options": { "import": { "clipboard": "Импортировать выбор из буфера обмена", "file": "Импортировать выбор из файла", "msg": "Импорт заменит ваш текущий выбор. Вы уверены, что хотите продолжить?" }, "export": { "clipboard": "Копировать выбор в буфер обмена", "file": "Экспортировать выбор в файл" }, "shareToAll": "Поделиться со всеми", "clearAllSelections": "Очистить весь выбор" }, "modes": { "all": "Все", "proxy": "Прокси", "bypass": "В обход", "allMsg": "Проксировать все приложения", "proxyMsg": "Проксировать только выбранные приложения", "bypassMsg": "Не проксировать выбранные приложения" }, "autoSelection": { "title": "Автовыбор", "performNow": "Выполнить сейчас", "resetToDefault": "Сбросить по умолчанию", "autoUpdateInterval": "Интервал автообновления", "toast": { "success": "Автовыбор приложений успешно завершен", "failure": "Ошибка автовыбора", "regionNotFound": "Автовыбор для региона \"${region}\" не найден", "alreadyInAuto": "Ваш выбор уже находится в списке автовыбора" }, "dialog": { "title": "Автовыбор приложений", "msg": "Функция автовыбора для прокси приложений была отключена из-за смены региона на \"${region}\"" } } }, "region": "Регион", "regions": { "ir": "Иран (ir)", "cn": "Китай (cn)", "ru": "Россия (ru)", "af": "Афганистан (af)", "id": "Индонезия (id)", "tr": "Турция (tr)", "br": "Бразилия (br)", "other": "Другой" }, "balancerStrategy": { "title": "Стратегия Balancer", "roundRobin": "Round robin", "consistentHash": "Consistent hash", "stickySession": "Sticky session" }, "blockAds": "Блокировать рекламу", "bypassLan": "Обход LAN", "resolveDestination": "Определять адрес назначения", "ipv6Route": "Маршрут IPv6", "ipv6Modes": { "disable": "Отключить", "enable": "Включить", "prefer": "Предпочтительно", "only": "Только" }, "routeRule": { "title": "Правила маршрутизации", "options": { "import": { "clipboard": "Импортировать правила из буфера обмена", "file": "Импортировать правила из файла" }, "export": { "clipboard": "Копировать правила в буфер обмена", "file": "Сохранить правила в файл" }, "reset": "Сбросить правила" }, "deleteRule": "Удалить правило", "createRule": "Создать новое правило", "rule": { "title": "Правило", "ruleChanged": "Правило изменено", "ruleChangedMsg": "Вы хотите сохранить изменения?", "onlyTunMode": "Доступно только в режиме TUN", "notAvailabeInThisPlatform": "Недоступно на этой платформе", "canNotBeEmpty": "Не может быть пустым", "validUrlEx": "https://example.com", "validUrl": "Валидный \"URL\", например\n@:pages.settings.routing.routeRule.rule.validUrlEx", "validProcessNameEx": "Chrome.exe или google chrome или chrome", "validProcessName": "Валидное \"Имя процесса\", например\n@:pages.settings.routing.routeRule.rule.validProcessNameEx", "validProcessPathEx": "C:\\Pro...\\chrome.exe\n/App.../Google Chrome\n/usr/lib.../chrome", "validProcessPath": "Валидный \"Путь к процессу\", например\n@:pages.settings.routing.routeRule.rule.validProcessPathEx", "validPortRangeEx": "80 или 1-65000", "validPortRange": "Валидный \"Порт\" или \"Диапазон портов\", например\n@:pages.settings.routing.routeRule.rule.validPortRangeEx", "validIpCidrEx": "8.8.8.8 или 10.0.0.0/24", "validIpCidr": "Валидный IP CIDR, например\n@:pages.settings.routing.routeRule.rule.validIpCidrEx", "validDomainEx": "Google.com или dl.google.com", "validDomain": "Валидный \"Домен\", например\n@:pages.settings.routing.routeRule.rule.validDomainEx", "validDomainSuffixEx": ".com или .ru", "validDomainSuffix": "Валидный \"Суффикс домена\", например\n@:pages.settings.routing.routeRule.rule.validDomainSuffixEx", "tileTitle(map)": { "name": "Имя", "outbound": "Исходящий при совпадении", "rule_set": "URL набора правил", "package_name": "Имена пакетов", "process_name": "Имена процессов", "process_path": "Пути к процессам", "network": "Сети", "port_range": "Порты назначения", "source_port_range": "Исходные порты", "protocol": "Протокол", "ip_cidr": "IP CIDR назначения", "source_ip_cidr": "Исходный IP CIDR", "domain": "Домен", "domain_suffixe": "Суффикс домена", "domain_keyword": "Ключевое слово домена", "domain_regex": "Регулярное выражение домена" }, "outbound(map)": { "proxy": "Прокси", "direct": "Напрямую", "direct_with_fragment": "Напрямую с фрагментом", "block": "Блокировать" }, "network(map)": { "all": "Все", "tcp": "TCP", "udp": "UDP" }, "protocol(map)": { "": "Все", "tls": "TLS", "http": "HTTP", "quic": "QUIC", "stun": "STUN", "dns": "DNS", "bittorrent": "BitTorrent" } }, "genericList": { "addNew": "Добавить новое значение", "update": "Обновить значение", "clearList": "Очистить список", "clearListMsg": "Все элементы удалены" }, "androidApps": { "pageTitle": "Приложения Android", "showSystemApps": "Показать системные приложения", "hideSystemApps": "Скрыть системные приложения", "clearSelection": "Очистить выбор", "uninstalled": "Удалено" } } }, "dns": { "title": "DNS", "remoteDns": "Удаленный DNS", "remoteDnsDomainStrategy": "Стратегия домена удаленного DNS", "directDns": "Распознаватель исходящего сервера (напрямую)", "directDnsDomainStrategy": "Стратегия исходящего домена", "enableDnsRouting": "Включить маршрутизацию DNS", "enableFakeDns": "Включить поддельный DNS", "domainStrategy": { "auto": "Авто", "preferIpv6": "Предпочитать IPv6", "preferIpv4": "Предпочитать IPv4", "ipv4Only": "Только IPv4", "ipv6Only": "Только IPv6" } }, "inbound": { "title": "Входящие", "serviceMode": "Режим службы", "serviceModes": { "proxy": "Только прокси-служба", "systemProxy": "Установить системный прокси", "tun": "VPN", "tunService": "Служба VPN" }, "shortServiceModes": { "proxy": "Прокси", "systemProxy": "Системный прокси", "tun": "VPN", "tunService": "Служба VPN" }, "strictRoute": "Строгая маршрутизация", "tunImplementation": "Реализация TUN", "tunImplementations": { "mixed": "Смешанная", "system": "Системная", "gvisor": "gVisor" }, "mixedPort": "Смешанный порт", "tproxyPort": "Порт прозрачного прокси", "directPort": "Локальный порт direct", "redirectPort": "Порт перенаправления", "allowConnectionFromLan": "Поделиться VPN в локальной сети" }, "tlsTricks": { "title": "Трюки TLS", "enable": "Включить фрагментацию", "packets": "Пакеты фрагментации", "packetsTlsHello": "TLS Hello", "packets1_1": "1-1", "packets1_2": "1-2", "packets1_3": "1-3", "packets1_4": "1-4", "packets1_5": "1-5", "size": "Размер фрагмента", "sleep": "Задержка фрагмента", "mixedSniCase": { "enable": "Включить смешанный регистр SNI" }, "padding": { "enable": "Включить дополнение", "size": "Размер дополнения" } }, "warp": { "title": "WARP", "enable": "Включить WARP", "generateConfig": "Сгенерировать конфигурацию WARP", "configGenerated": "Конфигурация Warp сгенерирована", "missingConfig": "Отсутствует конфигурация WARP", "detourMode": "Режим маршрутизации WARP", "detourModes": { "proxyOverWarp": "Направлять прокси через WARP", "warpOverProxy": "Направлять WARP через прокси", "proxyOverWarpExplain": "Разблокировать прокси с помощью WARP", "warpOverProxyExplain": "Дополнительная безопасность с WARP" }, "licenseKey": "Лицензионный ключ", "cleanIp": "Чистый IP", "port": "Порт", "noise": { "count": "Количество шума", "mode": "Режим шума", "size": "Размер шума", "delay": "Задержка шума" } } } }, "components": { "stats": { "connection": "Соединение", "traffic": "Трафик", "trafficLive": "Текущий трафик", "trafficTotal": "Общий трафик", "uplink": "Отправка", "downlink": "Прием", "speed": "Скорость", "totalTransferred": "Всего передано" }, "subscriptionInfo": { "upload": "Отправлено", "download": "Получено", "total": "Всего трафика", "expireDate": "Дата окончания", "expired": "Истек", "noTraffic": "Квота исчерпана", "remainingTime": "Оставшееся время", "remainingDuration": "осталось ${duration} дней", "remainingDurationNew": "${duration} дней", "remainingTrafficSemanticLabel": "израсходовано ${consumed} из ${total} трафика", "remainingTraffic": "Оставшийся трафик", "remainingUsage": "Осталось", "profileSite": "Провайдер", "profileSupport": "Поддержка" } }, "dialogs": { "sortProfiles": { "title": "Сортировать по", "sort": { "name": "По алфавиту", "lastUpdate": "Последнему обновлению" } }, "warpLicense": { "title": "Соглашение о WARP от Cloudflare", "description(rich)": "Cloudflare WARP - это бесплатный провайдер WireGuard VPN. Включая эту опцию, вы соглашаетесь с ${tos(Условиями обслуживания)} и ${privacy(Политикой конфиденциальности)} Cloudflare WARP." }, "newVersion": { "title": "Доступно обновление", "msg": "Доступна новая версия @:common.appTitle. Хотите обновить сейчас?", "currentVersion": "Текущая версия: ", "newVersion": "Новая версия: ", "updateNow": "Обновить сейчас" }, "confirmation": { "settings": { "import": { "msg": "Это перезапишет все параметры конфигурации предоставленными значениями. Вы уверены?" } }, "profile": { "delete": { "title": "Удалить профиль", "msg": "Вы уверены, что хотите навсегда удалить этот профиль?" } }, "perAppProxy": { "shareOnGithub": { "title": "Улучшение автовыбора", "msg": "Делясь выбранными приложениями, вы помогаете пополнить список \"автовыбора\"" }, "import": { "msg": "Это заменит все ваши текущие настройки прокси для приложений. Вы уверены, что хотите продолжить?" } }, "routeRule": { "delete": { "title": "Удалить правило", "msg": "Вы уверены, что хотите удалить правило \"$rulename\"?" } } }, "experimentalNotice": { "title": "Используются экспериментальные функции", "msg": "Вы включили некоторые экспериментальные функции, которые могут повлиять на качество соединения и вызвать непредвиденные ошибки. Вы всегда можете изменить или сбросить эти параметры на странице настроек.", "disable": "Больше не показывать" }, "noActiveProfile": { "title": "Выберите профиль", "msg": "Давайте начнем с добавления профиля подключения, который содержит данные вашего VPN-соединения.\n\nЕще нет VPN-сервера? Не беспокойтесь — просто следуйте руководству ниже, чтобы быстро и бесплатно настроить его.", "helpBtn": { "label": "Покажите, как", "url": "https://hiddify.com/manager/" } }, "unknownDomainsWarning": { "title": "Предупреждение о внешней ссылке", "youAreAboutToVisit": "Вы собираетесь перейти на сайт:", "thisWebsiteIsNotInOurTrustedList": "Этот сайт не входит в наш список доверенных. Пожалуйста, действуйте с осторожностью." }, "proxyInfo": { "fullTag": "Полный тег:", "type": "Тип:", "testTime": "Время теста:", "testDelay": "Задержка теста:", "isSelected": "Выбран:", "isGroup": "Это группа", "isSecure": "Защищено:", "port": "Порт:", "host": "Хост:", "ip": "IP:", "countryCode": "Код страны:", "region": "Регион:", "city": "Город:", "asn": "ASN:", "organization": "Организация:", "location": "Местоположение:", "postalCode": "Почтовый индекс:" }, "windowClosing": { "askEachTime": "Спрашивать каждый раз", "alertMessage": "Скрыть или выйти из приложения?", "remember": "Запомнить мой выбор" } }, "connection": { "tapToConnect": "Нажмите для подключения", "connect": "Подключить", "connecting": "Подключение...", "connected": "Подключено", "disconnect": "Отключить", "disconnecting": "Отключение...", "reconnect": "Переподключить", "reconnectMsg": "Переподключение для учета изменений...", "secure": "Защищено с помощью WARP" }, "errors": { "unexpected": "Непредвиденная ошибка", "connection": { "unexpected": "Непредвиденная ошибка подключения", "timeout": "Тайм-аут подключения", "badResponse": "Неверный ответ", "connectionError": "Ошибка подключения", "badCertificate": "Недействительный сертификат" }, "profiles": { "unexpected": "Непредвиденная ошибка", "notFound": "Профиль не найден", "invalidConfig": "Неверная конфигурация", "invalidUrl": "Неверный URL", "canceledByUser": "Отменено пользователем" }, "connectivity": { "unexpected": "Непредвиденный сбой", "missingVpnPermission": "Отсутствует разрешение на VPN", "missingNotificationPermission": "Отсутствует разрешение на уведомления", "core": "Ошибка ядра" }, "singbox": { "serviceNotRunning": "Служба не запущена", "missingPrivilege": "Отсутствуют права", "missingPrivilegeMsg": "Режим VPN требует прав администратора. Либо перезапустите приложение от имени администратора, либо измените режим службы.", "invalidConfigOptions": "Неверные параметры конфигурации", "invalidConfig": "Неверная конфигурация" }, "warp": { "missingLicense": "Отсутствует лицензия Warp", "missingLicenseMsg": "Выбранный профиль использует функцию WARP; для использования этой функции необходимо принять лицензию WARP." } } } ================================================ FILE: assets/translations/tr.i18n.json ================================================ { "common": { "appTitle": "Hiddify", "start": "Başlat", "version": "Sürüm", "ok": "Tamam", "cancel": "İptal", "kContinue": "Devam et", "showMore": "Daha fazla göster", "showLess": "Daha az göster", "filter": "Filtrele", "all": "Tümü", "pause": "Duraklat", "resume": "Devam et", "clear": "Temizle", "close": "Kapat", "auto": "Otomatik", "manually": "Manuel", "name": "İsim", "url": "URL", "add": "Ekle", "clipboard": "Pano", "addToClipboard": "Panoya ekle", "scanQr": "QR Tara", "free": "Ücretsiz", "warp": "WARP", "fragment": "Fragment", "help": "Yardım", "save": "Kaydet", "update": "Güncelle", "share": "Paylaş", "edit": "Düzenle", "delete": "Sil", "discard": "Vazgeç", "import": "İçe aktar", "export": "Dışa aktar", "later": "Daha sonra", "ignore": "Yoksay", "quit": "Çık", "notSet": "Ayarlanmadı", "hide": "Gizle", "exit": "Çıkış", "reset": "Sıfırla", "done": "Bitti", "search": "Ara", "decline": "Reddet", "agree": "Kabul et", "empty": "Boş", "unknown": "Bilinmeyen", "hidden": "Gizli", "timeout": "Zaman aşımı", "sort": "Sırala", "dashboard": "Gösterge Paneli", "interval": { "day": { "zero": "", "one": "$n gün", "other": "$n gün" }, "hour": { "zero": "", "one": "$n saat", "other": "$n saat" } }, "msg": { "permission": { "denied": "İzin reddedildi" }, "export": { "clipboard": { "success": "Panoya başarıyla eklendi", "failure": "Panoya kopyalanamadı", "contentTooLarge": "İçerik çok büyük. Bunun yerine dosyaya aktarmayı kullanın" }, "file": { "success": "JSON dosyası başarıyla oluşturuldu", "failure": "Dosya oluşturulamadı" } }, "import": { "confirm": "İçe aktarmayı onayla", "success": "Başarıyla içe aktarıldı", "failure": "İçe aktarılamadı" } } }, "intro": { "banner": "Sınırsız bir internet için ihtiyacınız olan her şey", "termsAndPolicyCaution(rich)": "Devam ederek ${tap(@:pages.about.termsAndConditions)} kabul etmiş olursunuz", "info(rich)": "Hiddify tarafından ❤️ ile yapıldı - ${tap_source(Açık Kaynak)} (${tap_license(Lisans)})" }, "pages": { "home": { "title": "Ana Sayfa", "quickSettings": "Hızlı ayarlar" }, "proxies": { "title": "Proxy'ler", "sort": "Proxy'leri sırala", "testDelay": "Gecikmeyi test et", "empty": "Kullanılabilir proxy yok", "activeProxy": "Aktif proxy", "unknownIp": "Bilinmeyen IP", "sortOptions": { "unsorted": "Varsayılan", "name": "Alfabetik", "delay": "Gecikmeye göre" }, "ipInfo": { "address": "IP adresi", "country": "Ülke", "organization": "Kuruluş" }, "delay": { "result": "Gecikme: ${delay}ms", "timeout": "Gecikme testi zaman aşımına uğradı", "testing": "Gecikme: test ediliyor..." } }, "profiles": { "title": "Profiller", "add": "Profil ekle", "update": "Profili güncelle", "viewAllProfiles": "Tüm profilleri görüntüle", "activeProfileName": "Aktif profil adı: \"${name}\".", "nonActiveProfileName": "Aktif profil olarak \"${name}\" seçin", "freeSubNotFound": "Ücretsiz abonelik bulunamadı", "freeSubNotFoundForRegion": "\"${region}\" bölgesi için ücretsiz abonelik bulunamadı", "failedToLoad": "Yüklenemedi", "updateSubscriptions": "Abonelikleri güncelle", "share": { "urlToClipboard": "URL'yi panoya kopyala", "showUrlQr": "URL QR kodunu göster", "jsonToClipboard": "JSON'u panoya kopyala" }, "msg": { "save": { "success": "Profil başarıyla kaydedildi" }, "invalidUrl": "Geçersiz URL", "add": { "failure": "Profil eklenemedi" }, "update": { "success": "Profil başarıyla güncellendi", "successNamed": "\"${name}\" başarıyla güncellendi", "failure": "Profil güncellenemedi", "failureNamed": "\"${name}\" güncellenemedi" }, "delete": { "success": "Profil başarıyla silindi" } } }, "profileDetails": { "title": "Profil", "lastUpdate": "Son güncelleme", "form": { "nameHint": "Profil adı", "emptyName": "İsim gerekli", "invalidUrl": "Geçersiz URL", "urlHint": "Tam yapılandırma URL'si", "disableAutoUpdate": "Otomatik güncellemeyi devre dışı bırak", "autoUpdateInterval": "Otomatik güncelleme aralığı", "loading": "Profil ekleniyor..." } }, "logs": { "title": "Loglar", "shareCoreLogs": "Çekirdek loglarını paylaş", "shareAppLogs": "Uygulama loglarını paylaş" }, "about": { "title": "Hakkında", "notAvailableMsg": "Zaten en son sürümü kullanıyorsunuz", "checkForUpdate": "Güncellemeleri kontrol et", "openWorkingDir": "Çalışma dizinini aç", "sourceCode": "Kaynak kodu", "telegramChannel": "Telegram kanalı", "termsAndConditions": "Şartlar ve Koşullar", "privacyPolicy": "Gizlilik Politikası" }, "settings": { "title": "Ayarlar", "resetTunnel": "VPN profilini sıfırla", "options": { "import": { "clipboard": "Seçenekleri panodan içe aktar", "file": "Seçenekleri dosyadan içe aktar" }, "export": { "anonymousToClipboard": "Anonim seçenekleri panoya kopyala", "anonymousToFile": "Anonim seçenekleri dosyaya aktar", "allToClipboard": "Tüm seçenekleri panoya kopyala", "allToFile": "Tüm seçenekleri dosyaya aktar" }, "reset": "Seçenekleri sıfırla" }, "general": { "title": "Genel", "locale": "Dil", "themeMode": "Tema modu", "themeModes": { "system": "Sistem varsayılanı", "dark": "Karanlık mod", "light": "Açık mod", "black": "Siyah mod" }, "enableAnalytics": "Analizi etkinleştir", "enableAnalyticsMsg": "Uygulamayı iyileştirmek için analiz ve kilitlenme raporları toplanmasına izin verin", "autoIpCheck": "Bağlantı IP'sini otomatik kontrol et", "dynamicNotification": "Hızı bildirimde göster", "hapticFeedback": "Dokunsal geri bildirim", "actionAtClosing": "Kapatma eylemi", "autoStart": "Oturum açılışında başlat", "silentStart": "Simge durumunda başlat", "ignoreBatteryOptimizations": "Pil optimizasyonunu devre dışı bırak", "ignoreBatteryOptimizationsMsg": "Optimum VPN performansı için kısıtlamaları kaldırın", "memoryLimit": "Bellek limiti", "memoryLimitMsg": "Bellek yetersizliği hataları veya sık uygulama çökmeleri yaşıyorsanız etkinleştirin", "debugMode": "Hata ayıklama modu", "debugModeMsg": "Bu değişikliği uygulamak için uygulamayı yeniden başlatın", "logLevel": "Log seviyesi", "connectionTestUrl": "Bağlantı testi URL'si", "urlTestInterval": "URL testi aralığı", "clashApiPort": "Clash API portu", "useXrayCoreWhenPossible": "Mümkün olduğunda xray-core kullan", "useXrayCoreWhenPossibleMsg": "Abonelik bağlantılarını ayrıştırırken xray-core kullanın. Bu seçeneği etkinleştirmek için bağlantıyı yeniden içe aktarmanız gerekir." }, "routing": { "title": "Yönlendirme", "perAppProxy": { "title": "Uygulama bazlı proxy", "hideSysApps": "Sistem uygulamalarını gizle", "options": { "import": { "clipboard": "Seçimi panodan içe aktar", "file": "Seçimi dosyadan içe aktar", "msg": "İçe aktarma mevcut seçimlerinizin üzerine yazacaktır. Devam etmek istediğinizden emin misiniz?" }, "export": { "clipboard": "Seçimi panoya kopyala", "file": "Seçimi dosyaya aktar" }, "shareToAll": "Herkesle paylaş", "clearAllSelections": "Tüm seçimleri temizle" }, "modes": { "all": "Tümü", "proxy": "Proxy", "bypass": "Atla", "allMsg": "Tüm uygulamaları proxy'le", "proxyMsg": "Yalnızca seçili uygulamaları proxy'le", "bypassMsg": "Seçili uygulamaları proxy'leme" }, "autoSelection": { "title": "Otomatik seçim", "performNow": "Şimdi gerçekleştir", "resetToDefault": "Varsayılana sıfırla", "autoUpdateInterval": "Otomatik güncelleme aralığı", "toast": { "success": "Otomatik uygulama seçimi başarıyla tamamlandı", "failure": "Otomatik seçim başarısız oldu", "regionNotFound": "\"${region}\" bölgesi için otomatik seçim bulunamadı", "alreadyInAuto": "Seçimleriniz zaten otomatik listede" }, "dialog": { "title": "Otomatik uygulama seçimi", "msg": "Uygulama bazlı proxy için otomatik seçim özelliği, bölge \"${region}\" olarak değiştirildiği için devre dışı bırakıldı" } } }, "region": "Bölge", "regions": { "ir": "İran (ir)", "cn": "Çin (cn)", "ru": "Rusya (ru)", "af": "Afganistan (af)", "id": "Endonezya (id)", "tr": "Türkiye (tr)", "br": "Brezilya (br)", "other": "Diğer" }, "balancerStrategy": { "title": "Balancer stratejisi", "roundRobin": "Round robin", "consistentHash": "Consistent hash", "stickySession": "Sticky session" }, "blockAds": "Reklamları engelle", "bypassLan": "LAN'ı atla", "resolveDestination": "Hedefi çözümle", "ipv6Route": "IPv6 rotası", "ipv6Modes": { "disable": "Devre dışı bırak", "enable": "Etkinleştir", "prefer": "Tercih et", "only": "Sadece" }, "routeRule": { "title": "Yönlendirme kuralları", "options": { "import": { "clipboard": "Kuralları panodan içe aktar", "file": "Kuralları dosyadan içe aktar" }, "export": { "clipboard": "Kuralları panoya kopyala", "file": "Kuralları dosyaya kaydet" }, "reset": "Kuralları sıfırla" }, "deleteRule": "Kuralı sil", "createRule": "Yeni kural oluştur", "rule": { "title": "Kural", "ruleChanged": "Kural değiştirildi", "ruleChangedMsg": "Düzenlemelerinizi kaydetmek istiyor musunuz?", "onlyTunMode": "Yalnızca TUN modunda kullanılabilir", "notAvailabeInThisPlatform": "Bu platformda mevcut değil", "canNotBeEmpty": "Boş olamaz", "validUrlEx": "https://example.com", "validUrl": "Geçerli \"URL\", örn.\n@:pages.settings.routing.routeRule.rule.validUrlEx", "validProcessNameEx": "Chrome.exe veya google chrome veya chrome", "validProcessName": "Geçerli \"İşlem Adı\", örn.\n@:pages.settings.routing.routeRule.rule.validProcessNameEx", "validProcessPathEx": "C:\\Pro...\\chrome.exe\n/App.../Google Chrome\n/usr/lib.../chrome", "validProcessPath": "Geçerli \"İşlem Yolu\", örn.\n@:pages.settings.routing.routeRule.rule.validProcessPathEx", "validPortRangeEx": "80 veya 1-65000", "validPortRange": "Geçerli \"Port\" veya \"Port Aralığı\", örn.\n@:pages.settings.routing.routeRule.rule.validPortRangeEx", "validIpCidrEx": "8.8.8.8 veya 10.0.0.0/24", "validIpCidr": "Geçerli IP CIDR, örn.\n@:pages.settings.routing.routeRule.rule.validIpCidrEx", "validDomainEx": "Google.com veya dl.google.com", "validDomain": "Geçerli \"Alan Adı\", örn.\n@:pages.settings.routing.routeRule.rule.validDomainEx", "validDomainSuffixEx": ".com veya .tr", "validDomainSuffix": "Geçerli \"Alan Adı Son Eki\", örn.\n@:pages.settings.routing.routeRule.rule.validDomainSuffixEx", "tileTitle(map)": { "name": "İsim", "outbound": "Eşleşirse giden", "rule_set": "Kural seti URL'si", "package_name": "Paket adları", "process_name": "İşlem adları", "process_path": "İşlem yolları", "network": "Ağlar", "port_range": "Hedef portlar", "source_port_range": "Kaynak portlar", "protocol": "Protokol", "ip_cidr": "Hedef IP CIDR", "source_ip_cidr": "Kaynak IP CIDR", "domain": "Alan adı", "domain_suffixe": "Alan adı son eki", "domain_keyword": "Alan adı anahtar kelimesi", "domain_regex": "Alan adı regex" }, "outbound(map)": { "proxy": "Proxy", "direct": "Doğrudan", "direct_with_fragment": "Doğrudan (fragment ile)", "block": "Engelle" }, "network(map)": { "all": "Tümü", "tcp": "TCP", "udp": "UDP" }, "protocol(map)": { "": "Tümü", "tls": "TLS", "http": "HTTP", "quic": "QUIC", "stun": "STUN", "dns": "DNS", "bittorrent": "BitTorrent" } }, "genericList": { "addNew": "Yeni değer ekle", "update": "Değeri güncelle", "clearList": "Listeyi temizle", "clearListMsg": "Tüm öğeler silindi" }, "androidApps": { "pageTitle": "Android uygulamaları", "showSystemApps": "Sistem uygulamalarını göster", "hideSystemApps": "Sistem uygulamalarını gizle", "clearSelection": "Seçimi temizle", "uninstalled": "Kaldırıldı" } } }, "dns": { "title": "DNS", "remoteDns": "Uzak DNS", "remoteDnsDomainStrategy": "Uzak DNS alan adı stratejisi", "directDns": "Giden sunucu çözümleyicisi (doğrudan)", "directDnsDomainStrategy": "Giden alan adı stratejisi", "enableDnsRouting": "DNS yönlendirmeyi etkinleştir", "enableFakeDns": "Sahte DNS'i etkinleştir", "domainStrategy": { "auto": "Otomatik", "preferIpv6": "IPv6 tercih et", "preferIpv4": "IPv4 tercih et", "ipv4Only": "Sadece IPv4", "ipv6Only": "Sadece IPv6" } }, "inbound": { "title": "Gelen", "serviceMode": "Servis modu", "serviceModes": { "proxy": "Yalnızca proxy servisi", "systemProxy": "Sistem proxy'sini ayarla", "tun": "VPN", "tunService": "VPN servisi" }, "shortServiceModes": { "proxy": "Proxy", "systemProxy": "Sistem proxy'si", "tun": "VPN", "tunService": "VPN servisi" }, "strictRoute": "Katı yönlendirme", "tunImplementation": "TUN uygulaması", "tunImplementations": { "mixed": "Karışık", "system": "Sistem", "gvisor": "gVisor" }, "mixedPort": "Karışık port", "tproxyPort": "Şeffaf proxy portu", "directPort": "Direct portu", "redirectPort": "Yönlendirme portu", "allowConnectionFromLan": "VPN'i yerel ağda paylaş" }, "tlsTricks": { "title": "TLS hileleri", "enable": "Fragment'ı etkinleştir", "packets": "Fragmentation Paketleri", "packetsTlsHello": "TLS Hello", "packets1_1": "1-1", "packets1_2": "1-2", "packets1_3": "1-3", "packets1_4": "1-4", "packets1_5": "1-5", "size": "Fragment boyutu", "sleep": "Fragment gecikmesi", "mixedSniCase": { "enable": "Karışık SNI harf durumunu etkinleştir" }, "padding": { "enable": "Dolguyu etkinleştir", "size": "Dolgu boyutu" } }, "warp": { "title": "WARP", "enable": "WARP'ı etkinleştir", "generateConfig": "WARP yapılandırması oluştur", "configGenerated": "WARP yapılandırması oluşturuldu", "missingConfig": "WARP yapılandırması eksik", "detourMode": "Yönlendirme modu", "detourModes": { "proxyOverWarp": "Proxy'leri WARP üzerinden yönlendir", "warpOverProxy": "WARP'ı proxy'ler üzerinden yönlendir", "proxyOverWarpExplain": "Proxy'lerin engelini WARP ile kaldır", "warpOverProxyExplain": "WARP ile ekstra güvenlik" }, "licenseKey": "Lisans anahtarı", "cleanIp": "Temiz IP", "port": "Port", "noise": { "count": "Gürültü sayısı", "mode": "Gürültü modu", "size": "Gürültü boyutu", "delay": "Gürültü gecikmesi" } } } }, "components": { "stats": { "connection": "Bağlantı", "traffic": "Trafik", "trafficLive": "Canlı trafik", "trafficTotal": "Toplam trafik", "uplink": "Yükleme", "downlink": "İndirme", "speed": "Hız", "totalTransferred": "Toplam aktarılan" }, "subscriptionInfo": { "upload": "Yükleme", "download": "İndirme", "total": "Toplam trafik", "expireDate": "Bitiş tarihi", "expired": "Süresi doldu", "noTraffic": "Kota doldu", "remainingTime": "Kalan süre", "remainingDuration": "kalan ${duration} gün", "remainingDurationNew": "${duration} gün", "remainingTrafficSemanticLabel": "${consumed} / ${total} trafik kullanıldı", "remainingTraffic": "Kalan trafik", "remainingUsage": "Kalan", "profileSite": "Sağlayıcı", "profileSupport": "Destek" } }, "dialogs": { "sortProfiles": { "title": "Sıralama ölçütü", "sort": { "name": "Alfabetik", "lastUpdate": "Son güncelleme" } }, "warpLicense": { "title": "Cloudflare WARP onayı", "description(rich)": "Cloudflare WARP ücretsiz bir WireGuard VPN sağlayıcısıdır. Bu seçeneği etkinleştirerek Cloudflare WARP'ın ${tos(Hizmet Şartları)}'nı ve ${privacy(Gizlilik Politikası)}'nı kabul etmiş olursunuz." }, "newVersion": { "title": "Güncelleme mevcut", "msg": "@:common.appTitle'ın yeni bir sürümü mevcut. Şimdi güncellemek ister misiniz?", "currentVersion": "Mevcut sürüm: ", "newVersion": "Yeni sürüm: ", "updateNow": "Şimdi güncelle" }, "confirmation": { "settings": { "import": { "msg": "Bu işlem tüm yapılandırma seçeneklerini verilen değerlerle yeniden yazacaktır. Emin misiniz?" } }, "profile": { "delete": { "title": "Profili sil", "msg": "Bu profili kalıcı olarak silmek istediğinizden emin misiniz?" } }, "perAppProxy": { "shareOnGithub": { "title": "Otomatik seçimi iyileştirme", "msg": "Seçili uygulamaları paylaşarak \"otomatik seçim\" listesini tamamlamaya yardımcı olursunuz" }, "import": { "msg": "Bu işlem mevcut tüm uygulama bazlı proxy seçimlerinizi değiştirecektir. Devam etmek istediğinizden emin misiniz?" } }, "routeRule": { "delete": { "title": "Kuralı sil", "msg": "\"$rulename\" kuralını silmek istediğinizden emin misiniz?" } } }, "experimentalNotice": { "title": "Deneysel özellikler kullanılıyor", "msg": "Bağlantı kalitesini etkileyebilecek ve beklenmedik hatalara neden olabilecek bazı deneysel özellikleri etkinleştirdiniz. Bu seçenekleri istediğiniz zaman yapılandırma sayfasından değiştirebilir veya sıfırlayabilirsiniz.", "disable": "Tekrar gösterme" }, "noActiveProfile": { "title": "Bir profil seçin", "msg": "VPN bağlantı ayrıntılarınızı içeren bir bağlantı profili ekleyerek başlayalım.\n\nHenüz bir VPN sunucunuz yok mu? Endişelenmeyin, hızlı ve ücretsiz bir şekilde kurmak için aşağıdaki eğitimi takip edin.", "helpBtn": { "label": "Nasıl yapıldığını göster", "url": "https://hiddify.com/manager/" } }, "unknownDomainsWarning": { "title": "Dış bağlantı uyarısı", "youAreAboutToVisit": "Şu adresi ziyaret etmek üzeresiniz:", "thisWebsiteIsNotInOurTrustedList": "Bu web sitesi güvenilir listemizde değil. Lütfen dikkatli devam edin." }, "proxyInfo": { "fullTag": "Tam etiket:", "type": "Tür:", "testTime": "Test zamanı:", "testDelay": "Test gecikmesi:", "isSelected": "Seçili:", "isGroup": "Grup", "isSecure": "Güvenli:", "port": "Port:", "host": "Sunucu:", "ip": "IP:", "countryCode": "Ülke kodu:", "region": "Bölge:", "city": "Şehir:", "asn": "ASN:", "organization": "Kuruluş:", "location": "Konum:", "postalCode": "Posta kodu:" }, "windowClosing": { "askEachTime": "Her seferinde sor", "alertMessage": "Uygulama gizlensin mi, kapatılsın mı?", "remember": "Seçimimi hatırla" } }, "connection": { "tapToConnect": "Bağlanmak için dokunun", "connect": "Bağlan", "connecting": "Bağlanıyor...", "connected": "Bağlı", "disconnect": "Bağlantıyı kes", "disconnecting": "Bağlantı kesiliyor...", "reconnect": "Yeniden bağlan", "reconnectMsg": "Değişiklikleri uygulamak için yeniden bağlanılıyor...", "secure": "WARP ile güvende" }, "errors": { "unexpected": "Beklenmeyen hata", "connection": { "unexpected": "Beklenmeyen bağlantı hatası", "timeout": "Bağlantı zaman aşımına uğradı", "badResponse": "Hatalı yanıt", "connectionError": "Bağlantı hatası", "badCertificate": "Geçersiz sertifika" }, "profiles": { "unexpected": "Beklenmeyen hata", "notFound": "Profil bulunamadı", "invalidConfig": "Geçersiz yapılandırmalar", "invalidUrl": "Geçersiz URL", "canceledByUser": "Kullanıcı tarafından iptal edildi" }, "connectivity": { "unexpected": "Beklenmeyen hata", "missingVpnPermission": "VPN izni eksik", "missingNotificationPermission": "Bildirim izni eksik", "core": "Çekirdek hatası" }, "singbox": { "serviceNotRunning": "Servis çalışmıyor", "missingPrivilege": "Eksik yetki", "missingPrivilegeMsg": "VPN modu yönetici yetkisi gerektirir. Lütfen uygulamayı yönetici olarak yeniden başlatın veya servis modunu değiştirin.", "invalidConfigOptions": "Geçersiz yapılandırma seçenekleri", "invalidConfig": "Geçersiz yapılandırma" }, "warp": { "missingLicense": "WARP lisansı eksik", "missingLicenseMsg": "Seçili profil WARP özelliğini kullanıyor. Bu özelliği kullanmak için WARP lisansını kabul etmeniz gerekir." } } } ================================================ FILE: assets/translations/zh-CN.i18n.json ================================================ { "common": { "appTitle": "Hiddify", "start": "开始", "version": "版本", "ok": "确定", "cancel": "取消", "kContinue": "继续", "showMore": "显示更多", "showLess": "显示更少", "filter": "筛选", "all": "全部", "pause": "暂停", "resume": "恢复", "clear": "清除", "close": "关闭", "auto": "自动", "manually": "手动", "name": "名称", "url": "URL", "add": "添加", "clipboard": "剪贴板", "addToClipboard": "添加到剪贴板", "scanQr": "扫描二维码", "free": "免费", "warp": "WARP", "fragment": "Fragment", "help": "帮助", "save": "保存", "update": "更新", "share": "分享", "edit": "编辑", "delete": "删除", "discard": "放弃", "import": "导入", "export": "导出", "later": "稍后", "ignore": "忽略", "quit": "退出", "notSet": "未设置", "hide": "隐藏", "exit": "退出", "reset": "重置", "done": "完成", "search": "搜索", "decline": "拒绝", "agree": "同意", "empty": "空", "unknown": "未知", "hidden": "隐藏", "timeout": "超时", "sort": "排序", "dashboard": "仪表盘", "interval": { "day": { "zero": "", "one": "$n 天", "other": "$n 天" }, "hour": { "zero": "", "one": "$n 小时", "other": "$n 小时" } }, "msg": { "permission": { "denied": "权限被拒绝" }, "export": { "clipboard": { "success": "已成功添加到剪贴板", "failure": "复制到剪贴板失败", "contentTooLarge": "内容过大,请使用导出文件" }, "file": { "success": "JSON 文件创建成功", "failure": "创建文件失败" } }, "import": { "confirm": "确认导入", "success": "导入成功", "failure": "导入失败" } } }, "intro": { "banner": "畅享无限制网络的所需一切", "termsAndPolicyCaution(rich)": "继续即表示您同意 ${tap(@:pages.about.termsAndConditions)}", "info(rich)": "由 Hiddify 用 ❤️ 制作 - ${tap_source(开源)} (${tap_license(许可证)})" }, "pages": { "home": { "title": "主页", "quickSettings": "快速设置" }, "proxies": { "title": "代理", "sort": "排序代理", "testDelay": "测试延迟", "empty": "无可用代理", "activeProxy": "当前代理", "unknownIp": "未知 IP", "sortOptions": { "unsorted": "默认", "name": "按名称", "delay": "按延迟" }, "ipInfo": { "address": "IP 地址", "country": "国家", "organization": "组织" }, "delay": { "result": "延迟:${delay}毫秒", "timeout": "延迟测试超时", "testing": "延迟:测试中..." } }, "profiles": { "title": "配置文件", "add": "添加配置文件", "update": "更新配置文件", "viewAllProfiles": "查看所有配置文件", "activeProfileName": "当前配置文件:\"${name}\"", "nonActiveProfileName": "选择 \"${name}\" 作为当前配置文件", "freeSubNotFound": "未找到免费订阅", "freeSubNotFoundForRegion": "未找到 \"${region}\" 地区的免费订阅", "failedToLoad": "加载失败", "updateSubscriptions": "更新订阅", "share": { "urlToClipboard": "URL 到剪贴板", "showUrlQr": "显示 URL 二维码", "jsonToClipboard": "JSON 到剪贴板" }, "msg": { "save": { "success": "配置文件保存成功" }, "invalidUrl": "无效的 URL", "add": { "failure": "添加配置文件失败" }, "update": { "success": "配置文件更新成功", "successNamed": "\"${name}\" 更新成功", "failure": "更新配置文件失败", "failureNamed": "更新 \"${name}\" 失败" }, "delete": { "success": "配置文件删除成功" } } }, "profileDetails": { "title": "配置文件", "lastUpdate": "最后更新", "form": { "nameHint": "配置文件名称", "emptyName": "名称为必填项", "invalidUrl": "无效的 URL", "urlHint": "完整的配置 URL", "disableAutoUpdate": "禁用自动更新", "autoUpdateInterval": "自动更新间隔", "loading": "正在添加配置文件..." } }, "logs": { "title": "日志", "shareCoreLogs": "分享核心日志", "shareAppLogs": "分享应用日志" }, "about": { "title": "关于", "notAvailableMsg": "已是最新版本", "checkForUpdate": "检查更新", "openWorkingDir": "打开工作目录", "sourceCode": "源代码", "telegramChannel": "Telegram 频道", "termsAndConditions": "条款与条件", "privacyPolicy": "隐私政策" }, "settings": { "title": "设置", "resetTunnel": "重置 VPN 配置文件", "options": { "import": { "clipboard": "从剪贴板导入选项", "file": "从文件导入选项" }, "export": { "anonymousToClipboard": "复制匿名选项到剪贴板", "anonymousToFile": "导出匿名选项到文件", "allToClipboard": "复制所有选项到剪贴板", "allToFile": "导出所有选项到文件" }, "reset": "重置选项" }, "general": { "title": "通用", "locale": "语言", "themeMode": "主题模式", "themeModes": { "system": "跟随系统", "dark": "深色模式", "light": "浅色模式", "black": "纯黑模式" }, "enableAnalytics": "启用分析", "enableAnalyticsMsg": "允许收集分析数据和发送崩溃报告以改进应用", "autoIpCheck": "自动检查连接 IP", "dynamicNotification": "在通知中显示速度", "hapticFeedback": "触觉反馈", "actionAtClosing": "关闭时操作", "autoStart": "开机自启", "silentStart": "静默启动", "ignoreBatteryOptimizations": "忽略电池优化", "ignoreBatteryOptimizationsMsg": "移除限制以获得最佳 VPN 性能", "memoryLimit": "内存限制", "memoryLimitMsg": "如果您遇到内存不足错误或应用频繁崩溃,请启用此选项", "debugMode": "调试模式", "debugModeMsg": "重启应用以应用此更改", "logLevel": "日志级别", "connectionTestUrl": "连接测试 URL", "urlTestInterval": "URL 测试间隔", "clashApiPort": "Clash API 端口", "useXrayCoreWhenPossible": "尽可能使用 xray-core", "useXrayCoreWhenPossibleMsg": "解析订阅链接时使用 xray-core。您需要重新导入订阅链接才能启用此选项" }, "routing": { "title": "路由", "perAppProxy": { "title": "分应用代理", "hideSysApps": "隐藏系统应用", "options": { "import": { "clipboard": "从剪贴板导入选择", "file": "从文件导入选择", "msg": "导入将替换您当前的选择。确定要继续吗?" }, "export": { "clipboard": "复制选择到剪贴板", "file": "导出选择到文件" }, "shareToAll": "分享给所有人", "clearAllSelections": "清除所有选择" }, "modes": { "all": "全部", "proxy": "代理", "bypass": "绕过", "allMsg": "代理所有应用", "proxyMsg": "仅代理选定应用", "bypassMsg": "不代理选定应用" }, "autoSelection": { "title": "自动选择", "performNow": "立即执行", "resetToDefault": "重置为默认", "autoUpdateInterval": "自动更新间隔", "toast": { "success": "自动选择应用成功完成", "failure": "自动选择失败", "regionNotFound": "未找到 \"${region}\" 地区的自动选择", "alreadyInAuto": "您的选择已在自动列表中" }, "dialog": { "title": "自动选择应用", "msg": "由于地区更改为 \"${region}\",分应用代理的自动选择功能已被禁用" } } }, "region": "地区", "regions": { "ir": "伊朗 (ir)", "cn": "中国 (cn)", "ru": "俄罗斯 (ru)", "af": "阿富汗 (af)", "id": "印度尼西亚 (id)", "tr": "土耳其 (tr)", "br": "巴西 (br)", "other": "其他" }, "balancerStrategy": { "title": "Balancer 策略", "roundRobin": "Round robin", "consistentHash": "Consistent hash", "stickySession": "Sticky session" }, "blockAds": "拦截广告", "bypassLan": "绕过局域网", "resolveDestination": "解析目的地", "ipv6Route": "IPv6 路由", "ipv6Modes": { "disable": "禁用", "enable": "启用", "prefer": "首选", "only": "仅" }, "routeRule": { "title": "路由规则", "options": { "import": { "clipboard": "从剪贴板导入规则", "file": "从文件导入规则" }, "export": { "clipboard": "复制规则到剪贴板", "file": "保存规则到文件" }, "reset": "重置规则" }, "deleteRule": "删除规则", "createRule": "创建新规则", "rule": { "title": "规则", "ruleChanged": "规则已更改", "ruleChangedMsg": "您想保存您的编辑吗?", "onlyTunMode": "仅在 TUN 模式下可用", "notAvailabeInThisPlatform": "在此平台上不可用", "canNotBeEmpty": "不能为空", "validUrlEx": "https://example.com", "validUrl": "有效的 \"URL\",例如\n@:pages.settings.routing.routeRule.rule.validUrlEx", "validProcessNameEx": "Chrome.exe 或 google chrome 或 chrome", "validProcessName": "有效的 \"进程名\",例如\n@:pages.settings.routing.routeRule.rule.validProcessNameEx", "validProcessPathEx": "C:\\Pro...\\chrome.exe\n/App.../Google Chrome\n/usr/lib.../chrome", "validProcessPath": "有效的 \"进程路径\",例如\n@:pages.settings.routing.routeRule.rule.validProcessPathEx", "validPortRangeEx": "80 或 1-65000", "validPortRange": "有效的 \"端口\" 或 \"端口范围\",例如\n@:pages.settings.routing.routeRule.rule.validPortRangeEx", "validIpCidrEx": "8.8.8.8 或 10.0.0.0/24", "validIpCidr": "有效的 IP CIDR,例如\n@:pages.settings.routing.routeRule.rule.validIpCidrEx", "validDomainEx": "Google.com 或 dl.google.com", "validDomain": "有效的 \"域名\",例如\n@:pages.settings.routing.routeRule.rule.validDomainEx", "validDomainSuffixEx": ".com 或 .cn", "validDomainSuffix": "有效的 \"域名后缀\",例如\n@:pages.settings.routing.routeRule.rule.validDomainSuffixEx", "tileTitle(map)": { "name": "名称", "outbound": "匹配时出站", "rule_set": "规则集 URL", "package_name": "包名", "process_name": "进程名", "process_path": "进程路径", "network": "网络", "port_range": "目标端口", "source_port_range": "源端口", "protocol": "协议", "ip_cidr": "目标 IP CIDR", "source_ip_cidr": "源 IP CIDR", "domain": "域名", "domain_suffixe": "域名后缀", "domain_keyword": "域名关键词", "domain_regex": "域名正则表达式" }, "outbound(map)": { "proxy": "代理", "direct": "直连", "direct_with_fragment": "直连并分片", "block": "拦截" }, "network(map)": { "all": "全部", "tcp": "TCP", "udp": "UDP" }, "protocol(map)": { "": "全部", "tls": "TLS", "http": "HTTP", "quic": "QUIC", "stun": "STUN", "dns": "DNS", "bittorrent": "BitTorrent" } }, "genericList": { "addNew": "添加新值", "update": "更新值", "clearList": "清空列表", "clearListMsg": "所有项目已删除" }, "androidApps": { "pageTitle": "安卓应用", "showSystemApps": "显示系统应用", "hideSystemApps": "隐藏系统应用", "clearSelection": "清除选择", "uninstalled": "已卸载" } } }, "dns": { "title": "DNS", "remoteDns": "远程 DNS", "remoteDnsDomainStrategy": "远程 DNS 域名策略", "directDns": "出站服务器解析器(直连)", "directDnsDomainStrategy": "出站域名策略", "enableDnsRouting": "启用 DNS 路由", "enableFakeDns": "启用伪造 DNS", "domainStrategy": { "auto": "自动", "preferIpv6": "偏好 IPv6", "preferIpv4": "偏好 IPv4", "ipv4Only": "仅 IPv4", "ipv6Only": "仅 IPv6" } }, "inbound": { "title": "入站", "serviceMode": "服务模式", "serviceModes": { "proxy": "仅代理服务", "systemProxy": "设置系统代理", "tun": "VPN", "tunService": "VPN 服务" }, "shortServiceModes": { "proxy": "代理", "systemProxy": "系统代理", "tun": "VPN", "tunService": "VPN 服务" }, "strictRoute": "严格路由", "tunImplementation": "TUN 实现", "tunImplementations": { "mixed": "混合", "system": "系统", "gvisor": "gVisor" }, "mixedPort": "混合端口", "tproxyPort": "透明代理端口", "directPort": "本地 Direct 端口", "redirectPort": "重定向端口", "allowConnectionFromLan": "允许来自局域网的连接" }, "tlsTricks": { "title": "TLS 技巧", "enable": "启用分片", "packets": "分片数据包", "packetsTlsHello": "TLS Hello", "packets1_1": "1-1", "packets1_2": "1-2", "packets1_3": "1-3", "packets1_4": "1-4", "packets1_5": "1-5", "size": "分片大小", "sleep": "分片延迟", "mixedSniCase": { "enable": "启用混合大小写 SNI" }, "padding": { "enable": "启用填充", "size": "填充大小" } }, "warp": { "title": "WARP", "enable": "启用 WARP", "generateConfig": "生成 WARP 配置", "configGenerated": "WARP 配置已生成", "missingConfig": "WARP 配置缺失", "detourMode": "WARP 路由模式", "detourModes": { "proxyOverWarp": "通过 WARP 路由代理", "warpOverProxy": "通过代理路由 WARP", "proxyOverWarpExplain": "通过 WARP 解锁代理", "warpOverProxyExplain": "通过 WARP 增强安全性" }, "licenseKey": "许可证密钥", "cleanIp": "优选 IP", "port": "端口", "noise": { "count": "噪声数量", "mode": "噪声模式", "size": "噪声大小", "delay": "噪声延迟" } } } }, "components": { "stats": { "connection": "连接", "traffic": "流量", "trafficLive": "实时流量", "trafficTotal": "总流量", "uplink": "上传", "downlink": "下载", "speed": "速度", "totalTransferred": "总传输量" }, "subscriptionInfo": { "upload": "上传", "download": "下载", "total": "总流量", "expireDate": "到期日期", "expired": "已过期", "noTraffic": "流量已用尽", "remainingTime": "剩余时间", "remainingDuration": "剩余 ${duration} 天", "remainingDurationNew": "${duration} 天", "remainingTrafficSemanticLabel": "已使用 ${consumed} / ${total} 流量", "remainingTraffic": "剩余流量", "remainingUsage": "剩余", "profileSite": "提供商", "profileSupport": "支持" } }, "dialogs": { "sortProfiles": { "title": "排序方式", "sort": { "name": "按字母顺序", "lastUpdate": "最近更新" } }, "warpLicense": { "title": "Cloudflare WARP 同意书", "description(rich)": "Cloudflare WARP 是一个免费的 WireGuard VPN 提供商。启用此选项即表示您同意 Cloudflare WARP 的 ${tos(服务条款)} 和 ${privacy(隐私政策)}。" }, "newVersion": { "title": "有可用更新", "msg": "@:common.appTitle 的新版本已发布。您想现在更新吗?", "currentVersion": "当前版本:", "newVersion": "新版本:", "updateNow": "立即更新" }, "confirmation": { "settings": { "import": { "msg": "这将用提供的值覆盖所有配置选项。您确定吗?" } }, "profile": { "delete": { "title": "删除配置文件", "msg": "您确定要永久删除此配置文件吗?" } }, "perAppProxy": { "shareOnGithub": { "title": "改进自动选择", "msg": "通过分享您选择的应用,可以帮助完善“自动选择”列表" }, "import": { "msg": "这将替换您当前所有的分应用代理选择。您确定要继续吗?" } }, "routeRule": { "delete": { "title": "删除规则", "msg": "您确定要删除规则 \"$rulename\" 吗?" } } }, "experimentalNotice": { "title": "正在使用实验性功能", "msg": "您已启用了一些实验性功能,这可能会影响连接质量并导致意外错误。您可以随时在配置选项页面更改或重置这些选项。", "disable": "不再显示" }, "noActiveProfile": { "title": "选择一个配置文件", "msg": "让我们从添加一个包含您 VPN 连接详细信息的配置文件开始。\n\n还没有 VPN 服务器?别担心,按照下面的教程,您可以快速免费地设置一个。", "helpBtn": { "label": "告诉我如何操作", "url": "https://hiddify.com/manager/" } }, "unknownDomainsWarning": { "title": "外部链接警告", "youAreAboutToVisit": "您即将访问:", "thisWebsiteIsNotInOurTrustedList": "此网站不在我们的信任列表中。请谨慎操作。" }, "proxyInfo": { "fullTag": "完整标签:", "type": "类型:", "testTime": "测试时间:", "testDelay": "测试延迟:", "isSelected": "已选择:", "isGroup": "是组", "isSecure": "安全:", "port": "端口:", "host": "主机:", "ip": "IP:", "countryCode": "国家代码:", "region": "地区:", "city": "城市:", "asn": "ASN:", "organization": "组织:", "location": "位置:", "postalCode": "邮政编码:" }, "windowClosing": { "askEachTime": "每次询问", "alertMessage": "隐藏还是退出应用程序?", "remember": "记住我的选择" } }, "connection": { "tapToConnect": "点击连接", "connect": "连接", "connecting": "连接中...", "connected": "已连接", "disconnect": "断开连接", "disconnecting": "断开连接中...", "reconnect": "重新连接", "reconnectMsg": "正在重新连接以应用更改...", "secure": "由 WARP 保护" }, "errors": { "unexpected": "意外错误", "connection": { "unexpected": "意外连接错误", "timeout": "连接超时", "badResponse": "响应错误", "connectionError": "连接错误", "badCertificate": "证书无效" }, "profiles": { "unexpected": "意外错误", "notFound": "未找到配置文件", "invalidConfig": "配置无效", "invalidUrl": "URL 无效", "canceledByUser": "用户已取消" }, "connectivity": { "unexpected": "意外失败", "missingVpnPermission": "缺少 VPN 权限", "missingNotificationPermission": "缺少通知权限", "core": "核心错误" }, "singbox": { "serviceNotRunning": "服务未运行", "missingPrivilege": "缺少权限", "missingPrivilegeMsg": "VPN 模式需要管理员权限。请以管理员身份重新启动应用,或更改服务模式。", "invalidConfigOptions": "配置选项无效", "invalidConfig": "配置无效" }, "warp": { "missingLicense": "WARP 许可证缺失", "missingLicenseMsg": "所选配置文件使用 WARP 功能;要使用此功能,必须同意 WARP 许可证。" } } } ================================================ FILE: assets/translations/zh-TW.i18n.json ================================================ { "common": { "appTitle": "Hiddify", "start": "開始", "version": "版本", "ok": "確定", "cancel": "取消", "kContinue": "繼續", "showMore": "顯示更多", "showLess": "顯示較少", "filter": "篩選", "all": "全部", "pause": "暫停", "resume": "恢復", "clear": "清除", "close": "關閉", "auto": "自動", "manually": "手動", "name": "名稱", "url": "URL", "add": "新增", "clipboard": "剪貼簿", "addToClipboard": "新增至剪貼簿", "scanQr": "掃描 QR Code", "free": "免費", "warp": "WARP", "fragment": "Fragment", "help": "說明", "save": "儲存", "update": "更新", "share": "分享", "edit": "編輯", "delete": "刪除", "discard": "捨棄", "import": "匯入", "export": "匯出", "later": "稍後", "ignore": "忽略", "quit": "退出", "notSet": "未設定", "hide": "隱藏", "exit": "退出", "reset": "重設", "done": "完成", "search": "搜尋", "decline": "拒絕", "agree": "同意", "empty": "空", "unknown": "未知", "hidden": "隱藏", "timeout": "超時", "sort": "排序", "dashboard": "儀表板", "interval": { "day": { "zero": "", "one": "$n 天", "other": "$n 天" }, "hour": { "zero": "", "one": "$n 小時", "other": "$n 小時" } }, "msg": { "permission": { "denied": "權限被拒絕" }, "export": { "clipboard": { "success": "已成功新增至剪貼簿", "failure": "複製到剪貼簿失敗", "contentTooLarge": "內容過大,請改用匯出檔案" }, "file": { "success": "JSON 檔案建立成功", "failure": "建立檔案失敗" } }, "import": { "confirm": "確認匯入", "success": "匯入成功", "failure": "匯入失敗" } } }, "intro": { "banner": "暢享無限制網路的所需一切", "termsAndPolicyCaution(rich)": "繼續即表示您同意 ${tap(@:pages.about.termsAndConditions)}", "info(rich)": "由 Hiddify 以 ❤️ 製作 - ${tap_source(開源)} (${tap_license(授權)})" }, "pages": { "home": { "title": "首頁", "quickSettings": "快速設定" }, "proxies": { "title": "代理", "sort": "排序代理", "testDelay": "測試延遲", "empty": "無可用代理", "activeProxy": "當前代理", "unknownIp": "未知 IP", "sortOptions": { "unsorted": "預設", "name": "按名稱", "delay": "按延遲" }, "ipInfo": { "address": "IP 位址", "country": "國家", "organization": "組織" }, "delay": { "result": "延遲:${delay}毫秒", "timeout": "延遲測試超時", "testing": "延遲:測試中..." } }, "profiles": { "title": "設定檔", "add": "新增設定檔", "update": "更新設定檔", "viewAllProfiles": "檢視所有設定檔", "activeProfileName": "目前設定檔:「${name}」", "nonActiveProfileName": "選擇「${name}」作為目前設定檔", "freeSubNotFound": "未找到免費訂閱", "freeSubNotFoundForRegion": "未找到「${region}」地區的免費訂閱", "failedToLoad": "載入失敗", "updateSubscriptions": "更新訂閱", "share": { "urlToClipboard": "URL 到剪貼簿", "showUrlQr": "顯示 URL QR Code", "jsonToClipboard": "JSON 到剪貼簿" }, "msg": { "save": { "success": "設定檔儲存成功" }, "invalidUrl": "無效的 URL", "add": { "failure": "新增設定檔失敗" }, "update": { "success": "設定檔更新成功", "successNamed": "「${name}」更新成功", "failure": "更新設定檔失敗", "failureNamed": "更新「${name}」失敗" }, "delete": { "success": "設定檔刪除成功" } } }, "profileDetails": { "title": "設定檔", "lastUpdate": "最後更新", "form": { "nameHint": "設定檔名稱", "emptyName": "名稱為必填項", "invalidUrl": "無效的 URL", "urlHint": "完整的設定 URL", "disableAutoUpdate": "停用自動更新", "autoUpdateInterval": "自動更新間隔", "loading": "正在新增設定檔..." } }, "logs": { "title": "日誌", "shareCoreLogs": "分享核心日誌", "shareAppLogs": "分享應用程式日誌" }, "about": { "title": "關於", "notAvailableMsg": "已是最新版本", "checkForUpdate": "檢查更新", "openWorkingDir": "開啟工作目錄", "sourceCode": "原始碼", "telegramChannel": "Telegram 頻道", "termsAndConditions": "條款與條件", "privacyPolicy": "隱私權政策" }, "settings": { "title": "設定", "resetTunnel": "重設 VPN 設定檔", "options": { "import": { "clipboard": "從剪貼簿匯入選項", "file": "從檔案匯入選項" }, "export": { "anonymousToClipboard": "複製匿名選項到剪貼簿", "anonymousToFile": "匯出匿名選項到檔案", "allToClipboard": "複製所有選項到剪貼簿", "allToFile": "匯出所有選項到檔案" }, "reset": "重設選項" }, "general": { "title": "一般", "locale": "語言", "themeMode": "主題模式", "themeModes": { "system": "跟隨系統", "dark": "深色模式", "light": "淺色模式", "black": "純黑模式" }, "enableAnalytics": "啟用分析", "enableAnalyticsMsg": "允許收集分析資料和傳送崩潰報告以改進應用程式", "autoIpCheck": "自動檢查連線 IP", "dynamicNotification": "在通知中顯示速度", "hapticFeedback": "觸覺回饋", "actionAtClosing": "關閉時操作", "autoStart": "開機自啟", "silentStart": "靜默啟動", "ignoreBatteryOptimizations": "忽略電池最佳化", "ignoreBatteryOptimizationsMsg": "移除限制以獲得最佳 VPN 效能", "memoryLimit": "記憶體限制", "memoryLimitMsg": "如果您遇到記憶體不足錯誤或應用程式頻繁崩潰,請啟用此選項", "debugMode": "偵錯模式", "debugModeMsg": "重新啟動應用程式以套用此變更", "logLevel": "日誌級別", "connectionTestUrl": "連線測試 URL", "urlTestInterval": "URL 測試間隔", "clashApiPort": "Clash API 連接埠", "useXrayCoreWhenPossible": "盡可能使用 xray-core", "useXrayCoreWhenPossibleMsg": "解析訂閱連結時使用 xray-core。您需要重新匯入訂閱連結才能啟用此選項" }, "routing": { "title": "路由", "perAppProxy": { "title": "依應用程式代理", "hideSysApps": "隱藏系統應用程式", "options": { "import": { "clipboard": "從剪貼簿匯入選擇", "file": "從檔案匯入選擇", "msg": "匯入將取代您目前的選擇。確定要繼續嗎?" }, "export": { "clipboard": "複製選擇到剪貼簿", "file": "匯出選擇到檔案" }, "shareToAll": "分享給所有人", "clearAllSelections": "清除所有選擇" }, "modes": { "all": "全部", "proxy": "代理", "bypass": "繞過", "allMsg": "代理所有應用程式", "proxyMsg": "僅代理選定應用程式", "bypassMsg": "不代理選定應用程式" }, "autoSelection": { "title": "自動選擇", "performNow": "立即執行", "resetToDefault": "重設為預設", "autoUpdateInterval": "自動更新間隔", "toast": { "success": "自動選擇應用程式成功完成", "failure": "自動選擇失敗", "regionNotFound": "未找到「${region}」地區的自動選擇", "alreadyInAuto": "您的選擇已在自動清單中" }, "dialog": { "title": "自動選擇應用程式", "msg": "由於地區更改為「${region}」,依應用程式代理的自動選擇功能已被停用" } } }, "region": "地區", "regions": { "ir": "伊朗 (ir)", "cn": "中國 (cn)", "ru": "俄羅斯 (ru)", "af": "阿富汗 (af)", "id": "印尼 (id)", "tr": "土耳其 (tr)", "br": "巴西 (br)", "other": "其他" }, "balancerStrategy": { "title": "Balancer 策略", "roundRobin": "Round robin", "consistentHash": "Consistent hash", "stickySession": "Sticky session" }, "blockAds": "攔截廣告", "bypassLan": "繞過區域網路", "resolveDestination": "解析目的地", "ipv6Route": "IPv6 路由", "ipv6Modes": { "disable": "停用", "enable": "啟用", "prefer": "首選", "only": "僅" }, "routeRule": { "title": "路由規則", "options": { "import": { "clipboard": "從剪貼簿匯入規則", "file": "從檔案匯入規則" }, "export": { "clipboard": "複製規則到剪貼簿", "file": "儲存規則到檔案" }, "reset": "重設規則" }, "deleteRule": "刪除規則", "createRule": "建立新規則", "rule": { "title": "規則", "ruleChanged": "規則已變更", "ruleChangedMsg": "您想儲存您的編輯嗎?", "onlyTunMode": "僅在 TUN 模式下可用", "notAvailabeInThisPlatform": "在此平台上不可用", "canNotBeEmpty": "不能為空", "validUrlEx": "https://example.com", "validUrl": "有效的「URL」,例如\n@:pages.settings.routing.routeRule.rule.validUrlEx", "validProcessNameEx": "Chrome.exe 或 google chrome 或 chrome", "validProcessName": "有效的「處理程序名稱」,例如\n@:pages.settings.routing.routeRule.rule.validProcessNameEx", "validProcessPathEx": "C:\\Pro...\\chrome.exe\n/App.../Google Chrome\n/usr/lib.../chrome", "validProcessPath": "有效的「處理程序路徑」,例如\n@:pages.settings.routing.routeRule.rule.validProcessPathEx", "validPortRangeEx": "80 或 1-65000", "validPortRange": "有效的「連接埠」或「連接埠範圍」,例如\n@:pages.settings.routing.routeRule.rule.validPortRangeEx", "validIpCidrEx": "8.8.8.8 或 10.0.0.0/24", "validIpCidr": "有效的 IP CIDR,例如\n@:pages.settings.routing.routeRule.rule.validIpCidrEx", "validDomainEx": "Google.com 或 dl.google.com", "validDomain": "有效的「網域」,例如\n@:pages.settings.routing.routeRule.rule.validDomainEx", "validDomainSuffixEx": ".com 或 .tw", "validDomainSuffix": "有效的「網域後綴」,例如\n@:pages.settings.routing.routeRule.rule.validDomainSuffixEx", "tileTitle(map)": { "name": "名稱", "outbound": "符合時出站", "rule_set": "規則集 URL", "package_name": "套件名稱", "process_name": "處理程序名稱", "process_path": "處理程序路徑", "network": "網路", "port_range": "目標連接埠", "source_port_range": "來源連接埠", "protocol": "協定", "ip_cidr": "目標 IP CIDR", "source_ip_cidr": "來源 IP CIDR", "domain": "網域", "domain_suffixe": "網域後綴", "domain_keyword": "網域關鍵字", "domain_regex": "網域正規表示式" }, "outbound(map)": { "proxy": "代理", "direct": "直連", "direct_with_fragment": "直連並分片", "block": "攔截" }, "network(map)": { "all": "全部", "tcp": "TCP", "udp": "UDP" }, "protocol(map)": { "": "全部", "tls": "TLS", "http": "HTTP", "quic": "QUIC", "stun": "STUN", "dns": "DNS", "bittorrent": "BitTorrent" } }, "genericList": { "addNew": "新增值", "update": "更新值", "clearList": "清除清單", "clearListMsg": "所有項目已刪除" }, "androidApps": { "pageTitle": "Android 應用程式", "showSystemApps": "顯示系統應用程式", "hideSystemApps": "隱藏系統應用程式", "clearSelection": "清除選擇", "uninstalled": "已解除安裝" } } }, "dns": { "title": "DNS", "remoteDns": "遠端 DNS", "remoteDnsDomainStrategy": "遠端 DNS 網域策略", "directDns": "出站伺服器解析器(直連)", "directDnsDomainStrategy": "出站網域策略", "enableDnsRouting": "啟用 DNS 路由", "enableFakeDns": "啟用偽造 DNS", "domainStrategy": { "auto": "自動", "preferIpv6": "偏好 IPv6", "preferIpv4": "偏好 IPv4", "ipv4Only": "僅 IPv4", "ipv6Only": "僅 IPv6" } }, "inbound": { "title": "入站", "serviceMode": "服務模式", "serviceModes": { "proxy": "僅代理服務", "systemProxy": "設定系統代理", "tun": "VPN", "tunService": "VPN 服務" }, "shortServiceModes": { "proxy": "代理", "systemProxy": "系統代理", "tun": "VPN", "tunService": "VPN 服務" }, "strictRoute": "嚴格路由", "tunImplementation": "TUN 實現", "tunImplementations": { "mixed": "混合", "system": "系統", "gvisor": "gVisor" }, "mixedPort": "混合連接埠", "tproxyPort": "透明代理連接埠", "directPort": "本地 Direct 連接埠", "redirectPort": "重新導向連接埠", "allowConnectionFromLan": "允許來自區域網路的連線" }, "tlsTricks": { "title": "TLS 技巧", "enable": "啟用分片", "packets": "分片封包", "packetsTlsHello": "TLS Hello", "packets1_1": "1-1", "packets1_2": "1-2", "packets1_3": "1-3", "packets1_4": "1-4", "packets1_5": "1-5", "size": "分片大小", "sleep": "分片延遲", "mixedSniCase": { "enable": "啟用混合大小寫 SNI" }, "padding": { "enable": "啟用填充", "size": "填充大小" } }, "warp": { "title": "WARP", "enable": "啟用 WARP", "generateConfig": "產生 WARP 設定", "configGenerated": "WARP 設定已產生", "missingConfig": "WARP 設定缺失", "detourMode": "WARP 路由模式", "detourModes": { "proxyOverWarp": "透過 WARP 路由代理", "warpOverProxy": "透過代理路由 WARP", "proxyOverWarpExplain": "透過 WARP 解鎖代理", "warpOverProxyExplain": "透過 WARP 增強安全性" }, "licenseKey": "授權金鑰", "cleanIp": "優選 IP", "port": "連接埠", "noise": { "count": "噪音數量", "mode": "噪音模式", "size": "噪音大小", "delay": "噪音延遲" } } } }, "components": { "stats": { "connection": "連線", "traffic": "流量", "trafficLive": "即時流量", "trafficTotal": "總流量", "uplink": "上傳", "downlink": "下載", "speed": "速度", "totalTransferred": "總傳輸量" }, "subscriptionInfo": { "upload": "上傳", "download": "下載", "total": "總流量", "expireDate": "到期日期", "expired": "已到期", "noTraffic": "流量已用盡", "remainingTime": "剩餘時間", "remainingDuration": "剩餘 ${duration} 天", "remainingDurationNew": "${duration} 天", "remainingTrafficSemanticLabel": "已使用 ${consumed} / ${total} 流量", "remainingTraffic": "剩餘流量", "remainingUsage": "剩餘", "profileSite": "提供商", "profileSupport": "支援" } }, "dialogs": { "sortProfiles": { "title": "排序方式", "sort": { "name": "按字母順序", "lastUpdate": "最近更新" } }, "warpLicense": { "title": "Cloudflare WARP 同意書", "description(rich)": "Cloudflare WARP 是一個免費的 WireGuard VPN 提供商。啟用此選項即表示您同意 Cloudflare WARP 的 ${tos(服務條款)} 和 ${privacy(隱私權政策)}。" }, "newVersion": { "title": "有可用更新", "msg": "@:common.appTitle 的新版本已發布。您想立即更新嗎?", "currentVersion": "目前版本:", "newVersion": "新版本:", "updateNow": "立即更新" }, "confirmation": { "settings": { "import": { "msg": "這會使用提供的值覆蓋所有設定選項。您確定嗎?" } }, "profile": { "delete": { "title": "刪除設定檔", "msg": "您確定要永久刪除此設定檔嗎?" } }, "perAppProxy": { "shareOnGithub": { "title": "改進自動選擇", "msg": "透過分享您選擇的應用程式,可以幫助完善「自動選擇」清單" }, "import": { "msg": "這將取代您目前所有的依應用程式代理選擇。您確定要繼續嗎?" } }, "routeRule": { "delete": { "title": "刪除規則", "msg": "您確定要刪除規則「$rulename」嗎?" } } }, "experimentalNotice": { "title": "正在使用實驗性功能", "msg": "您已啟用了一些實驗性功能,這可能會影響連線品質並導致意外錯誤。您可以隨時在設定選項頁面變更或重設這些選項。", "disable": "不再顯示" }, "noActiveProfile": { "title": "選擇一個設定檔", "msg": "讓我們從新增一個包含您 VPN 連線詳細資訊的設定檔開始。\n\n還沒有 VPN 伺服器?別擔心,按照下面的教學,您可以快速免費地設定一個。", "helpBtn": { "label": "告訴我如何操作", "url": "https://hiddify.com/manager/" } }, "unknownDomainsWarning": { "title": "外部連結警告", "youAreAboutToVisit": "您即將造訪:", "thisWebsiteIsNotInOurTrustedList": "此網站不在我們的信任清單中。請謹慎操作。" }, "proxyInfo": { "fullTag": "完整標籤:", "type": "類型:", "testTime": "測試時間:", "testDelay": "測試延遲:", "isSelected": "已選擇:", "isGroup": "是群組", "isSecure": "安全:", "port": "連接埠:", "host": "主機:", "ip": "IP:", "countryCode": "國家代碼:", "region": "地區:", "city": "城市:", "asn": "ASN:", "organization": "組織:", "location": "位置:", "postalCode": "郵遞區號:" }, "windowClosing": { "askEachTime": "每次詢問", "alertMessage": "隱藏還是退出應用程式?", "remember": "記住我的選擇" } }, "connection": { "tapToConnect": "點擊連線", "connect": "連線", "connecting": "連線中...", "connected": "已連線", "disconnect": "中斷連線", "disconnecting": "中斷連線中...", "reconnect": "重新連線", "reconnectMsg": "正在重新連線以套用變更...", "secure": "由 WARP 保護" }, "errors": { "unexpected": "意外錯誤", "connection": { "unexpected": "意外連線錯誤", "timeout": "連線超時", "badResponse": "回應錯誤", "connectionError": "連線錯誤", "badCertificate": "憑證無效" }, "profiles": { "unexpected": "意外錯誤", "notFound": "未找到設定檔", "invalidConfig": "設定無效", "invalidUrl": "URL 無效", "canceledByUser": "使用者已取消" }, "connectivity": { "unexpected": "意外失敗", "missingVpnPermission": "缺少 VPN 權限", "missingNotificationPermission": "缺少通知權限", "core": "核心錯誤" }, "singbox": { "serviceNotRunning": "服務未運行", "missingPrivilege": "缺少權限", "missingPrivilegeMsg": "VPN 模式需要管理員權限。請以管理員身份重新啟動應用,或更改服務模式。", "invalidConfigOptions": "設定選項無效", "invalidConfig": "設定無效" }, "warp": { "missingLicense": "WARP 授權缺失", "missingLicenseMsg": "所選設定檔使用 WARP 功能;要使用此功能,必須同意 WARP 授權條款。" } } } ================================================ FILE: build.yaml ================================================ targets: $default: builders: json_serializable: options: explicit_to_json: true drift_dev: options: databases: db: "lib/core/db/db.dart" schema_dir: "lib/core/db/schemas" store_date_time_values_as_text: true slang_build_runner: options: base_locale: en fallback_strategy: base_locale input_directory: assets/translations input_file_pattern: .i18n.json output_directory: lib/gen output_file_name: translations.g.dart translation_class_visibility: public locale_handling: false # protoc_builder: # options: # protobuf_version: "3.1.0" # protoc_plugin_version: "21.1.2" # root_dir: "assets/proto" # proto_paths: # - "assets/proto" # out_dir: "lib/gen/proto" # use_installed_protoc: true # precompile_protoc_plugin: false ================================================ FILE: dependencies.properties ================================================ core.version=4.1.0 ================================================ FILE: devtools_options.yaml ================================================ extensions: ================================================ FILE: ios/.gitignore ================================================ **/dgph *.mode1v3 *.mode2v3 *.moved-aside *.pbxuser *.perspectivev3 **/*sync/ .sconsign.dblite .tags* **/.vagrant/ **/DerivedData/ Icon? **/Pods/ **/.symlinks/ profile xcuserdata **/.generated/ Flutter/App.framework Flutter/Flutter.framework Flutter/Flutter.podspec Flutter/Generated.xcconfig Flutter/ephemeral/ Flutter/app.flx Flutter/app.zip Flutter/flutter_assets/ Flutter/flutter_export_environment.sh ServiceDefinitions.json Runner/GeneratedPluginRegistrant.* # Exceptions to above rules. !default.mode1v3 !default.mode2v3 !default.pbxuser !default.perspectivev3 ================================================ FILE: ios/.stignore ================================================ /**/dgph /*.mode1v3 /*.mode2v3 /*.moved-aside /*.pbxuser /*.perspectivev3 /**/*sync/ /.sconsign.dblite /.tags* /**/.vagrant/ /**/DerivedData/ /Icon? /**/Pods/ /**/.symlinks/ /profile /xcuserdata /**/.generated/ /Flutter/App.framework /Flutter/Flutter.framework /Flutter/Flutter.podspec /Flutter/Generated.xcconfig /Flutter/ephemeral/ /Flutter/app.flx /Flutter/app.zip /Flutter/flutter_assets/ /Flutter/flutter_export_environment.sh /ServiceDefinitions.json /Runner/GeneratedPluginRegistrant.* !default.mode1v3 !default.mode2v3 !default.pbxuser !default.perspectivev3 ================================================ FILE: ios/Base.xcconfig ================================================ // // Base.xcconfig // Runner // // Created by GFWFighter on 7/24/1402 AP. // BASE_BUNDLE_IDENTIFIER=apple.hiddify.com SERVICE_IDENTIFIER=com.hiddify.app DEVELOPMENT_TEAM=3JFTY5BP58 ================================================ FILE: ios/Flutter/AppFrameworkInfo.plist ================================================ CFBundleDevelopmentRegion en CFBundleExecutable App CFBundleIdentifier io.flutter.flutter.app CFBundleInfoDictionaryVersion 6.0 CFBundleName App CFBundlePackageType FMWK CFBundleShortVersionString 1.0 CFBundleSignature ???? CFBundleVersion 1.0 MinimumOSVersion 15.0 ================================================ FILE: ios/Flutter/Debug.xcconfig ================================================ #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" #include "Generated.xcconfig" #include "Base.xcconfig" ================================================ FILE: ios/Flutter/Release.xcconfig ================================================ #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" #include "Generated.xcconfig" #include "Base.xcconfig" ================================================ FILE: ios/Frameworks/.gitkeep ================================================ ================================================ FILE: ios/HiddifyPacketTunnel/HiddifyPacketTunnel.entitlements ================================================ com.apple.developer.networking.networkextension app-proxy-provider dns-proxy packet-tunnel-provider content-filter-provider com.apple.security.app-sandbox com.apple.security.application-groups group.$(BASE_BUNDLE_IDENTIFIER) com.apple.security.network.client com.apple.security.network.server com.apple.developer.networking.vpn.api allow-vpn ================================================ FILE: ios/HiddifyPacketTunnel/Info.plist ================================================ BASE_BUNDLE_IDENTIFIER $(BASE_BUNDLE_IDENTIFIER) NSExtension NSExtensionPointIdentifier com.apple.networkextension.packet-tunnel NSExtensionPrincipalClass $(PRODUCT_MODULE_NAME).PacketTunnelProvider ================================================ FILE: ios/HiddifyPacketTunnel/Logger.swift ================================================ // // Logger.swift // SingBoxPacketTunnel // // Created by GFWFighter on 10/24/23. // import Foundation class Logger2 { private static let queue = DispatchQueue.init(label: "\(FilePath.packageName).PacketTunnelLog", qos: .utility) private let fileManager = FileManager.default private let url: URL private var _fileHandle: FileHandle? private var fileHandle: FileHandle? { get { if let _fileHandle { return _fileHandle } let handle = try? FileHandle(forWritingTo: url) _fileHandle = handle return handle } } private var lock = NSLock() init(path: URL) { url = path } func write(_ message: String) { Logger2.queue.async { [message, unowned self] () in lock.lock() defer { lock.unlock() } let output = message + "\n" do { if !self.fileManager.fileExists(atPath: url.path) { try output.write(to: url, atomically: true, encoding: .utf8) } else { guard let fileHandle else { return } fileHandle.seekToEndOfFile() if let data = output.data(using: .utf8) { fileHandle.write(data) } } } catch {} } } } ================================================ FILE: ios/HiddifyPacketTunnel/PacketTunnelProvider.swift ================================================ // // PacketTunnelProvider.swift // SingBoxPacketTunnel // // Created by GFWFighter on 7/24/1402 AP. // import NetworkExtension class PacketTunnelProvider: ExtensionProvider { private var upload: Int64 = 0 private var download: Int64 = 0 // private var trafficLock: NSLock = NSLock() // var trafficReader: TrafficReader! override func startTunnel(options: [String : NSObject]?) async throws { // override func startTunnel(options: [String : NSObject]?, completionHandler: @escaping (Error?) -> Void) { NSLog("H?C1") try await super.startTunnel(options: options) /*trafficReader = TrafficReader { [unowned self] traffic in trafficLock.lock() upload += traffic.up download += traffic.down trafficLock.unlock() }*/ } override func handleAppMessage(_ messageData: Data) async -> Data? { let message = String(data: messageData, encoding: .utf8) // NSLog("H?C2"+message??"") switch message { case "stats": return "\(upload),\(download)".data(using: .utf8)! default: return nil } } } ================================================ FILE: ios/HiddifyPacketTunnel/PrivacyInfo.xcprivacy ================================================ NSPrivacyAccessedAPITypes NSPrivacyAccessedAPIType NSPrivacyAccessedAPICategoryFileTimestamp NSPrivacyAccessedAPITypeReasons C617.1 NSPrivacyAccessedAPIType NSPrivacyAccessedAPICategorySystemBootTime NSPrivacyAccessedAPITypeReasons 35F9.1 ================================================ FILE: ios/HiddifyPacketTunnel/SingBox/Extension+RunBlocking.swift ================================================ // // Extension+RunBlocking.swift // SingBoxPacketTunnel // // Created by GFWFighter on 7/25/1402 AP. // import Foundation import HiddifyCore import NetworkExtension func runBlocking(_ block: @escaping () async -> T) -> T { let semaphore = DispatchSemaphore(value: 0) let box = resultBox() Task.detached { let value = await block() box.result0 = value semaphore.signal() } semaphore.wait() return box.result0 } func runBlocking(_ tBlock: @escaping () async throws -> T) throws -> T { let semaphore = DispatchSemaphore(value: 0) let box = resultBox() Task.detached { do { let value = try await tBlock() box.result = .success(value) } catch { box.result = .failure(error) } semaphore.signal() } semaphore.wait() return try box.result.get() } private class resultBox { var result: Result! var result0: T! } ================================================ FILE: ios/HiddifyPacketTunnel/SingBox/ExtensionPlatformInterface.swift ================================================ // // ExtensionPlatformInterface.swift // SingBoxPacketTunnel // // Created by GFWFighter on 7/25/1402 AP. // import Foundation import HiddifyCore import NetworkExtension public class ExtensionPlatformInterface: NSObject, LibboxPlatformInterfaceProtocol { private var tunnel: ExtensionProvider private var networkSettings: NEPacketTunnelNetworkSettings? init(_ tunnel: ExtensionProvider) { self.tunnel = tunnel } public func openTun(_ options: LibboxTunOptionsProtocol?, ret0_: UnsafeMutablePointer?) throws { NSLog("H?A1") try runBlocking { [self] in try await openTun0(options, ret0_) } } private func openTun0(_ options: LibboxTunOptionsProtocol?, _ ret0_: UnsafeMutablePointer?) async throws { guard let options else { throw NSError(domain: "ExtensionPlatformInterface", code: 0, userInfo: [NSLocalizedDescriptionKey: String(localized: "Nil options")]) } guard let ret0_ else { throw NSError(domain: "ExtensionPlatformInterface", code: 0, userInfo: [NSLocalizedDescriptionKey: String(localized: "Nil return pointer")]) } // let prefs = tunnel.overridePreferences ?? ExtensionProvider.OverridePreferences() let autoRouteUseSubRangesByDefault = true//prefs.autoRouteUseSubRangesByDefault // await SharedPreferences.autoRouteUseSubRangesByDefault.get() let excludeAPNs = false//prefs.excludeAPNsRoute //await SharedPreferences.excludeAPNsRoute.get() let excludeDefaultRoute = false// prefs.excludeDefaultRoute let systemProxyEnabled = false//prefs.systemProxyEnabled let settings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: "::1") if options.getAutoRoute() { settings.mtu = NSNumber(value: options.getMTU()) let dnsServer = try options.getDNSServerAddress() let dnsSettings = NEDNSSettings(servers: [dnsServer.value,"fdfe:dcba:9876::1"]) dnsSettings.matchDomains = [""] dnsSettings.matchDomainsNoSearch = true settings.dnsSettings = dnsSettings var ipv4Address: [String] = [] var ipv4Mask: [String] = [] let ipv4AddressIterator = options.getInet4Address()! while ipv4AddressIterator.hasNext() { let ipv4Prefix = ipv4AddressIterator.next()! ipv4Address.append(ipv4Prefix.address()) ipv4Mask.append(ipv4Prefix.mask()) } let ipv4Settings = NEIPv4Settings(addresses: ipv4Address, subnetMasks: ipv4Mask) var ipv4Routes: [NEIPv4Route] = [] var ipv4ExcludeRoutes: [NEIPv4Route] = [] let inet4RouteAddressIterator = options.getInet4RouteAddress()! if inet4RouteAddressIterator.hasNext() { while inet4RouteAddressIterator.hasNext() { let ipv4RoutePrefix = inet4RouteAddressIterator.next()! ipv4Routes.append(NEIPv4Route(destinationAddress: ipv4RoutePrefix.address(), subnetMask: ipv4RoutePrefix.mask())) } } else if autoRouteUseSubRangesByDefault { ipv4Routes.append(NEIPv4Route(destinationAddress: "1.0.0.0", subnetMask: "255.0.0.0")) ipv4Routes.append(NEIPv4Route(destinationAddress: "2.0.0.0", subnetMask: "254.0.0.0")) ipv4Routes.append(NEIPv4Route(destinationAddress: "4.0.0.0", subnetMask: "252.0.0.0")) ipv4Routes.append(NEIPv4Route(destinationAddress: "8.0.0.0", subnetMask: "248.0.0.0")) ipv4Routes.append(NEIPv4Route(destinationAddress: "16.0.0.0", subnetMask: "240.0.0.0")) ipv4Routes.append(NEIPv4Route(destinationAddress: "32.0.0.0", subnetMask: "224.0.0.0")) ipv4Routes.append(NEIPv4Route(destinationAddress: "64.0.0.0", subnetMask: "192.0.0.0")) ipv4Routes.append(NEIPv4Route(destinationAddress: "128.0.0.0", subnetMask: "128.0.0.0")) ipv4Routes.append(NEIPv4Route.default()) } else { ipv4Routes.append(NEIPv4Route.default()) } let inet4RouteExcludeAddressIterator = options.getInet4RouteExcludeAddress()! while inet4RouteExcludeAddressIterator.hasNext() { let ipv4RoutePrefix = inet4RouteExcludeAddressIterator.next()! ipv4ExcludeRoutes.append(NEIPv4Route(destinationAddress: ipv4RoutePrefix.address(), subnetMask: ipv4RoutePrefix.mask())) } if excludeDefaultRoute, !ipv4Routes.isEmpty { if !ipv4ExcludeRoutes.contains(where: { it in it.destinationAddress == "0.0.0.0" && it.destinationSubnetMask == "255.255.255.254" }) { ipv4ExcludeRoutes.append(NEIPv4Route(destinationAddress: "0.0.0.0", subnetMask: "255.255.255.254")) } } if excludeAPNs, !ipv4Routes.isEmpty { if !ipv4ExcludeRoutes.contains(where: { it in it.destinationAddress == "17.0.0.0" && it.destinationSubnetMask == "255.0.0.0" }) { ipv4ExcludeRoutes.append(NEIPv4Route(destinationAddress: "17.0.0.0", subnetMask: "255.0.0.0")) } } ipv4Settings.includedRoutes = ipv4Routes ipv4Settings.excludedRoutes = ipv4ExcludeRoutes settings.ipv4Settings = ipv4Settings var ipv6Address: [String] = [] var ipv6Prefixes: [NSNumber] = [] let ipv6AddressIterator = options.getInet6Address()! while ipv6AddressIterator.hasNext() { let ipv6Prefix = ipv6AddressIterator.next()! ipv6Address.append(ipv6Prefix.address()) ipv6Prefixes.append(NSNumber(value: ipv6Prefix.prefix())) } let ipv6Settings = NEIPv6Settings(addresses: ipv6Address, networkPrefixLengths: ipv6Prefixes) var ipv6Routes: [NEIPv6Route] = [] var ipv6ExcludeRoutes: [NEIPv6Route] = [] let inet6RouteAddressIterator = options.getInet6RouteAddress()! if inet6RouteAddressIterator.hasNext() { while inet6RouteAddressIterator.hasNext() { let ipv6RoutePrefix = inet6RouteAddressIterator.next()! ipv6Routes.append(NEIPv6Route(destinationAddress: ipv6RoutePrefix.address(), networkPrefixLength: NSNumber(value: ipv6RoutePrefix.prefix()))) } } else if autoRouteUseSubRangesByDefault { ipv6Routes.append(NEIPv6Route(destinationAddress: "100::", networkPrefixLength: 8)) ipv6Routes.append(NEIPv6Route(destinationAddress: "200::", networkPrefixLength: 7)) ipv6Routes.append(NEIPv6Route(destinationAddress: "400::", networkPrefixLength: 6)) ipv6Routes.append(NEIPv6Route(destinationAddress: "800::", networkPrefixLength: 5)) ipv6Routes.append(NEIPv6Route(destinationAddress: "1000::", networkPrefixLength: 4)) ipv6Routes.append(NEIPv6Route(destinationAddress: "2000::", networkPrefixLength: 3)) ipv6Routes.append(NEIPv6Route(destinationAddress: "4000::", networkPrefixLength: 2)) ipv6Routes.append(NEIPv6Route(destinationAddress: "8000::", networkPrefixLength: 1)) ipv6Routes.append(NEIPv6Route.default()) } else { ipv6Routes.append(NEIPv6Route.default()) } let inet6RouteExcludeAddressIterator = options.getInet6RouteExcludeAddress()! while inet6RouteExcludeAddressIterator.hasNext() { let ipv6RoutePrefix = inet6RouteExcludeAddressIterator.next()! ipv6ExcludeRoutes.append(NEIPv6Route(destinationAddress: ipv6RoutePrefix.address(), networkPrefixLength: NSNumber(value: ipv6RoutePrefix.prefix()))) } if excludeDefaultRoute, !ipv6Routes.isEmpty { if !ipv6ExcludeRoutes.contains(where: { it in it.destinationAddress == "::" && it.destinationNetworkPrefixLength == 127 }) { ipv6ExcludeRoutes.append(NEIPv6Route(destinationAddress: "::", networkPrefixLength: 127)) } } ipv6Settings.includedRoutes = ipv6Routes ipv6Settings.excludedRoutes = ipv6ExcludeRoutes settings.ipv6Settings = ipv6Settings } if options.isHTTPProxyEnabled() { let proxySettings = NEProxySettings() let proxyServer = NEProxyServer(address: options.getHTTPProxyServer(), port: Int(options.getHTTPProxyServerPort())) proxySettings.httpServer = proxyServer proxySettings.httpsServer = proxyServer if systemProxyEnabled { proxySettings.httpEnabled = true proxySettings.httpsEnabled = true } var bypassDomains: [String] = [] let bypassDomainIterator = options.getHTTPProxyBypassDomain()! while bypassDomainIterator.hasNext() { bypassDomains.append(bypassDomainIterator.next()) } if excludeAPNs { if !bypassDomains.contains(where: { it in it == "push.apple.com" }) { bypassDomains.append("push.apple.com") } } if !bypassDomains.isEmpty { proxySettings.exceptionList = bypassDomains } var matchDomains: [String] = [] let matchDomainIterator = options.getHTTPProxyMatchDomain()! while matchDomainIterator.hasNext() { matchDomains.append(matchDomainIterator.next()) } if !matchDomains.isEmpty { proxySettings.matchDomains = matchDomains } settings.proxySettings = proxySettings } networkSettings = settings try await tunnel.setTunnelNetworkSettings(settings) if let tunFd = tunnel.packetFlow.value(forKeyPath: "socket.fileDescriptor") as? Int32 { ret0_.pointee = tunFd return } let tunFdFromLoop = LibboxGetTunnelFileDescriptor() if tunFdFromLoop != -1 { ret0_.pointee = tunFdFromLoop } else { throw NSError(domain: "missing file descriptor", code: 0) } } public func usePlatformAutoDetectControl() -> Bool { false } public func findConnectionOwner(_ ipProtocol: Int32, sourceAddress: String?, sourcePort: Int32, destinationAddress: String?, destinationPort: Int32) throws -> LibboxConnectionOwner { #if os(macOS) if Variant.useSystemExtension { guard let sourceAddress, let destinationAddress else { throw NSError(domain: "findConnectionOwner", code: 0, userInfo: [ NSLocalizedDescriptionKey: "Missing source or destination address", ]) } let owner = try RootHelperClient.shared.findConnectionOwner( ipProtocol: ipProtocol, sourceAddress: sourceAddress, sourcePort: sourcePort, destinationAddress: destinationAddress, destinationPort: destinationPort ) let result = LibboxConnectionOwner() result.userId = owner.userId result.userName = owner.userName result.processPath = owner.processPath return result } #endif throw NSError(domain: "ExtensionPlatformInterface", code: 0, userInfo: [NSLocalizedDescriptionKey: String(localized: "Not implemented")]) } public func useProcFS() -> Bool { false } public func writeLog(_ message: String?) { guard let message else { return } tunnel.writeMessage(message) } private var nwMonitor: NWPathMonitor? public func startDefaultInterfaceMonitor(_ listener: LibboxInterfaceUpdateListenerProtocol?) throws { return guard let listener else { return } let monitor = NWPathMonitor() nwMonitor = monitor let semaphore = DispatchSemaphore(value: 0) monitor.pathUpdateHandler = { path in self.onUpdateDefaultInterface(listener, path) semaphore.signal() monitor.pathUpdateHandler = { path in self.onUpdateDefaultInterface(listener, path) } } monitor.start(queue: DispatchQueue.global()) semaphore.wait() } private func onUpdateDefaultInterface(_ listener: LibboxInterfaceUpdateListenerProtocol, _ path: Network.NWPath) { return guard path.status != .unsatisfied, let defaultInterface = path.availableInterfaces.first else { listener.updateDefaultInterface("", interfaceIndex: -1, isExpensive: false, isConstrained: false) return } listener.updateDefaultInterface(defaultInterface.name, interfaceIndex: Int32(defaultInterface.index), isExpensive: path.isExpensive, isConstrained: path.isConstrained) } public func closeDefaultInterfaceMonitor(_: LibboxInterfaceUpdateListenerProtocol?) throws { nwMonitor?.cancel() nwMonitor = nil } public func getInterfaces() throws -> LibboxNetworkInterfaceIteratorProtocol { throw NSError(domain: "not implemented", code: 0) guard let nwMonitor else { throw NSError(domain: "ExtensionPlatformInterface", code: 0, userInfo: [NSLocalizedDescriptionKey: String(localized: "NWMonitor not started")]) } let path = nwMonitor.currentPath if path.status == .unsatisfied { return networkInterfaceArray([]) } var interfaces: [LibboxNetworkInterface] = [] for it in path.availableInterfaces { let interface = LibboxNetworkInterface() interface.name = it.name interface.index = Int32(it.index) switch it.type { case .wifi: interface.type = LibboxInterfaceTypeWIFI case .cellular: interface.type = LibboxInterfaceTypeCellular case .wiredEthernet: interface.type = LibboxInterfaceTypeEthernet default: interface.type = LibboxInterfaceTypeOther } interfaces.append(interface) } return networkInterfaceArray(interfaces) } class networkInterfaceArray: NSObject, LibboxNetworkInterfaceIteratorProtocol { private var iterator: IndexingIterator<[LibboxNetworkInterface]> init(_ array: [LibboxNetworkInterface]) { iterator = array.makeIterator() } private var nextValue: LibboxNetworkInterface? func hasNext() -> Bool { nextValue = iterator.next() return nextValue != nil } func next() -> LibboxNetworkInterface? { nextValue } } public func underNetworkExtension() -> Bool { true } public func includeAllNetworks() -> Bool { #if os(tvOS) return false #else return false // return tunnel.overridePreferences?.includeAllNetworks ?? false #endif } public func clearDNSCache() { guard let networkSettings else { return } runBlocking { self.tunnel.reasserting = true defer { self.tunnel.reasserting = false } await withCheckedContinuation { continuation in self.tunnel.setTunnelNetworkSettings(nil) { _ in continuation.resume() } } await withCheckedContinuation { continuation in self.tunnel.setTunnelNetworkSettings(networkSettings) { _ in continuation.resume() } } } } public func readWIFIState() -> LibboxWIFIState? { // #if os(iOS) // let network = runBlocking { // await NEHotspotNetwork.fetchCurrent() // } // guard let network else { // return nil // } // return LibboxWIFIState(network.ssid, wifiBSSID: network.bssid)! // #elseif os(macOS) // if Variant.useSystemExtension { // return UserServiceClient.shared.readWIFIState() // } // guard let interface = CWWiFiClient.shared().interface() else { // return nil // } // guard let ssid = interface.ssid() else { // return nil // } // guard let bssid = interface.bssid() else { // return nil // } // return LibboxWIFIState(ssid, wifiBSSID: bssid)! // #else return nil // #endif } public func readWIFISSID() -> String? { // #if os(iOS) // return runBlocking { // await NEHotspotNetwork.fetchCurrent()?.ssid // } // #elseif os(macOS) // return CWWiFiClient.shared().interface()?.ssid() // #else return nil // #endif } // public func serviceStop() throws { // tunnel.stopService() // } public func serviceReload() throws { try runBlocking { [self] in try await tunnel.reloadService() } } public func getSystemProxyStatus() throws -> LibboxSystemProxyStatus { let status = LibboxSystemProxyStatus() guard let networkSettings else { return status } guard let proxySettings = networkSettings.proxySettings else { return status } if proxySettings.httpServer == nil { return status } status.available = true status.enabled = proxySettings.httpEnabled return status } public func setSystemProxyEnabled(_ isEnabled: Bool) throws { guard let networkSettings else { return } guard let proxySettings = networkSettings.proxySettings else { return } if proxySettings.httpServer == nil { return } if proxySettings.httpEnabled == isEnabled { return } proxySettings.httpEnabled = isEnabled proxySettings.httpsEnabled = isEnabled networkSettings.proxySettings = proxySettings try runBlocking { try await self.tunnel.setTunnelNetworkSettings(networkSettings) } } public func writeDebugMessage(_ message: String?) { guard let message else { return } // tunnel.writeMessage(message) } func reset() { networkSettings = nil nwMonitor?.cancel() nwMonitor = nil } public func send(_ notification: LibboxNotification?) throws { #if !os(tvOS) guard let notification else { return } #if os(macOS) if Variant.useSystemExtension { try UserServiceClient.shared.sendNotification(notification) return } #endif // let center = UNUserNotificationCenter.current() // let content = UNMutableNotificationContent() // // content.title = notification.title // content.subtitle = notification.subtitle // content.body = notification.body // if !notification.openURL.isEmpty { // content.userInfo["OPEN_URL"] = notification.openURL // content.categoryIdentifier = "OPEN_URL" // } // content.interruptionLevel = .active // let request = UNNotificationRequest(identifier: notification.identifier, content: content, trigger: nil) // try runBlocking { // try await center.requestAuthorization(options: [.alert]) // try await center.add(request) // } #endif } public func localDNSTransport() -> (any LibboxLocalDNSTransportProtocol)? { nil } public func systemCertificates() -> (any LibboxStringIteratorProtocol)? { nil } public func autoDetectControl(_: Int32) throws {} } ================================================ FILE: ios/HiddifyPacketTunnel/SingBox/ExtensionProvider.swift ================================================ import Foundation import HiddifyCore import NetworkExtension import os.log open class ExtensionProvider: NEPacketTunnelProvider { public static let errorFile = FilePath.workingDirectory.appendingPathComponent("network_extension_error.log") private let logger = Logger(subsystem: "apple.hiddify.com.HiddifyPacketTunnel", category: "PacketTunnel") // private var commandServer: LibboxCommandServer! private var systemProxyAvailable = false private var systemProxyEnabled = false private var platformInterface: ExtensionPlatformInterface! private var config: String! override open func startTunnel(options: [String: NSObject]?) async throws { // Clear previous logs try? FileManager.default.removeItem(at: ExtensionProvider.errorFile) try? FileManager.default.removeItem(at: FilePath.workingDirectory.appendingPathComponent("TestLog")) do { writeMessage("(packet-tunnel) starting") // Extract options with better error handling let disableMemoryLimit = false && (options?["DisableMemoryLimit"] as? NSString as? String ?? "NO") == "YES" let grpcServiceModePort = (options?["GrpcServiceModePort"] as? NSNumber)?.intValue ?? 17079 let config = options?["Config"] as? NSString as? String ?? "" // guard let config = SingBox.setupConfig(config: config2) else { // writeFatalError("(packet-tunnel) error: config is invalid") // return // } // self.config = config do { try FileManager.default.createDirectory(at: FilePath.workingDirectory, withIntermediateDirectories: true) } catch { writeFatalError("(packet-tunnel) error: create working directory: \(error.localizedDescription)") return } // Ensure directories exist try createRequiredDirectories() // Log directory paths for debugging let sharedDir = FilePath.sharedDirectory.relativePath let workDir = FilePath.workingDirectory.relativePath let cacheDir = FilePath.cacheDirectory.relativePath if platformInterface == nil { platformInterface = ExtensionPlatformInterface(self) } // Initialize mobile setup with error handling var setupError: NSError? let opts = MobileSetupOptions() opts.basePath = sharedDir opts.workingDir = workDir opts.tempDir = cacheDir opts.listen = "127.0.0.1:\(grpcServiceModePort)" opts.secret = "" opts.debug = false opts.mode = 4 opts.fixAndroidStack = false MobileSetup(opts, platformInterface, &setupError ) if let setupError = setupError { throw setupError } LibboxSetMemoryLimit(!disableMemoryLimit) writeMessage("(packet-tunnel) setup completed successfully") if (config==""){ try await startService1(config) } } catch { logger.error("Tunnel setup failed: \(error.localizedDescription)") writeFatalError("(packet-tunnel) setup failed: \(error.localizedDescription)") throw error } } private func startService1(_ config: String) async throws { writeMessage("Starting service") var error: NSError? // do { try MobileStart(config, "", &error) if let error = error { throw error } writeMessage("(packet-tunnel) service started successfully") } catch { writeFatalError("(packet-tunnel) error: start service: \(error.localizedDescription)") throw error } } private func createRequiredDirectories() throws { let directories = [ FilePath.workingDirectory, FilePath.sharedDirectory, FilePath.cacheDirectory ] for directory in directories { do { try FileManager.default.createDirectory( at: directory, withIntermediateDirectories: true, attributes: nil ) } catch { logger.error("Failed to create directory at \(directory.path): \(error.localizedDescription)") throw error } } } func writeMessage(_ message: String) { logger.debug("\(message)") writeError(message) } func writeError(_ message: String) { let messageWithNewline = "[\(Date())] \(message)\n" do { if FileManager.default.fileExists(atPath: ExtensionProvider.errorFile.path) { if let fileHandle = try? FileHandle(forWritingTo: ExtensionProvider.errorFile) { defer { fileHandle.closeFile() } fileHandle.seekToEndOfFile() if let data = messageWithNewline.data(using: .utf8) { fileHandle.write(data) } } } else { try messageWithNewline.write(to: ExtensionProvider.errorFile, atomically: true, encoding: .utf8) } } catch { logger.error("Failed to write to error file: \(error.localizedDescription)") } } public func writeFatalError(_ message: String) { logger.fault("Fatal error: \(message)") writeError("FATAL: \(message)") cancelTunnelWithError(NSError(domain: "ExtensionProvider", code: 0, userInfo: [NSLocalizedDescriptionKey: message])) } override open func stopTunnel(with reason: NEProviderStopReason) async { // logger.debug("Stopping tunnel with reason: \(reason)") writeMessage("(packet-tunnel) stopping, reason: \(reason)") stopService() // // Allow time for cleanup // try? await Task.sleep(nanoseconds: 100 * NSEC_PER_MSEC) // // if let server = commandServer { // try? server.close() // commandServer = nil // } } private func stopService() { logger.debug("Stopping service") MobileClose(4) if let platformInterface { platformInterface.reset() } } func reloadService() async { logger.debug("Reloading service") writeMessage("(packet-tunnel) reloading service") // reasserting = true // defer { reasserting = false } // // stopService() // do { // guard let config = try? String(contentsOf: FilePath.configFile) else { // writeFatalError("(packet-tunnel) error: cannot read config file") // return // } // try await startService(config) // } catch { // writeFatalError("(packet-tunnel) error: reload service: \(error.localizedDescription)") // } } override open func handleAppMessage(_ messageData: Data) async -> Data? { logger.debug("Handling app message") return messageData } override open func sleep() async { logger.debug("Entering sleep mode") // MobilePause() // Add any sleep mode handling if needed } override open func wake() { logger.debug("Waking from sleep") MobileWake() // Add any wake handling if needed } } // Extension to support error handling extension ExtensionProvider { enum ExtensionError: Error { case configurationMissing case directoryCreationFailed case serviceStartFailed var localizedDescription: String { switch self { case .configurationMissing: return "Configuration not provided" case .directoryCreationFailed: return "Failed to create required directories" case .serviceStartFailed: return "Failed to start the service" } } } } ================================================ FILE: ios/HiddifyPacketTunnel/SingBox/SingBox.swift ================================================ // // SingBox.swift // SingBoxPacketTunnel // // Created by GFWFighter on 7/25/1402 AP. // import Foundation class SingBox { static func setupConfig(config: String, mtu: Int = 9000) -> String? { NSLog("H?B1") guard let config = config.data(using: .utf8), var json = try? JSONSerialization .jsonObject( with: config, options: [.mutableLeaves, .mutableContainers] ) as? [String:Any] else { return nil } /*json["log"] = [ "disabled": false, "level": "info", "output": "log", "timestamp": true ] as [String:Any] json["experimental"] = [ "clash_api": [ "external_controller": "127.0.0.1:10864" ] ] json["inbounds"] = [ [ "type": "tun", "inet4_address": "172.19.0.1/30", "auto_route": true, "mtu": mtu, "sniff": true ] as [String:Any] ] var routing = (json["route"] as? [String:Any]) ?? [ "rules": [Any](), "auto_detect_interface": true, "final": (json["inbounds"] as? [[String:Any]])?.first?["tag"] ?? "proxy" ] routing["geoip"] = [ "path": FilePath.assetsDirectory.appendingPathComponent("geoip.db"), ] routing["geosite"] = [ "path": FilePath.assetsDirectory.appendingPathComponent("geosite.db"), ] json["route"] = routing*/ guard let data = try? JSONSerialization.data(withJSONObject: json) else { return nil } return String(data: data, encoding: .utf8) } } ================================================ FILE: ios/HiddifyPacketTunnel/TrafficReader.swift ================================================ // // TrafficReader.swift // SingBoxPacketTunnel // // Created by GFWFighter on 7/25/1402 AP. // import Foundation struct TrafficReaderUpdate: Codable { let up: Int64 let down: Int64 } class TrafficReader { private var task: URLSessionWebSocketTask! private let callback: (TrafficReaderUpdate) -> () init(onUpdate: @escaping (TrafficReaderUpdate) -> ()) { NSLog("H?D2") self.callback = onUpdate Task(priority: .background) { [weak self] () in await self?.setup() } } private func setup() async { NSLog("H?D1") try? await Task.sleep(nanoseconds: 5_000_000_000) //return while true { do { let (_, response) = try await URLSession.shared.data(from: URL(string: "http://127.0.0.1:10864")!) let code = (response as? HTTPURLResponse)?.statusCode ?? -1 if code >= 200 && code < 300 { break } } catch { // pass } try? await Task.sleep(nanoseconds: 5_000_000) } let task = URLSession.shared.webSocketTask(with: URL(string: "ws://127.0.0.1:10864/traffic")!) self.task = task read() task.resume() } private func read() { NSLog("H?D3") task.receive { [weak self] result in switch result { case .failure(_): break case .success(let message): switch message { case .string(let message): guard let data = message.data(using: .utf8) else { break } guard let response = try? JSONDecoder().decode(TrafficReaderUpdate.self, from: data) else { break } self?.callback(response) default: break } self?.read() } } } } ================================================ FILE: ios/Local Packages/Package.swift ================================================ // swift-tools-version: 5.4 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription let package = Package( name: "Hiddify Packages", platforms: [ // Minimum platform version .iOS(.v13) ], products: [ .library( name: "HiddifyCore", targets: ["HiddifyCore"]), ], dependencies: [ // No dependencies ], targets: [ .binaryTarget( name: "HiddifyCore", path: "../Frameworks/HiddifyCore.xcframework" ) ] ) ================================================ FILE: ios/Podfile ================================================ # Uncomment this line to define a global platform for your project platform :ios, '15.5' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' project 'Runner', { 'Debug' => :debug, 'Profile' => :release, 'Release' => :release, } def flutter_root generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) unless File.exist?(generated_xcode_build_settings_path) raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" end File.foreach(generated_xcode_build_settings_path) do |line| matches = line.match(/FLUTTER_ROOT\=(.*)/) return matches[1].strip if matches end raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" end require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) flutter_ios_podfile_setup target 'Runner' do use_frameworks! use_modular_headers! flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) pod 'EasyPermissionX/Camera' target 'RunnerTests' do inherit! :search_paths end end post_install do |installer| installer.pods_project.targets.each do |target| flutter_additional_ios_build_settings(target) target.build_configurations.each do |config| config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '15.0' end end end ================================================ FILE: ios/Runner/AppDelegate.swift ================================================ import UIKit import Flutter import HiddifyCore import Sentry @main @objc class AppDelegate: FlutterAppDelegate { override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { setupFileManager() registerHandlers() GeneratedPluginRegistrant.register(with: self) return super.application(application, didFinishLaunchingWithOptions: launchOptions) } func setupFileManager() { try? FileManager.default.createDirectory(at: FilePath.workingDirectory, withIntermediateDirectories: true) FileManager.default.changeCurrentDirectoryPath(FilePath.sharedDirectory.path) } func registerHandlers() { MethodHandler.register(with: self.registrar(forPlugin: MethodHandler.name)!) PlatformMethodHandler.register(with: self.registrar(forPlugin: PlatformMethodHandler.name)!) FileMethodHandler.register(with: self.registrar(forPlugin: FileMethodHandler.name)!) StatusEventHandler.register(with: self.registrar(forPlugin: StatusEventHandler.name)!) AlertsEventHandler.register(with: self.registrar(forPlugin: AlertsEventHandler.name)!) // LogsEventHandler.register(with: self.registrar(forPlugin: LogsEventHandler.name)!) // GroupsEventHandler.register(with: self.registrar(forPlugin: GroupsEventHandler.name)!) // ActiveGroupsEventHandler.register(with: self.registrar(forPlugin: ActiveGroupsEventHandler.name)!) // StatsEventHandler.register(with: self.registrar(forPlugin: StatsEventHandler.name)!) } } ================================================ FILE: ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json ================================================ { "images" : [ { "filename" : "app-icon-1024.png", "idiom" : "universal", "platform" : "ios", "size" : "1024x1024" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: ios/Runner/Assets.xcassets/LaunchBackground.imageset/Contents.json ================================================ { "images" : [ { "filename" : "background.png", "idiom" : "universal", "scale" : "1x" }, { "idiom" : "universal", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json ================================================ { "images" : [ { "filename" : "LaunchImage.png", "idiom" : "universal", "scale" : "1x" }, { "filename" : "LaunchImage@2x.png", "idiom" : "universal", "scale" : "2x" }, { "filename" : "LaunchImage@3x.png", "idiom" : "universal", "scale" : "3x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md ================================================ # Launch Screen Assets You can customize the launch screen with your own desired assets by replacing the image files in this directory. You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. ================================================ FILE: ios/Runner/Base.lproj/LaunchScreen.storyboard ================================================ ================================================ FILE: ios/Runner/Base.lproj/Main.storyboard ================================================ ================================================ FILE: ios/Runner/Extensions/Bundle+Properties.swift ================================================ // // Bundle+Properties.swift // Runner // // Created by Hiddify on 12/26/23. // import Foundation extension Bundle { var serviceIdentifier: String { (infoDictionary?["SERVICE_IDENTIFIER"] as? String)! } var baseBundleIdentifier: String { (infoDictionary?["BASE_BUNDLE_IDENTIFIER"] as? String)! } } ================================================ FILE: ios/Runner/Handlers/ActiveGroupsEventHandler.swift ================================================ import Foundation import Combine import HiddifyCore public class ActiveGroupsEventHandler: NSObject, FlutterPlugin, FlutterStreamHandler { static let name = "\(Bundle.main.serviceIdentifier)/active-groups" private var commandClient: CommandClient? private var channel: FlutterEventChannel? private var events: FlutterEventSink? private var cancellable: AnyCancellable? public static func register(with registrar: FlutterPluginRegistrar) { let instance = ActiveGroupsEventHandler() instance.channel = FlutterEventChannel(name: Self.name, binaryMessenger: registrar.messenger()) instance.channel?.setStreamHandler(instance) } public func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? { FileManager.default.changeCurrentDirectoryPath(FilePath.sharedDirectory.path) self.events = events commandClient = CommandClient(.groupsInfoOnly) commandClient?.connect() cancellable = commandClient?.$groups.sink{ [self] sbGroups in self.writeGroups(sbGroups) } return nil } public func onCancel(withArguments arguments: Any?) -> FlutterError? { commandClient?.disconnect() cancellable?.cancel() events = nil return nil } func writeGroups(_ sbGroups: [SBGroup]?) { guard let sbGroups else {return} if let groups = try? JSONEncoder().encode(sbGroups), let groups = String(data: groups, encoding: .utf8) { DispatchQueue.main.async { [events = self.events, groups] () in events?(groups) } } } } ================================================ FILE: ios/Runner/Handlers/AlertsEventHandler.swift ================================================ // // AlertEventHandler.swift // Runner // // Created by GFWFighter on 10/24/23. // import Foundation import Combine public class AlertsEventHandler: NSObject, FlutterPlugin, FlutterStreamHandler { static let name = "\(Bundle.main.serviceIdentifier)/service.alerts" private var channel: FlutterEventChannel? private var cancellable: AnyCancellable? public static func register(with registrar: FlutterPluginRegistrar) { let instance = AlertsEventHandler() instance.channel = FlutterEventChannel(name: Self.name, binaryMessenger: registrar.messenger(), codec: FlutterJSONMethodCodec()) instance.channel?.setStreamHandler(instance) } public func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? { cancellable = VPNManager.shared.$alert.sink { [events] alert in var data = [ "status": "Stopped", "alert": alert.alert?.rawValue, "message": alert.message, ] for key in data.keys { if data[key] == nil { data.removeValue(forKey: key) } } events(data) } return nil } public func onCancel(withArguments arguments: Any?) -> FlutterError? { cancellable?.cancel() return nil } } ================================================ FILE: ios/Runner/Handlers/FileMethodHandler.swift ================================================ // // FileMethodHandler.swift // Runner // // Created by GFWFighter on 10/24/23. // import Foundation public class FileMethodHandler: NSObject, FlutterPlugin { public static let name = "\(Bundle.main.serviceIdentifier)/files.method" public static func register(with registrar: FlutterPluginRegistrar) { let channel = FlutterMethodChannel(name: Self.name, binaryMessenger: registrar.messenger()) let instance = FileMethodHandler() registrar.addMethodCallDelegate(instance, channel: channel) instance.channel = channel } private var channel: FlutterMethodChannel? public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { switch call.method { case "get_paths": result([ "working": FilePath.workingDirectory.path, "temp": FilePath.cacheDirectory.path, "base": FilePath.sharedDirectory.path ]) default: result(FlutterMethodNotImplemented) } } } ================================================ FILE: ios/Runner/Handlers/GroupsEventHandler.swift ================================================ import Foundation import Combine import HiddifyCore public class GroupsEventHandler: NSObject, FlutterPlugin, FlutterStreamHandler{ static let name = "\(Bundle.main.serviceIdentifier)/groups" private var commandClient: CommandClient? private var channel: FlutterEventChannel? private var events: FlutterEventSink? private var cancellable: AnyCancellable? public static func register(with registrar: FlutterPluginRegistrar) { let instance = GroupsEventHandler() instance.channel = FlutterEventChannel(name: Self.name, binaryMessenger: registrar.messenger()) instance.channel?.setStreamHandler(instance) } public func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? { FileManager.default.changeCurrentDirectoryPath(FilePath.sharedDirectory.path) self.events = events commandClient = CommandClient(.groups) commandClient?.connect() cancellable = commandClient?.$groups.sink{ [self] groups in self.writeGroups(groups) } return nil } public func onCancel(withArguments arguments: Any?) -> FlutterError? { commandClient?.disconnect() cancellable?.cancel() events = nil return nil } func writeGroups(_ sbGroups: [SBGroup]?) { guard let sbGroups else {return} if let groups = try? JSONEncoder().encode(sbGroups), let groups = String(data: groups, encoding: .utf8) { DispatchQueue.main.async { [events = self.events, groups] in events?(groups) } } } } ================================================ FILE: ios/Runner/Handlers/LogsEventHandler.swift ================================================ import Foundation import Combine import HiddifyCore class LogsEventHandler: NSObject, FlutterPlugin, FlutterStreamHandler { static let name = "\(Bundle.main.serviceIdentifier)/service.logs" private var commandClient: CommandClient? private var events: FlutterEventSink? private var channel: FlutterEventChannel? private var cancellable: AnyCancellable? public static func register(with registrar: FlutterPluginRegistrar) { let instance = LogsEventHandler() instance.channel = FlutterEventChannel(name: Self.name, binaryMessenger: registrar.messenger()) instance.channel?.setStreamHandler(instance) } public func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? { FileManager.default.changeCurrentDirectoryPath(FilePath.sharedDirectory.path) self.events = events commandClient = CommandClient(.log) commandClient?.connect() cancellable = commandClient?.$logList.sink{ [self] logs in events(logs) } return nil } public func onCancel(withArguments arguments: Any?) -> FlutterError? { commandClient?.disconnect() cancellable?.cancel() events = nil return nil } } /* extension LogsEventHandler { public func clearLog() {} public func connected() {} public func disconnected(_ message: String?) {} public func initializeClashMode(_ modeList: LibboxStringIteratorProtocol?, currentMode: String?) {} public func updateClashMode(_ newMode: String?) {} public func writeGroups(_ message: LibboxOutboundGroupIteratorProtocol?) {} public func writeStatus(_ message: LibboxStatusMessage?) {} } */ ================================================ FILE: ios/Runner/Handlers/MethodHandler.swift ================================================ // // MethodHandler.swift // Runner // // Created by GFWFighter on 10/23/23. // import Flutter import Combine import HiddifyCore public class MethodHandler: NSObject, FlutterPlugin { private var cancelBag: Set = [] public static let name = "\(Bundle.main.serviceIdentifier)/method" public static func register(with registrar: FlutterPluginRegistrar) { let channel = FlutterMethodChannel(name: Self.name, binaryMessenger: registrar.messenger()) let instance = MethodHandler() registrar.addMethodCallDelegate(instance, channel: channel) instance.channel = channel } private var channel: FlutterMethodChannel? public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { @Sendable func mainResult(_ res: Any?) async -> Void { await MainActor.run { result(res) } } switch call.method { case "get_grpc_server_public_key": result("") case "add_grpc_client_public_key": result("") case "parse_config": guard let args = call.arguments as? [String:Any?], let path = args["path"] as? String, let tempPath = args["tempPath"] as? String, let debug = (args["debug"] as? NSNumber)?.boolValue else { result(FlutterError(code: "INVALID_ARGS", message: nil, details: nil)) return } var error: NSError? //MobileParse(path, tempPath, debug, &error) if let error { result(FlutterError(code: String(error.code), message: error.description, details: nil)) return } result("") case "change_hiddify_options": guard let options = call.arguments as? String else { result(FlutterError(code: "INVALID_ARGS", message: nil, details: nil)) return } VPNConfig.shared.configOptions = options result(true) case "setup": Task { guard let args = call.arguments as? [String: Any?], let baseDir = args["baseDir"] as? String, let workingDir = args["workingDir"] as? String, let tempDir = args["tempDir"] as? String, let mode = args["mode"] as? Int, let grpcPort = args["grpcPort"] as? Int else { result(FlutterError(code: "INVALID_ARGS", message: nil, details: nil)) return } VPNConfig.shared.baseDir=baseDir VPNConfig.shared.workingDir=workingDir VPNConfig.shared.tempDir=tempDir var error: NSError? let opts = MobileSetupOptions() opts.basePath = baseDir opts.workingDir = workingDir opts.tempDir = tempDir opts.listen = "127.0.0.1:\(grpcPort)" opts.secret = "" opts.debug = false opts.mode = 4 opts.fixAndroidStack = false MobileSetup(opts, nil, &error ) if let error { result(FlutterError(code: String(error.code), message: error.localizedDescription, details: nil)) return } do { try await VPNManager.shared.setup() } catch { result(FlutterError(code: "SETUP", message: error.localizedDescription, details: nil)) return } result(true) } case "start": Task { guard let args = call.arguments as? [String:Any?], let path = args["path"] as? String, let name = args["name"] as? String, let grpcPort=args["grpcPort"] as? Int else { await mainResult(FlutterError(code: "INVALID_ARGS", message: nil, details: nil)) return } VPNConfig.shared.activeConfigPath = path VPNConfig.shared.activeProfileName = name VPNConfig.shared.grpcServiceModePort=grpcPort var error: NSError? //let configstr=MobileBuildConfig(path,&error) as String if let error { await mainResult(FlutterError(code: String(error.code), message: error.description, details: nil)) return } do { try await VPNManager.shared.setup() try await VPNManager.shared.connect(with: path, grpcServiceModePort: grpcPort, disableMemoryLimit: VPNConfig.shared.disableMemoryLimit) } catch { await mainResult(FlutterError(code: "SETUP_CONNECTION", message: error.localizedDescription, details: nil)) return } await mainResult(true) } // case "restart": // Task { [unowned self] in // guard // let args = call.arguments as? [String:Any?], // let path = args["path"] as? String, // let name = args["name"] as? String, // let grpcPort=args["grpcPort"] as? Int // else { // await mainResult(FlutterError(code: "INVALID_ARGS", message: nil, details: nil)) // return // } // VPNConfig.shared.activeConfigPath = path // VPNConfig.shared.activeProfileName = name // VPNConfig.shared.grpcServiceModePort=grpcPort // // // VPNManager.shared.disconnect() // await waitForStop().value // var error: NSError? // do { // try await VPNManager.shared.setup() // try await VPNManager.shared.connect(with: path, disableMemoryLimit: VPNConfig.shared.disableMemoryLimit) // } catch { // await mainResult(FlutterError(code: "SETUP_CONNECTION", message: error.localizedDescription, details: nil)) // return // } // await mainResult(true) // } case "stop": VPNManager.shared.disconnect() result(true) case "reset": VPNManager.shared.reset() result(true) case "url_test": guard let args = call.arguments as? [String:Any?] else { result(FlutterError(code: "INVALID_ARGS", message: nil, details: nil)) return } let group = args["groupTag"] as? String FileManager.default.changeCurrentDirectoryPath(FilePath.sharedDirectory.path) do { try LibboxNewStandaloneCommandClient()?.urlTest(group) } catch { result(FlutterError(code: "URL_TEST", message: error.localizedDescription, details: nil)) return } result(true) case "select_outbound": guard let args = call.arguments as? [String:Any?], let group = args["groupTag"] as? String, let outbound = args["outboundTag"] as? String else { result(FlutterError(code: "INVALID_ARGS", message: nil, details: nil)) return } FileManager.default.changeCurrentDirectoryPath(FilePath.sharedDirectory.path) do { try LibboxNewStandaloneCommandClient()?.selectOutbound(group, outboundTag: outbound) } catch { result(FlutterError(code: "SELECT_OUTBOUND", message: error.localizedDescription, details: nil)) return } result(true) case "generate_config": guard let args = call.arguments as? [String:Any?], let path = args["path"] as? String else { result(FlutterError(code: "INVALID_ARGS", message: nil, details: nil)) return } var error: NSError? // let config = MobileBuildConfig(path, VPNConfig.shared.configOptions, &error) // if let error { // result(FlutterError(code: "BUILD_CONFIG", message: error.localizedDescription, details: nil)) // return // } // result(config) case "generate_warp_config": guard let args = call.arguments as? [String: Any], let licenseKey = args["license-key"] as? String, let accountId = args["previous-account-id"] as? String, let accessToken = args["previous-access-token"] as? String else { result(FlutterError(code: "INVALID_ARGS", message: nil, details: nil)) return } // let warpConfig = MobileGenerateWarpConfig(licenseKey, accountId, accessToken, nil) // result(warpConfig) default: result(FlutterMethodNotImplemented) } } private func waitForStop() -> Future { return Future { promise in var cancellable: AnyCancellable? = nil cancellable = VPNManager.shared.$state .filter { $0 == .disconnected } .first() .delay(for: 0.5, scheduler: RunLoop.current) .sink(receiveValue: { _ in promise(.success(())) cancellable?.cancel() }) } } } ================================================ FILE: ios/Runner/Handlers/PlatformMethodHandler.swift ================================================ // // PlatformMethodHandler.swift // Runner // // Created by Hiddify on 12/27/23. // import Flutter import Combine import HiddifyCore public class PlatformMethodHandler: NSObject, FlutterPlugin { public static let name = "\(Bundle.main.serviceIdentifier)/platform" public static func register(with registrar: FlutterPluginRegistrar) { let channel = FlutterMethodChannel(name: Self.name, binaryMessenger: registrar.messenger()) let instance = PlatformMethodHandler() registrar.addMethodCallDelegate(instance, channel: channel) instance.channel = channel } private var channel: FlutterMethodChannel? public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { switch call.method { case "get_paths": result(getPaths(args: call.arguments) as NSDictionary) default: result(FlutterMethodNotImplemented) } } public func getPaths(args: Any?) -> [String:String] { return [ "base": FilePath.sharedDirectory.path, "working": FilePath.workingDirectory.path, "temp": FilePath.cacheDirectory.path ] } } ================================================ FILE: ios/Runner/Handlers/StatsEventHandler.swift ================================================ import Foundation import Flutter import Combine import HiddifyCore public class StatsEventHandler: NSObject, FlutterPlugin, FlutterStreamHandler { static let name = "\(Bundle.main.serviceIdentifier)/stats" private var commandClient: CommandClient? private var channel: FlutterEventChannel? private var events: FlutterEventSink? private var cancellable: AnyCancellable? public static func register(with registrar: FlutterPluginRegistrar) { let instance = StatsEventHandler() instance.channel = FlutterEventChannel(name: Self.name, binaryMessenger: registrar.messenger(), codec: FlutterJSONMethodCodec()) instance.channel?.setStreamHandler(instance) } public func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? { FileManager.default.changeCurrentDirectoryPath(FilePath.sharedDirectory.path) self.events = events commandClient = CommandClient(.status) commandClient?.connect() cancellable = commandClient?.$status.sink{ [self] status in self.writeStatus(status) } return nil } public func onCancel(withArguments arguments: Any?) -> FlutterError? { commandClient?.disconnect() cancellable?.cancel() events = nil return nil } func writeStatus(_ message: LibboxStatusMessage?) { guard let message else { return } let data = [ "connections-in": message.connectionsIn, "connections-out": message.connectionsOut, "uplink": message.uplink, "downlink": message.downlink, "uplink-total": message.uplinkTotal, "downlink-total": message.downlinkTotal ] as [String:Any] events?(data) } } ================================================ FILE: ios/Runner/Handlers/StatusEventHandler.swift ================================================ // // StatusEventHandler.swift // Runner // // Created by GFWFighter on 10/24/23. // import Foundation import Combine public class StatusEventHandler: NSObject, FlutterPlugin, FlutterStreamHandler { static let name = "\(Bundle.main.serviceIdentifier)/service.status" private var channel: FlutterEventChannel? private var cancellable: AnyCancellable? public static func register(with registrar: FlutterPluginRegistrar) { let instance = StatusEventHandler() instance.channel = FlutterEventChannel(name: Self.name, binaryMessenger: registrar.messenger(), codec: FlutterJSONMethodCodec()) instance.channel?.setStreamHandler(instance) } public func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? { cancellable = VPNManager.shared.$state.sink { [events] status in switch status { case .reasserting, .connecting: events(["status": "Starting"]) case .connected: events(["status": "Started"]) case .disconnecting: events(["status": "Stopping"]) case .disconnected, .invalid: events(["status": "Stopped"]) @unknown default: events(["status": "Stopped"]) } } return nil } public func onCancel(withArguments arguments: Any?) -> FlutterError? { cancellable?.cancel() return nil } } ================================================ FILE: ios/Runner/Info.plist ================================================ BASE_BUNDLE_IDENTIFIER $(BASE_BUNDLE_IDENTIFIER) CADisableMinimumFrameDurationOnPhone CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName Hiddify CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType APPL CFBundleShortVersionString $(FLUTTER_BUILD_NAME) CFBundleSignature ???? CFBundleURLTypes CFBundleURLSchemes hiddify v2ray v2rayn v2rayng clash clashmeta sing-box CFBundleTypeRole Editor CFBundleURLName com.hiddify.ios FlutterDeepLinkingEnabled CFBundleVersion $(FLUTTER_BUILD_NUMBER) EXAppExtensionAttributes EXExtensionPointIdentifier com.apple.appintents-extension ITSAppUsesNonExemptEncryption LSRequiresIPhoneOS NSCameraUsageDescription This app needs camera access to scan QR codes NSPhotoLibraryUsageDescription This app needs photos access to get QR code from photo library SERVICE_IDENTIFIER $(SERVICE_IDENTIFIER) UIApplicationSupportsIndirectInputEvents UILaunchStoryboardName LaunchScreen UIMainStoryboardFile Main UIRequiresFullScreen UIStatusBarHidden UISupportedInterfaceOrientations UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIViewControllerBasedStatusBarAppearance CFBundleLocalizations ar en es fa fr id pt-BR ru tr zh-CN zh-TW ================================================ FILE: ios/Runner/PrivacyInfo.xcprivacy ================================================ NSPrivacyAccessedAPITypes NSPrivacyAccessedAPIType NSPrivacyAccessedAPICategoryFileTimestamp NSPrivacyAccessedAPITypeReasons C617.1 NSPrivacyAccessedAPIType NSPrivacyAccessedAPICategoryDiskSpace NSPrivacyAccessedAPITypeReasons E174.1 NSPrivacyAccessedAPIType NSPrivacyAccessedAPICategorySystemBootTime NSPrivacyAccessedAPITypeReasons 35F9.1 NSPrivacyAccessedAPIType NSPrivacyAccessedAPICategoryUserDefaults NSPrivacyAccessedAPITypeReasons 1C8F.1 ================================================ FILE: ios/Runner/Runner-Bridging-Header.h ================================================ #import "GeneratedPluginRegistrant.h" ================================================ FILE: ios/Runner/Runner.entitlements ================================================ aps-environment development com.apple.developer.networking.networkextension app-proxy-provider dns-proxy packet-tunnel-provider com.apple.developer.networking.vpn.api allow-vpn com.apple.security.app-sandbox com.apple.security.application-groups group.$(BASE_BUNDLE_IDENTIFIER) com.apple.security.network.client com.apple.security.network.server ================================================ FILE: ios/Runner/VPN/Helpers/Stored.swift ================================================ // // Stored.swift // Runner // // Created by GFWFighter on 10/24/23. // import Foundation import Combine enum StoredLocation { case standard func data(for key: String) -> Data? { switch self { case .standard: return UserDefaults.standard.data(forKey: key) } } func set(_ value: Data, for key: String) { switch self { case .standard: UserDefaults.standard.set(value, forKey: key) } } } @propertyWrapper struct Stored { let location: StoredLocation let key: String var wrappedValue: Value { willSet { // Before modifying wrappedValue publisher.subject.send(newValue) guard let value = try? JSONEncoder().encode(newValue) else { return } location.set(value, for: key) } } var projectedValue: Publisher { publisher } private var publisher: Publisher struct Publisher: Combine.Publisher { typealias Output = Value typealias Failure = Never var subject: CurrentValueSubject // PassthroughSubject will lack the call of initial assignment func receive(subscriber: S) where S: Subscriber, Self.Failure == S.Failure, Self.Output == S.Input { subject.subscribe(subscriber) } init(_ output: Output) { subject = .init(output) } } init(wrappedValue: Value, key: String, in location: StoredLocation = .standard) { self.location = location self.key = key var value = wrappedValue if let data = location.data(for: key) { do { value = try JSONDecoder().decode(Value.self, from: data) } catch {} } self.wrappedValue = value publisher = Publisher(value) } static subscript( _enclosingInstance observed: OuterSelf, wrapped wrappedKeyPath: ReferenceWritableKeyPath, storage storageKeyPath: ReferenceWritableKeyPath ) -> Value { get { observed[keyPath: storageKeyPath].wrappedValue } set { if let subject = observed.objectWillChange as? ObservableObjectPublisher { subject.send() // Before modifying wrappedValue observed[keyPath: storageKeyPath].wrappedValue = newValue } } } } ================================================ FILE: ios/Runner/VPN/VPNConfig.swift ================================================ // // VPNConfig.swift // Runner // // Created by GFWFighter on 10/24/23. // import Foundation import Combine class VPNConfig: ObservableObject { static let shared = VPNConfig() @Stored(key: "VPN.ActiveConfigPath") var activeConfigPath: String = "" @Stored(key: "VPN.ActiveProfileName") var activeProfileName: String = "" @Stored(key: "VPN.GrpcServiceModePort") var grpcServiceModePort: Int = 0 @Stored(key: "VPN.workingDir") var workingDir: String = "" @Stored(key: "VPN.tempDir") var tempDir: String = "" @Stored(key: "VPN.baseDir") var baseDir: String = "" // @Stored(key: "VPN.grpcFlutterPublicKey") // var grpcFlutterPublicKey: String = "" @Stored(key: "VPN.ConfigOptions") var configOptions: String = "" @Stored(key: "VPN.DisableMemoryLimit") var disableMemoryLimit: Bool = false } ================================================ FILE: ios/Runner/VPN/VPNManager.swift ================================================ // // VPNManager.swift // Runner // // Created by GFWFighter on 7/25/1402 AP. // import Foundation import Combine import NetworkExtension enum VPNManagerAlertType: String { case RequestVPNPermission case RequestNotificationPermission case EmptyConfiguration case StartCommandServer case CreateService case StartService } struct VPNManagerAlert { let alert: VPNManagerAlertType? let message: String? } class VPNManager: ObservableObject { private var cancelBag: Set = [] private var observer: NSObjectProtocol? private var manager = NEVPNManager.shared() private var loaded: Bool = false private var timer: Timer? static let shared: VPNManager = VPNManager() @Published private(set) var state: NEVPNStatus = .invalid @Published private(set) var alert: VPNManagerAlert = .init(alert: nil, message: nil) @Published private(set) var upload: Int64 = 0 @Published private(set) var download: Int64 = 0 @Published private(set) var elapsedTime: TimeInterval = 0 private var _connectTime: Date? private var connectTime: Date? { set { UserDefaults(suiteName: FilePath.groupName)?.set(newValue?.timeIntervalSince1970, forKey: "SingBoxConnectTime") _connectTime = newValue } get { if let _connectTime { return _connectTime } guard let interval = UserDefaults(suiteName: FilePath.groupName)?.value(forKey: "SingBoxConnectTime") as? TimeInterval else { return nil } return Date(timeIntervalSince1970: interval) } } private var readingWS: Bool = false @Published var isConnectedToAnyVPN: Bool = false init() { observer = NotificationCenter.default.addObserver(forName: .NEVPNStatusDidChange, object: nil, queue: nil) { [weak self] notification in guard let connection = notification.object as? NEVPNConnection else { return } self?.state = connection.status } timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] _ in guard let self else { return } updateStats() elapsedTime = -1 * (connectTime?.timeIntervalSinceNow ?? 0) } } deinit { if let observer { NotificationCenter.default.removeObserver(observer) } timer?.invalidate() } func setup() async throws { // guard !loaded else { return } loaded = true do { try await loadVPNPreference() } catch { print(error.localizedDescription) } } private func loadVPNPreference() async throws { do { let managers = try await NETunnelProviderManager.loadAllFromPreferences() if let manager = managers.first { self.manager = manager return } let newManager = NETunnelProviderManager() let `protocol` = NETunnelProviderProtocol() `protocol`.providerBundleIdentifier = Bundle.main.baseBundleIdentifier + ".HiddifyPacketTunnel" `protocol`.serverAddress = "localhost" newManager.protocolConfiguration = `protocol` newManager.localizedDescription = "Hiddify" try await newManager.saveToPreferences() try await newManager.loadFromPreferences() self.manager = newManager } catch { print(error.localizedDescription) } } private func enableVPNManager() async throws { manager.isEnabled = true let rule = NEOnDemandRuleConnect() rule.interfaceTypeMatch = .any rule.probeURL = URL(string: "http://captive.apple.com") manager.onDemandRules = [rule] manager.isOnDemandEnabled = true do { try await manager.saveToPreferences() try await manager.loadFromPreferences() } catch { print(error.localizedDescription) } } @MainActor private func set(upload: Int64, download: Int64) { self.upload = upload self.download = download } var isAnyVPNConnected: Bool { guard let cfDict = CFNetworkCopySystemProxySettings() else { return false } let nsDict = cfDict.takeRetainedValue() as NSDictionary guard let keys = nsDict["__SCOPED__"] as? NSDictionary else { return false } for key: String in keys.allKeys as! [String] { if key == "tap" || key == "tun" || key == "ppp" || key == "ipsec" || key == "ipsec0" { return true } else if key.starts(with: "utun") { return true } } return false } func reset() { loaded = false if state != .disconnected && state != .invalid { disconnect() } $state.filter { $0 == .disconnected || $0 == .invalid }.first().sink { [weak self] _ in Task { [weak self] () in self?.manager = .shared() do { let managers = try await NETunnelProviderManager.loadAllFromPreferences() for manager in managers ?? [] { try await manager.removeFromPreferences() } try await self?.loadVPNPreference() } catch { print(error.localizedDescription) } } }.store(in: &cancelBag) } private func updateStats() { let isAnyVPNConnected = self.isAnyVPNConnected if isConnectedToAnyVPN != isAnyVPNConnected { isConnectedToAnyVPN = isAnyVPNConnected } guard state == .connected else { return } guard let connection = manager.connection as? NETunnelProviderSession else { return } do { try connection.sendProviderMessage("stats".data(using: .utf8)!) { [weak self] response in guard let response, let response = String(data: response, encoding: .utf8) else { return } let responseComponents = response.components(separatedBy: ",") guard responseComponents.count == 2, let upload = Int64(responseComponents[0]), let download = Int64(responseComponents[1]) else { return } Task { [upload, download, weak self] () in await self?.set(upload: upload, download: download) } } } catch { print(error.localizedDescription) } } func connect(with config: String, grpcServiceModePort:Int, disableMemoryLimit: Bool = false) async throws { await set(upload: 0, download: 0) // guard state == .disconnected else { return } do { try await enableVPNManager() try manager.connection.startVPNTunnel(options: [ "Config": config as NSString, "GrpcServiceModePort":NSNumber(value: grpcServiceModePort), "DisableMemoryLimit": (disableMemoryLimit ? "YES" : "NO") as NSString, ]) } catch { print(error.localizedDescription) } connectTime = .now } func disconnect() { if manager.isOnDemandEnabled { manager.isOnDemandEnabled = false manager.onDemandRules = [] manager.saveToPreferences { error in if let error = error { print("save error:", error) return } } } // guard state == .connected else { return } manager.connection.stopVPNTunnel() } } ================================================ FILE: ios/Runner.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 54; objects = { /* Begin PBXBuildFile section */ 032158B82ADDF8BF008D943B /* VPNManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 032158B72ADDF8BF008D943B /* VPNManager.swift */; }; 032158BA2ADDFCC9008D943B /* TrafficReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 032158B92ADDFCC9008D943B /* TrafficReader.swift */; }; 032158BC2ADDFD09008D943B /* SingBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 032158BB2ADDFD09008D943B /* SingBox.swift */; }; 03B516672AE6B93A00EA47E2 /* MethodHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B516662AE6B93A00EA47E2 /* MethodHandler.swift */; }; 03B516692AE7306B00EA47E2 /* StatusEventHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B516682AE7306B00EA47E2 /* StatusEventHandler.swift */; }; 03B5166B2AE7315E00EA47E2 /* AlertsEventHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B5166A2AE7315E00EA47E2 /* AlertsEventHandler.swift */; }; 03B5166D2AE7325500EA47E2 /* LogsEventHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B5166C2AE7325500EA47E2 /* LogsEventHandler.swift */; }; 03B516712AE74CCD00EA47E2 /* VPNConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B516702AE74CCD00EA47E2 /* VPNConfig.swift */; }; 03B516742AE74D2200EA47E2 /* Stored.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B516732AE74D2200EA47E2 /* Stored.swift */; }; 03B516762AE762F700EA47E2 /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B516752AE762F700EA47E2 /* Logger.swift */; }; 03B516772AE7634400EA47E2 /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B516752AE762F700EA47E2 /* Logger.swift */; }; 03B5167B2AE79DB400EA47E2 /* FileMethodHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B5167A2AE79DB400EA47E2 /* FileMethodHandler.swift */; }; 03B5167D2AE7AC6200EA47E2 /* GroupsEventHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B5167C2AE7AC6200EA47E2 /* GroupsEventHandler.swift */; }; 03E392BB2ADDA00F000ADF15 /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E392BA2ADDA00F000ADF15 /* PacketTunnelProvider.swift */; }; 03E392CC2ADDE078000ADF15 /* ExtensionProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E392CB2ADDE078000ADF15 /* ExtensionProvider.swift */; }; 03E392CF2ADDEFC8000ADF15 /* FilePath.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E392CE2ADDEFC8000ADF15 /* FilePath.swift */; }; 03E392D02ADDF1BD000ADF15 /* FilePath.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E392CE2ADDEFC8000ADF15 /* FilePath.swift */; }; 03E392D22ADDF1F4000ADF15 /* ExtensionPlatformInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E392D12ADDF1F4000ADF15 /* ExtensionPlatformInterface.swift */; }; 03E392D42ADDF262000ADF15 /* Extension+RunBlocking.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E392D32ADDF262000ADF15 /* Extension+RunBlocking.swift */; }; 0736958B2B3AC96D007249BE /* Bundle+Properties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0736958A2B3AC96D007249BE /* Bundle+Properties.swift */; }; 0736958D2B3B79E0007249BE /* StatsEventHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0736958C2B3B79E0007249BE /* StatsEventHandler.swift */; }; 0736958F2B3B8048007249BE /* PlatformMethodHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0736958E2B3B8047007249BE /* PlatformMethodHandler.swift */; }; 075637BA2B01583F005AFB8E /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60F1D4AAC33ACF5C8307310D /* Pods_Runner.framework */; }; 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 41E9FF922D1C0CC3003780C1 /* HiddifyPacketTunnel.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 03E392B62ADDA00E000ADF15 /* HiddifyPacketTunnel.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 41E9FF962D1C82BA003780C1 /* HiddifyCore.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 413270682C8A9BA2003A1E9B /* HiddifyCore.xcframework */; }; 59E8864FB99B37076B22F32B /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 30560DB3EFDF5E86AAD00AB8 /* Pods_RunnerTests.framework */; }; 68885DD72B4EF33400D214BA /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 03E392B72ADDA00E000ADF15 /* NetworkExtension.framework */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; D703EE932B764EA3001D88B3 /* CommandClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = D703EE922B764EA3001D88B3 /* CommandClient.swift */; }; D703EE962B765176001D88B3 /* ActiveGroupsEventHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = D703EE952B765176001D88B3 /* ActiveGroupsEventHandler.swift */; }; D7CC50862B768C50006BC140 /* Outbound.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7CC50852B768C50006BC140 /* Outbound.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ 03E392BE2ADDA00F000ADF15 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 97C146E61CF9000F007C117D /* Project object */; proxyType = 1; remoteGlobalIDString = 03E392B52ADDA00E000ADF15; remoteInfo = HiddifyPacketTunnel; }; 331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 97C146E61CF9000F007C117D /* Project object */; proxyType = 1; remoteGlobalIDString = 97C146ED1CF9000F007C117D; remoteInfo = Runner; }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ 03E392C12ADDA00F000ADF15 /* Embed Foundation Extensions */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 13; files = ( 41E9FF922D1C0CC3003780C1 /* HiddifyPacketTunnel.appex in Embed Foundation Extensions */, ); name = "Embed Foundation Extensions"; runOnlyForDeploymentPostprocessing = 0; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ 032158B72ADDF8BF008D943B /* VPNManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNManager.swift; sourceTree = ""; }; 032158B92ADDFCC9008D943B /* TrafficReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrafficReader.swift; sourceTree = ""; }; 032158BB2ADDFD09008D943B /* SingBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SingBox.swift; sourceTree = ""; }; 0337C822BEDF7A95576475B3 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; 03B516662AE6B93A00EA47E2 /* MethodHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MethodHandler.swift; sourceTree = ""; }; 03B516682AE7306B00EA47E2 /* StatusEventHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusEventHandler.swift; sourceTree = ""; }; 03B5166A2AE7315E00EA47E2 /* AlertsEventHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertsEventHandler.swift; sourceTree = ""; }; 03B5166C2AE7325500EA47E2 /* LogsEventHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogsEventHandler.swift; sourceTree = ""; }; 03B516702AE74CCD00EA47E2 /* VPNConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNConfig.swift; sourceTree = ""; }; 03B516732AE74D2200EA47E2 /* Stored.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Stored.swift; sourceTree = ""; }; 03B516752AE762F700EA47E2 /* Logger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = ""; }; 03B5167A2AE79DB400EA47E2 /* FileMethodHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileMethodHandler.swift; sourceTree = ""; }; 03B5167C2AE7AC6200EA47E2 /* GroupsEventHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupsEventHandler.swift; sourceTree = ""; }; 03E392B62ADDA00E000ADF15 /* HiddifyPacketTunnel.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = HiddifyPacketTunnel.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 03E392B72ADDA00E000ADF15 /* NetworkExtension.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = NetworkExtension.framework; path = System/Library/Frameworks/NetworkExtension.framework; sourceTree = SDKROOT; }; 03E392BA2ADDA00F000ADF15 /* PacketTunnelProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PacketTunnelProvider.swift; sourceTree = ""; }; 03E392BC2ADDA00F000ADF15 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 03E392BD2ADDA00F000ADF15 /* HiddifyPacketTunnel.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = HiddifyPacketTunnel.entitlements; sourceTree = ""; }; 03E392C62ADDA064000ADF15 /* Base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Base.xcconfig; sourceTree = ""; }; 03E392C72ADDA26A000ADF15 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = ""; }; 03E392CB2ADDE078000ADF15 /* ExtensionProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionProvider.swift; sourceTree = ""; }; 03E392CE2ADDEFC8000ADF15 /* FilePath.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilePath.swift; sourceTree = ""; }; 03E392D12ADDF1F4000ADF15 /* ExtensionPlatformInterface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionPlatformInterface.swift; sourceTree = ""; }; 03E392D32ADDF262000ADF15 /* Extension+RunBlocking.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Extension+RunBlocking.swift"; sourceTree = ""; }; 040FA3EB0673B72D60CC7145 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; 0736954E2B1FEB3E007249BE /* mobile_scanner.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = mobile_scanner.xcframework; path = ../build/ios/framework/Release/mobile_scanner.xcframework; sourceTree = ""; }; 0736958A2B3AC96D007249BE /* Bundle+Properties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Bundle+Properties.swift"; sourceTree = ""; }; 0736958C2B3B79E0007249BE /* StatsEventHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatsEventHandler.swift; sourceTree = ""; }; 0736958E2B3B8047007249BE /* PlatformMethodHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlatformMethodHandler.swift; sourceTree = ""; }; 07A63A832B1E728C00CAFA4D /* Release */ = {isa = PBXFileReference; lastKnownFileType = folder; name = Release; path = ../build/ios/framework/release; sourceTree = ""; }; 07A63A842B1E72AE00CAFA4D /* App.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = App.xcframework; path = ../build/ios/framework/release/App.xcframework; sourceTree = ""; }; 07A63A872B1E72C800CAFA4D /* Flutter.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Flutter.xcframework; path = ../build/ios/framework/release/Flutter.xcframework; sourceTree = ""; }; 07A63A8C2B1E72FA00CAFA4D /* GTMSessionFetcher.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = GTMSessionFetcher.xcframework; path = ../build/ios/framework/release/GTMSessionFetcher.xcframework; sourceTree = ""; }; 07A63A8D2B1E72FB00CAFA4D /* package_info_plus.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = package_info_plus.xcframework; path = ../build/ios/framework/release/package_info_plus.xcframework; sourceTree = ""; }; 07A63A8E2B1E72FB00CAFA4D /* SentryPrivate.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = SentryPrivate.xcframework; path = ../build/ios/framework/release/SentryPrivate.xcframework; sourceTree = ""; }; 07A63A8F2B1E72FB00CAFA4D /* share_plus.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = share_plus.xcframework; path = ../build/ios/framework/release/share_plus.xcframework; sourceTree = ""; }; 07A63A902B1E72FB00CAFA4D /* url_launcher_ios.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = url_launcher_ios.xcframework; path = ../build/ios/framework/release/url_launcher_ios.xcframework; sourceTree = ""; }; 07A63A912B1E72FB00CAFA4D /* mobile_scanner.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = mobile_scanner.xcframework; path = ../build/ios/framework/release/mobile_scanner.xcframework; sourceTree = ""; }; 07A63A922B1E72FB00CAFA4D /* sqlite3_flutter_libs.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = sqlite3_flutter_libs.xcframework; path = ../build/ios/framework/release/sqlite3_flutter_libs.xcframework; sourceTree = ""; }; 07A63A932B1E72FB00CAFA4D /* cupertino_http.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = cupertino_http.xcframework; path = ../build/ios/framework/release/cupertino_http.xcframework; sourceTree = ""; }; 07A63A942B1E72FB00CAFA4D /* flutter_native_splash.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = flutter_native_splash.xcframework; path = ../build/ios/framework/release/flutter_native_splash.xcframework; sourceTree = ""; }; 07A63A952B1E72FB00CAFA4D /* FBLPromises.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = FBLPromises.xcframework; path = ../build/ios/framework/release/FBLPromises.xcframework; sourceTree = ""; }; 07A63A962B1E72FB00CAFA4D /* GoogleUtilities.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = GoogleUtilities.xcframework; path = ../build/ios/framework/release/GoogleUtilities.xcframework; sourceTree = ""; }; 07A63A972B1E72FB00CAFA4D /* device_info_plus.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = device_info_plus.xcframework; path = ../build/ios/framework/release/device_info_plus.xcframework; sourceTree = ""; }; 07A63A982B1E72FB00CAFA4D /* GoogleDataTransport.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = GoogleDataTransport.xcframework; path = ../build/ios/framework/release/GoogleDataTransport.xcframework; sourceTree = ""; }; 07A63A992B1E72FB00CAFA4D /* sentry_flutter.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = sentry_flutter.xcframework; path = ../build/ios/framework/release/sentry_flutter.xcframework; sourceTree = ""; }; 07A63A9A2B1E72FB00CAFA4D /* protocol_handler.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = protocol_handler.xcframework; path = ../build/ios/framework/release/protocol_handler.xcframework; sourceTree = ""; }; 07A63A9B2B1E72FC00CAFA4D /* sqlite3.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = sqlite3.xcframework; path = ../build/ios/framework/release/sqlite3.xcframework; sourceTree = ""; }; 07A63A9C2B1E72FC00CAFA4D /* GoogleToolboxForMac.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = GoogleToolboxForMac.xcframework; path = ../build/ios/framework/release/GoogleToolboxForMac.xcframework; sourceTree = ""; }; 07A63A9D2B1E72FC00CAFA4D /* GoogleUtilitiesComponents.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = GoogleUtilitiesComponents.xcframework; path = ../build/ios/framework/release/GoogleUtilitiesComponents.xcframework; sourceTree = ""; }; 07A63A9E2B1E72FC00CAFA4D /* nanopb.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = nanopb.xcframework; path = ../build/ios/framework/release/nanopb.xcframework; sourceTree = ""; }; 07A63A9F2B1E72FC00CAFA4D /* path_provider_foundation.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = path_provider_foundation.xcframework; path = ../build/ios/framework/release/path_provider_foundation.xcframework; sourceTree = ""; }; 07A63AA02B1E72FC00CAFA4D /* shared_preferences_foundation.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = shared_preferences_foundation.xcframework; path = ../build/ios/framework/release/shared_preferences_foundation.xcframework; sourceTree = ""; }; 07A63AA12B1E72FC00CAFA4D /* Sentry.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Sentry.xcframework; path = ../build/ios/framework/release/Sentry.xcframework; sourceTree = ""; }; 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 30560DB3EFDF5E86AAD00AB8 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 413270682C8A9BA2003A1E9B /* HiddifyCore.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = HiddifyCore.xcframework; path = Frameworks/HiddifyCore.xcframework; sourceTree = ""; }; 574F12C7748958784380337F /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 60F1D4AAC33ACF5C8307310D /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 6836D3FA2B57FDFF00A79D75 /* Package.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = ""; }; 68DCEB762BD7D7590081FF65 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; 68DCEB772BD7DA3F0081FF65 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 7CA62594950187FCFE36B54C /* Pods-Runner-HiddifyPacketTunnel.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner-HiddifyPacketTunnel.debug.xcconfig"; path = "Target Support Files/Pods-Runner-HiddifyPacketTunnel/Pods-Runner-HiddifyPacketTunnel.debug.xcconfig"; sourceTree = ""; }; 808A94F72872B54716770416 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; 90E93DE403BDFA627F3AA51E /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 9AC67B4DCF829F5B6F63AA7D /* Pods-Runner-HiddifyPacketTunnel.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner-HiddifyPacketTunnel.release.xcconfig"; path = "Target Support Files/Pods-Runner-HiddifyPacketTunnel/Pods-Runner-HiddifyPacketTunnel.release.xcconfig"; sourceTree = ""; }; C20A211B58CE31B2738D133C /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; D703EE922B764EA3001D88B3 /* CommandClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommandClient.swift; sourceTree = ""; }; D703EE952B765176001D88B3 /* ActiveGroupsEventHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActiveGroupsEventHandler.swift; sourceTree = ""; }; D7CC50852B768C50006BC140 /* Outbound.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Outbound.swift; sourceTree = ""; }; F3FFE1D9C2D5629FACC123EE /* Pods-Runner-HiddifyPacketTunnel.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner-HiddifyPacketTunnel.profile.xcconfig"; path = "Target Support Files/Pods-Runner-HiddifyPacketTunnel/Pods-Runner-HiddifyPacketTunnel.profile.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 03E392B32ADDA00E000ADF15 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 41E9FF962D1C82BA003780C1 /* HiddifyCore.xcframework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; 531FE8242BCD501C24C8E9FA /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 59E8864FB99B37076B22F32B /* Pods_RunnerTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; 97C146EB1CF9000F007C117D /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 075637BA2B01583F005AFB8E /* Pods_Runner.framework in Frameworks */, 68885DD72B4EF33400D214BA /* NetworkExtension.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 032158B62ADDF8AF008D943B /* VPN */ = { isa = PBXGroup; children = ( 03B516722AE74D1700EA47E2 /* Helpers */, 032158B72ADDF8BF008D943B /* VPNManager.swift */, 03B516702AE74CCD00EA47E2 /* VPNConfig.swift */, ); path = VPN; sourceTree = ""; }; 03B5166E2AE7325D00EA47E2 /* Handlers */ = { isa = PBXGroup; children = ( 03B516662AE6B93A00EA47E2 /* MethodHandler.swift */, 0736958E2B3B8047007249BE /* PlatformMethodHandler.swift */, 03B516682AE7306B00EA47E2 /* StatusEventHandler.swift */, 03B5167A2AE79DB400EA47E2 /* FileMethodHandler.swift */, 03B5166A2AE7315E00EA47E2 /* AlertsEventHandler.swift */, 03B5166C2AE7325500EA47E2 /* LogsEventHandler.swift */, 03B5167C2AE7AC6200EA47E2 /* GroupsEventHandler.swift */, 0736958C2B3B79E0007249BE /* StatsEventHandler.swift */, D703EE952B765176001D88B3 /* ActiveGroupsEventHandler.swift */, ); path = Handlers; sourceTree = ""; }; 03B516722AE74D1700EA47E2 /* Helpers */ = { isa = PBXGroup; children = ( 03B516732AE74D2200EA47E2 /* Stored.swift */, ); path = Helpers; sourceTree = ""; }; 03E392B92ADDA00F000ADF15 /* HiddifyPacketTunnel */ = { isa = PBXGroup; children = ( 03E392CA2ADDE063000ADF15 /* SingBox */, 03E392BA2ADDA00F000ADF15 /* PacketTunnelProvider.swift */, 032158B92ADDFCC9008D943B /* TrafficReader.swift */, 03B516752AE762F700EA47E2 /* Logger.swift */, 03E392BC2ADDA00F000ADF15 /* Info.plist */, 03E392BD2ADDA00F000ADF15 /* HiddifyPacketTunnel.entitlements */, 68DCEB762BD7D7590081FF65 /* PrivacyInfo.xcprivacy */, ); path = HiddifyPacketTunnel; sourceTree = ""; }; 03E392CA2ADDE063000ADF15 /* SingBox */ = { isa = PBXGroup; children = ( 03E392CB2ADDE078000ADF15 /* ExtensionProvider.swift */, 03E392D12ADDF1F4000ADF15 /* ExtensionPlatformInterface.swift */, 03E392D32ADDF262000ADF15 /* Extension+RunBlocking.swift */, 032158BB2ADDFD09008D943B /* SingBox.swift */, ); path = SingBox; sourceTree = ""; }; 03E392CD2ADDE103000ADF15 /* Shared */ = { isa = PBXGroup; children = ( 03E392CE2ADDEFC8000ADF15 /* FilePath.swift */, D703EE922B764EA3001D88B3 /* CommandClient.swift */, D7CC50852B768C50006BC140 /* Outbound.swift */, ); path = Shared; sourceTree = ""; }; 073695892B3AC954007249BE /* Extensions */ = { isa = PBXGroup; children = ( 0736958A2B3AC96D007249BE /* Bundle+Properties.swift */, ); path = Extensions; sourceTree = ""; }; 311A4F4314861E02331B8DAC /* Pods */ = { isa = PBXGroup; children = ( 574F12C7748958784380337F /* Pods-Runner.debug.xcconfig */, 90E93DE403BDFA627F3AA51E /* Pods-Runner.release.xcconfig */, C20A211B58CE31B2738D133C /* Pods-Runner.profile.xcconfig */, 7CA62594950187FCFE36B54C /* Pods-Runner-HiddifyPacketTunnel.debug.xcconfig */, 9AC67B4DCF829F5B6F63AA7D /* Pods-Runner-HiddifyPacketTunnel.release.xcconfig */, F3FFE1D9C2D5629FACC123EE /* Pods-Runner-HiddifyPacketTunnel.profile.xcconfig */, 0337C822BEDF7A95576475B3 /* Pods-RunnerTests.debug.xcconfig */, 808A94F72872B54716770416 /* Pods-RunnerTests.release.xcconfig */, 040FA3EB0673B72D60CC7145 /* Pods-RunnerTests.profile.xcconfig */, ); path = Pods; sourceTree = ""; }; 331C8082294A63A400263BE5 /* RunnerTests */ = { isa = PBXGroup; children = ( 331C807B294A618700263BE5 /* RunnerTests.swift */, ); path = RunnerTests; sourceTree = ""; }; 6836D3FF2B57FECF00A79D75 /* Local Packages */ = { isa = PBXGroup; children = ( 6836D3FA2B57FDFF00A79D75 /* Package.swift */, ); path = "Local Packages"; sourceTree = ""; }; 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 9740EEB21CF90195004384FC /* Debug.xcconfig */, 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 9740EEB31CF90195004384FC /* Generated.xcconfig */, 03E392C62ADDA064000ADF15 /* Base.xcconfig */, ); name = Flutter; sourceTree = ""; }; 97C146E51CF9000F007C117D = { isa = PBXGroup; children = ( 6836D3FF2B57FECF00A79D75 /* Local Packages */, 03E392CD2ADDE103000ADF15 /* Shared */, 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, 03E392B92ADDA00F000ADF15 /* HiddifyPacketTunnel */, 97C146EF1CF9000F007C117D /* Products */, 331C8082294A63A400263BE5 /* RunnerTests */, 311A4F4314861E02331B8DAC /* Pods */, B8133545EEE13EDD5549E6A3 /* Frameworks */, ); sourceTree = ""; }; 97C146EF1CF9000F007C117D /* Products */ = { isa = PBXGroup; children = ( 97C146EE1CF9000F007C117D /* Runner.app */, 331C8081294A63A400263BE5 /* RunnerTests.xctest */, 03E392B62ADDA00E000ADF15 /* HiddifyPacketTunnel.appex */, ); name = Products; sourceTree = ""; }; 97C146F01CF9000F007C117D /* Runner */ = { isa = PBXGroup; children = ( 073695892B3AC954007249BE /* Extensions */, 03B5166E2AE7325D00EA47E2 /* Handlers */, 032158B62ADDF8AF008D943B /* VPN */, 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 03E392C72ADDA26A000ADF15 /* Runner.entitlements */, 97C146FA1CF9000F007C117D /* Main.storyboard */, 97C146FD1CF9000F007C117D /* Assets.xcassets */, 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 97C147021CF9000F007C117D /* Info.plist */, 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, 68DCEB772BD7DA3F0081FF65 /* PrivacyInfo.xcprivacy */, ); path = Runner; sourceTree = ""; }; B8133545EEE13EDD5549E6A3 /* Frameworks */ = { isa = PBXGroup; children = ( 413270682C8A9BA2003A1E9B /* HiddifyCore.xcframework */, 0736954E2B1FEB3E007249BE /* mobile_scanner.xcframework */, 07A63A932B1E72FB00CAFA4D /* cupertino_http.xcframework */, 07A63A972B1E72FB00CAFA4D /* device_info_plus.xcframework */, 07A63A952B1E72FB00CAFA4D /* FBLPromises.xcframework */, 07A63A942B1E72FB00CAFA4D /* flutter_native_splash.xcframework */, 07A63A982B1E72FB00CAFA4D /* GoogleDataTransport.xcframework */, 07A63A9C2B1E72FC00CAFA4D /* GoogleToolboxForMac.xcframework */, 07A63A962B1E72FB00CAFA4D /* GoogleUtilities.xcframework */, 07A63A9D2B1E72FC00CAFA4D /* GoogleUtilitiesComponents.xcframework */, 07A63A8C2B1E72FA00CAFA4D /* GTMSessionFetcher.xcframework */, 07A63A912B1E72FB00CAFA4D /* mobile_scanner.xcframework */, 07A63A9E2B1E72FC00CAFA4D /* nanopb.xcframework */, 07A63A8D2B1E72FB00CAFA4D /* package_info_plus.xcframework */, 07A63A9F2B1E72FC00CAFA4D /* path_provider_foundation.xcframework */, 07A63A9A2B1E72FB00CAFA4D /* protocol_handler.xcframework */, 07A63A992B1E72FB00CAFA4D /* sentry_flutter.xcframework */, 07A63AA12B1E72FC00CAFA4D /* Sentry.xcframework */, 07A63A8E2B1E72FB00CAFA4D /* SentryPrivate.xcframework */, 07A63A8F2B1E72FB00CAFA4D /* share_plus.xcframework */, 07A63AA02B1E72FC00CAFA4D /* shared_preferences_foundation.xcframework */, 07A63A922B1E72FB00CAFA4D /* sqlite3_flutter_libs.xcframework */, 07A63A9B2B1E72FC00CAFA4D /* sqlite3.xcframework */, 07A63A902B1E72FB00CAFA4D /* url_launcher_ios.xcframework */, 07A63A872B1E72C800CAFA4D /* Flutter.xcframework */, 07A63A842B1E72AE00CAFA4D /* App.xcframework */, 07A63A832B1E728C00CAFA4D /* Release */, 60F1D4AAC33ACF5C8307310D /* Pods_Runner.framework */, 03E392B72ADDA00E000ADF15 /* NetworkExtension.framework */, 30560DB3EFDF5E86AAD00AB8 /* Pods_RunnerTests.framework */, ); name = Frameworks; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ 03E392B52ADDA00E000ADF15 /* HiddifyPacketTunnel */ = { isa = PBXNativeTarget; buildConfigurationList = 03E392C52ADDA00F000ADF15 /* Build configuration list for PBXNativeTarget "HiddifyPacketTunnel" */; buildPhases = ( 03E392B22ADDA00E000ADF15 /* Sources */, 03E392B32ADDA00E000ADF15 /* Frameworks */, 03E392B42ADDA00E000ADF15 /* Resources */, ); buildRules = ( ); dependencies = ( ); name = HiddifyPacketTunnel; productName = HiddifyPacketTunnel; productReference = 03E392B62ADDA00E000ADF15 /* HiddifyPacketTunnel.appex */; productType = "com.apple.product-type.app-extension"; }; 331C8080294A63A400263BE5 /* RunnerTests */ = { isa = PBXNativeTarget; buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; buildPhases = ( 2058E420D1A8B6F0E5E03873 /* [CP] Check Pods Manifest.lock */, 331C807D294A63A400263BE5 /* Sources */, 331C807F294A63A400263BE5 /* Resources */, 531FE8242BCD501C24C8E9FA /* Frameworks */, ); buildRules = ( ); dependencies = ( 331C8086294A63A400263BE5 /* PBXTargetDependency */, ); name = RunnerTests; productName = RunnerTests; productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; 97C146ED1CF9000F007C117D /* Runner */ = { isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( B971F0749B278D190A7A7315 /* [CP] Check Pods Manifest.lock */, 03E392C12ADDA00F000ADF15 /* Embed Foundation Extensions */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EC1CF9000F007C117D /* Resources */, 97C146EA1CF9000F007C117D /* Sources */, FBEFD3291AEA65EDE2F5AEF6 /* [CP] Embed Pods Frameworks */, 97C146EB1CF9000F007C117D /* Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, ); buildRules = ( ); dependencies = ( 03E392BF2ADDA00F000ADF15 /* PBXTargetDependency */, ); name = Runner; productName = Runner; productReference = 97C146EE1CF9000F007C117D /* Runner.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = YES; LastSwiftUpdateCheck = 1500; LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 03E392B52ADDA00E000ADF15 = { CreatedOnToolsVersion = 15.0; DevelopmentTeamName = "Mark Pashmfouroush"; ProvisioningStyle = Automatic; }; 331C8080294A63A400263BE5 = { CreatedOnToolsVersion = 14.0; DevelopmentTeamName = "Mark Pashmfouroush"; ProvisioningStyle = Automatic; TestTargetID = 97C146ED1CF9000F007C117D; }; 97C146ED1CF9000F007C117D = { CreatedOnToolsVersion = 7.3.1; DevelopmentTeamName = "Mark Pashmfouroush"; LastSwiftMigration = 1100; ProvisioningStyle = Automatic; }; }; }; buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; compatibilityVersion = "Xcode 13.0"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = 97C146E51CF9000F007C117D; packageReferences = ( 413270672C8A9B88003A1E9B /* XCLocalSwiftPackageReference "Local Packages" */, ); productRefGroup = 97C146EF1CF9000F007C117D /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( 97C146ED1CF9000F007C117D /* Runner */, 331C8080294A63A400263BE5 /* RunnerTests */, 03E392B52ADDA00E000ADF15 /* HiddifyPacketTunnel */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ 03E392B42ADDA00E000ADF15 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; 331C807F294A63A400263BE5 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; 97C146EC1CF9000F007C117D /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ 2058E420D1A8B6F0E5E03873 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( ); inputPaths = ( "${PODS_PODFILE_DIR_PATH}/Podfile.lock", "${PODS_ROOT}/Manifest.lock", ); name = "[CP] Check Pods Manifest.lock"; outputFileListPaths = ( ); outputPaths = ( "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); inputPaths = ( "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", ); name = "Thin Binary"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin\n"; }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); name = "Run Script"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build\n"; }; B971F0749B278D190A7A7315 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( ); inputPaths = ( "${PODS_PODFILE_DIR_PATH}/Podfile.lock", "${PODS_ROOT}/Manifest.lock", ); name = "[CP] Check Pods Manifest.lock"; outputFileListPaths = ( ); outputPaths = ( "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; FBEFD3291AEA65EDE2F5AEF6 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); name = "[CP] Embed Pods Frameworks"; outputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ 03E392B22ADDA00E000ADF15 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 032158BA2ADDFCC9008D943B /* TrafficReader.swift in Sources */, 032158BC2ADDFD09008D943B /* SingBox.swift in Sources */, 03E392D22ADDF1F4000ADF15 /* ExtensionPlatformInterface.swift in Sources */, 03E392CC2ADDE078000ADF15 /* ExtensionProvider.swift in Sources */, 03E392BB2ADDA00F000ADF15 /* PacketTunnelProvider.swift in Sources */, 03E392CF2ADDEFC8000ADF15 /* FilePath.swift in Sources */, 03E392D42ADDF262000ADF15 /* Extension+RunBlocking.swift in Sources */, 03B516762AE762F700EA47E2 /* Logger.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 331C807D294A63A400263BE5 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 97C146EA1CF9000F007C117D /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 03B516742AE74D2200EA47E2 /* Stored.swift in Sources */, 03B5167B2AE79DB400EA47E2 /* FileMethodHandler.swift in Sources */, 03B516772AE7634400EA47E2 /* Logger.swift in Sources */, 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, D703EE962B765176001D88B3 /* ActiveGroupsEventHandler.swift in Sources */, 03B516712AE74CCD00EA47E2 /* VPNConfig.swift in Sources */, 03B5166B2AE7315E00EA47E2 /* AlertsEventHandler.swift in Sources */, 0736958B2B3AC96D007249BE /* Bundle+Properties.swift in Sources */, 0736958D2B3B79E0007249BE /* StatsEventHandler.swift in Sources */, 03B516692AE7306B00EA47E2 /* StatusEventHandler.swift in Sources */, D7CC50862B768C50006BC140 /* Outbound.swift in Sources */, 032158B82ADDF8BF008D943B /* VPNManager.swift in Sources */, 0736958F2B3B8048007249BE /* PlatformMethodHandler.swift in Sources */, D703EE932B764EA3001D88B3 /* CommandClient.swift in Sources */, 03B516672AE6B93A00EA47E2 /* MethodHandler.swift in Sources */, 03B5166D2AE7325500EA47E2 /* LogsEventHandler.swift in Sources */, 03E392D02ADDF1BD000ADF15 /* FilePath.swift in Sources */, 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 03B5167D2AE7AC6200EA47E2 /* GroupsEventHandler.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ 03E392BF2ADDA00F000ADF15 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 03E392B52ADDA00E000ADF15 /* HiddifyPacketTunnel */; targetProxy = 03E392BE2ADDA00F000ADF15 /* PBXContainerItemProxy */; }; 331C8086294A63A400263BE5 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 97C146ED1CF9000F007C117D /* Runner */; targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ 97C146FA1CF9000F007C117D /* Main.storyboard */ = { isa = PBXVariantGroup; children = ( 97C146FB1CF9000F007C117D /* Base */, ); name = Main.storyboard; sourceTree = ""; }; 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { isa = PBXVariantGroup; children = ( 97C147001CF9000F007C117D /* Base */, ); name = LaunchScreen.storyboard; sourceTree = ""; }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ 03E392C22ADDA00F000ADF15 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 03E392C62ADDA064000ADF15 /* Base.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_ENTITLEMENTS = HiddifyPacketTunnel/HiddifyPacketTunnel.entitlements; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 40102; DEVELOPMENT_TEAM = M7Q8ASP66Z; ENABLE_USER_SCRIPT_SANDBOXING = YES; EXCLUDED_ARCHS = armv7; GCC_C_LANGUAGE_STANDARD = gnu17; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = HiddifyPacketTunnel/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = HiddifyPacketTunnel; INFOPLIST_KEY_NSHumanReadableCopyright = ""; IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = ( "$(SDKROOT)/usr/lib/swift", "$(TOOLCHAIN_DIR)/usr/lib/swift-5.5/$(PLATFORM_NAME)", "$(inherited)", "@executable_path/Frameworks", "@executable_path/../../Frameworks", "@executable_path/../hiddify-core/", "@executable_path/hiddify-core/", ); LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MARKETING_VERSION = 4.1.2; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; OTHER_LDFLAGS = "-lresolv"; PRODUCT_BUNDLE_IDENTIFIER = "$(BASE_BUNDLE_IDENTIFIER).HiddifyPacketTunnel"; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; TVOS_DEPLOYMENT_TARGET = 15.0; }; name = Debug; }; 03E392C32ADDA00F000ADF15 /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 03E392C62ADDA064000ADF15 /* Base.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_ENTITLEMENTS = HiddifyPacketTunnel/HiddifyPacketTunnel.entitlements; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 40102; DEVELOPMENT_TEAM = M7Q8ASP66Z; ENABLE_USER_SCRIPT_SANDBOXING = YES; EXCLUDED_ARCHS = armv7; GCC_C_LANGUAGE_STANDARD = gnu17; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = HiddifyPacketTunnel/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = HiddifyPacketTunnel; INFOPLIST_KEY_NSHumanReadableCopyright = ""; IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = ( "$(SDKROOT)/usr/lib/swift", "$(TOOLCHAIN_DIR)/usr/lib/swift-5.5/$(PLATFORM_NAME)", "$(inherited)", "@executable_path/Frameworks", "@executable_path/../../Frameworks", "@executable_path/../hiddify-core/", "@executable_path/hiddify-core/", ); LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MARKETING_VERSION = 4.1.2; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = NO; OTHER_LDFLAGS = "-lresolv"; PRODUCT_BUNDLE_IDENTIFIER = "$(BASE_BUNDLE_IDENTIFIER).HiddifyPacketTunnel"; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; TVOS_DEPLOYMENT_TARGET = 15.0; }; name = Release; }; 03E392C42ADDA00F000ADF15 /* Profile */ = { isa = XCBuildConfiguration; baseConfigurationReference = 03E392C62ADDA064000ADF15 /* Base.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_ENTITLEMENTS = HiddifyPacketTunnel/HiddifyPacketTunnel.entitlements; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 40102; DEVELOPMENT_TEAM = M7Q8ASP66Z; ENABLE_USER_SCRIPT_SANDBOXING = YES; EXCLUDED_ARCHS = armv7; GCC_C_LANGUAGE_STANDARD = gnu17; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = HiddifyPacketTunnel/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = HiddifyPacketTunnel; INFOPLIST_KEY_NSHumanReadableCopyright = ""; IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = ( "$(SDKROOT)/usr/lib/swift", "$(TOOLCHAIN_DIR)/usr/lib/swift-5.5/$(PLATFORM_NAME)", "$(inherited)", "@executable_path/Frameworks", "@executable_path/../../Frameworks", "@executable_path/../hiddify-core/", "@executable_path/hiddify-core/", ); LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MARKETING_VERSION = 4.1.2; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = NO; OTHER_LDFLAGS = "-lresolv"; PRODUCT_BUNDLE_IDENTIFIER = "$(BASE_BUNDLE_IDENTIFIER).HiddifyPacketTunnel"; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; TVOS_DEPLOYMENT_TARGET = 15.0; }; name = Profile; }; 249021D3217E4FDB00AE95B9 /* Profile */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = YES; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 15.0; MTL_ENABLE_DEBUG_INFO = NO; ONLY_ACTIVE_ARCH = NO; OTHER_CPLUSPLUSFLAGS = ( "-fcxx-modules", "$(OTHER_CFLAGS)", ); SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; name = Profile; }; 249021D4217E4FDB00AE95B9 /* Profile */ = { isa = XCBuildConfiguration; baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; BUILD_LIBRARY_FOR_DISTRIBUTION = NO; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = M7Q8ASP66Z; ENABLE_BITCODE = NO; "EXCLUDED_ARCHS[sdk=iphoneos*]" = armv7; INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; LD_RUNPATH_SEARCH_PATHS = ( "$(PROJECT_DIR)/build/ios/framework/$(CONFIGURATION)", "$(PROJECT_DIR)/../build/ios/framework/$(CONFIGURATION)", "$(SDKROOT)/usr/lib/swift", "$(TOOLCHAIN_DIR)/usr/lib/swift-5.5/$(PLATFORM_NAME)", "$(inherited)", "@executable_path/Frameworks", "@executable_path/../../Frameworks", "@executable_path/../hiddify-core/", "@executable_path/hiddify-core/", ); ONLY_ACTIVE_ARCH = NO; OTHER_CPLUSPLUSFLAGS = ( "-fcxx-modules", "$(OTHER_CFLAGS)", ); OTHER_LDFLAGS = ( "$(inherited)", "-ld_classic", "-lresolv", ); PRODUCT_BUNDLE_IDENTIFIER = "$(BASE_BUNDLE_IDENTIFIER)"; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; }; name = Profile; }; 331C8088294A63A400263BE5 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 0337C822BEDF7A95576475B3 /* Pods-RunnerTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 40102; DEVELOPMENT_TEAM = M7Q8ASP66Z; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 4.1.2; PRODUCT_BUNDLE_IDENTIFIER = com.hiddify.ios.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; }; name = Debug; }; 331C8089294A63A400263BE5 /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 808A94F72872B54716770416 /* Pods-RunnerTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 40102; DEVELOPMENT_TEAM = M7Q8ASP66Z; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 4.1.2; PRODUCT_BUNDLE_IDENTIFIER = com.hiddify.ios.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; }; name = Release; }; 331C808A294A63A400263BE5 /* Profile */ = { isa = XCBuildConfiguration; baseConfigurationReference = 040FA3EB0673B72D60CC7145 /* Pods-RunnerTests.profile.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 40102; DEVELOPMENT_TEAM = M7Q8ASP66Z; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 4.1.2; PRODUCT_BUNDLE_IDENTIFIER = com.hiddify.ios.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; }; name = Profile; }; 97C147031CF9000F007C117D /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = YES; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 15.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; OTHER_CPLUSPLUSFLAGS = ( "-fcxx-modules", "$(OTHER_CFLAGS)", ); SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; 97C147041CF9000F007C117D /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = YES; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 15.0; MTL_ENABLE_DEBUG_INFO = NO; ONLY_ACTIVE_ARCH = NO; OTHER_CPLUSPLUSFLAGS = ( "-fcxx-modules", "$(OTHER_CFLAGS)", ); SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; name = Release; }; 97C147061CF9000F007C117D /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; BUILD_LIBRARY_FOR_DISTRIBUTION = NO; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = M7Q8ASP66Z; ENABLE_BITCODE = NO; "EXCLUDED_ARCHS[sdk=iphoneos*]" = armv7; "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "i386 arm64"; INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; LD_RUNPATH_SEARCH_PATHS = ( "$(PROJECT_DIR)/build/ios/framework/$(CONFIGURATION)", "$(PROJECT_DIR)/../build/ios/framework/$(CONFIGURATION)", "$(SDKROOT)/usr/lib/swift", "$(TOOLCHAIN_DIR)/usr/lib/swift-5.5/$(PLATFORM_NAME)", "$(inherited)", "@executable_path/Frameworks", "@executable_path/../../Frameworks", "@executable_path/../hiddify-core/", "@executable_path/hiddify-core/", ); OTHER_CPLUSPLUSFLAGS = ( "-fcxx-modules", "$(OTHER_CFLAGS)", ); OTHER_LDFLAGS = ( "$(inherited)", "-ld_classic", "-lresolv", ); PRODUCT_BUNDLE_IDENTIFIER = "$(BASE_BUNDLE_IDENTIFIER)"; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; }; name = Debug; }; 97C147071CF9000F007C117D /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; BUILD_LIBRARY_FOR_DISTRIBUTION = NO; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = M7Q8ASP66Z; ENABLE_BITCODE = NO; "EXCLUDED_ARCHS[sdk=iphoneos*]" = armv7; INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; LD_RUNPATH_SEARCH_PATHS = ( "$(PROJECT_DIR)/build/ios/framework/$(CONFIGURATION)", "$(PROJECT_DIR)/../build/ios/framework/$(CONFIGURATION)", "$(SDKROOT)/usr/lib/swift", "$(TOOLCHAIN_DIR)/usr/lib/swift-5.5/$(PLATFORM_NAME)", "$(inherited)", "@executable_path/Frameworks", "@executable_path/../../Frameworks", "@executable_path/../hiddify-core/", "@executable_path/hiddify-core/", ); ONLY_ACTIVE_ARCH = NO; OTHER_CPLUSPLUSFLAGS = ( "-fcxx-modules", "$(OTHER_CFLAGS)", ); OTHER_LDFLAGS = ( "$(inherited)", "-ld_classic", "-lresolv", ); PRODUCT_BUNDLE_IDENTIFIER = "$(BASE_BUNDLE_IDENTIFIER)"; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 03E392C52ADDA00F000ADF15 /* Build configuration list for PBXNativeTarget "HiddifyPacketTunnel" */ = { isa = XCConfigurationList; buildConfigurations = ( 03E392C22ADDA00F000ADF15 /* Debug */, 03E392C32ADDA00F000ADF15 /* Release */, 03E392C42ADDA00F000ADF15 /* Profile */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { isa = XCConfigurationList; buildConfigurations = ( 331C8088294A63A400263BE5 /* Debug */, 331C8089294A63A400263BE5 /* Release */, 331C808A294A63A400263BE5 /* Profile */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { isa = XCConfigurationList; buildConfigurations = ( 97C147031CF9000F007C117D /* Debug */, 97C147041CF9000F007C117D /* Release */, 249021D3217E4FDB00AE95B9 /* Profile */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { isa = XCConfigurationList; buildConfigurations = ( 97C147061CF9000F007C117D /* Debug */, 97C147071CF9000F007C117D /* Release */, 249021D4217E4FDB00AE95B9 /* Profile */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ /* Begin XCLocalSwiftPackageReference section */ 413270672C8A9B88003A1E9B /* XCLocalSwiftPackageReference "Local Packages" */ = { isa = XCLocalSwiftPackageReference; relativePath = "Local Packages"; }; /* End XCLocalSwiftPackageReference section */ }; rootObject = 97C146E61CF9000F007C117D /* Project object */; } ================================================ FILE: ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist ================================================ IDEDidComputeMac32BitWarning ================================================ FILE: ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings ================================================ PreviewsEnabled ================================================ FILE: ios/Runner.xcodeproj/xcshareddata/xcschemes/HiddifyPacketTunnel.xcscheme ================================================ ================================================ FILE: ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme ================================================ ================================================ FILE: ios/Runner.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist ================================================ IDEDidComputeMac32BitWarning ================================================ FILE: ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings ================================================ PreviewsEnabled ================================================ FILE: ios/RunnerTests/RunnerTests.swift ================================================ import Flutter import UIKit import XCTest class RunnerTests: XCTestCase { func testExample() { // If you add code to the Runner application, consider adding tests here. // See https://developer.apple.com/documentation/xctest for more information about using XCTest. } } ================================================ FILE: ios/Shared/CommandClient.swift ================================================ import Foundation import HiddifyCore public class CommandClient: ObservableObject { public enum ConnectionType { case status case groups case log case groupsInfoOnly } private let connectionType: ConnectionType private let logMaxLines: Int private var commandClient: LibboxCommandClient? private var connectTask: Task? @Published private(set) var isConnected: Bool @Published private(set) var status: LibboxStatusMessage? @Published private(set) var groups: [SBGroup]? @Published private(set) var logList: [String] public init(_ connectionType: ConnectionType, logMaxLines: Int = 300) { self.connectionType = connectionType self.logMaxLines = logMaxLines logList = [] isConnected = false } public func connect() { if isConnected { return } if let connectTask { connectTask.cancel() } connectTask = Task { await connect0() } } public func disconnect() { if let connectTask { connectTask.cancel() self.connectTask = nil } if let commandClient { try? commandClient.disconnect() self.commandClient = nil } } private nonisolated func connect0() async { // let clientOptions = LibboxCommandClientOptions() // switch connectionType { // case .status: // clientOptions.command = LibboxCommandStatus // case .groups: // clientOptions.command = LibboxCommandGroup // case .log: // clientOptions.command = LibboxCommandLog // case .groupsInfoOnly: // clientOptions.command = LibboxCommandGroupInfoOnly // } // clientOptions.statusInterval = Int64(2 * NSEC_PER_SEC) // let client = LibboxNewCommandClient(clientHandler(self), clientOptions)! // do { // for i in 0 ..< 10 { // try await Task.sleep(nanoseconds: UInt64(Double(100 + (i * 50)) * Double(NSEC_PER_MSEC))) // try Task.checkCancellation() // do { // try client.connect() // await MainActor.run { // commandClient = client // } // return // } catch {} // try Task.checkCancellation() // } // } catch { // try? client.disconnect() // } } // private class clientHandler: NSObject, LibboxCommandClientHandlerProtocol { // // // private let commandClient: CommandClient // // init(_ commandClient: CommandClient) { // self.commandClient = commandClient // } // // func connected() { // DispatchQueue.main.async { [self] in // if commandClient.connectionType == .log { // commandClient.logList = [] // } // commandClient.isConnected = true // } // } // // func disconnected(_: String?) { // DispatchQueue.main.async { [self] in // commandClient.isConnected = false // } // } // // func clearLogs() { // DispatchQueue.main.async { [self] in // commandClient.logList.removeAll() // } // } // // func writeLogs(_ messageList: (any LibboxStringIteratorProtocol)?) { //// guard let message else { //// return //// } //// DispatchQueue.main.async { [self] in //// if commandClient.logList.count > commandClient.logMaxLines { //// commandClient.logList.removeFirst() //// } //// commandClient.logList.append(message) //// } // } // // func writeStatus(_ message: LibboxStatusMessage?) { // DispatchQueue.main.async { [self] in // commandClient.status = message // } // } // // func writeGroups(_ groups: LibboxOutboundGroupIteratorProtocol?) { // guard let groups else { // return // } // var sbGroups = [SBGroup]() // while groups.hasNext() { // let group = groups.next()! // var items = [SBItem]() // let groupItems = group.getItems() // while groupItems?.hasNext() ?? false { // let item = groupItems?.next()! // items.append(SBItem(tag: item!.tag, // type: item!.type, // urlTestDelay: Int(item!.urlTestDelay) // ) // ) // } // // sbGroups.append(.init(tag: group.tag, // type: group.type, // selected: group.selected, // items: items) // ) // // } // DispatchQueue.main.async { [self] in // commandClient.groups = sbGroups // } // } // // func initializeClashMode(_ modeList: LibboxStringIteratorProtocol?, currentMode: String?) { // } // // func updateClashMode(_ newMode: String?) { // } // func write(_ message: LibboxConnections?) { // } // // } } ================================================ FILE: ios/Shared/FilePath.swift ================================================ // // FilePath.swift // SingBoxPacketTunnel // // Created by GFWFighter on 7/25/1402 AP. // import Foundation public enum FilePath { public static let packageName = { Bundle.main.infoDictionary?["BASE_BUNDLE_IDENTIFIER"] as? String ?? "unknown" }() } public extension FilePath { static let groupName = "group.\(packageName)" private static let defaultSharedDirectory: URL! = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: FilePath.groupName) static let sharedDirectory = defaultSharedDirectory! static let cacheDirectory = sharedDirectory .appendingPathComponent("Library", isDirectory: true) .appendingPathComponent("Caches", isDirectory: true) static let workingDirectory = cacheDirectory.appendingPathComponent("Working", isDirectory: true) } public extension URL { var fileName: String { var path = relativePath if let index = path.lastIndex(of: "/") { path = String(path[path.index(index, offsetBy: 1)...]) } return path } } ================================================ FILE: ios/Shared/Outbound.swift ================================================ public struct SBItem: Codable { let tag: String let type: String let urlTestDelay: Int enum CodingKeys: String, CodingKey { case tag case type case urlTestDelay = "url-test-delay" } } public struct SBGroup: Codable { let tag: String let type: String let selected: String let items: [SBItem] } ================================================ FILE: ios/exportOptions.plist ================================================ destination export manageAppVersionAndBuildNumber method app-store provisioningProfiles apple.hiddify.com dist.apple.apple.hiddify.com apple.hiddify.com.SingBoxPacketTunnel dist.apple.apple.hiddify.com.singboxpackettunnel signingCertificate E2217AF6F3AD11BA09F9FD95E025D7E637F8B081 signingStyle manual stripSwiftSymbols teamID 3JFTY5BP58 uploadSymbols ================================================ FILE: ios/packaging/ios/make_config.yaml ================================================ display_name: Hiddify icon: ..\..\assets\images\windows.ico keywords: - Hiddify - Proxy - VPN - V2ray - Nekoray - Xray - Psiphon - OpenVPN generic_name: Hiddify actions: - name: Start label: start arguments: - --start - name: Stop label: stop arguments: - --stop categories: - Internet startup_notify: true # You can specify the shared libraries that you want to bundle with your app # # flutter_distributor automatically detects the shared libraries that your app # depends on, but you can also specify them manually here. # # The following example shows how to bundle the libcurl library with your app. # # include: # - libcurl.so.4 include: [] ================================================ FILE: lib/bootstrap.dart ================================================ import 'dart:async'; import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_displaymode/flutter_displaymode.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/localization/translations.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'; import 'package:hiddify/core/preferences/preferences_provider.dart'; import 'package:hiddify/features/app/widget/app.dart'; import 'package:hiddify/features/auto_start/notifier/auto_start_notifier.dart'; import 'package:hiddify/features/log/data/log_data_providers.dart'; import 'package:hiddify/features/profile/data/profile_data_providers.dart'; import 'package:hiddify/features/profile/notifier/active_profile_notifier.dart'; import 'package:hiddify/features/system_tray/notifier/system_tray_notifier.dart'; import 'package:hiddify/features/window/notifier/window_notifier.dart'; import 'package:hiddify/hiddifycore/hiddify_core_service_provider.dart'; import 'package:hiddify/riverpod_observer.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; Future lazyBootstrap(WidgetsBinding widgetsBinding, Environment env) async { if (!kIsWeb) { FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding); } LoggerController.preInit(); FlutterError.onError = Logger.logFlutterError; WidgetsBinding.instance.platformDispatcher.onError = Logger.logPlatformDispatcherError; final stopWatch = Stopwatch()..start(); final container = ProviderContainer(overrides: [environmentProvider.overrideWithValue(env)]); await _init("directories", () => container.read(appDirectoriesProvider.future)); LoggerController.init(container.read(logPathResolverProvider).appFile().path); final appInfo = await _init("app info", () => container.read(appInfoProvider.future)); await _init("preferences", () => container.read(sharedPreferencesProvider.future)); final enableAnalytics = await container.read(analyticsControllerProvider.future); 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; if (PlatformUtils.isDesktop) { await _init("window controller", () => container.read(windowNotifierProvider.future)); final silentStart = container.read(Preferences.silentStart); Logger.bootstrap.debug("silent start [${silentStart ? "Enabled" : "Disabled"}]"); if (!silentStart) { await container.read(windowNotifierProvider.notifier).show(focus: false); } else { Logger.bootstrap.debug("silent start, remain hidden accessible via tray"); } await _init("auto start service", () => container.read(autoStartNotifierProvider.future)); } await _init("logs repository", () => container.read(logRepositoryProvider.future)); await _init("logger controller", () => LoggerController.postInit(debug)); Logger.bootstrap.info(appInfo.format()); await _init("profile repository", () => container.read(profileRepositoryProvider.future)); await _init("translations", () => container.read(translationsProvider.future)); await _safeInit("active profile", () => container.read(activeProfileProvider.future), timeout: 1000); await _init("hiddify-core", () => container.read(hiddifyCoreServiceProvider).init()); if (!kIsWeb) { // await _safeInit( // "deep link service", // () => container.read(deepLinkNotifierProvider.future), // timeout: 1000, // ); if (PlatformUtils.isDesktop) { await _safeInit("system tray", () => container.read(systemTrayNotifierProvider.future), timeout: 1000); } if (PlatformUtils.isAndroid) { await _safeInit("android display mode", () async { await FlutterDisplayMode.setHighRefreshRate(); }); } } Logger.bootstrap.info("bootstrap took [${stopWatch.elapsedMilliseconds}ms]"); stopWatch.stop(); runApp( ProviderScope( parent: container, observers: [RiverpodObserver()], child: SentryUserInteractionWidget(child: const App()), ), ); if (!kIsWeb) { FlutterNativeSplash.remove(); } // SentryFlutter.s(DateTime.now().toUtc()); } Future _init(String name, Future Function() initializer, {int? timeout}) async { final stopWatch = Stopwatch()..start(); Logger.bootstrap.info("initializing [$name]"); 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 (e) { return null; } } ================================================ FILE: lib/core/analytics/analytics_controller.dart ================================================ 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/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 Future build() async { return _preferences.getBool(enableAnalyticsPrefKey) ?? true; } SharedPreferences get _preferences => ref.read(sharedPreferencesProvider).requireValue; Future enableAnalytics() async { if (state case AsyncData(value: final enabled)) { loggy.debug("enabling analytics"); state = const AsyncLoading(); if (!enabled) { 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.autoAppStart = false; // options.attachScreenshot = true; options.serverName = ""; options.attachThreads = true; options.tracesSampleRate = 0.20; options.enableUserInteractionTracing = true; options.addIntegration(sentryLogger); options.beforeSend = sentryBeforeSend; }); state = const AsyncData(true); } } Future disableAnalytics() async { if (state case AsyncData()) { loggy.debug("disabling analytics"); state = const AsyncLoading(); await _preferences.setBool(enableAnalyticsPrefKey, false); await Sentry.close(); LoggerController.instance.removePrinter("analytics"); state = const AsyncData(false); } } } ================================================ FILE: lib/core/analytics/analytics_filter.dart ================================================ 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) async { if (!canSendEvent(event.throwable)) return null; return event.copyWith( user: SentryUser(email: "", username: "", ipAddress: "0.0.0.0"), ); } 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), }; ================================================ FILE: lib/core/analytics/analytics_logger.dart ================================================ import 'package:hiddify/utils/sentry_utils.dart'; import 'package:loggy/loggy.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; // modified version of https://github.com/getsentry/sentry-dart/tree/main/logging class SentryLoggyIntegration extends LoggyPrinter implements Integration { SentryLoggyIntegration({LogLevel minBreadcrumbLevel = LogLevel.info, LogLevel minEventLevel = LogLevel.error}) : _minBreadcrumbLevel = minBreadcrumbLevel, _minEventLevel = minEventLevel; final LogLevel _minBreadcrumbLevel; final LogLevel _minEventLevel; late Hub _hub; @override void call(Hub hub, SentryOptions options) { _hub = hub; options.sdk.addIntegration('LoggyIntegration'); } @override Future close() async {} bool _shouldLog(LogLevel logLevel, LogLevel minLevel) { if (logLevel == LogLevel.off) { return false; } return logLevel.priority >= minLevel.priority; } @override Future onLog(LogRecord record) async { if (!canLogEvent(record.error)) return; if (_shouldLog(record.level, _minEventLevel)) { await _hub.captureEvent( record.toEvent(), stackTrace: record.stackTrace, hint: Hint.withMap({TypeCheckHint.record: record}), ); } if (_shouldLog(record.level, _minBreadcrumbLevel)) { await _hub.addBreadcrumb(record.toBreadcrumb(), hint: Hint.withMap({TypeCheckHint.record: record})); } } } extension LogRecordX on LogRecord { Breadcrumb toBreadcrumb() { return Breadcrumb( category: 'log', type: 'debug', timestamp: time.toUtc(), level: level.toSentryLevel(), message: message, data: { if (object != null) 'LogRecord.object': object!, if (error != null) 'LogRecord.error': error!, if (stackTrace != null) 'LogRecord.stackTrace': stackTrace!, 'LogRecord.loggerName': loggerName, 'LogRecord.sequenceNumber': sequenceNumber, }, ); } SentryEvent toEvent() { return SentryEvent( timestamp: time.toUtc(), logger: loggerName, level: level.toSentryLevel(), message: SentryMessage(message), throwable: error, // ignore: deprecated_member_use extra: { if (object != null) 'LogRecord.object': object!, 'LogRecord.sequenceNumber': sequenceNumber, }, ); } } extension LogLevelX on LogLevel { SentryLevel? toSentryLevel() => switch (this) { LogLevel.all || LogLevel.debug => SentryLevel.debug, LogLevel.info => SentryLevel.info, LogLevel.warning => SentryLevel.warning, LogLevel.error => SentryLevel.error, _ => null, }; } ================================================ FILE: lib/core/app_info/app_info_provider.dart ================================================ import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:hiddify/core/model/app_info_entity.dart'; import 'package:hiddify/core/model/environment.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'app_info_provider.g.dart'; @Riverpod(keepAlive: true) Environment environment(EnvironmentRef ref) => throw Exception("override environmentProvider"); @Riverpod(keepAlive: true) class AppInfo extends _$AppInfo { @override Future build() async { final packageInfo = await PackageInfo.fromPlatform(); final environment = ref.watch(environmentProvider); return AppInfoEntity( name: packageInfo.appName, version: packageInfo.version, buildNumber: packageInfo.buildNumber, release: Release.read(), operatingSystem: kIsWeb ? "web" : Platform.operatingSystem, operatingSystemVersion: kIsWeb ? "web" : Platform.operatingSystemVersion, environment: environment, ); } } ================================================ FILE: lib/core/db/converters/duration_converter.dart ================================================ import 'package:drift/drift.dart'; class DurationTypeConverter extends TypeConverter { @override Duration fromSql(int fromDb) { return Duration(seconds: fromDb); } @override int toSql(Duration value) { return value.inSeconds; } } ================================================ FILE: lib/core/db/db.dart ================================================ import 'package:drift/drift.dart'; import 'package:drift_flutter/drift_flutter.dart'; import 'package:hiddify/core/db/converters/duration_converter.dart'; import 'package:hiddify/core/db/db.steps.dart'; import 'package:hiddify/core/directories/directories_provider.dart'; import 'package:hiddify/features/per_app_proxy/model/per_app_proxy_mode.dart'; import 'package:hiddify/features/profile/model/profile_entity.dart'; import 'package:hiddify/utils/custom_loggers.dart'; part 'db.g.dart'; @DriftDatabase(tables: [ProfileEntries, AppProxyEntries]) class Db extends _$Db with InfraLogger { Db([QueryExecutor? executor]) : super(executor ?? _openConnection()); @override int get schemaVersion => 5; static QueryExecutor _openConnection() { return LazyDatabase( () => driftDatabase( name: "db", native: const DriftNativeOptions(databaseDirectory: AppDirectories.getDatabaseDirectory), web: DriftWebOptions(sqlite3Wasm: Uri.parse('sqlite3.wasm'), driftWorker: Uri.parse('drift_worker.js')), ), ); } @override MigrationStrategy get migration { return MigrationStrategy( onCreate: (Migrator m) async { await m.createAll(); }, onUpgrade: stepByStep( from1To2: (m, schema) async { await m.alterTable( TableMigration( schema.profileEntries, columnTransformer: {schema.profileEntries.type: const Constant("remote")}, newColumns: [schema.profileEntries.type], ), ); }, from2To3: (m, schema) async { await m.createTable(schema.geoAssetEntries); }, from3To4: (m, schema) async { final testUrlExists = await _columnExists( schema.profileEntries.actualTableName, schema.profileEntries.testUrl.name, ); if (!testUrlExists) { await m.addColumn(schema.profileEntries, schema.profileEntries.testUrl); } }, from4To5: (m, schema) async { await m.deleteTable('geo_asset_entries'); await m.renameColumn(schema.profileEntries, 'test_url', schema.profileEntries.profileOverride); final userOverrideExists = await _columnExists( schema.profileEntries.actualTableName, schema.profileEntries.userOverride.name, ); if (!userOverrideExists) { await m.addColumn(schema.profileEntries, schema.profileEntries.userOverride); } final populatedHeadersExists = await _columnExists( schema.profileEntries.actualTableName, schema.profileEntries.populatedHeaders.name, ); if (!populatedHeadersExists) { await m.addColumn(schema.profileEntries, schema.profileEntries.populatedHeaders); } await m.createTable(schema.appProxyEntries); }, ), ); } Future _columnExists(String table, String column) async { final result = await customSelect('PRAGMA table_info($table);').get(); return result.any((row) => row.data['name'] == column); } } @DataClassName('ProfileEntry') class ProfileEntries extends Table { TextColumn get id => text()(); TextColumn get type => textEnum()(); BoolColumn get active => boolean()(); TextColumn get name => text().withLength(min: 1)(); TextColumn get url => text().nullable()(); DateTimeColumn get lastUpdate => dateTime()(); IntColumn get updateInterval => integer().nullable().map(DurationTypeConverter())(); IntColumn get upload => integer().nullable()(); IntColumn get download => integer().nullable()(); IntColumn get total => integer().nullable()(); DateTimeColumn get expire => dateTime().nullable()(); TextColumn get webPageUrl => text().nullable()(); TextColumn get supportUrl => text().nullable()(); TextColumn get populatedHeaders => text().nullable()(); TextColumn get profileOverride => text().nullable()(); TextColumn get userOverride => text().nullable()(); @override Set get primaryKey => {id}; } @DataClassName('AppProxyEntry') class AppProxyEntries extends Table { TextColumn get mode => textEnum()(); TextColumn get pkgName => text()(); IntColumn get flags => integer().withDefault(const Constant(0))(); @override Set get primaryKey => {mode, pkgName}; } ================================================ FILE: lib/core/db/db.steps.dart ================================================ // dart format width=80 import 'package:drift/internal/versioned_schema.dart' as i0; import 'package:drift/drift.dart' as i1; import 'package:drift/drift.dart'; // ignore_for_file: type=lint,unused_import // GENERATED BY drift_dev, DO NOT MODIFY. final class Schema2 extends i0.VersionedSchema { Schema2({required super.database}) : super(version: 2); @override late final List entities = [profileEntries]; late final Shape0 profileEntries = Shape0( source: i0.VersionedTable( entityName: 'profile_entries', withoutRowId: false, isStrict: false, tableConstraints: ['PRIMARY KEY(id)'], columns: [ _column_0, _column_1, _column_2, _column_3, _column_4, _column_5, _column_6, _column_7, _column_8, _column_9, _column_10, _column_11, _column_12, ], attachedDatabase: database, ), alias: null, ); } class Shape0 extends i0.VersionedTable { Shape0({required super.source, required super.alias}) : super.aliased(); i1.GeneratedColumn get id => columnsByName['id']! as i1.GeneratedColumn; i1.GeneratedColumn get type => columnsByName['type']! as i1.GeneratedColumn; i1.GeneratedColumn get active => columnsByName['active']! as i1.GeneratedColumn; i1.GeneratedColumn get name => columnsByName['name']! as i1.GeneratedColumn; i1.GeneratedColumn get url => columnsByName['url']! as i1.GeneratedColumn; i1.GeneratedColumn get lastUpdate => columnsByName['last_update']! as i1.GeneratedColumn; i1.GeneratedColumn get updateInterval => columnsByName['update_interval']! as i1.GeneratedColumn; i1.GeneratedColumn get upload => columnsByName['upload']! as i1.GeneratedColumn; i1.GeneratedColumn get download => columnsByName['download']! as i1.GeneratedColumn; i1.GeneratedColumn get total => columnsByName['total']! as i1.GeneratedColumn; i1.GeneratedColumn get expire => columnsByName['expire']! as i1.GeneratedColumn; i1.GeneratedColumn get webPageUrl => columnsByName['web_page_url']! as i1.GeneratedColumn; i1.GeneratedColumn get supportUrl => columnsByName['support_url']! as i1.GeneratedColumn; } i1.GeneratedColumn _column_0(String aliasedName) => i1.GeneratedColumn( 'id', aliasedName, false, type: i1.DriftSqlType.string, ); i1.GeneratedColumn _column_1(String aliasedName) => i1.GeneratedColumn( 'type', aliasedName, false, type: i1.DriftSqlType.string, ); i1.GeneratedColumn _column_2(String aliasedName) => i1.GeneratedColumn( 'active', aliasedName, false, type: i1.DriftSqlType.bool, defaultConstraints: i1.GeneratedColumn.constraintIsAlways( 'CHECK ("active" IN (0, 1))', ), ); i1.GeneratedColumn _column_3(String aliasedName) => i1.GeneratedColumn( 'name', aliasedName, false, additionalChecks: i1.GeneratedColumn.checkTextLength(minTextLength: 1), type: i1.DriftSqlType.string, ); i1.GeneratedColumn _column_4(String aliasedName) => i1.GeneratedColumn( 'url', aliasedName, true, type: i1.DriftSqlType.string, ); i1.GeneratedColumn _column_5(String aliasedName) => i1.GeneratedColumn( 'last_update', aliasedName, false, type: i1.DriftSqlType.dateTime, ); i1.GeneratedColumn _column_6(String aliasedName) => i1.GeneratedColumn( 'update_interval', aliasedName, true, type: i1.DriftSqlType.int, ); i1.GeneratedColumn _column_7(String aliasedName) => i1.GeneratedColumn( 'upload', aliasedName, true, type: i1.DriftSqlType.int, ); i1.GeneratedColumn _column_8(String aliasedName) => i1.GeneratedColumn( 'download', aliasedName, true, type: i1.DriftSqlType.int, ); i1.GeneratedColumn _column_9(String aliasedName) => i1.GeneratedColumn( 'total', aliasedName, true, type: i1.DriftSqlType.int, ); i1.GeneratedColumn _column_10(String aliasedName) => i1.GeneratedColumn( 'expire', aliasedName, true, type: i1.DriftSqlType.dateTime, ); i1.GeneratedColumn _column_11(String aliasedName) => i1.GeneratedColumn( 'web_page_url', aliasedName, true, type: i1.DriftSqlType.string, ); i1.GeneratedColumn _column_12(String aliasedName) => i1.GeneratedColumn( 'support_url', aliasedName, true, type: i1.DriftSqlType.string, ); final class Schema3 extends i0.VersionedSchema { Schema3({required super.database}) : super(version: 3); @override late final List entities = [ profileEntries, geoAssetEntries, ]; late final Shape0 profileEntries = Shape0( source: i0.VersionedTable( entityName: 'profile_entries', withoutRowId: false, isStrict: false, tableConstraints: ['PRIMARY KEY(id)'], columns: [ _column_0, _column_1, _column_2, _column_3, _column_4, _column_5, _column_6, _column_7, _column_8, _column_9, _column_10, _column_11, _column_12, ], attachedDatabase: database, ), alias: null, ); late final Shape1 geoAssetEntries = Shape1( source: i0.VersionedTable( entityName: 'geo_asset_entries', withoutRowId: false, isStrict: false, tableConstraints: ['PRIMARY KEY(id)', 'UNIQUE(name, provider_name)'], columns: [ _column_0, _column_1, _column_2, _column_3, _column_13, _column_14, _column_15, ], attachedDatabase: database, ), alias: null, ); } class Shape1 extends i0.VersionedTable { Shape1({required super.source, required super.alias}) : super.aliased(); i1.GeneratedColumn get id => columnsByName['id']! as i1.GeneratedColumn; i1.GeneratedColumn get type => columnsByName['type']! as i1.GeneratedColumn; i1.GeneratedColumn get active => columnsByName['active']! as i1.GeneratedColumn; i1.GeneratedColumn get name => columnsByName['name']! as i1.GeneratedColumn; i1.GeneratedColumn get providerName => columnsByName['provider_name']! as i1.GeneratedColumn; i1.GeneratedColumn get version => columnsByName['version']! as i1.GeneratedColumn; i1.GeneratedColumn get lastCheck => columnsByName['last_check']! as i1.GeneratedColumn; } i1.GeneratedColumn _column_13(String aliasedName) => i1.GeneratedColumn( 'provider_name', aliasedName, false, additionalChecks: i1.GeneratedColumn.checkTextLength(minTextLength: 1), type: i1.DriftSqlType.string, ); i1.GeneratedColumn _column_14(String aliasedName) => i1.GeneratedColumn( 'version', aliasedName, true, type: i1.DriftSqlType.string, ); i1.GeneratedColumn _column_15(String aliasedName) => i1.GeneratedColumn( 'last_check', aliasedName, true, type: i1.DriftSqlType.dateTime, ); final class Schema4 extends i0.VersionedSchema { Schema4({required super.database}) : super(version: 4); @override late final List entities = [ profileEntries, geoAssetEntries, ]; late final Shape2 profileEntries = Shape2( source: i0.VersionedTable( entityName: 'profile_entries', withoutRowId: false, isStrict: false, tableConstraints: ['PRIMARY KEY(id)'], columns: [ _column_0, _column_1, _column_2, _column_3, _column_4, _column_5, _column_6, _column_7, _column_8, _column_9, _column_10, _column_11, _column_12, _column_16, ], attachedDatabase: database, ), alias: null, ); late final Shape1 geoAssetEntries = Shape1( source: i0.VersionedTable( entityName: 'geo_asset_entries', withoutRowId: false, isStrict: false, tableConstraints: ['PRIMARY KEY(id)', 'UNIQUE(name, provider_name)'], columns: [ _column_0, _column_1, _column_2, _column_3, _column_13, _column_14, _column_15, ], attachedDatabase: database, ), alias: null, ); } class Shape2 extends i0.VersionedTable { Shape2({required super.source, required super.alias}) : super.aliased(); i1.GeneratedColumn get id => columnsByName['id']! as i1.GeneratedColumn; i1.GeneratedColumn get type => columnsByName['type']! as i1.GeneratedColumn; i1.GeneratedColumn get active => columnsByName['active']! as i1.GeneratedColumn; i1.GeneratedColumn get name => columnsByName['name']! as i1.GeneratedColumn; i1.GeneratedColumn get url => columnsByName['url']! as i1.GeneratedColumn; i1.GeneratedColumn get lastUpdate => columnsByName['last_update']! as i1.GeneratedColumn; i1.GeneratedColumn get updateInterval => columnsByName['update_interval']! as i1.GeneratedColumn; i1.GeneratedColumn get upload => columnsByName['upload']! as i1.GeneratedColumn; i1.GeneratedColumn get download => columnsByName['download']! as i1.GeneratedColumn; i1.GeneratedColumn get total => columnsByName['total']! as i1.GeneratedColumn; i1.GeneratedColumn get expire => columnsByName['expire']! as i1.GeneratedColumn; i1.GeneratedColumn get webPageUrl => columnsByName['web_page_url']! as i1.GeneratedColumn; i1.GeneratedColumn get supportUrl => columnsByName['support_url']! as i1.GeneratedColumn; i1.GeneratedColumn get testUrl => columnsByName['test_url']! as i1.GeneratedColumn; } i1.GeneratedColumn _column_16(String aliasedName) => i1.GeneratedColumn( 'test_url', aliasedName, true, type: i1.DriftSqlType.string, ); final class Schema5 extends i0.VersionedSchema { Schema5({required super.database}) : super(version: 5); @override late final List entities = [ profileEntries, appProxyEntries, ]; late final Shape3 profileEntries = Shape3( source: i0.VersionedTable( entityName: 'profile_entries', withoutRowId: false, isStrict: false, tableConstraints: ['PRIMARY KEY(id)'], columns: [ _column_0, _column_1, _column_2, _column_3, _column_4, _column_5, _column_6, _column_7, _column_8, _column_9, _column_10, _column_11, _column_12, _column_17, _column_18, _column_19, ], attachedDatabase: database, ), alias: null, ); late final Shape4 appProxyEntries = Shape4( source: i0.VersionedTable( entityName: 'app_proxy_entries', withoutRowId: false, isStrict: false, tableConstraints: ['PRIMARY KEY(mode, pkg_name)'], columns: [_column_20, _column_21, _column_22], attachedDatabase: database, ), alias: null, ); } class Shape3 extends i0.VersionedTable { Shape3({required super.source, required super.alias}) : super.aliased(); i1.GeneratedColumn get id => columnsByName['id']! as i1.GeneratedColumn; i1.GeneratedColumn get type => columnsByName['type']! as i1.GeneratedColumn; i1.GeneratedColumn get active => columnsByName['active']! as i1.GeneratedColumn; i1.GeneratedColumn get name => columnsByName['name']! as i1.GeneratedColumn; i1.GeneratedColumn get url => columnsByName['url']! as i1.GeneratedColumn; i1.GeneratedColumn get lastUpdate => columnsByName['last_update']! as i1.GeneratedColumn; i1.GeneratedColumn get updateInterval => columnsByName['update_interval']! as i1.GeneratedColumn; i1.GeneratedColumn get upload => columnsByName['upload']! as i1.GeneratedColumn; i1.GeneratedColumn get download => columnsByName['download']! as i1.GeneratedColumn; i1.GeneratedColumn get total => columnsByName['total']! as i1.GeneratedColumn; i1.GeneratedColumn get expire => columnsByName['expire']! as i1.GeneratedColumn; i1.GeneratedColumn get webPageUrl => columnsByName['web_page_url']! as i1.GeneratedColumn; i1.GeneratedColumn get supportUrl => columnsByName['support_url']! as i1.GeneratedColumn; i1.GeneratedColumn get populatedHeaders => columnsByName['populated_headers']! as i1.GeneratedColumn; i1.GeneratedColumn get profileOverride => columnsByName['profile_override']! as i1.GeneratedColumn; i1.GeneratedColumn get userOverride => columnsByName['user_override']! as i1.GeneratedColumn; } i1.GeneratedColumn _column_17(String aliasedName) => i1.GeneratedColumn( 'populated_headers', aliasedName, true, type: i1.DriftSqlType.string, ); i1.GeneratedColumn _column_18(String aliasedName) => i1.GeneratedColumn( 'profile_override', aliasedName, true, type: i1.DriftSqlType.string, ); i1.GeneratedColumn _column_19(String aliasedName) => i1.GeneratedColumn( 'user_override', aliasedName, true, type: i1.DriftSqlType.string, ); class Shape4 extends i0.VersionedTable { Shape4({required super.source, required super.alias}) : super.aliased(); i1.GeneratedColumn get mode => columnsByName['mode']! as i1.GeneratedColumn; i1.GeneratedColumn get pkgName => columnsByName['pkg_name']! as i1.GeneratedColumn; i1.GeneratedColumn get flags => columnsByName['flags']! as i1.GeneratedColumn; } i1.GeneratedColumn _column_20(String aliasedName) => i1.GeneratedColumn( 'mode', aliasedName, false, type: i1.DriftSqlType.string, ); i1.GeneratedColumn _column_21(String aliasedName) => i1.GeneratedColumn( 'pkg_name', aliasedName, false, type: i1.DriftSqlType.string, ); i1.GeneratedColumn _column_22(String aliasedName) => i1.GeneratedColumn( 'flags', aliasedName, false, type: i1.DriftSqlType.int, defaultValue: const CustomExpression('0'), ); i0.MigrationStepWithVersion migrationSteps({ required Future Function(i1.Migrator m, Schema2 schema) from1To2, required Future Function(i1.Migrator m, Schema3 schema) from2To3, required Future Function(i1.Migrator m, Schema4 schema) from3To4, required Future Function(i1.Migrator m, Schema5 schema) from4To5, }) { return (currentVersion, database) async { switch (currentVersion) { case 1: final schema = Schema2(database: database); final migrator = i1.Migrator(database, schema); await from1To2(migrator, schema); return 2; case 2: final schema = Schema3(database: database); final migrator = i1.Migrator(database, schema); await from2To3(migrator, schema); return 3; case 3: final schema = Schema4(database: database); final migrator = i1.Migrator(database, schema); await from3To4(migrator, schema); return 4; case 4: final schema = Schema5(database: database); final migrator = i1.Migrator(database, schema); await from4To5(migrator, schema); return 5; default: throw ArgumentError.value('Unknown migration from $currentVersion'); } }; } i1.OnUpgrade stepByStep({ required Future Function(i1.Migrator m, Schema2 schema) from1To2, required Future Function(i1.Migrator m, Schema3 schema) from2To3, required Future Function(i1.Migrator m, Schema4 schema) from3To4, required Future Function(i1.Migrator m, Schema5 schema) from4To5, }) => i0.VersionedSchema.stepByStepHelper( step: migrationSteps( from1To2: from1To2, from2To3: from2To3, from3To4: from3To4, from4To5: from4To5, ), ); ================================================ FILE: lib/core/db/provider/db_providers.dart ================================================ import 'package:hiddify/core/db/db.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'db_providers.g.dart'; @Riverpod(keepAlive: true) Db db(Ref ref) => Db(); ================================================ FILE: lib/core/db/schemas/db/drift_schema_v1.json ================================================ { "_meta": { "description": "This file contains a serialized version of schema entities for drift.", "version": "1.1.0" }, "options": { "store_date_time_values_as_text": true }, "entities": [ { "id": 0, "references": [], "type": "table", "data": { "name": "profile_entries", "was_declared_in_moor": false, "columns": [ { "name": "id", "getter_name": "id", "moor_type": "string", "nullable": false, "customConstraints": null, "default_dart": null, "default_client_dart": null, "dsl_features": [] }, { "name": "active", "getter_name": "active", "moor_type": "bool", "nullable": false, "customConstraints": null, "defaultConstraints": "CHECK (\"active\" IN (0, 1))", "default_dart": null, "default_client_dart": null, "dsl_features": [] }, { "name": "name", "getter_name": "name", "moor_type": "string", "nullable": false, "customConstraints": null, "default_dart": null, "default_client_dart": null, "dsl_features": [ { "allowed-lengths": { "min": 1, "max": null } } ] }, { "name": "url", "getter_name": "url", "moor_type": "string", "nullable": false, "customConstraints": null, "default_dart": null, "default_client_dart": null, "dsl_features": [] }, { "name": "last_update", "getter_name": "lastUpdate", "moor_type": "dateTime", "nullable": false, "customConstraints": null, "default_dart": null, "default_client_dart": null, "dsl_features": [] }, { "name": "update_interval", "getter_name": "updateInterval", "moor_type": "int", "nullable": true, "customConstraints": null, "default_dart": null, "default_client_dart": null, "dsl_features": [], "type_converter": { "dart_expr": "DurationTypeConverter()", "dart_type_name": "Duration" } }, { "name": "upload", "getter_name": "upload", "moor_type": "int", "nullable": true, "customConstraints": null, "default_dart": null, "default_client_dart": null, "dsl_features": [] }, { "name": "download", "getter_name": "download", "moor_type": "int", "nullable": true, "customConstraints": null, "default_dart": null, "default_client_dart": null, "dsl_features": [] }, { "name": "total", "getter_name": "total", "moor_type": "int", "nullable": true, "customConstraints": null, "default_dart": null, "default_client_dart": null, "dsl_features": [] }, { "name": "expire", "getter_name": "expire", "moor_type": "dateTime", "nullable": true, "customConstraints": null, "default_dart": null, "default_client_dart": null, "dsl_features": [] }, { "name": "web_page_url", "getter_name": "webPageUrl", "moor_type": "string", "nullable": true, "customConstraints": null, "default_dart": null, "default_client_dart": null, "dsl_features": [] }, { "name": "support_url", "getter_name": "supportUrl", "moor_type": "string", "nullable": true, "customConstraints": null, "default_dart": null, "default_client_dart": null, "dsl_features": [] } ], "is_virtual": false, "without_rowid": false, "constraints": [], "explicit_pk": [ "id" ] } } ] } ================================================ FILE: lib/core/db/schemas/db/drift_schema_v2.json ================================================ { "_meta": { "description": "This file contains a serialized version of schema entities for drift.", "version": "1.1.0" }, "options": { "store_date_time_values_as_text": true }, "entities": [ { "id": 0, "references": [], "type": "table", "data": { "name": "profile_entries", "was_declared_in_moor": false, "columns": [ { "name": "id", "getter_name": "id", "moor_type": "string", "nullable": false, "customConstraints": null, "default_dart": null, "default_client_dart": null, "dsl_features": [] }, { "name": "type", "getter_name": "type", "moor_type": "string", "nullable": false, "customConstraints": null, "default_dart": null, "default_client_dart": null, "dsl_features": [], "type_converter": { "dart_expr": "const EnumNameConverter(ProfileType.values)", "dart_type_name": "ProfileType" } }, { "name": "active", "getter_name": "active", "moor_type": "bool", "nullable": false, "customConstraints": null, "defaultConstraints": "CHECK (\"active\" IN (0, 1))", "default_dart": null, "default_client_dart": null, "dsl_features": [] }, { "name": "name", "getter_name": "name", "moor_type": "string", "nullable": false, "customConstraints": null, "default_dart": null, "default_client_dart": null, "dsl_features": [ { "allowed-lengths": { "min": 1, "max": null } } ] }, { "name": "url", "getter_name": "url", "moor_type": "string", "nullable": true, "customConstraints": null, "default_dart": null, "default_client_dart": null, "dsl_features": [] }, { "name": "last_update", "getter_name": "lastUpdate", "moor_type": "dateTime", "nullable": false, "customConstraints": null, "default_dart": null, "default_client_dart": null, "dsl_features": [] }, { "name": "update_interval", "getter_name": "updateInterval", "moor_type": "int", "nullable": true, "customConstraints": null, "default_dart": null, "default_client_dart": null, "dsl_features": [], "type_converter": { "dart_expr": "DurationTypeConverter()", "dart_type_name": "Duration" } }, { "name": "upload", "getter_name": "upload", "moor_type": "int", "nullable": true, "customConstraints": null, "default_dart": null, "default_client_dart": null, "dsl_features": [] }, { "name": "download", "getter_name": "download", "moor_type": "int", "nullable": true, "customConstraints": null, "default_dart": null, "default_client_dart": null, "dsl_features": [] }, { "name": "total", "getter_name": "total", "moor_type": "int", "nullable": true, "customConstraints": null, "default_dart": null, "default_client_dart": null, "dsl_features": [] }, { "name": "expire", "getter_name": "expire", "moor_type": "dateTime", "nullable": true, "customConstraints": null, "default_dart": null, "default_client_dart": null, "dsl_features": [] }, { "name": "web_page_url", "getter_name": "webPageUrl", "moor_type": "string", "nullable": true, "customConstraints": null, "default_dart": null, "default_client_dart": null, "dsl_features": [] }, { "name": "support_url", "getter_name": "supportUrl", "moor_type": "string", "nullable": true, "customConstraints": null, "default_dart": null, "default_client_dart": null, "dsl_features": [] } ], "is_virtual": false, "without_rowid": false, "constraints": [], "explicit_pk": [ "id" ] } } ] } ================================================ FILE: lib/core/db/schemas/db/drift_schema_v3.json ================================================ { "_meta": { "description": "This file contains a serialized version of schema entities for drift.", "version": "1.1.0" }, "options": { "store_date_time_values_as_text": true }, "entities": [ { "id": 0, "references": [], "type": "table", "data": { "name": "profile_entries", "was_declared_in_moor": false, "columns": [ { "name": "id", "getter_name": "id", "moor_type": "string", "nullable": false, "customConstraints": null, "default_dart": null, "default_client_dart": null, "dsl_features": [] }, { "name": "type", "getter_name": "type", "moor_type": "string", "nullable": false, "customConstraints": null, "default_dart": null, "default_client_dart": null, "dsl_features": [], "type_converter": { "dart_expr": "const EnumNameConverter(ProfileType.values)", "dart_type_name": "ProfileType" } }, { "name": "active", "getter_name": "active", "moor_type": "bool", "nullable": false, "customConstraints": null, "defaultConstraints": "CHECK (\"active\" IN (0, 1))", "default_dart": null, "default_client_dart": null, "dsl_features": [] }, { "name": "name", "getter_name": "name", "moor_type": "string", "nullable": false, "customConstraints": null, "default_dart": null, "default_client_dart": null, "dsl_features": [ { "allowed-lengths": { "min": 1, "max": null } } ] }, { "name": "url", "getter_name": "url", "moor_type": "string", "nullable": true, "customConstraints": null, "default_dart": null, "default_client_dart": null, "dsl_features": [] }, { "name": "last_update", "getter_name": "lastUpdate", "moor_type": "dateTime", "nullable": false, "customConstraints": null, "default_dart": null, "default_client_dart": null, "dsl_features": [] }, { "name": "update_interval", "getter_name": "updateInterval", "moor_type": "int", "nullable": true, "customConstraints": null, "default_dart": null, "default_client_dart": null, "dsl_features": [], "type_converter": { "dart_expr": "DurationTypeConverter()", "dart_type_name": "Duration" } }, { "name": "upload", "getter_name": "upload", "moor_type": "int", "nullable": true, "customConstraints": null, "default_dart": null, "default_client_dart": null, "dsl_features": [] }, { "name": "download", "getter_name": "download", "moor_type": "int", "nullable": true, "customConstraints": null, "default_dart": null, "default_client_dart": null, "dsl_features": [] }, { "name": "total", "getter_name": "total", "moor_type": "int", "nullable": true, "customConstraints": null, "default_dart": null, "default_client_dart": null, "dsl_features": [] }, { "name": "expire", "getter_name": "expire", "moor_type": "dateTime", "nullable": true, "customConstraints": null, "default_dart": null, "default_client_dart": null, "dsl_features": [] }, { "name": "web_page_url", "getter_name": "webPageUrl", "moor_type": "string", "nullable": true, "customConstraints": null, "default_dart": null, "default_client_dart": null, "dsl_features": [] }, { "name": "support_url", "getter_name": "supportUrl", "moor_type": "string", "nullable": true, "customConstraints": null, "default_dart": null, "default_client_dart": null, "dsl_features": [] } ], "is_virtual": false, "without_rowid": false, "constraints": [], "explicit_pk": [ "id" ] } }, { "id": 1, "references": [], "type": "table", "data": { "name": "geo_asset_entries", "was_declared_in_moor": false, "columns": [ { "name": "id", "getter_name": "id", "moor_type": "string", "nullable": false, "customConstraints": null, "default_dart": null, "default_client_dart": null, "dsl_features": [] }, { "name": "type", "getter_name": "type", "moor_type": "string", "nullable": false, "customConstraints": null, "default_dart": null, "default_client_dart": null, "dsl_features": [], "type_converter": { "dart_expr": "const EnumNameConverter(GeoAssetType.values)", "dart_type_name": "GeoAssetType" } }, { "name": "active", "getter_name": "active", "moor_type": "bool", "nullable": false, "customConstraints": null, "defaultConstraints": "CHECK (\"active\" IN (0, 1))", "default_dart": null, "default_client_dart": null, "dsl_features": [] }, { "name": "name", "getter_name": "name", "moor_type": "string", "nullable": false, "customConstraints": null, "default_dart": null, "default_client_dart": null, "dsl_features": [ { "allowed-lengths": { "min": 1, "max": null } } ] }, { "name": "provider_name", "getter_name": "providerName", "moor_type": "string", "nullable": false, "customConstraints": null, "default_dart": null, "default_client_dart": null, "dsl_features": [ { "allowed-lengths": { "min": 1, "max": null } } ] }, { "name": "version", "getter_name": "version", "moor_type": "string", "nullable": true, "customConstraints": null, "default_dart": null, "default_client_dart": null, "dsl_features": [] }, { "name": "last_check", "getter_name": "lastCheck", "moor_type": "dateTime", "nullable": true, "customConstraints": null, "default_dart": null, "default_client_dart": null, "dsl_features": [] } ], "is_virtual": false, "without_rowid": false, "constraints": [], "explicit_pk": [ "id" ], "unique_keys": [ [ "name", "provider_name" ] ] } } ] } ================================================ FILE: lib/core/db/schemas/db/drift_schema_v4.json ================================================ {"_meta":{"description":"This file contains a serialized version of schema entities for drift.","version":"1.2.0"},"options":{"store_date_time_values_as_text":true},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"profile_entries","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumNameConverter(ProfileType.values)","dart_type_name":"ProfileType"}},{"name":"active","getter_name":"active","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"active\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"active\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[{"allowed-lengths":{"min":1,"max":null}}]},{"name":"url","getter_name":"url","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"last_update","getter_name":"lastUpdate","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"update_interval","getter_name":"updateInterval","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"DurationTypeConverter()","dart_type_name":"Duration"}},{"name":"upload","getter_name":"upload","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"download","getter_name":"download","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"total","getter_name":"total","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"expire","getter_name":"expire","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"web_page_url","getter_name":"webPageUrl","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"support_url","getter_name":"supportUrl","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"test_url","getter_name":"testUrl","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":false,"constraints":[],"explicit_pk":["id"]}},{"id":1,"references":[],"type":"table","data":{"name":"geo_asset_entries","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumNameConverter(GeoAssetType.values)","dart_type_name":"GeoAssetType"}},{"name":"active","getter_name":"active","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"active\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"active\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[{"allowed-lengths":{"min":1,"max":null}}]},{"name":"provider_name","getter_name":"providerName","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[{"allowed-lengths":{"min":1,"max":null}}]},{"name":"version","getter_name":"version","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"last_check","getter_name":"lastCheck","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":false,"constraints":[],"explicit_pk":["id"],"unique_keys":[["name","provider_name"]]}}]} ================================================ FILE: lib/core/db/schemas/db/drift_schema_v5.json ================================================ {"_meta":{"description":"This file contains a serialized version of schema entities for drift.","version":"1.2.0"},"options":{"store_date_time_values_as_text":true},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"profile_entries","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumNameConverter(ProfileType.values)","dart_type_name":"ProfileType"}},{"name":"active","getter_name":"active","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"active\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"active\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[{"allowed-lengths":{"min":1,"max":null}}]},{"name":"url","getter_name":"url","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"last_update","getter_name":"lastUpdate","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"update_interval","getter_name":"updateInterval","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"DurationTypeConverter()","dart_type_name":"Duration"}},{"name":"upload","getter_name":"upload","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"download","getter_name":"download","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"total","getter_name":"total","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"expire","getter_name":"expire","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"web_page_url","getter_name":"webPageUrl","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"support_url","getter_name":"supportUrl","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"populated_headers","getter_name":"populatedHeaders","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"profile_override","getter_name":"profileOverride","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"user_override","getter_name":"userOverride","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":false,"constraints":[],"explicit_pk":["id"]}},{"id":1,"references":[],"type":"table","data":{"name":"app_proxy_entries","was_declared_in_moor":false,"columns":[{"name":"mode","getter_name":"mode","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumNameConverter(AppProxyMode.values)","dart_type_name":"AppProxyMode"}},{"name":"pkg_name","getter_name":"pkgName","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"flags","getter_name":"flags","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":false,"constraints":[],"explicit_pk":["mode","pkg_name"]}}]} ================================================ FILE: lib/core/directories/directories_provider.dart ================================================ import 'dart:io'; import 'package:flutter/foundation.dart' show kIsWeb; import 'package:flutter/services.dart'; import 'package:hiddify/core/model/directories.dart'; import 'package:hiddify/core/model/environment.dart'; import 'package:hiddify/utils/custom_loggers.dart'; import 'package:hiddify/utils/platform_utils.dart'; import 'package:path/path.dart' as p; 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("com.hiddify.app/platform"); @override Future build() async { final Directories dirs; if (kIsWeb) { return (baseDir: Directory("."), workingDir: Directory("."), tempDir: Directory(".")); } if (PlatformUtils.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 if (PlatformUtils.isWindows && Environment.isPortable && await checkDirectoryAccess(getPortableDirectory())) { final portableDir = getPortableDirectory(); dirs = (baseDir: portableDir, workingDir: portableDir, tempDir: await getTemporaryDirectory()); } 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 (kIsWeb) { return Directory("."); } if (PlatformUtils.isIOS || PlatformUtils.isMacOS) { return await getLibraryDirectory(); } else if (PlatformUtils.isWindows && Environment.isPortable && await checkDirectoryAccess(getPortableDirectory())) { final portableDir = getPortableDirectory(); return portableDir; } else if (PlatformUtils.isWindows || PlatformUtils.isLinux) { return await getApplicationSupportDirectory(); } return await getApplicationDocumentsDirectory(); } static Directory getPortableDirectory() { final exeDir = File(Platform.resolvedExecutable).parent; return Directory(p.join(exeDir.path, 'hiddify_portable_data')); } static Future checkDirectoryAccess(Directory dir) async { final testFile = File(p.join(dir.path, 'access_test.txt')); try { if (!await dir.exists()) await dir.create(recursive: true); await testFile.writeAsString('Testing write permission...'); await testFile.readAsString(); await testFile.delete(); return true; } catch (_) { return false; } } } ================================================ FILE: lib/core/haptic/haptic_service.dart ================================================ import 'package:flutter/services.dart'; import 'package:hiddify/core/preferences/preferences_provider.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:shared_preferences/shared_preferences.dart'; part 'haptic_service.g.dart'; @Riverpod(keepAlive: true) class HapticService extends _$HapticService { @override bool build() { return _preferences.getBool(hapticFeedbackPrefKey) ?? true; } static const String hapticFeedbackPrefKey = "haptic_feedback"; SharedPreferences get _preferences => ref.read(sharedPreferencesProvider).requireValue; Future updatePreference(bool value) async { state = value; await _preferences.setBool(hapticFeedbackPrefKey, value); } Future lightImpact() async { if (state) { await HapticFeedback.lightImpact(); } } Future mediumImpact() async { if (state) { await HapticFeedback.mediumImpact(); } } Future heavyImpact() async { if (state) { await HapticFeedback.heavyImpact(); } } } ================================================ FILE: lib/core/http_client/dio_http_client.dart ================================================ import 'dart:convert'; import 'dart:io'; import 'package:dio/dio.dart'; import 'package:dio/io.dart'; import 'package:dio_smart_retry/dio_smart_retry.dart'; import 'package:hiddify/utils/custom_loggers.dart'; class DioHttpClient with InfraLogger { final Map _dio = {}; DioHttpClient({required Duration timeout, required this.userAgent, required bool debug}) { for (var mode in ["proxy", "direct", "both"]) { _dio[mode] = Dio( BaseOptions( connectTimeout: timeout, sendTimeout: timeout, receiveTimeout: timeout, headers: {"User-Agent": userAgent}, ), ); _dio[mode]!.interceptors.add( RetryInterceptor( dio: _dio[mode]!, retryDelays: [ const Duration(seconds: 1), if (mode != "proxy") ...[const Duration(seconds: 2), const Duration(seconds: 3)], ], ), ); _dio[mode]!.httpClientAdapter = IOHttpClientAdapter( createHttpClient: () { final client = HttpClient(); client.findProxy = (url) { if (mode == "proxy") { return "PROXY localhost:$port"; } else if (mode == "direct") { return "DIRECT"; } else { return "PROXY localhost:$port; DIRECT"; } }; return client; }, ); } if (debug) { // _dio.interceptors.add(LoggyDioInterceptor(requestHeader: true)); } } int port = 0; String userAgent; // bool isPortOpen(String host, int port, {Duration timeout = const Duration(milliseconds: 200)}) async{ // try { // Socket.connect(host, port, timeout: timeout).then((socket) { // socket.destroy(); // }); // return true; // } on SocketException catch (_) { // return false; // } catch (_) { // return false; // } // } Future isPortOpen(String host, int port, {Duration timeout = const Duration(seconds: 5)}) async { try { final socket = await Socket.connect(host, port, timeout: timeout); await socket.close(); return true; } on SocketException catch (_) { return false; } catch (_) { return false; } } void setProxyPort(int port) { this.port = port; loggy.debug("setting proxy port: [$port]"); } Future> get( String url, { CancelToken? cancelToken, String? userAgent, ({String username, String password})? credentials, bool proxyOnly = false, }) async { final mode = proxyOnly ? "proxy" : await isPortOpen("127.0.0.1", port) ? "both" : "direct"; final dio = _dio[mode]!; return dio.get( url, cancelToken: cancelToken, options: _options(url, userAgent: userAgent, credentials: credentials), ); } Future download( String url, String path, { CancelToken? cancelToken, String? userAgent, ({String username, String password})? credentials, bool proxyOnly = false, }) async { final mode = proxyOnly ? "proxy" : await isPortOpen("127.0.0.1", port) ? "both" : "direct"; final dio = _dio[mode]!; return dio.download( url, path, cancelToken: cancelToken, options: _options(url, userAgent: userAgent, credentials: credentials), ); } Options _options(String url, {String? userAgent, ({String username, String password})? credentials}) { final uri = Uri.parse(url); String? userInfo; if (credentials != null) { userInfo = "${credentials.username}:${credentials.password}"; } else if (uri.userInfo.isNotEmpty) { userInfo = uri.userInfo; } String? basicAuth; if (userInfo != null) { basicAuth = "Basic ${base64.encode(utf8.encode(userInfo))}"; } return Options( headers: { if (userAgent != null) "User-Agent": userAgent, if (basicAuth != null) "authorization": basicAuth, // "Accept": "application/json", // "Content-Type": "application/json", }, ); } } ================================================ FILE: lib/core/http_client/http_client_provider.dart ================================================ import 'package:flutter/foundation.dart'; import 'package:hiddify/core/app_info/app_info_provider.dart'; import 'package:hiddify/core/http_client/dio_http_client.dart'; import 'package:hiddify/features/settings/data/config_option_repository.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'http_client_provider.g.dart'; @Riverpod(keepAlive: true) DioHttpClient httpClient(Ref ref) { final client = DioHttpClient( timeout: const Duration(seconds: 15), userAgent: ref.watch(appInfoProvider).requireValue.userAgent, debug: kDebugMode, ); ref.listen(ConfigOptions.mixedPort, (_, next) => client.setProxyPort(next), fireImmediately: true); return client; } ================================================ FILE: lib/core/localization/locale_extensions.dart ================================================ import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:hiddify/gen/fonts.gen.dart'; import 'package:hiddify/gen/translations.g.dart'; extension AppLocaleX on AppLocale { String get preferredFontFamily => this == AppLocale.fa ? FontFamily.shabnam : (kIsWeb || !Platform.isWindows ? "" : FontFamily.emoji); String get localeName => switch (flutterLocale.toString()) { "ar" => "العربية", "en" => "English", "es" => "Spanish", "fa" => "فارسی", "fr" => "Français", "id" => "Indonesian", "pt_BR" => "Portuguese (Brazil)", "ru" => "Русский", "tr" => "Türkçe", "zh" || "zh_CN" => "中文 (中国)", "zh_TW" => "中文 (台湾)", _ => "Unknown", }; } ================================================ FILE: lib/core/localization/locale_preferences.dart ================================================ import 'package:hiddify/core/preferences/preferences_provider.dart'; import 'package:hiddify/gen/translations.g.dart'; import 'package:hiddify/utils/custom_loggers.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'locale_preferences.g.dart'; @Riverpod(keepAlive: true) class LocalePreferences extends _$LocalePreferences with AppLogger { @override AppLocale build() { final persisted = ref.watch(sharedPreferencesProvider).requireValue.getString("locale"); if (persisted == null) return AppLocaleUtils.findDeviceLocale(); // keep backward compatibility with chinese after changing zh to zh_CN if (persisted == "zh") { return AppLocale.zhCn; } try { return AppLocale.values.byName(persisted); } catch (e) { loggy.error("error setting locale: [$persisted]", e); return AppLocale.en; } } Future changeLocale(AppLocale value) async { state = value; await ref.read(sharedPreferencesProvider).requireValue.setString("locale", value.name); } } ================================================ FILE: lib/core/localization/translations.dart ================================================ import 'package:hiddify/core/localization/locale_preferences.dart'; import 'package:hiddify/gen/translations.g.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; export 'package:hiddify/gen/translations.g.dart'; part 'translations.g.dart'; @Riverpod(keepAlive: true) Future translations(Ref ref) async { return await ref.watch(localePreferencesProvider).build(); } ================================================ FILE: lib/core/logger/custom_logger.dart ================================================ // ignore_for_file: avoid_print import 'dart:io'; import 'package:loggy/loggy.dart'; class ConsolePrinter extends LoggyPrinter { const ConsolePrinter({this.showColors = false}); final bool showColors; static final _levelColors = { LogLevel.debug: AnsiColor(foregroundColor: AnsiColor.grey(0.5), italic: true), LogLevel.info: AnsiColor(foregroundColor: 35), LogLevel.warning: AnsiColor(foregroundColor: 214), LogLevel.error: AnsiColor(foregroundColor: 196), }; @override void onLog(LogRecord record) { final colorize = showColors && stdout.supportsAnsiEscapes; final time = record.time.toIso8601String().split('T')[1]; final callerFrame = record.callerFrame == null ? ' ' : ' (${record.callerFrame?.location}) '; final String logLevel; if (colorize) { logLevel = record.level.name.toUpperCase().padRight(8); } else { logLevel = "[${record.level.name.toUpperCase()}]".padRight(10); } final color = showColors ? levelColor(record.level) ?? AnsiColor() : AnsiColor(); print(color('$time $logLevel [${record.loggerName}]$callerFrame${record.message}')); if (record.stackTrace != null) { print(record.stackTrace); } } AnsiColor? levelColor(LogLevel level) { return _levelColors[level]; } } class FileLogPrinter extends LoggyPrinter { FileLogPrinter(String filePath, {this.minLevel = LogLevel.debug}) : _logFile = File(filePath); final File _logFile; final LogLevel minLevel; late final _sink = _logFile.openWrite(mode: FileMode.writeOnly); @override 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(); } } ================================================ FILE: lib/core/logger/logger.dart ================================================ 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; } } ================================================ FILE: lib/core/logger/logger_controller.dart ================================================ import 'dart:io'; import 'package:hiddify/core/logger/custom_logger.dart'; import 'package:hiddify/utils/custom_loggers.dart'; import 'package:loggy/loggy.dart'; import 'package:flutter/foundation.dart' show kIsWeb; 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 preInit() { Loggy.initLoggy(logPrinter: const ConsolePrinter()); } static void init(String appLogPath) { _instance = LoggerController(const ConsolePrinter(), {"app": kIsWeb ? const ConsolePrinter() : FileLogPrinter(appLogPath)}); Loggy.initLoggy(logPrinter: _instance); } static Future postInit(bool debugMode) async { final logLevel = debugMode && false ? LogLevel.all : LogLevel.info; final logToFile = debugMode || (!Platform.isAndroid && !Platform.isIOS); if (!logToFile || kIsWeb) _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); } } } ================================================ FILE: lib/core/model/app_info_entity.dart ================================================ import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:hiddify/core/model/environment.dart'; part 'app_info_entity.freezed.dart'; @freezed class AppInfoEntity with _$AppInfoEntity { const AppInfoEntity._(); const factory AppInfoEntity({ required String name, required String version, required String buildNumber, required Release release, required String operatingSystem, required String operatingSystemVersion, required Environment environment, }) = _AppInfoEntity; String get userAgent => "HiddifyNext/$version ($operatingSystem) like ClashMeta v2ray sing-box"; String get presentVersion => environment == Environment.prod ? version : "$version ${environment.name}"; /// formats app info for sharing String format() => ''' $name v$version ($buildNumber) [${environment.name}] ${release.name} release $operatingSystem [$operatingSystemVersion]'''; } ================================================ FILE: lib/core/model/constants.dart ================================================ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; abstract class Constants { static const appName = "Hiddify"; static const githubUrl = "https://github.com/hiddify/hiddify-next"; static const licenseUrl = "https://github.com/hiddify/hiddify-next?tab=License-1-ov-file#readme"; static const githubReleasesApiUrl = "https://api.github.com/repos/hiddify/hiddify-next/releases"; static const githubLatestReleaseUrl = "https://github.com/hiddify/hiddify-app/releases/latest"; static const appCastUrl = "https://raw.githubusercontent.com/hiddify/hiddify-next/main/appcast.xml"; static const telegramChannelUrl = "https://t.me/hiddify"; static const privacyPolicyUrl = "https://hiddify.com/privacy-policy/"; static const termsAndConditionsUrl = "https://hiddify.com/terms/"; static const cfWarpPrivacyPolicy = "https://www.cloudflare.com/application/privacypolicy/"; static const cfWarpTermsOfService = "https://www.cloudflare.com/application/terms/"; } const kAnimationDuration = Duration(milliseconds: 250); abstract class AddProfileModalConst { static const fixBtnsGap = 16.0; static const fixBtnsGapCount = 4; static const fixBtnsItemCount = 3; static const navBarGap = 16.0; static const navBarBottomGap = 4.0; //switch default height static const navBarcontentHeight = 32.0; static const navBarHeight = navBarGap + navBarBottomGap + navBarcontentHeight; } abstract class AlertDialogConst { static const minWidth = 280.0; static const maxWidth = 560.0; static const boxConstraints = BoxConstraints(minWidth: minWidth, maxWidth: maxWidth); } abstract class BottomSheetConst { static const maxWidth = 456.0; static const boxConstraints = BoxConstraints(maxWidth: maxWidth); static const borderRadius = BorderRadius.vertical(top: Radius.circular(32)); } abstract class ProfileTileConst { static const radius = Radius.circular(16); static const cardBorderRadius = BorderRadius.all(radius); static const borderRadiusRight = BorderRadius.horizontal(right: radius); static const borderRadiusLeft = BorderRadius.horizontal(left: radius); static BorderRadius startBorderRadius(TextDirection direction) => direction == TextDirection.ltr ? borderRadiusLeft : borderRadiusRight; static BorderRadius endBorderRadius(TextDirection direction) => direction == TextDirection.ltr ? borderRadiusRight : borderRadiusLeft; } abstract class IntroConst { static const maxwidth = 620; static const termsAndConditionsKey = 'terms-and-conditions'; static const githubKey = 'github'; static const licenseKey = 'license'; static const url = {IntroConst.termsAndConditionsKey: Constants.termsAndConditionsUrl, IntroConst.githubKey: Constants.githubUrl, IntroConst.licenseKey: Constants.licenseUrl}; } abstract class WarpConst { static const warpAccountId = 'warp-account-id'; static const warpAccessToken = "warp-access-token"; static const warpConsentGiven = "warp-consent-given"; static const warpTermsOfServiceKey = 'warp-terms-of-service'; static const warpPrivacyPolicyKey = 'warp-privacy-policy'; static const url = {WarpConst.warpTermsOfServiceKey: Constants.cfWarpTermsOfService, WarpConst.warpPrivacyPolicyKey: Constants.cfWarpPrivacyPolicy}; } abstract class KeyboardConst { static final allArrows = {LogicalKeyboardKey.arrowUp, LogicalKeyboardKey.arrowDown, LogicalKeyboardKey.arrowLeft, LogicalKeyboardKey.arrowRight}; static final horizontalArrows = {LogicalKeyboardKey.arrowLeft, LogicalKeyboardKey.arrowRight}; static final verticalArrows = {LogicalKeyboardKey.arrowUp, LogicalKeyboardKey.arrowDown}; static final select = {LogicalKeyboardKey.select, LogicalKeyboardKey.enter, LogicalKeyboardKey.tab}; } ================================================ FILE: lib/core/model/directories.dart ================================================ import 'dart:io'; typedef Directories = ({Directory baseDir, Directory workingDir, Directory tempDir}); ================================================ FILE: lib/core/model/environment.dart ================================================ import 'package:dartx/dartx.dart'; enum Environment { prod, dev; static const sentryDSN = String.fromEnvironment("sentry_dsn"); // This environment variable is set in the 'windows-release-zip' command static const isPortable = bool.fromEnvironment("portable"); } enum Release { general("general"), // This environment variable is set in the 'android-release-aab' command googlePlay("google-play"); const Release(this.key); final String key; bool get allowCustomUpdateChecker => this == general; static Release read() => Release.values.firstOrNullWhere((e) => e.key == const String.fromEnvironment("release")) ?? Release.general; } ================================================ FILE: lib/core/model/failures.dart ================================================ import 'package:dio/dio.dart'; import 'package:grpc/grpc.dart'; import 'package:hiddify/core/localization/translations.dart'; typedef PresentableError = ({String type, String? message}); mixin Failure { ({String type, String? message}) present(TranslationsEn t); } /// failures that are not expected to happen but depending on [error] type might not be relevant (eg network errors) mixin UnexpectedFailure { Object? get error; StackTrace? get stackTrace; } /// failures that are expected to happen and should be handled by the app /// and should be logged, eg missing permissions mixin ExpectedMeasuredFailure {} /// failures ignored by analytics service etc. mixin ExpectedFailure {} extension ErrorPresenter on TranslationsEn { PresentableError errorToPair(Object error) => switch (error) { GrpcError(message: final nestedErr?) => errorToPair(nestedErr), UnexpectedFailure(error: final nestedErr?) => errorToPair(nestedErr), Failure() => error.present(this), DioException() => error.present(this), _ => (type: errors.unexpected, message: error.toString()), }; PresentableError presentError(Object error, {String? action}) { final pair = errorToPair(error); if (action == null) return pair; return (type: action, message: pair.type + (pair.message == null ? "" : "\n${pair.message!}")); } String presentShortError(Object error, {String? action}) { final pair = errorToPair(error); if (action == null) return pair.type; return "$action: ${pair.type}"; } } extension DioExceptionPresenter on DioException { PresentableError present(TranslationsEn t) => switch (type) { DioExceptionType.connectionTimeout || DioExceptionType.sendTimeout || DioExceptionType.receiveTimeout => (type: t.errors.connection.timeout, message: null), DioExceptionType.badCertificate => (type: t.errors.connection.badCertificate, message: message), DioExceptionType.badResponse => (type: t.errors.connection.badResponse, message: message), DioExceptionType.connectionError => (type: t.errors.connection.connectionError, message: message), _ => (type: t.errors.connection.unexpected, message: message), }; } ================================================ FILE: lib/core/model/optional_range.dart ================================================ import 'package:dart_mappable/dart_mappable.dart'; import 'package:dartx/dartx.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:hiddify/core/localization/translations.dart'; part 'optional_range.mapper.dart'; @MappableClass() class OptionalRange with OptionalRangeMappable { const OptionalRange({this.min, this.max}); final int? min; final int? max; String format() => [min, max].whereNotNull().join("-"); String present(TranslationsEn t) => format().isEmpty ? t.common.notSet : format(); factory OptionalRange.parse(String input, {bool allowEmpty = false}) => switch (input.split("-")) { [final String val] when val.isEmpty && allowEmpty => const OptionalRange(), [final String min] => OptionalRange(min: int.parse(min)), [final String min, final String max] => OptionalRange(min: int.parse(min), max: int.parse(max)), _ => throw Exception("Invalid range: $input"), }; static OptionalRange? tryParse(String input, {bool allowEmpty = false}) { try { return OptionalRange.parse(input, allowEmpty: allowEmpty); } catch (_) { return null; } } } class OptionalRangeJsonConverter implements JsonConverter { const OptionalRangeJsonConverter(); @override OptionalRange fromJson(String json) => OptionalRange.parse(json, allowEmpty: true); @override String toJson(OptionalRange object) => object.format(); } ================================================ FILE: lib/core/model/region.dart ================================================ import 'package:hiddify/core/localization/translations.dart'; enum Region { ir, cn, ru, af, id, tr, br, other; String present(TranslationsEn t) => switch (this) { ir => t.pages.settings.routing.regions.ir, cn => t.pages.settings.routing.regions.cn, ru => t.pages.settings.routing.regions.ru, af => t.pages.settings.routing.regions.af, id => t.pages.settings.routing.regions.id, tr => t.pages.settings.routing.regions.tr, br => t.pages.settings.routing.regions.br, other => t.pages.settings.routing.regions.other, }; } ================================================ FILE: lib/core/notification/in_app_notification_controller.dart ================================================ import 'package:flutter/material.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:toastification/toastification.dart'; part 'in_app_notification_controller.g.dart'; @Riverpod(keepAlive: true) InAppNotificationController inAppNotificationController(Ref ref) { return InAppNotificationController(); } enum NotificationType { info, error, success } class InAppNotificationController with AppLogger { ToastificationItem _show( String message, { NotificationType type = NotificationType.info, Duration duration = const Duration(seconds: 3), }) { toastification.dismissAll(); return toastification.show( title: Text(message), type: type._toastificationType, alignment: AlignmentDirectional.bottomStart, autoCloseDuration: duration, style: ToastificationStyle.fillColored, pauseOnHover: true, showProgressBar: false, dragToClose: true, closeOnClick: true, closeButtonShowType: CloseButtonShowType.onHover, ); } ToastificationItem? showErrorToast(String message) => _show(message, type: NotificationType.error, duration: const Duration(seconds: 5)); ToastificationItem? showSuccessToast(String message) => _show(message, type: NotificationType.success); ToastificationItem? showInfoToast(String message, {Duration duration = const Duration(seconds: 3)}) => _show(message, duration: duration); } extension NotificationTypeX on NotificationType { ToastificationType get _toastificationType => switch (this) { NotificationType.success => ToastificationType.success, NotificationType.error => ToastificationType.error, NotificationType.info => ToastificationType.info, }; } ================================================ FILE: lib/core/preferences/actions_at_closing.dart ================================================ import 'package:hiddify/gen/translations.g.dart'; enum ActionsAtClosing { ask, hide, exit; String present(TranslationsEn t) => switch (this) { ask => t.dialogs.windowClosing.askEachTime, hide => t.common.hide, exit => t.common.exit, }; } ================================================ FILE: lib/core/preferences/general_preferences.dart ================================================ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:hiddify/core/app_info/app_info_provider.dart'; import 'package:hiddify/core/model/environment.dart'; import 'package:hiddify/core/model/region.dart'; import 'package:hiddify/core/preferences/actions_at_closing.dart'; import 'package:hiddify/core/preferences/preferences_provider.dart'; import 'package:hiddify/core/utils/preferences_utils.dart'; import 'package:hiddify/features/per_app_proxy/model/per_app_proxy_mode.dart'; import 'package:hiddify/features/window/notifier/window_notifier.dart'; import 'package:hiddify/utils/platform_utils.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'general_preferences.g.dart'; bool _debugIntroPage = false; abstract class Preferences { static final introCompleted = PreferencesNotifier.create( "intro_completed", false, overrideValue: _debugIntroPage && kDebugMode ? false : null, ); // Null means that auto selection has not been performed yet. static final autoAppsSelectionRegion = PreferencesNotifier.create( "auto_apps_selection_region", null, mapFrom: (value) => value == null || value.isEmpty ? null : Region.values.byName(value), mapTo: (value) => value == null ? '' : value.name, ); static final autoAppsSelectionUpdateInterval = PreferencesNotifier.create( "auto_apps_selection_update_interval", 1.0, ); static final autoAppsSelectionLastUpdate = PreferencesNotifier.create( "auto_apps_selection_last_update", null, mapFrom: (value) => value == null ? null : DateTime.tryParse(value), mapTo: (value) => value?.toIso8601String(), ); static final includeApps = PreferencesNotifier.create, List>( "per_app_proxy_include_list", [], ); static final excludeApps = PreferencesNotifier.create, List>( "per_app_proxy_exclude_list", [], ); static final windowMaximized = PreferencesNotifier.create("window_maximized", false); static final windowPosition = PreferencesNotifier.create( "window_position", null, mapFrom: (value) { if (value == null) return null; final list = value.split(',').map((e) => double.tryParse(e)).toList(); return Offset(list[0]!, list[1]!); }, mapTo: (value) { if (value == null) return null; return "${value.dx},${value.dy}"; }, ); static final windowSize = PreferencesNotifier.create( "window_size", defaultWindowSize, mapFrom: (value) { final list = value.split(',').map((e) => double.tryParse(e)).toList(); return Size(list[0]!, list[1]!); }, mapTo: (value) => "${value.width},${value.height}", ); static final silentStart = PreferencesNotifier.create("silent_start", false); static final disableMemoryLimit = PreferencesNotifier.create( "disable_memory_limit", // disable memory limit on desktop by default PlatformUtils.isDesktop, ); static final perAppProxyMode = PreferencesNotifier.create( "per_app_proxy_mode", PerAppProxyMode.off, mapFrom: PerAppProxyMode.values.byName, mapTo: (value) => value.name, ); static final markNewProfileActive = PreferencesNotifier.create("mark_new_profile_active", true); static final dynamicNotification = PreferencesNotifier.create("dynamic_notification", true); static final autoCheckIp = PreferencesNotifier.create("auto_check_ip", true); static final startedByUser = PreferencesNotifier.create("started_by_user", false); static final storeReviewedByUser = PreferencesNotifier.create("store_reviewed_by_user", false); static final actionAtClose = PreferencesNotifier.create( "action_at_close", ActionsAtClosing.ask, mapFrom: ActionsAtClosing.values.byName, mapTo: (value) => value.name, ); } @Riverpod(keepAlive: true) class DebugModeNotifier extends _$DebugModeNotifier { late final _pref = PreferencesEntry( preferences: ref.watch(sharedPreferencesProvider).requireValue, key: "debug_mode", defaultValue: ref.read(environmentProvider) == Environment.dev, ); @override bool build() => _pref.read(); Future update(bool value) { state = value; return _pref.write(value); } } ================================================ FILE: lib/core/preferences/preferences_migration.dart ================================================ import 'package:hiddify/utils/utils.dart'; import 'package:shared_preferences/shared_preferences.dart'; class PreferencesMigration with InfraLogger { PreferencesMigration({required this.sharedPreferences}); final SharedPreferences sharedPreferences; static const versionKey = "preferences_version"; Future migrate() async { final currentVersion = sharedPreferences.getInt(versionKey) ?? 0; final migrationSteps = [PreferencesVersion1Migration(sharedPreferences)]; if (currentVersion == migrationSteps.length) { loggy.debug("already using the latest version (v$currentVersion)"); return; } final stopWatch = Stopwatch()..start(); loggy.debug("migrating from v[$currentVersion] to v[${migrationSteps.length}]"); for (int i = currentVersion; i < migrationSteps.length; i++) { loggy.debug("step [$i](v${i + 1})"); await migrationSteps[i].migrate(); await sharedPreferences.setInt(versionKey, i + 1); } stopWatch.stop(); loggy.debug("migration took [${stopWatch.elapsedMilliseconds}]ms"); } } abstract interface class PreferencesMigrationStep { PreferencesMigrationStep(this.sharedPreferences); final SharedPreferences sharedPreferences; Future migrate(); } class PreferencesVersion1Migration extends PreferencesMigrationStep with InfraLogger { PreferencesVersion1Migration(super.sharedPreferences); @override Future migrate() async { if (sharedPreferences.getString("service-mode") case final String serviceMode) { final newMode = switch (serviceMode) { "proxy" || "system-proxy" || "vpn" => serviceMode, "systemProxy" => "system-proxy", "tun" => "vpn", _ => PlatformUtils.isDesktop ? "system-proxy" : "vpn", }; loggy.debug("changing service-mode from [$serviceMode] to [$newMode]"); await sharedPreferences.setString("service-mode", newMode); } if (sharedPreferences.getString("ipv6-mode") case final String ipv6Mode) { loggy.debug("changing ipv6-mode from [$ipv6Mode] to [${_ipv6Mapper(ipv6Mode)}]"); await sharedPreferences.setString("ipv6-mode", _ipv6Mapper(ipv6Mode)); } if (sharedPreferences.getString("remote-domain-dns-strategy") case final String remoteDomainStrategy) { loggy.debug( "changing [remote-domain-dns-strategy] = [$remoteDomainStrategy] to [remote-dns-domain-strategy] = [${_domainStrategyMapper(remoteDomainStrategy)}]", ); await sharedPreferences.remove("remote-domain-dns-strategy"); await sharedPreferences.setString("remote-dns-domain-strategy", _domainStrategyMapper(remoteDomainStrategy)); } if (sharedPreferences.getString("direct-domain-dns-strategy") case final String directDomainStrategy) { loggy.debug( "changing [direct-domain-dns-strategy] = [$directDomainStrategy] to [direct-dns-domain-strategy] = [${_domainStrategyMapper(directDomainStrategy)}]", ); await sharedPreferences.remove("direct-domain-dns-strategy"); await sharedPreferences.setString("direct-dns-domain-strategy", _domainStrategyMapper(directDomainStrategy)); } if (sharedPreferences.getInt("localDns-port") case final int directPort) { loggy.debug("changing [localDns-port] to [direct-port]"); await sharedPreferences.remove("localDns-port"); await sharedPreferences.setInt("direct-port", directPort); } await sharedPreferences.remove("execute-config-as-is"); await sharedPreferences.remove("enable-tun"); await sharedPreferences.remove("set-system-proxy"); await sharedPreferences.remove("cron_profiles_update"); } String _ipv6Mapper(String persisted) => switch (persisted) { "ipv4_only" || "prefer_ipv4" || "prefer_ipv4" || "ipv6_only" => persisted, "disable" => "ipv4_only", "enable" => "prefer_ipv4", "prefer" => "prefer_ipv6", "only" => "ipv6_only", _ => "ipv4_only", }; String _domainStrategyMapper(String persisted) => switch (persisted) { "ipv4_only" || "prefer_ipv4" || "prefer_ipv4" || "ipv6_only" => persisted, "auto" => "", "preferIpv6" => "prefer_ipv6", "preferIpv4" => "prefer_ipv4", "ipv4Only" => "ipv4_only", "ipv6Only" => "ipv6_only", _ => "", }; } ================================================ FILE: lib/core/preferences/preferences_provider.dart ================================================ import 'dart:io'; import 'package:hiddify/core/model/environment.dart'; import 'package:hiddify/utils/platform_utils.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:loggy/loggy.dart'; import 'package:path/path.dart' as p; import 'package:path_provider/path_provider.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:shared_preferences/shared_preferences.dart'; part 'preferences_provider.g.dart'; @Riverpod(keepAlive: true) Future sharedPreferences(Ref ref) async { final logger = Loggy("preferences"); SharedPreferences? sharedPreferences; logger.debug("initializing preferences"); try { if (PlatformUtils.isWindows && Environment.isPortable) SharedPreferences.setPrefix('portable.'); sharedPreferences = await SharedPreferences.getInstance(); } catch (e) { logger.error("error initializing preferences", e); if (!Platform.isWindows && !Platform.isLinux) { rethrow; } // https://github.com/flutter/flutter/issues/89211 final directory = await getApplicationSupportDirectory(); final file = File(p.join(directory.path, 'shared_preferences.json')); if (file.existsSync()) { file.deleteSync(); } } return sharedPreferences ??= await SharedPreferences.getInstance(); } ================================================ FILE: lib/core/router/adaptive_layout/my_adaptive_layout.dart ================================================ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:go_router/go_router.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/model/constants.dart'; import 'package:hiddify/core/router/adaptive_layout/shell_route_action.dart'; import 'package:hiddify/core/router/go_router/helper/active_breakpoint_notifier.dart'; import 'package:hiddify/core/router/go_router/routing_config_notifier.dart'; import 'package:hiddify/features/stats/widget/side_bar_stats_overview.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class MyAdaptiveLayout extends HookConsumerWidget { const MyAdaptiveLayout({ super.key, required this.navigationShell, required this.isMobileBreakpoint, required this.showProfilesAction, }); // managed by go router(Shell Route) final StatefulNavigationShell navigationShell; final bool isMobileBreakpoint; final bool showProfilesAction; @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; // focus switch management final primaryFocusHash = useState(null); final navScopeNode = useFocusScopeNode(); useEffect(() { bool handler(KeyEvent event) { final arrows = isMobileBreakpoint ? KeyboardConst.verticalArrows : KeyboardConst.horizontalArrows; if (!arrows.contains(event.logicalKey)) return false; if (event is KeyDownEvent) { primaryFocusHash.value = FocusManager.instance.primaryFocus.hashCode; } else { // focus node does not change => true. if (primaryFocusHash.value == FocusManager.instance.primaryFocus.hashCode) { if (branchesScope.values.any((node) => node.hasFocus)) { navScopeNode.requestFocus(); } else if (navScopeNode.hasFocus) { branchesScope[getNameOfBranch(isMobileBreakpoint, showProfilesAction, navigationShell.currentIndex)] ?.requestFocus(); } } } return true; } HardwareKeyboard.instance.addHandler(handler); return () { HardwareKeyboard.instance.removeHandler(handler); }; }, [isMobileBreakpoint, showProfilesAction, navigationShell.currentIndex]); return Material( child: Scaffold( body: isMobileBreakpoint ? navigationShell : Row( children: [ FocusScope( node: navScopeNode, child: NavigationRail( extended: Breakpoint(context).isDesktop(), destinations: _navRailDests(_actions(t, showProfilesAction, isMobileBreakpoint)), selectedIndex: navigationShell.currentIndex, onDestinationSelected: (index) => _onTap(context, index), trailing: Breakpoint(context).isDesktop() ? const Expanded( child: Align( alignment: Alignment.bottomCenter, child: SizedBox(width: 220, child: SideBarStatsOverview()), ), ) : null, ), ), Expanded(child: navigationShell), ], ), bottomNavigationBar: isMobileBreakpoint ? FocusScope( node: navScopeNode, child: NavigationBar( selectedIndex: navigationShell.currentIndex <= 1 ? navigationShell.currentIndex : 0, destinations: _navDests(_actions(t, showProfilesAction, isMobileBreakpoint)), onDestinationSelected: (index) => _onTap(context, index), ), ) : null, ), ); } // shell route action onTap void _onTap(BuildContext context, int index) { navigationShell.goBranch(index, initialLocation: index == navigationShell.currentIndex); } List _actions(Translations t, bool showProfilesAction, bool isMobileBreakpoint) => [ ShellRouteAction(Icons.power_settings_new_rounded, t.pages.home.title), if (showProfilesAction && !isMobileBreakpoint) ShellRouteAction(Icons.view_list_rounded, t.pages.profiles.title), ShellRouteAction(Icons.settings_rounded, t.pages.settings.title), if (!isMobileBreakpoint) ShellRouteAction(Icons.description_rounded, t.pages.logs.title), if (!isMobileBreakpoint) ShellRouteAction(Icons.info_rounded, t.pages.about.title), ]; List _navDests(List actions) => actions.map((e) => NavigationDestination(icon: Icon(e.icon), label: e.title)).toList(); List _navRailDests(List actions) => actions.map((e) => NavigationRailDestination(icon: Icon(e.icon), label: Text(e.title))).toList(); } ================================================ FILE: lib/core/router/adaptive_layout/shell_route_action.dart ================================================ import 'package:flutter/material.dart'; //Helper class for storing details for each navigation action in my_adaptive_layout.dart class ShellRouteAction { final IconData icon; final String title; ShellRouteAction(this.icon, this.title); } ================================================ FILE: lib/core/router/bottom_sheets/bottom_sheets_notifier.dart ================================================ import 'package:flutter/material.dart'; import 'package:hiddify/core/model/constants.dart'; import 'package:hiddify/core/router/bottom_sheets/widgets/auto_apps_selection_modal.dart'; import 'package:hiddify/core/router/bottom_sheets/widgets/quick_settings_modal.dart'; import 'package:hiddify/core/router/go_router/go_router_notifier.dart'; import 'package:hiddify/features/per_app_proxy/model/per_app_proxy_mode.dart'; import 'package:hiddify/features/profile/add/add_profile_modal.dart'; import 'package:hiddify/features/profile/overview/profiles_modal.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'bottom_sheets_notifier.g.dart'; @riverpod class BottomSheetsNotifier extends _$BottomSheetsNotifier { @override void build() {} Future _show({required Widget child, required bool isScrollControlled}) async { final context = rootNavKey.currentContext; if (context == null) return null; // ref.read(popupCountNotifierProvider.notifier).increase(); return await Navigator.of(context) .push( ModalBottomSheetRoute( constraints: BottomSheetConst.boxConstraints, isScrollControlled: isScrollControlled, builder: (context) => ClipRRect( borderRadius: BottomSheetConst.borderRadius, child: Material( child: Padding( padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom), child: child, ), ), ), ), ) .then((value) { // ref.read(popupCountNotifierProvider.notifier).decrease(); return value; }); } Future showAddProfile({String? url}) async => await _show(isScrollControlled: true, child: AddProfileModal(url: url)); Future showProfilesOverview() async => await _show(isScrollControlled: true, child: const ProfilesModal()); Future showQuickSettings() async => await _show(isScrollControlled: false, child: const QuickSettingsModal()); Future showAutoAppsSelection({required AppProxyMode mode}) async => await _show(isScrollControlled: false, child: AutoAppsSelectionModal(mode: mode)); } ================================================ FILE: lib/core/router/bottom_sheets/widgets/auto_apps_selection_modal.dart ================================================ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:gap/gap.dart'; import 'package:go_router/go_router.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/model/constants.dart'; import 'package:hiddify/core/preferences/general_preferences.dart'; import 'package:hiddify/features/per_app_proxy/model/per_app_proxy_mode.dart'; import 'package:hiddify/features/per_app_proxy/overview/per_app_proxy_loading_notifier.dart'; import 'package:hiddify/features/per_app_proxy/overview/per_app_proxy_notifier.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class AutoAppsSelectionModal extends HookConsumerWidget { const AutoAppsSelectionModal({super.key, required this.mode}); final AppProxyMode mode; String _genSliderText(Translations t, int sliderValue) { final day = t.common.interval.day(n: sliderValue); return day; } @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; final theme = Theme.of(context); final loading = ref.watch(appProxyLoadingProvider); final isAutoEnabled = ref.watch(Preferences.autoAppsSelectionRegion) != null; final updateInterval = ref.watch(Preferences.autoAppsSelectionUpdateInterval); final sliderFocusNode = useFocusNode( onKeyEvent: (node, event) { if (KeyboardConst.verticalArrows.contains(event.logicalKey) && event is KeyDownEvent) { if (event.logicalKey == LogicalKeyboardKey.arrowUp) { node.previousFocus(); } else { node.nextFocus(); } return KeyEventResult.handled; } return KeyEventResult.ignored; }, ); useEffect(() { if (!isAutoEnabled) { WidgetsBinding.instance.addPostFrameCallback((_) async { await ref .read(appProxyLoadingProvider.notifier) .doAsync(ref.read(PerAppProxyProvider(mode).notifier).applyAutoSelection); }); } return null; }, []); return SafeArea( child: SingleChildScrollView( child: Column( children: [ ListTile( title: Text(t.pages.settings.routing.perAppProxy.autoSelection.title), trailing: AnimatedSwitcher( duration: const Duration(milliseconds: 300), child: loading ? const Padding( padding: EdgeInsetsDirectional.only(end: 10), child: SizedBox(width: 32, height: 32, child: CircularProgressIndicator()), ) : Switch.adaptive( value: isAutoEnabled, onChanged: (value) async { final notifier = ref.read(appProxyLoadingProvider.notifier); if (value) { await notifier.doAsync(ref.read(PerAppProxyProvider(mode).notifier).applyAutoSelection); } else { await notifier.doAsync(ref.read(PerAppProxyProvider(mode).notifier).clearAutoSelected); if (context.mounted) context.pop(); } }, ), ), ), AnimatedSize( alignment: Alignment.topCenter, duration: const Duration(milliseconds: 300), curve: Curves.easeInOut, child: isAutoEnabled ? Column( children: [ const Divider(height: 1), Padding( padding: const EdgeInsets.symmetric(horizontal: 16).copyWith(top: 16), child: Row( children: [ Expanded( child: Text( t.pages.settings.routing.perAppProxy.autoSelection.autoUpdateInterval, style: theme.textTheme.titleSmall!.copyWith(color: theme.colorScheme.onSurface), ), ), Text( _genSliderText(t, updateInterval.round()), style: theme.textTheme.labelSmall!.copyWith(color: theme.colorScheme.onSurfaceVariant), ), ], ), ), const Gap(4), Padding( padding: const EdgeInsets.symmetric(horizontal: 10), child: Slider( focusNode: sliderFocusNode, value: updateInterval, min: 1, max: 7, divisions: 7, label: updateInterval.round().toString(), onChanged: (double value) { ref.read(Preferences.autoAppsSelectionUpdateInterval.notifier).update(value); }, ), ), Padding( padding: const EdgeInsets.symmetric(horizontal: 16).copyWith(bottom: 8), child: Row( children: [ Expanded( child: FilledButton( onPressed: loading ? null : () async { await ref .read(appProxyLoadingProvider.notifier) .doAsync(ref.read(PerAppProxyProvider(mode).notifier).applyAutoSelection); }, child: Text(t.pages.settings.routing.perAppProxy.autoSelection.performNow), ), ), const Gap(8), FilledButton.tonal( onPressed: loading ? null : () async { await ref .read(appProxyLoadingProvider.notifier) .doAsync( ref.read(PerAppProxyProvider(mode).notifier).revertForceDeselection, ); }, child: Text(t.pages.settings.routing.perAppProxy.autoSelection.resetToDefault), ), ], ), ), ], ) : const SizedBox.shrink(), ), ], ), ), ); } } ================================================ FILE: lib/core/router/bottom_sheets/widgets/quick_settings_modal.dart ================================================ import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; import 'package:go_router/go_router.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/features/settings/data/config_option_repository.dart'; import 'package:hiddify/features/settings/notifier/warp_option/warp_option_notifier.dart'; import 'package:hiddify/singbox/model/singbox_config_enum.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class QuickSettingsModal extends HookConsumerWidget { const QuickSettingsModal({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; return SafeArea( child: SingleChildScrollView( child: Column( children: [ Padding( padding: const EdgeInsets.symmetric(horizontal: 16).copyWith(top: 16), child: SegmentedButton( showSelectedIcon: false, segments: ServiceMode.choices .map( (e) => ButtonSegment( value: e, label: Padding( padding: const EdgeInsets.symmetric(vertical: 8), child: Text(e.presentShort(t), textAlign: TextAlign.center), ), // tooltip: e.isExperimental ? t.settings.experimental : null, ), ) .toList(), selected: {ref.watch(ConfigOptions.serviceMode)}, onSelectionChanged: (newSet) => ref.read(ConfigOptions.serviceMode.notifier).update(newSet.first), ), ), const Gap(12), ListTile( leading: const Icon(Icons.cloud_rounded), title: Text(ref.watch(ConfigOptions.warpDetourMode).presentExplain(t)), onLongPress: () { context.pop(); context.goNamed('warpOptions'); }, onTap: () async { final value = ref.watch(ConfigOptions.enableWarp); await ref.read(ConfigOptions.enableWarp.notifier).update(!value); }, trailing: Switch.adaptive( value: ref.watch(ConfigOptions.enableWarp), onChanged: (value) async { await ref.read(ConfigOptions.enableWarp.notifier).update(value); // await ref.read(warpOptionNotifierProvider.notifier).genWarps(); }, ), ), // ListTile( // leading: const Icon(Icons.content_cut_rounded), // title: Text(t.pages.settings.tlsTricks.title), // onTap: () { // context.pop(); // context.goNamed('tlsTricks'); // }, // trailing: Switch.adaptive( // value: ref.watch(ConfigOptions.enableTlsFragment), // onChanged: ref.read(ConfigOptions.enableTlsFragment.notifier).update, // ), // ), const Gap(16), ], ), ), ); } } ================================================ FILE: lib/core/router/deep_linking/my_app_links.dart ================================================ import 'package:app_links/app_links.dart'; import 'package:hiddify/core/router/deep_linking/url_protocol/api.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'my_app_links.g.dart'; @riverpod Stream myAppLinks(Ref ref) async* { if (PlatformUtils.isWindows) { for (final protocol in LinkParser.protocols) { registerProtocolHandler(protocol); } } yield* AppLinks().uriLinkStream.map((event) => event.toString()); } ================================================ FILE: lib/core/router/deep_linking/url_protocol/README_url_protocol.md ================================================ # Deep Linking for Windows ## Custom scheme setup for registration ## the source available in the app_links project was used instead of the win32_registry package. #### url_protocol folder: ``` https://github.com/llfbandit/app_links/tree/master/app_links/example/lib/url_protocol ``` #### doc: ``` https://github.com/llfbandit/app_links/blob/master/doc/README_windows.md ``` ================================================ FILE: lib/core/router/deep_linking/url_protocol/api.dart ================================================ import 'package:hiddify/core/router/deep_linking/url_protocol/windows_protocol.dart' if (dart.library.js_interop) 'web_url_protocol.dart'; /// Registers a protocol by [scheme] to allow for links in the form `://...` /// to be processed by this application. By default, opening a link will open /// the executable that was used to register the scheme with the URL as the first /// argument passed to the executable. /// /// If a protocol is already registered for the given scheme, this function will /// attempt to overwrite the previous handler with the current executable information. /// However, note that depending on process permissions, this operation may be /// disallowed by the underlying platform. /// /// You may pass an [executable] to override the path to the executable to run /// when accessing the URL. /// /// [arguments] is a list of arguments to be used when running the executable. /// If passed, the list must contain at least one element, and at least one of /// those elements must contain the literal value `%s` to denote the URL to open. /// Quoting arguments is not necessary, as this will be handled for you. /// Escaping the `%s` as an unprocessed literal is currently unsupported. void registerProtocolHandler(String scheme, {String? executable, List? arguments}) { WindowsProtocolHandler().register(scheme, executable: executable, arguments: arguments); } /// Unregisters the protocol handler with the underlying platform. The provided /// [scheme] will no longer be used in links. /// /// Note that this will unregister a protocol by scheme regardless of which process /// had registered it. Unregistering a scheme that was not registered by this /// application is undefined and depends on platform-specific restrictions. void unregisterProtocolHandler(String scheme) { WindowsProtocolHandler().unregister(scheme); } ================================================ FILE: lib/core/router/deep_linking/url_protocol/protocol.dart ================================================ abstract class ProtocolHandler { void register(String scheme, {String? executable, List? arguments}); void unregister(String scheme); List getArguments(List? arguments) { if (arguments == null) return ['%s']; if (arguments.isEmpty && !arguments.any((e) => e.contains('%s'))) { throw ArgumentError('arguments must contain at least 1 instance of "%s"'); } return arguments; } } ================================================ FILE: lib/core/router/deep_linking/url_protocol/web_url_protocol.dart ================================================ import 'package:hiddify/core/router/deep_linking/url_protocol/protocol.dart'; class WindowsProtocolHandler extends ProtocolHandler { @override void register(String scheme, {String? executable, List? arguments}) {} @override void unregister(String scheme) {} } ================================================ FILE: lib/core/router/deep_linking/url_protocol/windows_protocol.dart ================================================ import 'dart:io'; import 'package:ffi/ffi.dart'; import 'package:flutter/foundation.dart'; import 'package:hiddify/core/router/deep_linking/url_protocol/protocol.dart'; import 'package:win32/win32.dart'; const _hive = HKEY_CURRENT_USER; class WindowsProtocolHandler extends ProtocolHandler { @override void register(String scheme, {String? executable, List? arguments}) { if (defaultTargetPlatform != TargetPlatform.windows) return; final prefix = _regPrefix(scheme); final capitalized = scheme[0].toUpperCase() + scheme.substring(1); final args = getArguments(arguments).map((a) => _sanitize(a)); final cmd = '${executable ?? Platform.resolvedExecutable} ${args.join(' ')}'; _regCreateStringKey(_hive, prefix, '', 'URL:$capitalized'); _regCreateStringKey(_hive, prefix, 'URL Protocol', ''); _regCreateStringKey(_hive, '$prefix\\shell\\open\\command', '', cmd); } @override void unregister(String scheme) { if (defaultTargetPlatform != TargetPlatform.windows) return; final txtKey = TEXT(_regPrefix(scheme)); try { RegDeleteTree(HKEY_CURRENT_USER, txtKey); } finally { free(txtKey); } } String _regPrefix(String scheme) => 'SOFTWARE\\Classes\\$scheme'; int _regCreateStringKey(int hKey, String key, String valueName, String data) { final txtKey = TEXT(key); final txtValue = TEXT(valueName); final txtData = TEXT(data); try { return RegSetKeyValue(hKey, txtKey, txtValue, REG_SZ, txtData, txtData.length * 2 + 2); } finally { free(txtKey); free(txtValue); free(txtData); } } String _sanitize(String value) { value = value.replaceAll(r'%s', '%1').replaceAll(r'"', '\\"'); return '"$value"'; } } ================================================ FILE: lib/core/router/dialog/dialog_notifier.dart ================================================ import 'package:flutter/material.dart'; import 'package:hiddify/core/preferences/actions_at_closing.dart'; import 'package:hiddify/core/router/dialog/widgets/action_at_closing_dialog.dart'; import 'package:hiddify/core/router/dialog/widgets/confirmation_dialog.dart'; import 'package:hiddify/core/router/dialog/widgets/custom_alert_dialog.dart'; import 'package:hiddify/core/router/dialog/widgets/experimental_feature_notice.dart'; import 'package:hiddify/core/router/dialog/widgets/free_profile_consent_dialog.dart'; import 'package:hiddify/core/router/dialog/widgets/new_version_dialog.dart'; import 'package:hiddify/core/router/dialog/widgets/no_active_profile_dialog.dart'; import 'package:hiddify/core/router/dialog/widgets/ok_dialog.dart'; import 'package:hiddify/core/router/dialog/widgets/proxy_info_dialog.dart'; import 'package:hiddify/core/router/dialog/widgets/save_dialog.dart'; import 'package:hiddify/core/router/dialog/widgets/setting_checkbox_dialog.dart'; import 'package:hiddify/core/router/dialog/widgets/setting_input_dialog.dart'; import 'package:hiddify/core/router/dialog/widgets/setting_picker_dialog.dart'; import 'package:hiddify/core/router/dialog/widgets/setting_radio_dialog.dart'; import 'package:hiddify/core/router/dialog/widgets/setting_slider_dialog.dart'; import 'package:hiddify/core/router/dialog/widgets/setting_text_dialog.dart'; import 'package:hiddify/core/router/dialog/widgets/sort_profiles_dialog.dart'; import 'package:hiddify/core/router/dialog/widgets/unknown_domains_warning_dialog.dart'; import 'package:hiddify/core/router/dialog/widgets/warp_license_dialog.dart'; import 'package:hiddify/core/router/dialog/widgets/window_closing_dialog.dart'; import 'package:hiddify/core/router/go_router/go_router_notifier.dart'; import 'package:hiddify/features/app_update/model/remote_version_entity.dart'; import 'package:hiddify/features/common/qr_code_dialog.dart'; import 'package:hiddify/features/common/qr_code_scanner_screen.dart'; import 'package:hiddify/features/settings/data/config_option_repository.dart'; import 'package:hiddify/hiddifycore/generated/v2/hcore/hcore.pb.dart'; import 'package:protobuf/protobuf.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'dialog_notifier.g.dart'; @Riverpod(keepAlive: true) class DialogNotifier extends _$DialogNotifier { @override void build() {} Future _show(Widget child) async { final context = rootNavKey.currentContext; if (context == null) return null; // ref.read(popupCountNotifierProvider.notifier).increase(); return await Navigator.of(context).push(DialogRoute(context: context, builder: (context) => child)).then(( value, ) { // ref.read(popupCountNotifierProvider.notifier).decrease(); return value; }); } Future showQrScanner() async { return await _show(const QrCodeScannerDialog()); } Future showSortProfiles() async { return await _show(const SortProfilesDialog()); } Future showWarpLicense() async { return await _show(const WarpLicenseDialog()) ?? false; } Future showQrCode(String link, {String? message}) async { return await _show(QrCodeDialog(link, message: message)); } Future showOk(String title, String description) async { return await _show(OkDialog(title: title, description: description)); } Future showSettingSlider({ required String title, required double initialValue, VoidCallback? onReset, double min = 0, double max = 1, int? divisions, String Function(double value)? labelGen, }) async { return await _show( SettingsSliderDialog( title: title, initialValue: initialValue, onReset: onReset, min: min, max: max, divisions: divisions, labelGen: labelGen, ), ); } Future showNewVersion({ required String currentVersion, required RemoteVersionEntity newVersion, required bool canIgnore, }) async { return await _show(NewVersionDialog(currentVersion, newVersion, canIgnore: canIgnore)); } Future showConfirmation({ required String title, required String message, IconData? icon, String? positiveBtnTxt, }) async { return await _show( ConfirmationDialog(title: title, message: message, icon: icon, positiveBtnTxt: positiveBtnTxt), ) ?? false; } Future showActionAtClosing({required ActionsAtClosing selected}) async { return await _show(ActionsAtClosingDialog(selected: selected)); } Future showExperimentalFeatureNotice() async { final hasExperimental = ref.read(ConfigOptions.hasExperimentalFeatures); final canShowNotice = !ref.read(disableExperimentalFeatureNoticeProvider); if (hasExperimental && canShowNotice) { return await _show(const ExperimentalFeatureNoticeDialog()) ?? false; } return true; } Future showNoActiveProfile() async { return await _show(const NoActiveProfileDialog()); } Future showFreeProfileConsent({required String title, required String consent}) async { return await _show(FreeProfileConsentDialog(title: title, consent: consent)) ?? false; } Future showUnknownDomainsWarning({required String url}) async { return await _show(UnknownDomainsWarningDialog(url: url)) ?? false; } Future showProxyInfo({required OutboundInfo outboundInfo}) async { return await _show(ProxyInfoDialog(outboundInfo: outboundInfo)); } Future showSettingText({ required String lable, String value = '', String? defaultValue, FormFieldValidator? validator, }) async { return await _show( SettingTextDialog(lable: lable, value: value, defaultValue: defaultValue, validator: validator), ); } Future?> showSettingCheckbox({ required String title, required List values, required List selectedValues, List? defaultValue, Map? t, }) async { return await _show?>( SettingCheckboxDialog( title: title, values: values, selectedValues: selectedValues, defaultValue: defaultValue, t: t, ), ); } Future showSettingRadio({ required String title, required List values, required T value, T? defaultValue, Map? t, }) async { return await _show( SettingRadioDialog(title: title, values: values, value: value, defaultValue: defaultValue, t: t), ); } Future showSettingInput({ required String title, required T initialValue, T? Function(String value)? mapTo, bool Function(String value)? validator, String Function(T value)? valueFormatter, List? possibleValues, VoidCallback? onReset, (String text, VoidCallback)? optionalAction, IconData? icon, bool digitsOnly = false, }) async { return await _show( SettingInputDialog( title: title, initialValue: initialValue, mapTo: mapTo, validator: validator, valueFormatter: valueFormatter, possibleValues: possibleValues, onReset: onReset, optionalAction: optionalAction, icon: icon, digitsOnly: digitsOnly, ), ); } Future showSettingPicker({ required String title, bool showFlag = false, required T selected, required List options, required String Function(T e) getTitle, VoidCallback? onReset, }) async { return await _show( SettingPickerDialog( title: title, showFlag: showFlag, selected: selected, options: options, getTitle: getTitle, onReset: onReset, ), ); } Future showSave({required String title, required String description}) async { return await _show(SaveDialog(title: title, description: description)); } Future showWindowClosing() async { return await _show(const WindowClosingDialog()); } Future showCustomAlert({String? title, required String message}) async { return await _show(CustomAlertDialog(title: title, message: message)); } Future showCustomAlertFromErr(({String type, String? message}) err) async { return await _show(CustomAlertDialog.fromErr(err)); } } ================================================ FILE: lib/core/router/dialog/widgets/action_at_closing_dialog.dart ================================================ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/preferences/actions_at_closing.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class ActionsAtClosingDialog extends HookConsumerWidget { const ActionsAtClosingDialog({super.key, required this.selected}); final ActionsAtClosing selected; @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; return SimpleDialog( title: Text(t.pages.settings.general.actionAtClosing), children: ActionsAtClosing.values .map((e) => RadioListTile(title: Text(e.present(t)), value: e, groupValue: selected, onChanged: context.pop)) .toList(), ); } } ================================================ FILE: lib/core/router/dialog/widgets/confirmation_dialog.dart ================================================ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/model/constants.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class ConfirmationDialog extends HookConsumerWidget { const ConfirmationDialog({super.key, required this.title, required this.message, this.icon, this.positiveBtnTxt}); final String title; final String message; final IconData? icon; final String? positiveBtnTxt; @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; return AlertDialog( icon: icon != null ? Icon(icon) : null, title: Text(title), content: ConstrainedBox(constraints: AlertDialogConst.boxConstraints, child: Text(message)), actions: [ TextButton(onPressed: () => context.pop(false), child: Text(t.common.cancel)), TextButton(onPressed: () => context.pop(true), child: Text(positiveBtnTxt ?? t.common.ok)), ], ); } } ================================================ FILE: lib/core/router/dialog/widgets/custom_alert_dialog.dart ================================================ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class CustomAlertDialog extends HookConsumerWidget { const CustomAlertDialog({super.key, this.title, required this.message}); final String? title; final String message; factory CustomAlertDialog.fromErr(({String type, String? message}) err) => CustomAlertDialog(title: err.message == null ? null : err.type, message: err.message ?? err.type); @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; return AlertDialog( title: title != null ? Text(title!) : null, content: SingleChildScrollView( child: SizedBox(width: 468, child: Text(message, textDirection: TextDirection.ltr)), ), actions: [ TextButton( onPressed: () { context.pop(); }, child: Text(t.common.ok), ), ], ); } } ================================================ FILE: lib/core/router/dialog/widgets/experimental_feature_notice.dart ================================================ import 'package:fluentui_system_icons/fluentui_system_icons.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; import 'package:go_router/go_router.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/utils/preferences_utils.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; bool _testExperimentalNotice = false; final disableExperimentalFeatureNoticeProvider = PreferencesNotifier.createAutoDispose( "disable_experimental_feature_notice", false, overrideValue: _testExperimentalNotice && kDebugMode ? false : null, ); class ExperimentalFeatureNoticeDialog extends HookConsumerWidget { const ExperimentalFeatureNoticeDialog({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; final disableNotice = ref.watch(disableExperimentalFeatureNoticeProvider); return AlertDialog( title: Text(t.dialogs.experimentalNotice.title), content: SingleChildScrollView( child: SizedBox( width: 468, child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(t.dialogs.experimentalNotice.msg), const Gap(8), CheckboxListTile( value: disableNotice, title: Text(t.dialogs.experimentalNotice.disable), secondary: const Icon(FluentIcons.eye_off_24_regular), onChanged: (value) async => await ref.read(disableExperimentalFeatureNoticeProvider.notifier).update(value ?? false), dense: true, ), ListTile( title: Text(t.pages.settings.title), leading: const Icon(FluentIcons.box_edit_24_regular), trailing: const Icon(FluentIcons.chevron_right_20_regular), onTap: () { context.pop(false); context.goNamed('settings'); }, dense: true, ), ], ), ), ), actions: [ TextButton( onPressed: () => context.pop(false), child: Text(MaterialLocalizations.of(context).cancelButtonLabel.toUpperCase()), ), TextButton(onPressed: () => context.pop(true), child: Text(t.connection.connect)), ], ); } } ================================================ FILE: lib/core/router/dialog/widgets/free_profile_consent_dialog.dart ================================================ import 'package:flutter/material.dart'; import 'package:flutter_markdown_plus/flutter_markdown_plus.dart'; import 'package:go_router/go_router.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/model/constants.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class FreeProfileConsentDialog extends HookConsumerWidget { const FreeProfileConsentDialog({super.key, required this.title, required this.consent}); final String title; final String consent; @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; return AlertDialog( title: Text(title), content: ConstrainedBox( constraints: AlertDialogConst.boxConstraints, child: MarkdownBody( data: consent, // styleSheet: MarkdownStyleSheet(textAlign: WrapAlignment.spaceBetween), onTapLink: (text, href, title) => UriUtils.tryLaunch(Uri.parse(href!)), ), ), actions: [ TextButton(child: Text(t.common.cancel), onPressed: () => context.pop(false)), TextButton(child: Text(t.common.kContinue), onPressed: () => context.pop(true)), ], ); } } ================================================ FILE: lib/core/router/dialog/widgets/new_version_dialog.dart ================================================ import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; import 'package:go_router/go_router.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/features/app_update/model/remote_version_entity.dart'; import 'package:hiddify/features/app_update/notifier/app_update_notifier.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class NewVersionDialog extends HookConsumerWidget with PresLogger { NewVersionDialog(this.currentVersion, this.newVersion, {super.key, this.canIgnore = true}); final String currentVersion; final RemoteVersionEntity newVersion; final bool canIgnore; @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; final theme = Theme.of(context); return AlertDialog( title: Text(t.dialogs.newVersion.title), content: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(t.dialogs.newVersion.msg), const Gap(8), Text.rich( TextSpan( children: [ TextSpan(text: t.dialogs.newVersion.currentVersion, style: theme.textTheme.bodySmall), TextSpan(text: currentVersion, style: theme.textTheme.labelMedium), ], ), ), Text.rich( TextSpan( children: [ TextSpan(text: t.dialogs.newVersion.newVersion, style: theme.textTheme.bodySmall), TextSpan(text: newVersion.presentVersion, style: theme.textTheme.labelMedium), ], ), ), ], ), actions: [ if (canIgnore) TextButton( onPressed: () async { await ref.read(appUpdateNotifierProvider.notifier).ignoreRelease(newVersion); if (context.mounted) context.pop(); }, child: Text(t.common.ignore), ), TextButton(onPressed: context.pop, child: Text(t.common.later)), TextButton( onPressed: () async { await UriUtils.tryLaunch(Uri.parse(newVersion.url)); }, child: Text(t.dialogs.newVersion.updateNow), ), ], ); } } ================================================ FILE: lib/core/router/dialog/widgets/no_active_profile_dialog.dart ================================================ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class NoActiveProfileDialog extends HookConsumerWidget { const NoActiveProfileDialog({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; return AlertDialog( title: Text(t.dialogs.noActiveProfile.title), content: Text(t.dialogs.noActiveProfile.msg), actions: [ TextButton( onPressed: () async { await UriUtils.tryLaunch(Uri.parse(t.dialogs.noActiveProfile.helpBtn.url)); }, child: Text(t.dialogs.noActiveProfile.helpBtn.label), ), TextButton(onPressed: () => context.pop(), child: Text(t.common.ok)), ], ); } } ================================================ FILE: lib/core/router/dialog/widgets/ok_dialog.dart ================================================ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class OkDialog extends HookConsumerWidget { const OkDialog({super.key, required this.title, required this.description}); final String title; final String description; @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; return AlertDialog( title: Text(title), content: Text(description), actions: [TextButton(child: Text(t.common.ok), onPressed: () => context.pop())], ); } } ================================================ FILE: lib/core/router/dialog/widgets/proxy_info_dialog.dart ================================================ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/hiddifycore/generated/v2/hcore/hcore.pb.dart'; import 'package:hiddify/utils/platform_utils.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:intl/intl.dart'; import 'package:url_launcher/url_launcher.dart'; class ProxyInfoDialog extends HookConsumerWidget { const ProxyInfoDialog({super.key, required this.outboundInfo}); final OutboundInfo outboundInfo; @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; return AlertDialog( title: SelectionArea(child: Text(outboundInfo.tagDisplay)), content: OutboundInfoWidget(outboundInfo: outboundInfo), actions: [TextButton(onPressed: context.pop, child: Text(t.common.close))], ); } } class OutboundInfoWidget extends HookConsumerWidget { final OutboundInfo outboundInfo; const OutboundInfoWidget({super.key, required this.outboundInfo}); @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; // final subOutboundInfo = outboundInfo.groupSelectedTag ?? outboundInfo; return SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ // SizedBox(height: 16.0), _buildInfoRow(t.dialogs.proxyInfo.fullTag, outboundInfo.tag), _buildInfoRow(t.dialogs.proxyInfo.type, outboundInfo.type), _buildInfoRow( t.dialogs.proxyInfo.testTime, DateFormat('yyyy-MM-dd HH:mm:ss').format(outboundInfo.urlTestTime.toDateTime().toLocal()), ), _buildInfoRow(t.dialogs.proxyInfo.testDelay, '${outboundInfo.urlTestDelay} ms'), _buildIpInfo(outboundInfo.ipinfo, ref), _buildInfoRow(t.dialogs.proxyInfo.upload, formatBytes(outboundInfo.upload.toInt())), _buildInfoRow(t.dialogs.proxyInfo.download, formatBytes(outboundInfo.download.toInt())), _buildInfoRow(t.dialogs.proxyInfo.isSelected, outboundInfo.isSelected ? '✅' : '❌'), _buildInfoRow(t.dialogs.proxyInfo.isGroup, outboundInfo.isGroup ? '✅' : '❌'), _buildInfoRow(t.dialogs.proxyInfo.isSecure, outboundInfo.isSecure ? '✅' : '❌'), // _buildInfoRow('Is Visible:', outboundInfo.isVisible ? '✅' : '❌'), _buildInfoRow(t.dialogs.proxyInfo.port, outboundInfo.port.toString()), _buildInfoRow(t.dialogs.proxyInfo.host, outboundInfo.host), ], ), ); } String formatBytes(int bytes, {int decimals = 3}) { if (bytes <= 0) return '0 B'; const units = ['B', 'KB', 'MB', 'GB', 'TB']; int unitIndex = 0; double size = bytes.toDouble(); while (size >= 1024 && unitIndex < units.length - 1) { size /= 1024; unitIndex++; } final decimals2 = switch (unitIndex) { 0 => 0, 1 => 0, 2 => 1, _ => decimals, }; return '${size.toStringAsFixed(decimals2)} ${units[unitIndex]}'; } Widget _buildInfoRow(String title, String value, {Future? Function()? onTap}) { if (value.isEmpty || value == '0' || value == '0.0, 0.0') { return const SizedBox(); } return Padding( padding: const EdgeInsets.symmetric(vertical: 4.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text(title, style: const TextStyle(fontWeight: FontWeight.bold)), const SizedBox(width: 8.0), Flexible( child: onTap != null ? GestureDetector( onTap: onTap, child: SelectableText( value, textAlign: TextAlign.right, style: const TextStyle(decoration: TextDecoration.underline), ), ) : SelectableText(value, textAlign: TextAlign.right), ), ], ), ); } Widget _buildIpInfo(IpInfo ipInfo, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Text('IP Info:', style: TextStyle(fontWeight: FontWeight.bold)), _buildInfoRow(t.dialogs.proxyInfo.ip, ipInfo.ip), _buildInfoRow(t.dialogs.proxyInfo.countryCode, ipInfo.countryCode), _buildInfoRow(t.dialogs.proxyInfo.region, ipInfo.region), // Handle optional fields _buildInfoRow(t.dialogs.proxyInfo.city, ipInfo.city), _buildInfoRow(t.dialogs.proxyInfo.asn, ipInfo.asn.toString()), _buildInfoRow(t.dialogs.proxyInfo.organization, ipInfo.org), // _buildInfoRow(t.outboundInfo.latitude, ipInfo.latitude.toString()), // _buildInfoRow(t.outboundInfo.longitude, ipInfo.longitude.toString()), _buildInfoRow( t.dialogs.proxyInfo.location, "${ipInfo.latitude}, ${ipInfo.longitude}", onTap: () => launchUrl( Uri.parse( !PlatformUtils.isInAppStore ? 'https://maps.apple.com/?ll=${ipInfo.latitude},${ipInfo.longitude}' : 'https://www.google.com/maps/@${ipInfo.latitude},${ipInfo.longitude},18z', ), ), ), _buildInfoRow(t.dialogs.proxyInfo.postalCode, ipInfo.postalCode), ], ); } } ================================================ FILE: lib/core/router/dialog/widgets/save_dialog.dart ================================================ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/model/constants.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class SaveDialog extends HookConsumerWidget { const SaveDialog({super.key, required this.title, required this.description}); final String title; final String description; @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; return AlertDialog( title: Text(title), content: ConstrainedBox(constraints: AlertDialogConst.boxConstraints, child: Text(description)), actions: [ TextButton(onPressed: () => context.pop(false), child: Text(t.common.discard)), TextButton(onPressed: () => context.pop(true), child: Text(t.common.save)), ], ); } } ================================================ FILE: lib/core/router/dialog/widgets/setting_checkbox_dialog.dart ================================================ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/features/route_rules/notifier/rule_notifier.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:protobuf/protobuf.dart'; class SettingCheckboxDialog extends ConsumerWidget { const SettingCheckboxDialog({ super.key, required this.title, required this.values, required this.selectedValues, this.defaultValue, this.t, }); final String title; final List values; final List selectedValues; final List? defaultValue; final Map? t; String textWithTranslation(ProtobufEnum e) { if (t == null) return '$e'; return t!['$e'] ?? '$e'; } @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; final checkboxNotififier = dialogCheckboxNotifierProvider(selectedValues); final current = ref.watch(checkboxNotififier); return AlertDialog( title: Text(title), content: ConstrainedBox( constraints: const BoxConstraints(maxHeight: 300), child: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, children: values .map( (e) => CheckboxListTile( title: Text(textWithTranslation(e)), value: current.contains(e), onChanged: (_) => ref.read(checkboxNotififier.notifier).update(e), ), ) .toList(), ), ), ), actions: [ if (defaultValue != null) TextButton(child: Text(t.common.reset), onPressed: () => context.pop(defaultValue)), TextButton(child: Text(t.common.cancel), onPressed: () => context.pop()), TextButton(child: Text(t.common.done), onPressed: () => context.pop(current)), ], ); } } ================================================ FILE: lib/core/router/dialog/widgets/setting_input_dialog.dart ================================================ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_typeahead/flutter_typeahead.dart'; import 'package:go_router/go_router.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/model/constants.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class SettingInputDialog extends HookConsumerWidget with PresLogger { const SettingInputDialog({ super.key, required this.title, required this.initialValue, this.mapTo, this.validator, this.valueFormatter, this.possibleValues, this.onReset, this.optionalAction, this.icon, this.digitsOnly = false, }); final String title; final T initialValue; final T? Function(String value)? mapTo; final bool Function(String value)? validator; final String Function(T value)? valueFormatter; final List? possibleValues; final VoidCallback? onReset; final (String text, VoidCallback)? optionalAction; final IconData? icon; final bool digitsOnly; @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; final localizations = MaterialLocalizations.of(context); final textController = useTextEditingController( text: valueFormatter?.call(initialValue) ?? initialValue.toString(), ); // focus management final okBtnFocusNode = useFocusNode(); KeyEventResult handleKeyEvent(FocusNode node, KeyEvent event) { if (KeyboardConst.select.contains(event.logicalKey) && event is KeyDownEvent) { okBtnFocusNode.requestFocus(); return KeyEventResult.handled; } return KeyEventResult.ignored; } return AlertDialog( title: Text(title), icon: icon != null ? Icon(icon) : null, // material: (context, platform) => MaterialAlertDialogData( // icon: icon != null ? Icon(icon) : null, // ), content: Column( mainAxisSize: MainAxisSize.min, children: [ if (possibleValues != null) // AutocompleteField(initialValue: initialValue.toString(), options: possibleValues!.map((e) => e.toString()).toList()) TypeAheadField( controller: textController, builder: (context, controller, focusNode) { focusNode.onKeyEvent = handleKeyEvent; return TextField( controller: controller, focusNode: focusNode, textDirection: TextDirection.ltr, autofocus: true, // decoration: InputDecoration( // // border: OutlineInputBorder(), // // labelText: 'City', // ) ); }, // Callback to fetch suggestions based on user input suggestionsCallback: (pattern) { final items = possibleValues!.map((p) => p.toString()); var res = items .where((suggestion) => suggestion.toLowerCase().contains(pattern.toLowerCase())) .toList(); if (res.length <= 1) res = [pattern, ...items.where((s) => s != pattern)]; return res; }, // Widget to build each suggestion in the list itemBuilder: (context, suggestion) { return ListTile( contentPadding: const EdgeInsets.symmetric(vertical: 3, horizontal: 10), // Minimize ListTile padding minTileHeight: 0, title: Text( suggestion, textDirection: TextDirection.ltr, style: Theme.of(context).textTheme.bodySmall, ), ); }, // Callback when a suggestion is selected onSelected: (suggestion) { // Handle the selected suggestion // print('Selected: $suggestion'); textController.text = suggestion; }, ) else CustomTextFormField( controller: textController, inputFormatters: [ FilteringTextInputFormatter.singleLineFormatter, if (digitsOnly) FilteringTextInputFormatter.digitsOnly, ], autoCorrect: true, hint: title, ), ], ), actions: [ if (optionalAction != null) TextButton( onPressed: () { optionalAction!.$2(); context.pop(T == String ? textController.value.text : null); }, child: Text(optionalAction!.$1.toUpperCase()), ), if (onReset != null) TextButton( onPressed: () { onReset!(); context.pop(); }, child: Text(t.common.reset), ), TextButton( onPressed: () { context.pop(); }, child: Text(localizations.cancelButtonLabel.toUpperCase()), ), TextButton( focusNode: okBtnFocusNode, onPressed: () { if (validator?.call(textController.value.text) == false) { context.pop(); } else if (mapTo != null) { context.pop(mapTo!.call(textController.value.text)); } else { context.pop(T == String ? textController.value.text : null); } }, child: Text(localizations.okButtonLabel.toUpperCase()), ), ], ); } } ================================================ FILE: lib/core/router/dialog/widgets/setting_picker_dialog.dart ================================================ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/features/proxy/active/ip_widget.dart'; import 'package:hiddify/utils/custom_loggers.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class SettingPickerDialog extends HookConsumerWidget with PresLogger { const SettingPickerDialog({ super.key, required this.title, this.showFlag = false, required this.selected, required this.options, required this.getTitle, this.onReset, }); final String title; final bool showFlag; final T selected; final List options; final String Function(T e) getTitle; final VoidCallback? onReset; @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; return AlertDialog( title: Text(title), content: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, children: options.map((e) { final title = getTitle(e); final countryCode = title.substring(title.length - 3, title.length - 1); return RadioListTile( title: Text(title), secondary: showFlag ? IPCountryFlag(countryCode: countryCode, size: 32) : null, value: e, groupValue: selected, onChanged: (value) => context.pop(e), ); }).toList(), ), ), actions: [ if (onReset != null) TextButton( onPressed: () { onReset!(); context.pop(); }, child: Text(t.common.reset), ), TextButton(onPressed: () => context.pop(), child: Text(t.common.cancel)), ], // scrollable: true, ); } } ================================================ FILE: lib/core/router/dialog/widgets/setting_radio_dialog.dart ================================================ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/model/constants.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class SettingRadioDialog extends ConsumerWidget { const SettingRadioDialog({ super.key, required this.title, required this.values, required this.value, this.defaultValue, this.t, }); final String title; final List values; final T value; final T? defaultValue; final Map? t; String textWithTranslation(T e) { if (t == null) return '$e'; return t!['$e'] ?? '$e'; } @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; return AlertDialog( title: Text(title), content: ConstrainedBox( constraints: AlertDialogConst.boxConstraints, child: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, children: values .map( (e) => RadioListTile( title: Text(textWithTranslation(e)), value: e, groupValue: value, onChanged: (_) => context.pop(e), ), ) .toList(), ), ), ), actions: [ if (defaultValue != null) TextButton(child: Text(t.common.reset), onPressed: () => context.pop(defaultValue)), TextButton(child: Text(t.common.cancel), onPressed: () => context.pop()), ], ); } } ================================================ FILE: lib/core/router/dialog/widgets/setting_slider_dialog.dart ================================================ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:go_router/go_router.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/model/constants.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class SettingsSliderDialog extends HookConsumerWidget with PresLogger { const SettingsSliderDialog({ super.key, required this.title, required this.initialValue, this.onReset, this.min = 0, this.max = 1, this.divisions, this.labelGen, }); final String title; final double initialValue; final VoidCallback? onReset; final double min; final double max; final int? divisions; final String Function(double value)? labelGen; @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; final localizations = MaterialLocalizations.of(context); final sliderValue = useState(initialValue); final sliderFocusNode = useFocusNode( onKeyEvent: (node, event) { if (KeyboardConst.verticalArrows.contains(event.logicalKey) && event is KeyDownEvent) { if (event.logicalKey == LogicalKeyboardKey.arrowDown) { node.nextFocus(); } return KeyEventResult.handled; } return KeyEventResult.ignored; }, ); return AlertDialog( title: Text(title), content: IntrinsicHeight( child: Slider( focusNode: sliderFocusNode, value: sliderValue.value, min: min, max: max, divisions: divisions, onChanged: (value) => sliderValue.value = value, label: labelGen?.call(sliderValue.value), ), ), actions: [ if (onReset != null) TextButton( onPressed: () { onReset!(); context.pop(); }, child: Text(t.common.reset), ), TextButton(onPressed: () => context.pop(), child: Text(localizations.cancelButtonLabel.toUpperCase())), TextButton( onPressed: () => context.pop(sliderValue.value), child: Text(localizations.okButtonLabel.toUpperCase()), ), ], ); } } ================================================ FILE: lib/core/router/dialog/widgets/setting_text_dialog.dart ================================================ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:go_router/go_router.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/model/constants.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class SettingTextDialog extends HookConsumerWidget { const SettingTextDialog({super.key, required this.lable, this.value = '', this.defaultValue, this.validator}); final String lable; final String value; final String? defaultValue; final FormFieldValidator? validator; @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; final formKey = useMemoized(() => GlobalKey()); final tController = useTextEditingController(text: value); return AlertDialog( content: ConstrainedBox( constraints: AlertDialogConst.boxConstraints, child: Form( key: formKey, child: TextFormField( decoration: InputDecoration(label: Text(lable)), controller: tController, validator: (value) { if (value == null || value.isEmpty) return t.pages.settings.routing.routeRule.rule.canNotBeEmpty; if (validator == null) return null; return validator!.call(value); }, autofocus: true, ), ), ), actions: [ if (defaultValue != null) TextButton(child: Text(t.common.reset), onPressed: () => context.pop(defaultValue)), TextButton(child: Text(t.common.cancel), onPressed: () => context.pop()), TextButton( child: Text(t.common.ok), onPressed: () { if (formKey.currentState!.validate()) { context.pop(tController.text.trim()); } }, ), ], ); } } ================================================ FILE: lib/core/router/dialog/widgets/sort_profiles_dialog.dart ================================================ import 'package:fluentui_system_icons/fluentui_system_icons.dart'; import 'package:flutter/material.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/model/constants.dart'; import 'package:hiddify/features/profile/model/profile_sort_enum.dart'; import 'package:hiddify/features/profile/overview/profiles_notifier.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class SortProfilesDialog extends HookConsumerWidget { const SortProfilesDialog({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; final sort = ref.watch(profilesSortNotifierProvider); return AlertDialog( title: Text(t.dialogs.sortProfiles.title), content: ConstrainedBox( constraints: AlertDialogConst.boxConstraints, child: SingleChildScrollView( child: Column( children: [ ...ProfilesSort.values.map((e) { final selected = sort.by == e; final double arrowTurn = sort.mode == SortMode.ascending ? 0 : 0.5; return ListTile( title: Text(e.present(t)), onTap: () { if (selected) { ref.read(profilesSortNotifierProvider.notifier).toggleMode(); } else { ref.read(profilesSortNotifierProvider.notifier).changeSort(e); } }, selected: selected, leading: Icon(e.icon), trailing: selected ? IconButton( onPressed: () { ref.read(profilesSortNotifierProvider.notifier).toggleMode(); }, icon: AnimatedRotation( turns: arrowTurn, duration: const Duration(milliseconds: 100), child: Icon(FluentIcons.arrow_sort_up_24_regular, semanticLabel: sort.mode.name), ), ) : null, ); }), ], ), ), ), ); } } ================================================ FILE: lib/core/router/dialog/widgets/unknown_domains_warning_dialog.dart ================================================ import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; import 'package:go_router/go_router.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class UnknownDomainsWarningDialog extends HookConsumerWidget { const UnknownDomainsWarningDialog({super.key, required this.url}); final String url; @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; return AlertDialog( title: Row( children: [ const Icon(Icons.warning_rounded, color: Colors.orange), const Gap(8), Text(t.dialogs.unknownDomainsWarning.title), ], ), content: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(t.dialogs.unknownDomainsWarning.youAreAboutToVisit), const Gap(8), Text(url, style: const TextStyle(fontWeight: FontWeight.bold)), const Gap(16), Text(t.dialogs.unknownDomainsWarning.thisWebsiteIsNotInOurTrustedList), ], ), actions: [ TextButton(onPressed: () => context.pop(false), child: Text(t.common.cancel)), FilledButton(onPressed: () => context.pop(true), child: Text(t.common.kContinue)), ], ); } } ================================================ FILE: lib/core/router/dialog/widgets/warp_license_dialog.dart ================================================ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:gap/gap.dart'; import 'package:go_router/go_router.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/model/constants.dart'; import 'package:hiddify/utils/uri_utils.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class WarpLicenseDialog extends HookConsumerWidget { const WarpLicenseDialog({super.key}); // for focus management KeyEventResult _handleKeyEvent(KeyEvent event, String key) { if (KeyboardConst.select.contains(event.logicalKey) && event is KeyUpEvent) { UriUtils.tryLaunch(Uri.parse(WarpConst.url[key]!)); return KeyEventResult.handled; } return KeyEventResult.ignored; } @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; // for focus management final focusStates = >{ WarpConst.warpTermsOfServiceKey: useState(false), WarpConst.warpPrivacyPolicyKey: useState(false), }; final focusNodes = { WarpConst.warpTermsOfServiceKey: useFocusNode(), WarpConst.warpPrivacyPolicyKey: useFocusNode(), }; useEffect(() { for (final entry in focusNodes.entries) { entry.value.addListener(() => focusStates[entry.key]!.value = entry.value.hasPrimaryFocus); } return null; }, []); return AlertDialog( title: Text(t.dialogs.warpLicense.title), content: ConstrainedBox( constraints: AlertDialogConst.boxConstraints, child: SingleChildScrollView( child: Column( children: [ Focus( focusNode: focusNodes[WarpConst.warpTermsOfServiceKey], onKeyEvent: (node, event) => _handleKeyEvent(event, WarpConst.warpTermsOfServiceKey), child: const Gap(0.1), ), Focus( focusNode: focusNodes[WarpConst.warpPrivacyPolicyKey], onKeyEvent: (node, event) => _handleKeyEvent(event, WarpConst.warpPrivacyPolicyKey), child: const Gap(0.1), ), Text.rich( t.dialogs.warpLicense.description( tos: (text) => TextSpan( text: text, style: TextStyle( color: focusStates[WarpConst.warpTermsOfServiceKey]!.value ? Colors.green : Colors.blue, ), recognizer: TapGestureRecognizer() ..onTap = () async { await UriUtils.tryLaunch(Uri.parse(Constants.cfWarpTermsOfService)); }, ), privacy: (text) => TextSpan( text: text, style: TextStyle( color: focusStates[WarpConst.warpPrivacyPolicyKey]!.value ? Colors.green : Colors.blue, ), recognizer: TapGestureRecognizer() ..onTap = () async { await UriUtils.tryLaunch(Uri.parse(Constants.cfWarpPrivacyPolicy)); }, ), ), ), ], ), ), ), actions: [ TextButton( onPressed: () { context.pop(false); }, child: Text(t.common.decline), ), TextButton( onPressed: () { context.pop(true); }, child: Text(t.common.agree), ), ], ); } } ================================================ FILE: lib/core/router/dialog/widgets/window_closing_dialog.dart ================================================ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/preferences/actions_at_closing.dart'; import 'package:hiddify/core/preferences/general_preferences.dart'; import 'package:hiddify/features/window/notifier/window_notifier.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class WindowClosingDialog extends ConsumerStatefulWidget { const WindowClosingDialog({super.key}); @override ConsumerState createState() => _WindowClosingDialogState(); } class _WindowClosingDialogState extends ConsumerState { bool remember = false; @override Widget build(BuildContext context) { final t = ref.watch(translationsProvider).requireValue; return AlertDialog( title: Text(t.dialogs.windowClosing.alertMessage), content: GestureDetector( onTap: () => setState(() { remember = !remember; }), behavior: HitTestBehavior.translucent, child: Row( children: [ Checkbox( value: remember, onChanged: (v) { remember = v ?? remember; setState(() {}); }, ), const SizedBox(width: 16), Text(t.dialogs.windowClosing.remember, style: const TextStyle(fontSize: 16)), ], ), ), actions: [ TextButton( onPressed: () { if (remember) { ref.read(Preferences.actionAtClose.notifier).update(ActionsAtClosing.exit); } ref.read(windowNotifierProvider.notifier).exit(); }, child: Text(t.common.close), ), FilledButton( onPressed: () async { if (remember) { ref.read(Preferences.actionAtClose.notifier).update(ActionsAtClosing.hide); } context.pop(false); await ref.read(windowNotifierProvider.notifier).hide(); }, child: Text(t.common.hide), ), ], ); } } ================================================ FILE: lib/core/router/go_router/go_router_notifier.dart ================================================ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:hiddify/core/router/go_router/refresh_listenable.dart'; import 'package:hiddify/core/router/go_router/routing_config_notifier.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'go_router_notifier.g.dart'; // if 'stateful shell route' navigators not registered, this navigator key can be used for showing dialog or bottom sheet... final rootNavKey = GlobalKey(debugLabel: 'rootNav'); @Riverpod(keepAlive: true) class GoRouterNotifer extends _$GoRouterNotifer { static final rConfig = ValueNotifier(loadingConfig); @override GoRouter build() { ref.listen(routingConfigNotifierProvider, (_, next) => rConfig.value = next); return GoRouter.routingConfig( initialLocation: '/home', navigatorKey: rootNavKey, routingConfig: rConfig, refreshListenable: RefreshListenable(ref), errorBuilder: (context, state) { WidgetsBinding.instance.addPostFrameCallback((_) => context.goNamed('home')); return const Material(); }, ); } } ================================================ FILE: lib/core/router/go_router/helper/active_breakpoint_notifier.dart ================================================ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'active_breakpoint_notifier.g.dart'; @Riverpod(keepAlive: true) class ActiveBreakpointNotifier extends _$ActiveBreakpointNotifier { @override Breakpoints? build() { return null; } void update(Breakpoints breakpoint) { if (breakpoint == state) return; state = breakpoint; } } @riverpod bool? isMobileBreakpoint(Ref ref) { final breakpoint = ref.watch(activeBreakpointNotifierProvider); if (breakpoint == null) return null; return breakpoint == Breakpoints.mobile; } class Breakpoint { static const sMobile = 0.0; static const eMobile = 600.0; static const sTable = 601.0; static const eTable = 840.0; static const sDesktop = 841.0; static const eDesktop = double.infinity; Breakpoint(BuildContext context) { _width = MediaQuery.of(context).size.width; if (_width < eMobile) { activeBreakpoint = Breakpoints.mobile; } else if (_width < eTable) { activeBreakpoint = Breakpoints.tablet; } else { activeBreakpoint = Breakpoints.desktop; } } late Breakpoints activeBreakpoint; late double _width; bool isMobile() => activeBreakpoint == Breakpoints.mobile; bool isTablet() => activeBreakpoint == Breakpoints.tablet; bool isDesktop() => activeBreakpoint == Breakpoints.desktop; @override String toString() { return 'Breakpoint{name: ${activeBreakpoint.name}, width: $_width}'; } } enum Breakpoints { mobile, tablet, desktop } ================================================ FILE: lib/core/router/go_router/helper/custom_transition.dart ================================================ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; enum TransitionType { slide, fade } CustomTransitionPage customTransition(TransitionType transition, LocalKey pageKey, Widget child) => CustomTransitionPage( key: pageKey, child: child, transitionDuration: const Duration(milliseconds: 150), reverseTransitionDuration: const Duration(milliseconds: 100), transitionsBuilder: (context, animation, _, child) => switch (transition) { TransitionType.slide => SlideTransition( position: Tween(begin: const Offset(1.0, 0.0), end: Offset.zero).animate(animation), textDirection: Directionality.of(context), child: child, ), TransitionType.fade => FadeTransition(opacity: animation, child: child), }, ); ================================================ FILE: lib/core/router/go_router/helper/popup_count_notifier.dart ================================================ // import 'package:riverpod_annotation/riverpod_annotation.dart'; // part 'popup_count_notifier.g.dart'; // @Riverpod(keepAlive: true) // class PopupCountNotifier extends _$PopupCountNotifier { // @override // int build() => 0; // void increase() => state += 1; // void decrease() => state -= 1; // } ================================================ FILE: lib/core/router/go_router/refresh_listenable.dart ================================================ import 'package:flutter/material.dart'; import 'package:hiddify/core/preferences/general_preferences.dart'; import 'package:hiddify/core/router/deep_linking/my_app_links.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; // For temporary storage of the link received from AppLinks. String newUrlFromAppLink = ''; class RefreshListenable extends ChangeNotifier { RefreshListenable(this.ref) { ref.listen(myAppLinksProvider, (_, next) { if (next.value != null) { newUrlFromAppLink = next.value!; notifyListeners(); } }); ref.listen(Preferences.introCompleted, (_, _) => notifyListeners()); } final Ref ref; } ================================================ FILE: lib/core/router/go_router/routing_config_notifier.dart ================================================ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:hiddify/core/preferences/general_preferences.dart'; import 'package:hiddify/core/router/adaptive_layout/my_adaptive_layout.dart'; import 'package:hiddify/core/router/bottom_sheets/bottom_sheets_notifier.dart'; import 'package:hiddify/core/router/go_router/helper/active_breakpoint_notifier.dart'; import 'package:hiddify/core/router/go_router/helper/custom_transition.dart'; import 'package:hiddify/core/router/go_router/refresh_listenable.dart'; import 'package:hiddify/features/about/widget/about_page.dart'; import 'package:hiddify/features/home/widget/home_page.dart'; import 'package:hiddify/features/intro/widget/intro_page.dart'; import 'package:hiddify/features/log/overview/logs_page.dart'; import 'package:hiddify/features/per_app_proxy/overview/per_app_proxy_page.dart'; import 'package:hiddify/features/profile/details/profile_details_page.dart'; import 'package:hiddify/features/profile/notifier/active_profile_notifier.dart'; import 'package:hiddify/features/profile/overview/profiles_page.dart'; import 'package:hiddify/features/proxy/overview/proxies_overview_page.dart'; import 'package:hiddify/features/settings/overview/sections/dns_options_page.dart'; import 'package:hiddify/features/settings/overview/sections/general_page.dart'; import 'package:hiddify/features/settings/overview/sections/inbound_options_page.dart'; import 'package:hiddify/features/settings/overview/sections/route_options_page.dart'; import 'package:hiddify/features/settings/overview/sections/tls_tricks_page.dart'; import 'package:hiddify/features/settings/overview/sections/warp_options_page.dart'; import 'package:hiddify/features/settings/overview/settings_page.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'routing_config_notifier.g.dart'; // each branch in go router has its own focus scope final branchesScope = { 'home': FocusScopeNode(), 'profiles': FocusScopeNode(), 'settings': FocusScopeNode(), 'logs': FocusScopeNode(), 'about': FocusScopeNode(), }; // when the routing config is not yet initialized, this config is used final loadingConfig = RoutingConfig( routes: [GoRoute(path: '/home', builder: (context, state) => const Material())], ); String getNameOfBranch(bool isMobileBreakpoint, bool showProfilesAction, int index) => isMobileBreakpoint ? ['home', 'settings'][index] : ['home', if (showProfilesAction) 'profiles', 'settings', 'logs', 'about'][index]; int getIndexOfBranch(bool isMobileBreakpoint, bool showProfilesAction, String name) => isMobileBreakpoint ? ['home', 'settings'].indexOf(name) : ['home', if (showProfilesAction) 'profiles', 'settings', 'logs', 'about'].indexOf(name); @Riverpod(keepAlive: true) class RoutingConfigNotifier extends _$RoutingConfigNotifier { @override RoutingConfig build() { final isMobileBreakpoint = ref.watch(isMobileBreakpointProvider); final bool showProfilesAction; if (isMobileBreakpoint == true) { showProfilesAction = false; } else { showProfilesAction = ref.watch(hasAnyProfileProvider).value ?? false; } if (isMobileBreakpoint == null) return loadingConfig; return RoutingConfig( redirect: (context, state) { final introCompleted = ref.read(Preferences.introCompleted); final isIntro = state.matchedLocation == '/intro'; // fix path-parameters for deep link String? url; if (LinkParser.protocols.contains(state.uri.scheme)) { url = state.uri.toString(); } else if (PlatformUtils.isDesktop && newUrlFromAppLink.isNotEmpty) { url = newUrlFromAppLink; newUrlFromAppLink = ''; } else if (state.uri.queryParameters['url'] != null) { url = state.uri.queryParameters['url']; } if (!introCompleted) { return url != null ? '/intro?url=$url' : '/intro'; } else if (isIntro) { if (url != null) WidgetsBinding.instance.addPostFrameCallback( (_) => ref.read(bottomSheetsNotifierProvider.notifier).showAddProfile(url: url), ); return '/home'; } else if (url != null) { WidgetsBinding.instance.addPostFrameCallback( (_) => ref.read(bottomSheetsNotifierProvider.notifier).showAddProfile(url: url), ); return '/home'; } return null; }, routes: [ StatefulShellRoute.indexedStack( builder: (_, _, navigationShell) => MyAdaptiveLayout( navigationShell: navigationShell, isMobileBreakpoint: isMobileBreakpoint, showProfilesAction: showProfilesAction, ), branches: [ StatefulShellBranch( routes: [ GoRoute( name: 'home', path: '/home', builder: (_, _) => FocusScope(node: branchesScope['home'], child: const HomePage()), routes: [ GoRoute( name: 'proxies', path: '/proxies', pageBuilder: (_, state) => customTransition(TransitionType.fade, state.pageKey, const ProxiesOverviewPage()), ), if (isMobileBreakpoint) GoRoute( name: 'profileDetails', path: '/profile-details/:id', pageBuilder: (_, state) => customTransition( TransitionType.fade, state.pageKey, ProfileDetailsPage(id: state.pathParameters['id']!), ), ), ], ), ], ), if (showProfilesAction) StatefulShellBranch( routes: [ GoRoute( name: 'profiles', path: '/profiles', builder: (_, _) => FocusScope(node: branchesScope['profiles'], child: const ProfilesPage()), routes: [ GoRoute( name: 'profileDetails', path: '/profiles/:id', pageBuilder: (_, state) => customTransition( TransitionType.fade, state.pageKey, ProfileDetailsPage(id: state.pathParameters['id']!), ), ), ], ), ], ), StatefulShellBranch( routes: [ GoRoute( name: 'settings', path: '/settings', builder: (context, _) => FocusScope( node: branchesScope['settings'], child: PopScope( canPop: false, onPopInvokedWithResult: (_, _) => context.goNamed('home'), child: SettingsPage(), ), ), routes: [ GoRoute( name: 'general', path: '/general', pageBuilder: (_, state) => customTransition(TransitionType.slide, state.pageKey, const GeneralPage()), ), GoRoute( name: 'routeOptions', path: '/route-options', pageBuilder: (_, state) => customTransition(TransitionType.slide, state.pageKey, const RouteOptionsPage()), routes: [ GoRoute( name: 'perAppProxy', path: '/per-app-proxy', pageBuilder: (_, state) => customTransition(TransitionType.slide, state.pageKey, const PerAppProxyPage()), ), ], ), GoRoute( name: 'dnsOptions', path: '/dns-options', pageBuilder: (_, state) => customTransition(TransitionType.slide, state.pageKey, const DnsOptionsPage()), ), GoRoute( name: 'inboundOptions', path: '/inbound-options', pageBuilder: (_, state) => customTransition(TransitionType.slide, state.pageKey, const InboundOptionsPage()), ), GoRoute( name: 'tlsTricks', path: '/tls-tricks', pageBuilder: (_, state) => customTransition(TransitionType.slide, state.pageKey, const TlsTricksPage()), ), GoRoute( name: 'warpOptions', path: '/warp-options', pageBuilder: (_, state) => customTransition(TransitionType.slide, state.pageKey, const WarpOptionsPage()), ), if (isMobileBreakpoint) ...[ GoRoute( name: 'logs', path: '/logs', pageBuilder: (_, state) => customTransition(TransitionType.slide, state.pageKey, const LogsPage()), ), GoRoute( name: 'about', path: '/about', pageBuilder: (_, state) => customTransition(TransitionType.slide, state.pageKey, const AboutPage()), ), ], ], ), ], ), if (!isMobileBreakpoint) ...[ StatefulShellBranch( routes: [ GoRoute( name: 'logs', path: '/logs', builder: (_, _) => FocusScope(node: branchesScope['logs'], child: const LogsPage()), ), ], ), StatefulShellBranch( routes: [ GoRoute( name: 'about', path: '/about', builder: (_, _) => FocusScope(node: branchesScope['about'], child: const AboutPage()), ), ], ), ], ], ), GoRoute(name: 'intro', path: '/intro', builder: (_, _) => const IntroPage()), ], ); } } ================================================ FILE: lib/core/theme/app_theme.dart ================================================ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:hiddify/core/theme/app_theme_mode.dart'; import 'package:hiddify/core/theme/theme_extensions.dart'; class AppTheme { AppTheme(this.mode, this.fontFamily); final AppThemeMode mode; final String fontFamily; ThemeData lightTheme(ColorScheme? lightColorScheme) { final ColorScheme scheme = lightColorScheme ?? ColorScheme.fromSeed(seedColor: const Color(0xFF293CA0)); return ThemeData( useMaterial3: true, colorScheme: scheme, fontFamily: fontFamily, extensions: const >{ConnectionButtonTheme.light}, ); } ThemeData darkTheme(ColorScheme? darkColorScheme) { final ColorScheme scheme = darkColorScheme ?? ColorScheme.fromSeed(seedColor: const Color(0xFF293CA0), brightness: Brightness.dark); return ThemeData( useMaterial3: true, colorScheme: scheme, scaffoldBackgroundColor: mode.trueBlack ? Colors.black : scheme.background, fontFamily: fontFamily, extensions: const >{ConnectionButtonTheme.light}, ); } CupertinoThemeData cupertinoThemeData(bool sysDark, ColorScheme? lightColorScheme, ColorScheme? darkColorScheme) { final bool isDark = switch (mode) { AppThemeMode.system => sysDark, AppThemeMode.light => false, AppThemeMode.dark => true, AppThemeMode.black => true, }; final def = CupertinoThemeData(brightness: isDark ? Brightness.dark : Brightness.light); // final def = CupertinoThemeData(brightness: Brightness.dark); // return def; final defaultMaterialTheme = isDark ? darkTheme(darkColorScheme) : lightTheme(lightColorScheme); return MaterialBasedCupertinoThemeData( materialTheme: defaultMaterialTheme.copyWith( cupertinoOverrideTheme: def.copyWith( textTheme: CupertinoTextThemeData( textStyle: def.textTheme.textStyle.copyWith(fontFamily: fontFamily), actionTextStyle: def.textTheme.actionTextStyle.copyWith(fontFamily: fontFamily), navActionTextStyle: def.textTheme.navActionTextStyle.copyWith(fontFamily: fontFamily), navTitleTextStyle: def.textTheme.navTitleTextStyle.copyWith(fontFamily: fontFamily), navLargeTitleTextStyle: def.textTheme.navLargeTitleTextStyle.copyWith(fontFamily: fontFamily), pickerTextStyle: def.textTheme.pickerTextStyle.copyWith(fontFamily: fontFamily), dateTimePickerTextStyle: def.textTheme.dateTimePickerTextStyle.copyWith(fontFamily: fontFamily), tabLabelTextStyle: def.textTheme.tabLabelTextStyle.copyWith(fontFamily: fontFamily), ).copyWith(), barBackgroundColor: def.barBackgroundColor, scaffoldBackgroundColor: def.scaffoldBackgroundColor, ), ), ); } } ================================================ FILE: lib/core/theme/app_theme_mode.dart ================================================ import 'package:flutter/material.dart'; import 'package:hiddify/core/localization/translations.dart'; enum AppThemeMode { system, light, dark, black; String present(TranslationsEn t) => switch (this) { system => t.pages.settings.general.themeModes.system, light => t.pages.settings.general.themeModes.light, dark => t.pages.settings.general.themeModes.dark, black => t.pages.settings.general.themeModes.black, }; ThemeMode get flutterThemeMode => switch (this) { system => ThemeMode.system, light => ThemeMode.light, dark => ThemeMode.dark, black => ThemeMode.dark, }; bool get trueBlack => this == black; } ================================================ FILE: lib/core/theme/theme_extensions.dart ================================================ import 'package:flutter/material.dart'; class ConnectionButtonTheme extends ThemeExtension { const ConnectionButtonTheme({this.idleColor, this.connectedColor}); final Color? idleColor; final Color? connectedColor; static const ConnectionButtonTheme light = ConnectionButtonTheme( idleColor: Color(0xFF4a4d8b), connectedColor: Color(0xFF44a334), ); @override ThemeExtension copyWith({Color? idleColor, Color? connectedColor}) => ConnectionButtonTheme( idleColor: idleColor ?? this.idleColor, connectedColor: connectedColor ?? this.connectedColor, ); @override ThemeExtension lerp(covariant ThemeExtension? other, double t) { if (other is! ConnectionButtonTheme) { return this; } return ConnectionButtonTheme( idleColor: Color.lerp(idleColor, other.idleColor, t), connectedColor: Color.lerp(connectedColor, other.connectedColor, t), ); } } ================================================ FILE: lib/core/theme/theme_preferences.dart ================================================ import 'package:hiddify/core/preferences/preferences_provider.dart'; import 'package:hiddify/core/theme/app_theme_mode.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'theme_preferences.g.dart'; @Riverpod(keepAlive: true) class ThemePreferences extends _$ThemePreferences { @override AppThemeMode build() { final persisted = ref.watch(sharedPreferencesProvider).requireValue.getString("theme_mode"); if (persisted == null) return AppThemeMode.system; return AppThemeMode.values.byName(persisted); } Future changeThemeMode(AppThemeMode value) async { state = value; await ref.read(sharedPreferencesProvider).requireValue.setString("theme_mode", value.name); } } ================================================ FILE: lib/core/utils/exception_handler.dart ================================================ import 'package:fpdart/fpdart.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:rxdart/rxdart.dart'; mixin ExceptionHandler implements LoggerMixin { TaskEither exceptionHandler( Future> Function() run, F Function(Object error, StackTrace stackTrace) onError, ) { return TaskEither(() async { try { return await run(); } catch (error, stackTrace) { return Left(onError(error, stackTrace)); } }); } } extension StreamExceptionHandler on Stream { Stream> handleExceptions(F Function(Object error, StackTrace stackTrace) onError) { return map(right).onErrorReturnWith((error, stackTrace) { return Left(onError(error, stackTrace)); }); } } extension TaskEitherExceptionHandler on TaskEither { TaskEither handleExceptions(F Function(Object error, StackTrace stackTrace) onError) { return TaskEither(() async { try { return await run(); } catch (error, stackTrace) { return Left(onError(error, stackTrace)); } }); } } ================================================ FILE: lib/core/utils/ffi_utils.dart ================================================ import 'dart:ffi'; import 'package:ffi/ffi.dart'; R withMemory(int size, R Function(Pointer memory) action) { final memory = calloc(size); try { return action(memory.cast()); } finally { calloc.free(memory); } } ================================================ FILE: lib/core/utils/ip_utils.dart ================================================ const String fallbackObscuredAddress = "*.*.*.*"; String obscureIp(String ip) { try { if (ip.contains(".")) { final splits = ip.split("."); return "${splits.first}.*.*.${splits.last}"; } else if (ip.contains(":")) { final splits = ip.split(":"); return [splits.first, ...splits.sublist(1).map((part) => "*" * part.length)].join(":"); } // ignore: empty_catches } catch (e) {} return fallbackObscuredAddress; } ================================================ FILE: lib/core/utils/json_converters.dart ================================================ import 'package:freezed_annotation/freezed_annotation.dart'; class IntervalInSecondsConverter implements JsonConverter { const IntervalInSecondsConverter(); @override Duration fromJson(int json) => Duration(seconds: json); @override int toJson(Duration object) => object.inSeconds; } ================================================ FILE: lib/core/utils/laststeam.dart ================================================ import 'dart:async'; class LastStream { final Stream _source; T? _latest; bool _hasValue = false; late final StreamSubscription _sub; final List> _waiters = []; LastStream(Stream source) : _source = source { _sub = _source.listen((event) { _latest = event; _hasValue = true; // Complete all waiting futures for (final completer in _waiters) { if (!completer.isCompleted) completer.complete(event); } _waiters.clear(); }); } void clean() { _latest = null; _hasValue = false; } /// Returns the latest value if available, /// otherwise waits up to [timeout] for one. Future get({Duration? timeout}) async { if (_hasValue) return _latest as T; final completer = Completer(); _waiters.add(completer); if (timeout != null) { return completer.future.timeout( timeout, onTimeout: () { _waiters.remove(completer); throw TimeoutException('No value received in $timeout'); }, ); } return completer.future; } /// Dispose the listener and clear resources. Future close() async { await _sub.cancel(); for (final c in _waiters) { if (!c.isCompleted) { c.completeError(StateError('Stream closed')); } } _waiters.clear(); } } ================================================ FILE: lib/core/utils/preferences_utils.dart ================================================ import 'package:hiddify/core/preferences/preferences_provider.dart'; import 'package:hiddify/utils/custom_loggers.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:shared_preferences/shared_preferences.dart'; class PreferencesEntry with InfraLogger { PreferencesEntry({required this.preferences, required this.key, required this.defaultValue, this.mapFrom, this.mapTo, this.validator}); final SharedPreferences preferences; final String key; final T defaultValue; final T Function(P value)? mapFrom; final P Function(T value)? mapTo; final bool Function(T value)? validator; T read() { try { // loggy.debug("getting persisted preference [$key]($T)"); final T value; if (mapFrom != null) { final persisted = preferences.get(key) as P?; if (persisted == null) { value = defaultValue; } else { value = mapFrom!(persisted); } } else if (T == List) { final storedValue = preferences.getString(key); value = storedValue == null ? defaultValue : storedValue.split(";") as T; } else { value = preferences.get(key) as T? ?? defaultValue; } if (validator?.call(value) ?? true) return value; return defaultValue; } catch (e, stackTrace) { loggy.warning("error getting preference[$key]: $e", e, stackTrace); return defaultValue; } } Future write(T value) async { Object? mapped = value; if (mapTo != null) { mapped = mapTo!(value); } loggy.debug("updating preference [$key]($T) to [$mapped]"); try { if (!(validator?.call(value) ?? true)) { loggy.warning("invalid value [$value] for preference [$key]($T)"); return false; } return switch (mapped) { final String value => await preferences.setString(key, value), final bool value => await preferences.setBool(key, value), final int value => await preferences.setInt(key, value), final double value => await preferences.setDouble(key, value), final List value => await preferences.setString(key, value.join(";")), _ => throw const FormatException("Invalid Type"), }; } catch (e, stackTrace) { loggy.warning("error updating preference[$key]: $e", e, stackTrace); return false; } } Future writeRaw(P input) async { final T value; if (mapFrom != null) { value = mapFrom!(input); } else { value = input as T; } if (await write(value)) return value; return null; } Future remove() async { try { await preferences.remove(key); } catch (e, stackTrace) { loggy.warning("error removing preference[$key]: $e", e, stackTrace); } } } class PreferencesNotifier extends StateNotifier { PreferencesNotifier._({required Ref ref, required this.entry, this.overrideValue, this.possibleValues}) : _ref = ref, super(overrideValue ?? entry.read()); final Ref _ref; final PreferencesEntry entry; final T? overrideValue; final List? possibleValues; static StateNotifierProvider, T> create( String key, T defaultValue, { T Function(Ref ref)? defaultValueFunction, T Function(P value)? mapFrom, P Function(T value)? mapTo, bool Function(T value)? validator, T? overrideValue, List? possibleValues, }) => StateNotifierProvider( (ref) => PreferencesNotifier._( ref: ref, entry: PreferencesEntry(preferences: ref.read(sharedPreferencesProvider).requireValue, key: key, defaultValue: defaultValueFunction?.call(ref) ?? defaultValue, mapFrom: mapFrom, mapTo: mapTo, validator: validator), overrideValue: overrideValue, possibleValues: possibleValues, ), ); static AutoDisposeStateNotifierProvider, T> createAutoDispose(String key, T defaultValue, {T Function(P value)? mapFrom, P Function(T value)? mapTo, bool Function(T value)? validator, T? overrideValue}) => StateNotifierProvider.autoDispose( (ref) => PreferencesNotifier._( ref: ref, entry: PreferencesEntry(preferences: ref.read(sharedPreferencesProvider).requireValue, key: key, defaultValue: defaultValue, mapFrom: mapFrom, mapTo: mapTo, validator: validator), overrideValue: overrideValue, ), ); P raw() { final value = overrideValue ?? state; if (entry.mapTo != null) return entry.mapTo!(value); return value as P; } Future updateRaw(P input) async { final value = await entry.writeRaw(input); if (value != null) state = value; } Future update(T value) async { if (await entry.write(value)) state = value; } Future reset() async { await entry.remove(); _ref.invalidateSelf(); } } ================================================ FILE: lib/core/utils/throttler.dart ================================================ import 'package:flutter/foundation.dart'; class Throttler { Throttler(this.throttleFor); final Duration throttleFor; DateTime? _lastCall; void call(VoidCallback callback) { if (_lastCall == null || DateTime.now().difference(_lastCall!) > throttleFor) { callback(); _lastCall = DateTime.now(); } } } ================================================ FILE: lib/core/widget/adaptive_icon.dart ================================================ import 'package:fluentui_system_icons/fluentui_system_icons.dart'; import 'package:flutter/material.dart'; class AdaptiveIcon { AdaptiveIcon(BuildContext context) : platform = Theme.of(context).platform; final TargetPlatform platform; IconData get more => switch (platform) { TargetPlatform.iOS || TargetPlatform.macOS => FluentIcons.more_horizontal_24_regular, _ => FluentIcons.more_vertical_24_regular, }; IconData get share => switch (platform) { TargetPlatform.android => FluentIcons.share_android_24_regular, TargetPlatform.iOS || TargetPlatform.macOS => FluentIcons.share_ios_24_regular, _ => FluentIcons.share_24_regular, }; } ================================================ FILE: lib/core/widget/adaptive_menu.dart ================================================ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; typedef AdaptiveMenuBuilder = Widget Function(BuildContext context, void Function() toggleVisibility, Widget? child); class AdaptiveMenuItem { AdaptiveMenuItem({required this.title, this.icon, this.onTap, this.isSelected, this.subItems}); final String title; final IconData? icon; final T Function()? onTap; final bool? isSelected; final List? subItems; (String, IconData?, T Function()?, bool?, List?) _equality() => (title, icon, onTap, isSelected, subItems); @override bool operator ==(covariant AdaptiveMenuItem other) { if (identical(this, other)) return true; return other._equality() == _equality(); } @override int get hashCode => _equality().hashCode; } class AdaptiveMenu extends HookConsumerWidget { const AdaptiveMenu({super.key, required this.items, required this.builder, required this.child}); final Iterable items; final AdaptiveMenuBuilder builder; final Widget? child; @override Widget build(BuildContext context, WidgetRef ref) { List buildMenuItems(Iterable scopeItems) { final menuItems = []; for (final item in scopeItems) { if (item.subItems != null) { final subItems = buildMenuItems(item.subItems!); menuItems.add( SubmenuButton( menuChildren: subItems, leadingIcon: item.icon != null ? Icon(item.icon) : null, child: Text(item.title), ), ); } else { menuItems.add( MenuItemButton( leadingIcon: item.icon != null ? Icon(item.icon) : null, onPressed: item.onTap, child: Text(item.title), ), ); } } return menuItems; } return MenuAnchor( builder: (context, controller, child) => builder(context, () { if (controller.isOpen) { controller.close(); } else { controller.open(); } }, child), menuChildren: buildMenuItems(items), child: child, ); } } ================================================ FILE: lib/core/widget/animated_text.dart ================================================ import 'package:flutter/material.dart'; import 'package:hiddify/core/model/constants.dart'; class AnimatedText extends Text { const AnimatedText( super.data, { super.key, super.style, this.duration = kAnimationDuration, this.size = true, this.slide = true, }); final Duration duration; final bool size; final bool slide; @override Widget build(BuildContext context) { return AnimatedSwitcher( duration: duration, transitionBuilder: (child, animation) { child = FadeTransition(opacity: animation, child: child); if (size) { child = SizeTransition( axis: Axis.horizontal, fixedCrossAxisSizeFactor: 1, sizeFactor: Tween(begin: 0.88, end: 1).animate(animation), child: child, ); } if (slide) { child = SlideTransition( position: Tween(begin: const Offset(0.0, -0.2), end: Offset.zero).animate(animation), child: child, ); } return child; }, child: Text(data!, key: ValueKey(data!), style: style), ); } } ================================================ FILE: lib/core/widget/animated_visibility.dart ================================================ import 'package:flutter/widgets.dart'; class AnimatedVisibility extends StatelessWidget { const AnimatedVisibility({ super.key, required this.visible, this.axis = Axis.horizontal, this.padding = EdgeInsets.zero, required this.child, }); final bool visible; final Axis axis; final EdgeInsets padding; final Widget child; @override Widget build(BuildContext context) { final replacement = axis == Axis.vertical ? const SizedBox(width: double.infinity) : const SizedBox.shrink(); return AnimatedSwitcher( duration: const Duration(milliseconds: 200), transitionBuilder: (child, animation) => SizeTransition( sizeFactor: animation, child: FadeTransition(opacity: animation, child: child), ), child: visible ? AnimatedPadding(padding: padding, duration: const Duration(milliseconds: 200), child: child) : replacement, ); } } ================================================ FILE: lib/core/widget/shimmer_skeleton.dart ================================================ import 'package:flutter/material.dart'; import 'package:flutter_animate/flutter_animate.dart'; import 'package:hiddify/core/widget/skeleton_widget.dart'; class ShimmerSkeleton extends StatelessWidget { const ShimmerSkeleton({ this.width, this.height, this.widthFactor, this.heightFactor, this.color, this.duration = const Duration(seconds: 1), super.key, }); final double? width; final double? height; final double? widthFactor; final double? heightFactor; final Color? color; final Duration duration; @override Widget build(BuildContext context) { return Skeleton(width: width, height: height, widthFactor: widthFactor, heightFactor: heightFactor) .animate(onPlay: (controller) => controller.loop()) .shimmer(duration: duration, angle: 45, color: color ?? Theme.of(context).colorScheme.secondary); } } ================================================ FILE: lib/core/widget/skeleton_widget.dart ================================================ import 'package:flutter/material.dart'; class Skeleton extends StatelessWidget { const Skeleton({ this.width, this.height, this.widthFactor, this.heightFactor, this.shape = BoxShape.rectangle, this.alignment = AlignmentDirectional.center, }); final double? width; final double? height; final double? widthFactor; final double? heightFactor; final BoxShape shape; final AlignmentGeometry alignment; @override Widget build(BuildContext context) { final theme = Theme.of(context); return FractionallySizedBox( widthFactor: widthFactor, heightFactor: heightFactor, alignment: alignment, child: Container( width: width, height: height, decoration: BoxDecoration( borderRadius: BorderRadius.circular(8), shape: shape, color: theme.hintColor.withOpacity(.16), ), ), ); } } ================================================ FILE: lib/core/widget/spaced_list_widget.dart ================================================ import 'package:flutter/widgets.dart'; extension SpacedWidgets on List { List spaceBy({double? width, double? height}) => [ for (int i = 0; i < length; i++) ...[if (i > 0) SizedBox(width: width, height: height), this[i]], ]; } ================================================ FILE: lib/core/widget/tip_card.dart ================================================ import 'package:fluentui_system_icons/fluentui_system_icons.dart'; import 'package:flutter/material.dart'; class TipCard extends StatelessWidget { const TipCard({required this.message, super.key}); final String message; @override Widget build(BuildContext context) { return Card( margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 4), child: Row( children: [ const Padding(padding: EdgeInsets.all(8.0), child: Icon(FluentIcons.lightbulb_24_regular)), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Padding(padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 8), child: Text(message)), ], ), ), ], ), ); } } ================================================ FILE: lib/features/about/widget/about_page.dart ================================================ import 'package:fluentui_system_icons/fluentui_system_icons.dart'; 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'; import 'package:hiddify/core/router/dialog/dialog_notifier.dart'; import 'package:hiddify/core/widget/adaptive_icon.dart'; import 'package:hiddify/features/app_update/notifier/app_update_notifier.dart'; import 'package:hiddify/features/app_update/notifier/app_update_state.dart'; import 'package:hiddify/gen/assets.gen.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class AboutPage extends HookConsumerWidget { const AboutPage({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; final appInfo = ref.watch(appInfoProvider).requireValue; final appUpdate = ref.watch(appUpdateNotifierProvider); ref.listen(appUpdateNotifierProvider, (_, next) async { if (!context.mounted) return; switch (next) { case AppUpdateStateAvailable(:final versionInfo) || AppUpdateStateIgnored(:final versionInfo): return await ref .read(dialogNotifierProvider.notifier) .showNewVersion(currentVersion: appInfo.presentVersion, newVersion: versionInfo, canIgnore: false); case AppUpdateStateError(:final error): return CustomToast.error(t.presentShortError(error)).show(context); case AppUpdateStateNotAvailable(): return CustomToast.success(t.pages.about.notAvailableMsg).show(context); } }); final conditionalTiles = [ if (appInfo.release.allowCustomUpdateChecker) ListTile( title: Text(t.pages.about.checkForUpdate), trailing: switch (appUpdate) { AppUpdateStateChecking() => const SizedBox(width: 24, height: 24, child: CircularProgressIndicator()), _ => const Icon(FluentIcons.arrow_sync_24_regular), }, onTap: () async { await ref.read(appUpdateNotifierProvider.notifier).check(); }, ), if (PlatformUtils.isDesktop) ListTile( title: Text(t.pages.about.openWorkingDir), trailing: const Icon(FluentIcons.open_folder_24_regular), onTap: () async { final path = ref.watch(appDirectoriesProvider).requireValue.workingDir.uri; await UriUtils.tryLaunch(path); }, ), ]; return Scaffold( appBar: AppBar( title: Text(t.pages.about.title), actions: [ PopupMenuButton( icon: Icon(AdaptiveIcon(context).more), itemBuilder: (context) { return [ PopupMenuItem( child: Text(t.common.addToClipboard), onTap: () { Clipboard.setData(ClipboardData(text: appInfo.format())); }, ), ]; }, ), const Gap(8), ], ), body: CustomScrollView( slivers: [ SliverToBoxAdapter( child: Padding( padding: const EdgeInsets.all(16), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Assets.images.logo.svg(width: 64, height: 64), const Gap(16), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(t.common.appTitle, style: Theme.of(context).textTheme.titleLarge), const Gap(4), Text("${t.common.version} ${appInfo.presentVersion}"), ], ), ], ), ), ), SliverList( delegate: SliverChildListDelegate([ ...conditionalTiles, if (conditionalTiles.isNotEmpty) const Divider(), ListTile( title: Text(t.pages.about.sourceCode), trailing: const Icon(FluentIcons.open_24_regular), onTap: () async { await UriUtils.tryLaunch(Uri.parse(Constants.githubUrl)); }, ), ListTile( title: Text(t.pages.about.telegramChannel), trailing: const Icon(FluentIcons.open_24_regular), onTap: () async { await UriUtils.tryLaunch(Uri.parse(Constants.telegramChannelUrl)); }, ), ListTile( title: Text(t.pages.about.termsAndConditions), trailing: const Icon(FluentIcons.open_24_regular), onTap: () async { await UriUtils.tryLaunch(Uri.parse(Constants.termsAndConditionsUrl)); }, ), ListTile( title: Text(t.pages.about.privacyPolicy), trailing: const Icon(FluentIcons.open_24_regular), onTap: () async { await UriUtils.tryLaunch(Uri.parse(Constants.privacyPolicyUrl)); }, ), ]), ), ], ), ); } } ================================================ FILE: lib/features/app/widget/app.dart ================================================ import 'package:accessibility_tools/accessibility_tools.dart'; import 'package:dynamic_color/dynamic_color.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:hiddify/core/directories/directories_provider.dart'; import 'package:hiddify/core/localization/locale_extensions.dart'; import 'package:hiddify/core/localization/locale_preferences.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/model/constants.dart'; import 'package:hiddify/core/notification/in_app_notification_controller.dart'; import 'package:hiddify/core/router/go_router/go_router_notifier.dart'; import 'package:hiddify/core/router/go_router/helper/active_breakpoint_notifier.dart'; import 'package:hiddify/core/theme/app_theme.dart'; import 'package:hiddify/core/theme/theme_preferences.dart'; import 'package:hiddify/features/app_update/notifier/app_update_notifier.dart'; import 'package:hiddify/features/connection/widget/connection_wrapper.dart'; import 'package:hiddify/features/per_app_proxy/overview/per_app_proxy_service_notifier.dart'; import 'package:hiddify/features/profile/notifier/profiles_update_notifier.dart'; import 'package:hiddify/features/shortcut/shortcut_wrapper.dart'; import 'package:hiddify/features/system_tray/notifier/system_tray_notifier.dart'; import 'package:hiddify/features/window/widget/window_wrapper.dart'; import 'package:hiddify/hiddifycore/hiddify_core_service_provider.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:toastification/toastification.dart'; import 'package:upgrader/upgrader.dart'; bool _debugAccessibility = false; bool isOnPauseCalled = false; class App extends HookConsumerWidget with WidgetsBindingObserver, PresLogger { const App({super.key}); void onInactive(WidgetRef ref) { onPause(ref); } void onPause(WidgetRef ref) { if (PlatformUtils.isDesktop) return; isOnPauseCalled = true; ref.read(hiddifyCoreServiceProvider).closeFront(); } void onResume(WidgetRef ref) { // if (PlatformUtils.isDesktop) return; ref.read(hiddifyCoreServiceProvider).init(); WidgetsBinding.instance.addPostFrameCallback((_) { if (isOnPauseCalled && PlatformUtils.isAndroid) ref.invalidate(perAppProxyServiceProvider); isOnPauseCalled = false; }); } @override Widget build(BuildContext context, WidgetRef ref) { setupStateListener(ref); final router = ref.watch(goRouterNotiferProvider); final locale = ref.watch(localePreferencesProvider); final themeMode = ref.watch(themePreferencesProvider); final theme = AppTheme(themeMode, locale.preferredFontFamily); final upgrader = ref.watch(upgraderProvider); final activeBreakpoint = Breakpoint(context).activeBreakpoint; ref.listen(foregroundProfilesUpdateNotifierProvider, (_, _) {}); if (PlatformUtils.isAndroid) ref.listen(perAppProxyServiceProvider, (_, _) {}); if (PlatformUtils.isDesktop) ref.listen(systemTrayNotifierProvider, (_, _) {}); // updating ActiveBreakpointNotifier value useEffect(() { WidgetsBinding.instance.addPostFrameCallback((_) { ref.read(activeBreakpointNotifierProvider.notifier).update(activeBreakpoint); }); return null; }, [activeBreakpoint]); return WindowWrapper( ShortcutWrapper( ToastificationWrapper( child: ConnectionWrapper( DynamicColorBuilder( builder: (ColorScheme? lightColorScheme, ColorScheme? darkColorScheme) { return MaterialApp.router( routerConfig: router, locale: locale.flutterLocale, supportedLocales: AppLocaleUtils.supportedLocales, localizationsDelegates: GlobalMaterialLocalizations.delegates, debugShowCheckedModeBanner: false, themeMode: themeMode.flutterThemeMode, theme: theme.lightTheme(lightColorScheme), darkTheme: theme.darkTheme(darkColorScheme), title: Constants.appName, builder: (context, child) { final theme = Theme.of(context); child = UpgradeAlert( upgrader: upgrader, navigatorKey: router.routerDelegate.navigatorKey, child: child ?? const SizedBox(), ); if (kDebugMode && _debugAccessibility) { return AccessibilityTools(checkFontOverflows: true, child: child); } return AnnotatedRegion( value: SystemUiOverlayStyle( statusBarColor: theme.scaffoldBackgroundColor, systemNavigationBarColor: theme.scaffoldBackgroundColor, systemNavigationBarIconBrightness: theme.brightness == Brightness.dark ? Brightness.light : Brightness.dark, ), child: child, ); }, ); }, ), ), ), ), ); } // @override // Widget build1(BuildContext context, WidgetRef ref) { // setupStateListener(ref); // // setupQuickSettings(ref); // final router = ref.watch(routerProvider); // final locale = ref.watch(localePreferencesProvider); // final themeMode = ref.watch(themePreferencesProvider); // final theme = AppTheme(themeMode, locale.preferredFontFamily); // final upgrader = ref.watch(upgraderProvider); // ref.listen(foregroundProfilesUpdateNotifierProvider, (_, __) {}); // return WindowWrapper( // TrayWrapper( // ShortcutWrapper( // ConnectionWrapper( // PlatformProvider( // settings: PlatformSettingsData( // iosUsesMaterialWidgets: true, // ), // builder: (context) => DynamicColorBuilder( // builder: (ColorScheme? lightColorScheme, ColorScheme? darkColorScheme) { // return PlatformApp.router( // routerConfig: router, // locale: locale.flutterLocale, // supportedLocales: AppLocaleUtils.supportedLocales, // localizationsDelegates: GlobalMaterialLocalizations.delegates, // debugShowCheckedModeBanner: false, // material: (context, platform) => MaterialAppRouterData( // theme: theme.lightTheme(lightColorScheme), // darkTheme: theme.darkTheme(darkColorScheme), // themeMode: themeMode.flutterThemeMode, // ), // cupertino: (context, platform) { // final sysDark = MediaQuery.of(context).platformBrightness == Brightness.dark; // return CupertinoAppRouterData(theme: theme.cupertinoThemeData(sysDark, lightColorScheme, darkColorScheme)); // }, // title: Constants.appName, // builder: (context, child) { // child = UpgradeAlert( // upgrader: upgrader, // navigatorKey: router.routerDelegate.navigatorKey, // child: child ?? const SizedBox(), // ); // if (kDebugMode && _debugAccessibility) { // return AccessibilityTools( // checkFontOverflows: true, // child: child, // ); // } // return child; // }, // ); // }, // )), // ), // ), // ), // ); // } void setupStateListener(WidgetRef ref) { final appLifecycleState = useAppLifecycleState(); useEffect(() { loggy.info("current app state"); loggy.info(appLifecycleState); if (appLifecycleState == AppLifecycleState.paused) { onPause(ref); } else if (appLifecycleState == AppLifecycleState.inactive) { onInactive(ref); } else if (appLifecycleState == AppLifecycleState.resumed) { onResume(ref); } return null; }, [appLifecycleState]); } } ================================================ FILE: lib/features/app_update/data/app_update_data_providers.dart ================================================ import 'package:hiddify/core/http_client/http_client_provider.dart'; import 'package:hiddify/features/app_update/data/app_update_repository.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'app_update_data_providers.g.dart'; @Riverpod(keepAlive: true) AppUpdateRepository appUpdateRepository(AppUpdateRepositoryRef ref) { return AppUpdateRepositoryImpl(httpClient: ref.watch(httpClientProvider)); } ================================================ FILE: lib/features/app_update/data/app_update_repository.dart ================================================ import 'package:fpdart/fpdart.dart'; import 'package:hiddify/core/http_client/dio_http_client.dart'; import 'package:hiddify/core/model/constants.dart'; import 'package:hiddify/core/model/environment.dart'; import 'package:hiddify/core/utils/exception_handler.dart'; import 'package:hiddify/features/app_update/data/github_release_parser.dart'; import 'package:hiddify/features/app_update/model/app_update_failure.dart'; import 'package:hiddify/features/app_update/model/remote_version_entity.dart'; import 'package:hiddify/utils/utils.dart'; abstract interface class AppUpdateRepository { TaskEither getLatestVersion({ bool includePreReleases = false, Release release = Release.general, }); } class AppUpdateRepositoryImpl with ExceptionHandler, InfraLogger implements AppUpdateRepository { AppUpdateRepositoryImpl({required this.httpClient}); final DioHttpClient httpClient; @override TaskEither getLatestVersion({ bool includePreReleases = false, Release release = Release.general, }) { return exceptionHandler(() async { if (!release.allowCustomUpdateChecker) { throw Exception("custom update checkers are not supported"); } final response = await httpClient.get(Constants.githubReleasesApiUrl); if (response.statusCode != 200 || response.data == null) { loggy.warning("failed to fetch latest version info"); return left(const AppUpdateFailure.unexpected()); } final releases = response.data!.map((e) => GithubReleaseParser.parse(e as Map)); late RemoteVersionEntity latest; if (includePreReleases) { latest = releases.first; } else { latest = releases.firstWhere((e) => e.preRelease == false); } return right(latest); }, AppUpdateFailure.unexpected); } } ================================================ FILE: lib/features/app_update/data/github_release_parser.dart ================================================ import 'package:dartx/dartx.dart'; import 'package:hiddify/core/model/environment.dart'; import 'package:hiddify/features/app_update/model/remote_version_entity.dart'; abstract class GithubReleaseParser { static RemoteVersionEntity parse(Map json) { final fullTag = json['tag_name'] as String; final fullVersion = fullTag.removePrefix("v").split("-").first.split("+"); var version = fullVersion.first; var buildNumber = fullVersion.elementAtOrElse(1, (index) => ""); var flavor = Environment.prod; for (final env in Environment.values) { final suffix = ".${env.name}"; if (version.endsWith(suffix)) { version = version.removeSuffix(suffix); flavor = env; break; } else if (buildNumber.endsWith(suffix)) { buildNumber = buildNumber.removeSuffix(suffix); flavor = env; break; } } final preRelease = json["prerelease"] as bool; final publishedAt = DateTime.parse(json["published_at"] as String); return RemoteVersionEntity( version: version, buildNumber: buildNumber, releaseTag: fullTag, preRelease: preRelease, url: json["html_url"] as String, publishedAt: publishedAt, flavor: flavor, ); } } ================================================ FILE: lib/features/app_update/model/app_update_failure.dart ================================================ import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/model/failures.dart'; part 'app_update_failure.freezed.dart'; @freezed sealed class AppUpdateFailure with _$AppUpdateFailure, Failure { const AppUpdateFailure._(); @With() const factory AppUpdateFailure.unexpected([Object? error, StackTrace? stackTrace]) = AppUpdateUnexpectedFailure; @override ({String type, String? message}) present(TranslationsEn t) { return switch (this) { AppUpdateUnexpectedFailure() => (type: t.errors.unexpected, message: null), }; } } ================================================ FILE: lib/features/app_update/model/remote_version_entity.dart ================================================ import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:hiddify/core/model/environment.dart'; part 'remote_version_entity.freezed.dart'; @Freezed() class RemoteVersionEntity with _$RemoteVersionEntity { const RemoteVersionEntity._(); const factory RemoteVersionEntity({ required String version, required String buildNumber, required String releaseTag, required bool preRelease, required String url, required DateTime publishedAt, required Environment flavor, }) = _RemoteVersionEntity; String get presentVersion => flavor == Environment.prod ? version : "$version ${flavor.name}"; } ================================================ FILE: lib/features/app_update/notifier/app_update_notifier.dart ================================================ import 'package:flutter/foundation.dart'; import 'package:hiddify/core/app_info/app_info_provider.dart'; import 'package:hiddify/core/localization/locale_preferences.dart'; import 'package:hiddify/core/model/constants.dart'; import 'package:hiddify/core/model/environment.dart'; import 'package:hiddify/core/preferences/preferences_provider.dart'; import 'package:hiddify/core/utils/preferences_utils.dart'; import 'package:hiddify/features/app_update/data/app_update_data_providers.dart'; import 'package:hiddify/features/app_update/model/app_update_failure.dart'; import 'package:hiddify/features/app_update/model/remote_version_entity.dart'; import 'package:hiddify/features/app_update/notifier/app_update_state.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:upgrader/upgrader.dart'; import 'package:version/version.dart'; part 'app_update_notifier.g.dart'; const _debugUpgrader = true; @riverpod Upgrader upgrader(Ref ref) => Upgrader( storeController: UpgraderStoreController( onAndroid: () => ref.read(appInfoProvider).requireValue.release.allowCustomUpdateChecker ? UpgraderAppcastStore(appcastURL: Constants.appCastUrl) : UpgraderPlayStore(), oniOS: () => UpgraderAppStore(), onLinux: () => UpgraderAppcastStore(appcastURL: Constants.appCastUrl), onWindows: () => UpgraderAppcastStore(appcastURL: Constants.appCastUrl), onMacOS: () => UpgraderAppcastStore(appcastURL: Constants.appCastUrl), onWeb: () => UpgraderAppcastStore(appcastURL: Constants.appCastUrl), ), debugLogging: false && _debugUpgrader && kDebugMode, // durationUntilAlertAgain: const Duration(hours: 12), messages: UpgraderMessages(code: ref.watch(localePreferencesProvider).languageCode), ); @Riverpod(keepAlive: true) class AppUpdateNotifier extends _$AppUpdateNotifier with AppLogger { @override AppUpdateState build() => const AppUpdateState.initial(); PreferencesEntry get _ignoreReleasePref => PreferencesEntry( preferences: ref.read(sharedPreferencesProvider).requireValue, key: 'ignored_release_version', defaultValue: null, ); Future check() async { loggy.debug("checking for update"); state = const AppUpdateState.checking(); final appInfo = ref.watch(appInfoProvider).requireValue; if (!appInfo.release.allowCustomUpdateChecker) { loggy.debug("custom update checkers are not allowed for [${appInfo.release.name}] release"); return state = const AppUpdateState.disabled(); } return ref .watch(appUpdateRepositoryProvider) .getLatestVersion() .match( (err) { loggy.warning("failed to get latest version", err); return state = AppUpdateState.error(err); }, (remote) { try { final latestVersion = Version.parse(remote.version); final currentVersion = Version.parse(appInfo.version); if (latestVersion > currentVersion) { if (remote.version == _ignoreReleasePref.read()) { loggy.debug("ignored release [${remote.version}]"); return state = AppUpdateStateIgnored(remote); } loggy.debug("new version available: $remote"); return state = AppUpdateState.available(remote); } loggy.info("already using latest version[$currentVersion], remote: [${remote.version}]"); return state = const AppUpdateState.notAvailable(); } catch (error, stackTrace) { loggy.warning("error parsing versions", error, stackTrace); return state = AppUpdateState.error(AppUpdateFailure.unexpected(error, stackTrace)); } }, ) .run(); } Future ignoreRelease(RemoteVersionEntity version) async { loggy.debug("ignoring release [${version.version}]"); await _ignoreReleasePref.write(version.version); state = AppUpdateStateIgnored(version); } } ================================================ FILE: lib/features/app_update/notifier/app_update_state.dart ================================================ import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:hiddify/features/app_update/model/app_update_failure.dart'; import 'package:hiddify/features/app_update/model/remote_version_entity.dart'; part 'app_update_state.freezed.dart'; @freezed class AppUpdateState with _$AppUpdateState { const factory AppUpdateState.initial() = AppUpdateStateInitial; const factory AppUpdateState.disabled() = AppUpdateStateDisabled; const factory AppUpdateState.checking() = AppUpdateStateChecking; const factory AppUpdateState.error(AppUpdateFailure error) = AppUpdateStateError; const factory AppUpdateState.available(RemoteVersionEntity versionInfo) = AppUpdateStateAvailable; const factory AppUpdateState.ignored(RemoteVersionEntity versionInfo) = AppUpdateStateIgnored; const factory AppUpdateState.notAvailable() = AppUpdateStateNotAvailable; } ================================================ FILE: lib/features/auto_start/notifier/auto_start_notifier.dart ================================================ import 'dart:async'; import 'dart:io'; import 'package:hiddify/core/app_info/app_info_provider.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:launch_at_startup/launch_at_startup.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'auto_start_notifier.g.dart'; @Riverpod(keepAlive: true) class AutoStartNotifier extends _$AutoStartNotifier with InfraLogger { Timer? _timer; @override Future build() async { if (!PlatformUtils.isDesktop) return false; final appInfo = ref.watch(appInfoProvider).requireValue; launchAtStartup.setup( appName: appInfo.name, appPath: Platform.resolvedExecutable, packageName: "Hiddify.HiddifyNext", ); final isEnabled = await launchAtStartup.isEnabled(); loggy.info("auto start is [${isEnabled ? "Enabled" : "Disabled"}]"); _startTimer(); ref.onDispose(() => _timer?.cancel()); return isEnabled; } void _startTimer() { _timer?.cancel(); _timer = Timer.periodic(const Duration(minutes: 15), (timer) => updateStatus()); } Future updateStatus() async { loggy.debug("update auto start status"); final isEnabled = await launchAtStartup.isEnabled(); state = AsyncValue.data(isEnabled); return isEnabled; } Future enable() async { loggy.debug("enabling auto start"); await launchAtStartup.enable(); state = const AsyncValue.data(true); } Future disable() async { loggy.debug("disabling auto start"); await launchAtStartup.disable(); state = const AsyncValue.data(false); } } ================================================ FILE: lib/features/common/custom_text_scroll.dart ================================================ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:text_scroll/text_scroll.dart'; class CustomTextScroll extends ConsumerWidget { const CustomTextScroll(this.text, {super.key, this.style}); final String text; final TextStyle? style; double calculateHeight(BuildContext context) { final TextPainter textPainter = TextPainter( text: TextSpan(text: text, style: style), textDirection: Directionality.of(context), maxLines: 1, textScaler: MediaQuery.of(context).textScaler, )..layout(); return textPainter.height; } @override Widget build(BuildContext context, WidgetRef ref) { return SizedBox( height: calculateHeight(context), child: TextScroll( text, mode: TextScrollMode.bouncing, velocity: const Velocity(pixelsPerSecond: Offset(30, 0)), pauseOnBounce: const Duration(seconds: 2), pauseBetween: const Duration(seconds: 2), style: style, ), ); } } ================================================ FILE: lib/features/common/general_pref_tiles.dart ================================================ import 'package:flutter/material.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'; import 'package:hiddify/core/preferences/general_preferences.dart'; import 'package:hiddify/core/router/dialog/dialog_notifier.dart'; import 'package:hiddify/core/theme/app_theme_mode.dart'; import 'package:hiddify/core/theme/theme_preferences.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class LocalePrefTile extends ConsumerWidget { const LocalePrefTile({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; final locale = ref.watch(localePreferencesProvider); return ListTile( title: Text(t.pages.settings.general.locale), subtitle: Text(locale.localeName), leading: const Icon(Icons.translate_rounded), onTap: () async { final selectedLocale = await ref .read(dialogNotifierProvider.notifier) .showSettingPicker( title: t.pages.settings.general.locale, selected: locale, onReset: () => ref.read(localePreferencesProvider.notifier).changeLocale(AppLocale.en), options: AppLocale.values, getTitle: (e) => e.localeName, ); if (selectedLocale != null) { await ref.read(localePreferencesProvider.notifier).changeLocale(selectedLocale); } }, ); } } class EnableAnalyticsPrefTile extends ConsumerWidget { const EnableAnalyticsPrefTile({super.key, this.onChanged}); final ValueChanged? onChanged; @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; final enabled = ref.watch(analyticsControllerProvider).requireValue; return SwitchListTile.adaptive( title: Text(t.pages.settings.general.enableAnalytics), subtitle: Text(t.pages.settings.general.enableAnalyticsMsg, style: Theme.of(context).textTheme.bodySmall), secondary: const Icon(Icons.analytics_rounded), value: enabled, onChanged: (value) async { if (onChanged != null) { return onChanged!(value); } if (enabled) { await ref.read(analyticsControllerProvider.notifier).disableAnalytics(); } else { await ref.read(analyticsControllerProvider.notifier).enableAnalytics(); } }, ); } } class ThemeModePrefTile extends ConsumerWidget { const ThemeModePrefTile({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; final themeMode = ref.watch(themePreferencesProvider); return ListTile( title: Text(t.pages.settings.general.themeMode), subtitle: Text(themeMode.present(t)), leading: Icon(switch (ref.watch(themePreferencesProvider)) { AppThemeMode.system => Icons.auto_awesome_rounded, AppThemeMode.light => Icons.light_mode_rounded, AppThemeMode.dark => Icons.dark_mode_rounded, AppThemeMode.black => Icons.contrast_rounded, }), onTap: () async { final selectedThemeMode = await ref .read(dialogNotifierProvider.notifier) .showSettingPicker( title: t.pages.settings.general.themeMode, selected: themeMode, onReset: () => ref.read(themePreferencesProvider.notifier).changeThemeMode(AppThemeMode.system), options: AppThemeMode.values, getTitle: (e) => e.present(t), ); if (selectedThemeMode != null) { await ref.read(themePreferencesProvider.notifier).changeThemeMode(selectedThemeMode); } }, ); } } class ClosingPrefTile extends ConsumerWidget { const ClosingPrefTile({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; final action = ref.watch(Preferences.actionAtClose); return ListTile( title: Text(t.pages.settings.general.actionAtClosing), subtitle: Text(action.present(t)), leading: const Icon(Icons.logout_rounded), onTap: () async { final selectedAction = await ref.read(dialogNotifierProvider.notifier).showActionAtClosing(selected: action); if (selectedAction != null) { await ref.read(Preferences.actionAtClose.notifier).update(selectedAction); } }, ); } } ================================================ FILE: lib/features/common/qr_code_dialog.dart ================================================ import 'package:flutter/material.dart'; import 'package:qr_flutter/qr_flutter.dart'; class QrCodeDialog extends StatelessWidget { const QrCodeDialog(this.data, {super.key, this.message, this.width = 268, this.backgroundColor = Colors.white}); final String data; final String? message; final double width; final Color backgroundColor; @override Widget build(BuildContext context) { final theme = Theme.of(context); return Padding( padding: const EdgeInsets.all(8.0), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ SizedBox( width: width, child: QrImageView(data: data, backgroundColor: backgroundColor), ), if (message != null) SizedBox( width: width, child: Material( color: theme.colorScheme.surface, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Flexible( child: Text( message!, overflow: TextOverflow.ellipsis, style: TextStyle(color: theme.colorScheme.onSurface), ), ), ], ), ), ), ], ), ); } } ================================================ FILE: lib/features/common/qr_code_scanner_screen.dart ================================================ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:mobile_scanner/mobile_scanner.dart'; // // const permissions = [Permissions.CAMERA]; // // const permissionGroup = [PermissionGroup.Camera]; // class QRCodeScannerScreen extends StatefulHookConsumerWidget { // const QRCodeScannerScreen({super.key}); // Future open(BuildContext context) async { // return Navigator.of(context, rootNavigator: true).push( // MaterialPageRoute( // fullscreenDialog: true, // builder: (context) => const QRCodeScannerScreen(), // ), // ); // } // @override // ConsumerState createState() => _QRCodeScannerScreenState(); // } // class _QRCodeScannerScreenState extends ConsumerState with WidgetsBindingObserver, PresLogger { // final MobileScannerController controller = MobileScannerController( // detectionTimeoutMs: 500, // autoStart: false, // ); // bool started = false; // // late FlutterEasyPermission _easyPermission; // @override // void initState() { // super.initState(); // WidgetsBinding.instance.addObserver(this); // // _initializeScanner(); // // _easyPermission = FlutterEasyPermission() // // ..addPermissionCallback(onGranted: (requestCode, androidPerms, iosPerm) { // // debugPrint("android:$androidPerms"); // // debugPrint("iOS:$iosPerm"); // // startQrScannerIfPermissionGranted(); // // }, onDenied: (requestCode, androidPerms, iosPerm, isPermanent) { // // if (isPermanent) { // // FlutterEasyPermission.showAppSettingsDialog(title: "Camera"); // // } else { // // debugPrint("android:$androidPerms"); // // debugPrint("iOS:$iosPerm"); // // } // // }, onSettingsReturned: () { // // startQrScannerIfPermissionGranted(); // // }); // } // // Future _requestCameraPermission() async { // // final hasPermission = await FlutterEasyPermission.has( // // perms: permissions, // // permsGroup: permissionGroup, // // ); // // if (hasPermission) return true; // // final completer = Completer(); // // void permissionCallback(int requestCode, List? perms, PermissionGroup? perm) { // // if (!completer.isCompleted) { // // completer.complete(true); // // } // // } // // void permissionDeniedCallback(int requestCode, List? perms, PermissionGroup? perm, bool isPermanent) { // // if (!completer.isCompleted) { // // completer.complete(false); // // } // // } // // FlutterEasyPermission().addPermissionCallback( // // onGranted: permissionCallback, // // onDenied: permissionDeniedCallback, // // ); // // FlutterEasyPermission.request( // // perms: permissions, // // permsGroup: permissionGroup, // // rationale: "Camera permission is required to scan QR codes.", // // ); // // return completer.future; // // } // // Future _initializeScanner() async { // // final hasPermission = await _requestCameraPermission(); // // if (hasPermission) { // // _startScanner(); // // } else { // // _showPermissionDialog(); // // } // // } // // @override // // void dispose() { // // controller.dispose(); // // // _easyPermission.dispose(); // // FlutterEasyPermission().dispose(); // // WidgetsBinding.instance.removeObserver(this); // // super.dispose(); // // } // // @override // // void didChangeAppLifecycleState(AppLifecycleState state) { // // /// Checking app cycle so that when user returns from settings, need to recheck for permissions // // if (state == AppLifecycleState.resumed) { // // _checkPermissionAndStartScanner(); // // } // // } // // Future _checkPermissionAndStartScanner() async { // // final hasPermission = await FlutterEasyPermission.has( // // perms: permissions, // // permsGroup: permissionGroup, // // ); // // if (hasPermission) { // // _startScanner(); // // } else { // // setState(() {}); // Trigger rebuild to show permission denied UI // // } // // } // // Future _startScanner() async { // // loggy.info("Starting scanner"); // // await controller.stop(); // // await controller.start().whenComplete(() { // // setState(() { // // started = true; // // }); // // }).catchError((error) { // // loggy.warning("Error starting scanner: $error"); // // }); // // } // // Future startQrScannerIfPermissionIsGranted() async { // // final hasPermission = await FlutterEasyPermission.has( // // perms: permissions, // // permsGroup: permissionGroup, // // ); // // if (hasPermission) { // // _startScanner(); // // // } else { // // // _showPermissionDialog(); // // } // // } // // // void startQrScannerIfPermissionGranted() { // // // FlutterEasyPermission.has(perms: permissions, permsGroup: permissionGroup).then((value) { // // // if (value) { // // // controller.start().then((result) { // // // if (result != null) { // // // setState(() { // // // started = true; // // // }); // // // } // // // }).catchError((error) { // // // loggy.warning("Error starting scanner: $error"); // // // }); // // // } else {} // // // }); // // // } // // void _showPermissionDialog() { // // FlutterEasyPermission.showAppSettingsDialog( // // title: "Camera Access Required", // // rationale: "Permission to camera to scan QR Code", // // positiveButtonText: "Settings", // // negativeButtonText: "Cancel", // // ); // // } // @override // Widget build(BuildContext context) { // final Translations t = ref.watch(translationsProvider).requireValue; // // return FutureBuilder( // // future: FlutterEasyPermission.has( // // perms: permissions, // // permsGroup: permissionGroup, // // ), // // builder: (context, snapshot) { // // if (snapshot.connectionState == ConnectionState.waiting) { // // return const Center(child: CircularProgressIndicator()); // // } // // if (snapshot.data == true) { // // return _buildScannerUI(context, t); // // } else { // // return _buildPermissionDeniedUI(context, t); // // } // // }, // // ); // return MobileScanner( // overlayBuilder: (context, constraints) => ScannerOverlay(), // onDetect: (barcodes) { // final rawData = barcodes.barcodes.first.rawValue; // loggy.debug('captured raw: [$rawData]'); // if (rawData != null) { // final uri = Uri.tryParse(rawData); // if (context.mounted && uri != null) { // loggy.debug('captured url: [$uri]'); // Navigator.of(context, rootNavigator: true).pop(uri.toString()); // } // } else { // loggy.warning("unable to capture"); // } // }, // ); // return _buildScannerUI(context, t); // } // Widget _buildScannerUI(BuildContext context, Translations t) { // final size = MediaQuery.sizeOf(context); // final overlaySize = (size.shortestSide - 12).coerceAtMost(248); // // _startScanner(); // return Scaffold( // extendBodyBehindAppBar: true, // appBar: AppBar( // backgroundColor: Colors.transparent, // iconTheme: Theme.of(context).iconTheme.copyWith( // color: Colors.white, // size: 32, // ), // actions: [ // IconButton( // icon: const Icon(FluentIcons.flash_24_regular), // tooltip: t.profile.add.qrScanner.torchSemanticLabel, // onPressed: () => controller.toggleTorch(), // ), // // IconButton( // // icon: ValueListenableBuilder( // // valueListenable: controller.torchState, // // builder: (context, state, child) { // // switch (state) { // // case TorchState.off: // // return const Icon( // // FluentIcons.flash_off_24_regular, // // color: Colors.grey, // // ); // // case TorchState.on: // // return const Icon( // // FluentIcons.flash_24_regular, // // color: Colors.yellow, // // ); // // } // // }, // // ), // // tooltip: t.profile.add.qrScanner.torchSemanticLabel, // // onPressed: () => controller.toggleTorch(), // // ), // IconButton( // icon: const Icon(FluentIcons.camera_switch_24_regular), // tooltip: t.profile.add.qrScanner.facingSemanticLabel, // onPressed: () => controller.switchCamera(), // ), // ], // ), // body: Stack( // children: [ // MobileScanner( // controller: controller, // onDetect: (capture) { // final rawData = capture.barcodes.first.rawValue; // loggy.debug('captured raw: [$rawData]'); // if (rawData != null) { // final uri = Uri.tryParse(rawData); // if (context.mounted && uri != null) { // loggy.debug('captured url: [$uri]'); // Navigator.of(context, rootNavigator: true).pop(uri.toString()); // } // } else { // loggy.warning("unable to capture"); // } // }, // errorBuilder: (_, error, __) { // final message = switch (error.errorCode) { // MobileScannerErrorCode.permissionDenied => t.profile.add.qrScanner.permissionDeniedError, // _ => t.profile.add.qrScanner.unexpectedError, // }; // return Center( // child: Column( // mainAxisSize: MainAxisSize.min, // children: [ // const Padding( // padding: EdgeInsets.only(bottom: 8), // child: Icon( // FluentIcons.error_circle_24_regular, // color: Colors.white, // ), // ), // Text(message), // Text(error.errorDetails?.message ?? ''), // ], // ), // ); // }, // ), // if (started) // CustomPaint( // painter: ScannerOverlay( // Rect.fromCenter( // center: size.center(Offset.zero), // width: overlaySize, // height: overlaySize, // ), // ), // ), // ], // ), // ); // } // Widget _buildPermissionDeniedUI(BuildContext context, Translations t) { // return Scaffold( // appBar: AppBar( // backgroundColor: Colors.transparent, // iconTheme: Theme.of(context).iconTheme.copyWith( // color: Colors.white, // size: 32, // ), // ), // body: Center( // child: Column( // mainAxisAlignment: MainAxisAlignment.center, // children: [ // Text(t.profile.add.qrScanner.permissionDeniedError), // const SizedBox(height: 16), // ElevatedButton( // // onPressed: _showPermissionDialog, // onPressed: () {}, // child: const Text("Settings"), // ), // ], // ), // ), // ); // } // } // class ScannerOverlay extends CustomPainter { // ScannerOverlay(this.scanWindow); // final Rect scanWindow; // final double borderRadius = 12.0; // @override // void paint(Canvas canvas, Size size) { // final backgroundPath = Path()..addRect(Rect.largest); // final cutoutPath = Path() // ..addRRect( // RRect.fromRectAndCorners( // scanWindow, // topLeft: Radius.circular(borderRadius), // topRight: Radius.circular(borderRadius), // bottomLeft: Radius.circular(borderRadius), // bottomRight: Radius.circular(borderRadius), // ), // ); // final backgroundPaint = Paint() // ..color = Colors.black.withValues(alpha: .5) // ..style = PaintingStyle.fill // ..blendMode = BlendMode.dstOut; // final backgroundWithCutout = Path.combine( // PathOperation.difference, // backgroundPath, // cutoutPath, // ); // final borderPaint = Paint() // ..color = Colors.white // ..style = PaintingStyle.stroke // ..strokeWidth = 3.0; // final borderRect = RRect.fromRectAndCorners( // scanWindow, // topLeft: Radius.circular(borderRadius), // topRight: Radius.circular(borderRadius), // bottomLeft: Radius.circular(borderRadius), // bottomRight: Radius.circular(borderRadius), // ); // canvas.drawPath(backgroundWithCutout, backgroundPaint); // canvas.drawRRect(borderRect, borderPaint); // } // @override // bool shouldRepaint(covariant CustomPainter oldDelegate) { // return false; // } // } class QrCodeScannerDialog extends ConsumerWidget { const QrCodeScannerDialog({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.read(translationsProvider).requireValue; return Scaffold( body: SafeArea( child: Stack( alignment: Alignment.center, children: [ MobileScanner( placeholderBuilder: (context) => const Center(child: CircularProgressIndicator()), overlayBuilder: (context, constraints) => Container( width: MediaQuery.of(context).size.width * 0.7, height: MediaQuery.of(context).size.width * 0.7, decoration: BoxDecoration( borderRadius: BorderRadius.circular(16), border: Border.all(color: Theme.of(context).colorScheme.primaryContainer, width: 4), ), ), errorBuilder: (context, error) => Center(child: Text(t.common.msg.permission.denied)), onDetect: (barcodes) { final rawData = barcodes.barcodes.first.rawValue; if (rawData != null) context.pop(rawData); // loggy.debug('captured raw: [$rawData]'); // if (rawData != null) { // context.pop(rawData); // final uri = Uri.tryParse(rawData); // if (context.mounted && uri != null) { // // loggy.debug('captured url: [$uri]'); // context.pop(uri.toString()); // } // } else { // // loggy.warning("unable to capture"); // } }, ), Align( alignment: AlignmentDirectional.topStart, child: Container( decoration: BoxDecoration( color: Theme.of(context).colorScheme.primaryContainer, borderRadius: BorderRadius.circular(1000), ), margin: const EdgeInsets.all(8), child: IconButton( onPressed: () => context.pop(), icon: Icon(Icons.close, color: Theme.of(context).colorScheme.onPrimaryContainer), splashRadius: 24, ), ), ), ], ), ), ); } } ================================================ FILE: lib/features/connection/data/connection_data_providers.dart ================================================ import 'package:hiddify/core/directories/directories_provider.dart'; import 'package:hiddify/features/connection/data/connection_repository.dart'; import 'package:hiddify/features/profile/data/profile_data_providers.dart'; import 'package:hiddify/features/settings/data/config_option_data_providers.dart'; import 'package:hiddify/hiddifycore/hiddify_core_service_provider.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'connection_data_providers.g.dart'; @Riverpod(keepAlive: true) ConnectionRepository connectionRepository(Ref ref) { return ConnectionRepositoryImpl( ref: ref, directories: ref.watch(appDirectoriesProvider).requireValue, configOptionRepository: ref.watch(configOptionRepositoryProvider), singbox: ref.watch(hiddifyCoreServiceProvider), profilePathResolver: ref.watch(profilePathResolverProvider), ); } ================================================ FILE: lib/features/connection/data/connection_repository.dart ================================================ import 'package:fpdart/fpdart.dart'; import 'package:hiddify/core/model/directories.dart'; import 'package:hiddify/core/router/dialog/dialog_notifier.dart'; import 'package:hiddify/core/utils/exception_handler.dart'; import 'package:hiddify/features/connection/model/connection_failure.dart'; import 'package:hiddify/features/connection/model/connection_status.dart'; import 'package:hiddify/features/profile/data/profile_path_resolver.dart'; import 'package:hiddify/features/profile/model/profile_entity.dart'; import 'package:hiddify/features/settings/data/config_option_repository.dart'; import 'package:hiddify/features/settings/notifier/warp_option/warp_option_notifier.dart'; import 'package:hiddify/hiddifycore/hiddify_core_service.dart'; import 'package:hiddify/singbox/model/singbox_config_option.dart'; import 'package:hiddify/singbox/model/core_status.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:meta/meta.dart'; abstract interface class ConnectionRepository { SingboxConfigOption? get configOptionsSnapshot; TaskEither setup(); Stream watchConnectionStatus(); TaskEither connect(ProfileEntity activeProfile, bool disableMemoryLimit); TaskEither disconnect(); TaskEither reconnect(ProfileEntity activeProfile, bool disableMemoryLimit); } class ConnectionRepositoryImpl with ExceptionHandler, InfraLogger implements ConnectionRepository { ConnectionRepositoryImpl({ required this.ref, required this.directories, required this.singbox, required this.configOptionRepository, required this.profilePathResolver, }); final Ref ref; final Directories directories; final HiddifyCoreService singbox; final ConfigOptionRepository configOptionRepository; final ProfilePathResolver profilePathResolver; SingboxConfigOption? _configOptionsSnapshot; @override SingboxConfigOption? get configOptionsSnapshot => _configOptionsSnapshot; bool _initialized = false; @override TaskEither setup() { if (_initialized) return TaskEither.of(unit); return exceptionHandler(() { loggy.debug("setting up singbox"); return singbox .setup() .map((r) { _initialized = true; return r; }) .mapLeft(UnexpectedConnectionFailure.new) .run(); }, UnexpectedConnectionFailure.new); } @override Stream watchConnectionStatus() { return singbox.watchStatus().map( (event) => switch (event) { CoreStopped() => Disconnected(event.getCoreAlert()), CoreStarting() => const Connecting(), CoreStarted() => const Connected(), CoreStopping() => const Disconnecting(), }, ); } @override TaskEither connect(ProfileEntity activeProfile, bool disableMemoryLimit) => setup().flatMap( (_) => applyConfigOption(activeProfile).flatMap( (_) => singbox.start(profilePathResolver.file(activeProfile.id).path, activeProfile.name, disableMemoryLimit), // .mapLeft(UnexpectedConnectionFailure.new), ), ); @override TaskEither disconnect() => singbox.stop().mapLeft(UnexpectedConnectionFailure.new); @override TaskEither reconnect(ProfileEntity activeProfile, bool disableMemoryLimit) => applyConfigOption(activeProfile).flatMap( (_) => singbox .restart(profilePathResolver.file(activeProfile.id).path, activeProfile.name, disableMemoryLimit) .mapLeft(UnexpectedConnectionFailure.new), ); @visibleForTesting TaskEither applyConfigOption(ProfileEntity prof) => TaskEither.fromEither(configOptionRepository.fullOptionsOverrided(prof.profileOverride)) .mapLeft((l) => ConnectionFailure.invalidConfigOption(null, l)) .flatMap( (overridedOptions) => TaskEither.tryCatch(() async { final isWarpLicenseAgreed = ref.read(warpLicenseNotifierProvider); final isWarpEnabled = overridedOptions.warp.enable || overridedOptions.warp2.enable; if (!isWarpLicenseAgreed && isWarpEnabled) { final isAgreed = await ref.read(dialogNotifierProvider.notifier).showWarpLicense(); if (isAgreed == true) { await ref.read(warpLicenseNotifierProvider.notifier).agree(); // return (await applyConfigOption(prof).run()).match((l) => throw l, (_) => unit); } else { throw const MissingWarpLicense(); } } _configOptionsSnapshot = overridedOptions; await singbox.changeOptions(overridedOptions).run(); return unit; }, (err, st) => err is ConnectionFailure ? err : ConnectionFailure.unexpected(err, st)), ); } ================================================ FILE: lib/features/connection/model/connection_failure.dart ================================================ import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/model/failures.dart'; import 'package:hiddify/features/settings/model/config_option_failure.dart'; part 'connection_failure.freezed.dart'; @freezed sealed class ConnectionFailure with _$ConnectionFailure, Failure { const ConnectionFailure._(); @With() const factory ConnectionFailure.unexpected([Object? error, StackTrace? stackTrace]) = UnexpectedConnectionFailure; @With() const factory ConnectionFailure.missingVpnPermission([String? message]) = MissingVpnPermission; @With() const factory ConnectionFailure.missingNotificationPermission([String? message]) = MissingNotificationPermission; @With() const factory ConnectionFailure.missingPrivilege() = MissingPrivilege; @With() const factory ConnectionFailure.invalidConfigOption([String? message, ConfigOptionFailure? configOptionFailure]) = InvalidConfigOption; @With() const factory ConnectionFailure.invalidConfig([String? message]) = InvalidConfig; @With() const factory ConnectionFailure.backgroundCoreNotAvailable([String? message]) = BackgroundCoreNotAvailable; @With() const factory ConnectionFailure.missiingWarpLicense() = MissingWarpLicense; @override ({String type, String? message}) present(TranslationsEn t) { return switch (this) { UnexpectedConnectionFailure(:final error) when error != null => ( type: t.errors.connectivity.unexpected, message: "$error", ), UnexpectedConnectionFailure() => (type: t.errors.connectivity.unexpected, message: null), MissingVpnPermission(:final message) => (type: t.errors.connectivity.missingVpnPermission, message: message), MissingNotificationPermission(:final message) => ( type: t.errors.connectivity.missingNotificationPermission, message: message, ), MissingPrivilege() => (type: t.errors.singbox.missingPrivilege, message: t.errors.singbox.missingPrivilegeMsg), InvalidConfigOption(:final message, :final configOptionFailure) => configOptionFailure?.present(t) ?? (type: t.errors.singbox.invalidConfigOptions, message: message), InvalidConfig(:final message) => (type: t.errors.singbox.invalidConfig, message: message), BackgroundCoreNotAvailable(:final message) => (type: t.errors.connectivity.core, message: message), MissingWarpLicense() => (type: t.errors.warp.missingLicense, message: t.errors.warp.missingLicenseMsg), }; } } ================================================ FILE: lib/features/connection/model/connection_status.dart ================================================ import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/features/connection/model/connection_failure.dart'; part 'connection_status.freezed.dart'; @freezed sealed class ConnectionStatus with _$ConnectionStatus { const ConnectionStatus._(); const factory ConnectionStatus.disconnected([ConnectionFailure? connectionFailure]) = Disconnected; const factory ConnectionStatus.connecting() = Connecting; const factory ConnectionStatus.connected() = Connected; const factory ConnectionStatus.disconnecting() = Disconnecting; bool get isConnected => switch (this) { Connected() => true, _ => false, }; bool get isDisconnected => switch (this) { Disconnected() => true, _ => false, }; bool get isSwitching => switch (this) { Connecting() => true, Disconnecting() => true, _ => false, }; String format() => switch (this) { Disconnected(:final connectionFailure) => connectionFailure != null ? "CONNECTION FAILURE: $connectionFailure" : "DISCONNECTED", Connecting() => "CONNECTING", Connected() => "CONNECTED", Disconnecting() => "DISCONNECTING", }; String present(TranslationsEn t) => switch (this) { Disconnected() => t.connection.tapToConnect, Connecting() => t.connection.connecting, Connected() => t.connection.connected, Disconnecting() => t.connection.disconnecting, }; } ================================================ FILE: lib/features/connection/notifier/connection_notifier.dart ================================================ import 'dart:io'; import 'package:hiddify/core/haptic/haptic_service.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/preferences/general_preferences.dart'; import 'package:hiddify/core/router/dialog/dialog_notifier.dart'; import 'package:hiddify/features/connection/data/connection_data_providers.dart'; import 'package:hiddify/features/connection/data/connection_repository.dart'; import 'package:hiddify/features/connection/model/connection_failure.dart'; import 'package:hiddify/features/connection/model/connection_status.dart'; import 'package:hiddify/features/profile/model/profile_entity.dart'; import 'package:hiddify/features/profile/notifier/active_profile_notifier.dart'; import 'package:hiddify/hiddifycore/init_signal.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:in_app_review/in_app_review.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:rxdart/rxdart.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; part 'connection_notifier.g.dart'; @Riverpod(keepAlive: true) class ConnectionNotifier extends _$ConnectionNotifier with AppLogger { @override Stream build() async* { if (Platform.isIOS) { await _connectionRepo.setup().mapLeft((l) { loggy.error("error setting up connection repository", l); }).run(); } listenSelf((previous, next) async { if (previous == next) return; if (previous case AsyncData(:final value) when !value.isConnected) { if (next case AsyncData(value: final Connected _)) { await ref.read(hapticServiceProvider.notifier).heavyImpact(); if (Platform.isAndroid && !ref.read(Preferences.storeReviewedByUser)) { if (await InAppReview.instance.isAvailable()) { InAppReview.instance.requestReview(); ref.read(Preferences.storeReviewedByUser.notifier).update(true); } } } } }); ref.listen(activeProfileProvider.select((value) => value.asData?.value), (previous, next) async { if (previous == null) return; final shouldReconnect = next == null || previous.id != next.id; if (shouldReconnect) { await reconnect(next); } }); ref.watch(coreRestartSignalProvider); yield* _connectionRepo.watchConnectionStatus().doOnData((event) { if (event case Disconnected(connectionFailure: final _?) when PlatformUtils.isDesktop) { ref.read(Preferences.startedByUser.notifier).update(false); } loggy.info("connection status: ${event.format()}"); }); } ConnectionRepository get _connectionRepo => ref.read(connectionRepositoryProvider); Future mayConnect() async { if (state case AsyncData(:final value)) { if (value case Disconnected()) return _connect(); } } Future toggleConnection() async { final haptic = ref.read(hapticServiceProvider.notifier); if (state case AsyncError()) { await haptic.lightImpact(); await _connect(); } else if (state case AsyncData(:final value)) { switch (value) { case Disconnected(): await haptic.lightImpact(); await ref.read(Preferences.startedByUser.notifier).update(true); await _connect(); case Connected(): // default: await haptic.mediumImpact(); await ref.read(Preferences.startedByUser.notifier).update(false); await _disconnect(); default: loggy.warning("switching status, debounce"); } } } Future reconnect(ProfileEntity? profile) async { if (state case AsyncData(:final value) when value == const Connected()) { if (profile == null) { loggy.info("no active profile, disconnecting"); return _disconnect(); } loggy.info("active profile changed, reconnecting"); await ref.read(Preferences.startedByUser.notifier).update(true); await _connectionRepo.reconnect(profile, ref.read(Preferences.disableMemoryLimit)).mapLeft((err) async { loggy.warning("error reconnecting", err); state = AsyncError(err, StackTrace.current); await ref .read(dialogNotifierProvider.notifier) .showCustomAlertFromErr(err.present(ref.read(translationsProvider).requireValue)); }).run(); } } Future abortConnection() async { if (state case AsyncData(:final value)) { switch (value) { case Connected() || Connecting(): loggy.debug("aborting connection"); await _disconnect(); default: } } } final _singleStart = SingleCall(); Future _connect() async { _singleStart.run( () async { await _connectThrottled(); }, onIgnored: () { loggy.debug("connect called while another connect/disconnect is still running, ignoring"); }, ); } Future _connectThrottled() async { final activeProfile = await ref.read(activeProfileProvider.future); if (activeProfile == null) { loggy.info("no active profile, not connecting"); return; } await _connectionRepo.connect(activeProfile, ref.read(Preferences.disableMemoryLimit)).mapLeft(( ConnectionFailure err, ) async { loggy.warning("error connecting", err); //Go err is not normal object to see the go errors are string and need to be dumped await ref .read(dialogNotifierProvider.notifier) .showCustomAlertFromErr(err.present(ref.read(translationsProvider).requireValue)); loggy.warning(err); if (err.toString().contains("panic")) { await Sentry.captureException(Exception(err.toString())); } await ref.read(Preferences.startedByUser.notifier).update(false); state = AsyncError(err, StackTrace.current); }).run(); } Future _disconnect() async { await _connectionRepo.disconnect().mapLeft((err) { loggy.warning("error disconnecting", err); ref .read(dialogNotifierProvider.notifier) .showCustomAlertFromErr(err.present(ref.read(translationsProvider).requireValue)); state = AsyncError(err, StackTrace.current); }).run(); } } @Riverpod(keepAlive: true) Future serviceRunning(Ref ref) async { // ref.watch(coreRestartSignalProvider); return await ref .watch(connectionNotifierProvider.selectAsync((data) => data.isConnected)) .onError((error, stackTrace) => false); } class SingleCall { bool _running = false; Future run(Future Function() task, {required T onIgnored}) async { if (_running) return onIgnored; _running = true; try { return await task(); } finally { _running = false; } } } ================================================ FILE: lib/features/connection/widget/connection_wrapper.dart ================================================ import 'package:flutter/material.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/notification/in_app_notification_controller.dart'; import 'package:hiddify/features/connection/notifier/connection_notifier.dart'; import 'package:hiddify/features/profile/notifier/active_profile_notifier.dart'; import 'package:hiddify/features/settings/notifier/config_option/config_option_notifier.dart'; import 'package:hiddify/utils/custom_loggers.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class ConnectionWrapper extends StatefulHookConsumerWidget { const ConnectionWrapper(this.child, {super.key}); final Widget child; @override ConsumerState createState() => _ConnectionWrapperState(); } class _ConnectionWrapperState extends ConsumerState with AppLogger { @override Widget build(BuildContext context) { ref.listen(connectionNotifierProvider, (_, _) {}); ref.listen(configOptionNotifierProvider, (previous, next) async { if (next case AsyncData(value: true)) { final t = ref.read(translationsProvider).requireValue; ref .read(inAppNotificationControllerProvider) .showInfoToast( t.connection.reconnectMsg, // actionText: t.connection.reconnect, // callback: () async { // await ref // .read(connectionNotifierProvider.notifier) // .reconnect(await ref.read(activeProfileProvider.future)); // }, ); await ref.read(connectionNotifierProvider.notifier).reconnect(await ref.read(activeProfileProvider.future)); } }); return widget.child; } @override void initState() { super.initState(); // remove for now... // // Future.delayed(const Duration(seconds: 2)).then( // (_) async { // if (ref.read(startedByUserProvider) && PlatformUtils.isDesktop) { // loggy.debug("previously started by user, trying to connect"); // return ref.read(connectionNotifierProvider.notifier).mayConnect(); // } // }, // ); } } ================================================ FILE: lib/features/deep_link/notifier/deep_link_notifier.dart ================================================ // part 'deep_link_notifier.g.dart'; // typedef NewProfileLink = ({String? url, String? name}); // @Riverpod(keepAlive: true) // class DeepLinkNotifier extends _$DeepLinkNotifier // with ProtocolListener, InfraLogger { // @override // Future build() async { // if (Platform.isLinux) return null; // for (final protocol in LinkParser.protocols) { // await protocolHandler.register(protocol); // } // protocolHandler.addListener(this); // ref.onDispose(() { // protocolHandler.removeListener(this); // }); // final initialPayload = await protocolHandler.getInitialUrl(); // if (initialPayload != null) { // loggy.debug('initial payload: [$initialPayload]'); // final link = LinkParser.deep(initialPayload); // return link; // } // return null; // } // @override // void onProtocolUrlReceived(String url) { // super.onProtocolUrlReceived(url); // loggy.debug("url received: [$url]"); // final link = LinkParser.deep(url); // if (link == null) { // loggy.debug("link was not valid"); // return; // } // update((_) => link); // } // } ================================================ FILE: lib/features/home/widget/connection_button.dart ================================================ import 'package:flutter/material.dart'; import 'package:flutter_animate/flutter_animate.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:gap/gap.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/model/failures.dart'; import 'package:hiddify/core/router/bottom_sheets/bottom_sheets_notifier.dart'; import 'package:hiddify/core/router/dialog/dialog_notifier.dart'; import 'package:hiddify/core/router/dialog/widgets/custom_alert_dialog.dart'; import 'package:hiddify/core/theme/theme_extensions.dart'; import 'package:hiddify/core/widget/animated_text.dart'; import 'package:hiddify/features/connection/model/connection_status.dart'; import 'package:hiddify/features/connection/notifier/connection_notifier.dart'; import 'package:hiddify/features/profile/notifier/active_profile_notifier.dart'; import 'package:hiddify/features/proxy/active/active_proxy_notifier.dart'; import 'package:hiddify/features/settings/data/config_option_repository.dart'; import 'package:hiddify/features/settings/notifier/config_option/config_option_notifier.dart'; import 'package:hiddify/gen/assets.gen.dart'; import 'package:hiddify/singbox/model/singbox_config_enum.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; // TODO: rewrite class ConnectionButton extends HookConsumerWidget { const ConnectionButton({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; final connectionStatus = ref.watch(connectionNotifierProvider); final activeProxy = ref.watch(activeProxyNotifierProvider); final delay = activeProxy.valueOrNull?.urlTestDelay ?? 0; final requiresReconnect = ref.watch(configOptionNotifierProvider).valueOrNull; final today = DateTime.now(); // final animationController = useAnimationController( // duration: const Duration(seconds: 1), // )..repeat(reverse: true); // Ensure the animation loops indefinitely // // Listen to the animation's value // final animationValue = useAnimation(Tween(begin: 0.8, end: 1).animate(animationController)); // // useEffect(() { // // if (true) { // // Start repeating animation // // } else { // // animationController.stop(); // Stop animation if connected, disconnected, or error // // } // // // Cleanup when widget is disposed // // return animationController.dispose; // // }, [connectionStatus.value]); // // ref.listen( // // connectionNotifierProvider, // // (_, next) { // // if (next case AsyncError(:final error)) { // // CustomAlertDialog.fromErr(t.presentError(error)).show(context); // // } // // if (next case AsyncData(value: Disconnected(:final connectionFailure?))) { // // CustomAlertDialog.fromErr(t.presentError(connectionFailure)).show(context); // // } // // }, // // ); const buttonTheme = ConnectionButtonTheme.light; // // return CircleDesignWidget( // // onTap: switch (connectionStatus) { // // // AsyncData(value: Disconnected()) || AsyncError() => () async { // // // if (await showExperimentalNotice()) { // // // return await ref.read(connectionNotifierProvider.notifier).toggleConnection(); // // // } // // // }, // // // AsyncData(value: Connected()) => () async { // // // if (requiresReconnect == true && await showExperimentalNotice()) { // // // return await ref.read(connectionNotifierProvider.notifier).reconnect(await ref.read(activeProfileProvider.future)); // // // } // // // return await ref.read(connectionNotifierProvider.notifier).toggleConnection(); // // // }, // // _ => () {}, // // }, // // // enabled: switch (connectionStatus) { // // // AsyncData(value: Connected()) || AsyncData(value: Disconnected()) || AsyncError() => true, // // // _ => false, // // // }, // // // label: switch (connectionStatus) { // // // AsyncData(value: Connected()) when requiresReconnect == true => t.connection.reconnect, // // // AsyncData(value: Connected()) when delay <= 0 || delay >= 65000 => t.connection.connecting, // // // AsyncData(value: final status) => status.present(t), // // // _ => "", // // // }, // // color: switch (connectionStatus) { // // AsyncData(value: Connected()) when requiresReconnect == true => Colors.teal, // // AsyncData(value: Connected()) when delay <= 0 || delay >= 65000 => Color.fromARGB(255, 157, 139, 1), // // AsyncData(value: Connected()) => Colors.green.shade900, // // AsyncData(value: _) => Colors.indigo.shade700, // Color(0xFF3446A5), //buttonTheme.idleColor!, // // _ => Colors.red, // // }, // // animated: true || // // switch (connectionStatus) { // // AsyncData(value: Connected()) when requiresReconnect == true => false, // // AsyncData(value: Connected()) when delay <= 0 || delay >= 65000 => false, // // AsyncData(value: Connected()) => true, // // AsyncData(value: _) => true, // // _ => false, // // }, // // animationValue: animationValue, // // ); // } var secureLabel = (ref.watch(ConfigOptions.enableWarp) && ref.watch(ConfigOptions.warpDetourMode) == WarpDetourMode.warpOverProxy) ? t.connection.secure : ""; if (delay <= 0 || delay > 65000 || connectionStatus.value != const Connected()) { secureLabel = ""; } return _ConnectionButton( onTap: switch (connectionStatus) { AsyncData(value: Connected()) when requiresReconnect == true => () async { final activeProfile = await ref.read(activeProfileProvider.future); return await ref.read(connectionNotifierProvider.notifier).reconnect(activeProfile); }, AsyncData(value: Disconnected()) || AsyncError() => () async { if (ref.read(activeProfileProvider).valueOrNull == null) { await ref.read(dialogNotifierProvider.notifier).showNoActiveProfile(); ref.read(bottomSheetsNotifierProvider.notifier).showAddProfile(); } if (await ref.read(dialogNotifierProvider.notifier).showExperimentalFeatureNotice()) { return await ref.read(connectionNotifierProvider.notifier).toggleConnection(); } }, AsyncData(value: Connected()) => () async { if (requiresReconnect == true && await ref.read(dialogNotifierProvider.notifier).showExperimentalFeatureNotice()) { return await ref .read(connectionNotifierProvider.notifier) .reconnect(await ref.read(activeProfileProvider.future)); } return await ref.read(connectionNotifierProvider.notifier).toggleConnection(); }, _ => () {}, }, enabled: switch (connectionStatus) { AsyncData(value: Connected()) || AsyncData(value: Disconnected()) || AsyncError() => true, _ => false, }, label: switch (connectionStatus) { AsyncData(value: Connected()) when requiresReconnect == true => t.connection.reconnect, AsyncData(value: Connected()) when delay <= 0 || delay >= 65000 => t.connection.connecting, AsyncData(value: final status) => status.present(t), _ => "", }, buttonColor: switch (connectionStatus) { AsyncData(value: Connected()) when requiresReconnect == true => Colors.teal, AsyncData(value: Connected()) when delay <= 0 || delay >= 65000 => const Color.fromARGB(255, 185, 176, 103), AsyncData(value: Connected()) => buttonTheme.connectedColor!, AsyncData(value: _) => buttonTheme.idleColor!, _ => Colors.red, }, image: switch (connectionStatus) { AsyncData(value: Connected()) when requiresReconnect == true => Assets.images.disconnectNorouz, AsyncData(value: Connected()) => Assets.images.connectNorouz, AsyncData(value: _) => Assets.images.disconnectNorouz, _ => Assets.images.disconnectNorouz, AsyncData(value: Disconnected()) || AsyncError() => Assets.images.disconnectNorouz, AsyncData(value: Connected()) => Assets.images.connectNorouz, _ => Assets.images.disconnectNorouz, }, newButtonColor: switch (connectionStatus) { AsyncData(value: Connected()) when requiresReconnect == true => Colors.teal, AsyncData(value: Connected()) when delay <= 0 || delay >= 65000 => const Color.fromARGB(255, 185, 176, 103), AsyncData(value: Connected()) => buttonTheme.connectedColor!, AsyncData(value: _) => buttonTheme.idleColor!, _ => Colors.red, }, animated: switch (connectionStatus) { AsyncData(value: Connected()) when requiresReconnect == true => false, AsyncData(value: Connected()) when delay <= 0 || delay >= 65000 => false, AsyncData(value: Connected()) => true, AsyncData(value: _) => true, _ => false, }, useImage: today.day >= 19 && today.day <= 23 && today.month == 3, secureLabel: secureLabel, ); } } class _ConnectionButton extends StatelessWidget { const _ConnectionButton({ required this.onTap, required this.enabled, required this.label, required this.buttonColor, required this.image, required this.useImage, required this.newButtonColor, required this.animated, required this.secureLabel, }); final VoidCallback onTap; final bool enabled; final String label; final Color buttonColor; final AssetGenImage image; final bool useImage; final String secureLabel; final Color newButtonColor; final bool animated; @override Widget build(BuildContext context) { return Column( mainAxisAlignment: MainAxisAlignment.center, children: [ // CircleDesignWidget(newButtonColor: newButtonColor, onTap: onTap, animated: animated), Semantics( button: true, enabled: enabled, label: label, child: Container( clipBehavior: Clip.antiAlias, decoration: BoxDecoration( shape: BoxShape.circle, boxShadow: [BoxShadow(blurRadius: 16, color: buttonColor.withValues(alpha: .5))], ), width: 148, height: 148, child: Material( key: const ValueKey("home_connection_button"), shape: const CircleBorder(), color: Colors.white, child: InkWell( focusColor: Colors.grey, onTap: onTap, child: Padding( padding: const EdgeInsets.all(36), child: TweenAnimationBuilder( tween: ColorTween(end: buttonColor), duration: const Duration(milliseconds: 250), builder: (context, value, child) { if (useImage) { return image.image(); } else { return Assets.images.logo.svg(colorFilter: ColorFilter.mode(value!, BlendMode.srcIn)); } }, ), ), ), ).animate(target: enabled ? 0 : 1).blurXY(end: 1), ).animate(target: enabled ? 0 : 1).scaleXY(end: .88, curve: Curves.easeIn), ), const Gap(16), ExcludeSemantics( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ AnimatedText(label, style: Theme.of(context).textTheme.titleMedium), if (secureLabel.isNotEmpty) ...[ Row( mainAxisAlignment: MainAxisAlignment.center, children: [ // const Gap(8), Icon(FontAwesomeIcons.shieldHalved, size: 16, color: Theme.of(context).colorScheme.secondary), const Gap(4), Text( secureLabel, style: Theme.of( context, ).textTheme.titleSmall?.copyWith(color: Theme.of(context).colorScheme.secondary), ), ], ), ], ], ), ), ], ); } } ================================================ FILE: lib/features/home/widget/empty_profiles_home_body.dart ================================================ import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/router/bottom_sheets/bottom_sheets_notifier.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class EmptyProfilesHomeBody extends HookConsumerWidget { const EmptyProfilesHomeBody({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; return SliverFillRemaining( hasScrollBody: false, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text(t.dialogs.noActiveProfile.msg), const Gap(16), ElevatedButton( onPressed: () => ref.read(bottomSheetsNotifierProvider.notifier).showAddProfile(), // icon: const Icon(FluentIcons.add_24_regular), child: Text(t.pages.profiles.add), ), ], ), ); } } // class EmptyActiveProfileHomeBody extends HookConsumerWidget { // const EmptyActiveProfileHomeBody({super.key}); // @override // Widget build(BuildContext context, WidgetRef ref) { // final t = ref.watch(translationsProvider).requireValue; // return SliverFillRemaining( // hasScrollBody: false, // child: Column( // mainAxisAlignment: MainAxisAlignment.center, // children: [ // Text(t.home.noActiveProfileMsg), // const Gap(16), // OutlinedButton( // onPressed: () => const ProfilesOverviewRoute().push(context), // child: Text(t.profile.overviewPageTitle), // ), // ], // ), // ); // } // } ================================================ FILE: lib/features/home/widget/home_page.dart ================================================ import 'package:dartx/dartx.dart'; import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; import 'package:hiddify/core/app_info/app_info_provider.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/router/bottom_sheets/bottom_sheets_notifier.dart'; import 'package:hiddify/features/home/widget/connection_button.dart'; import 'package:hiddify/features/profile/notifier/active_profile_notifier.dart'; import 'package:hiddify/features/profile/widget/profile_tile.dart'; import 'package:hiddify/features/proxy/active/active_proxy_card.dart'; import 'package:hiddify/features/proxy/active/active_proxy_delay_indicator.dart'; import 'package:hiddify/gen/assets.gen.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:sliver_tools/sliver_tools.dart'; class HomePage extends HookConsumerWidget { const HomePage({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final theme = Theme.of(context); final t = ref.watch(translationsProvider).requireValue; // final hasAnyProfile = ref.watch(hasAnyProfileProvider); final activeProfile = ref.watch(activeProfileProvider); return Scaffold( appBar: AppBar( // leading: (RootScaffold.stateKey.currentState?.hasDrawer ?? false) && showDrawerButton(context) // ? DrawerButton( // onPressed: () { // RootScaffold.stateKey.currentState?.openDrawer(); // }, // ) // : null, title: Row( children: [ Assets.images.logo.svg(height: 24), const Gap(8), Text.rich( TextSpan( children: [ TextSpan(text: t.common.appTitle), const TextSpan(text: " "), const WidgetSpan(child: AppVersionLabel(), alignment: PlaceholderAlignment.middle), ], ), ), ], ), actions: [ // IconButton( // onPressed: () => const QuickSettingsRoute().push(context), // icon: const Icon(FluentIcons.options_24_filled), // material: (context, platform) => MaterialIconButtonData( // tooltip: t.config.quickSettings, // )), // IconButton( // onPressed: () => const AddProfileRoute().push(context), // icon: const Icon(FluentIcons.add_circle_24_filled), // material: (context, platform) => MaterialIconButtonData( // tooltip: t.profile.add.buttonText, // )), Semantics( key: const ValueKey("profile_quick_settings"), label: t.pages.home.quickSettings, child: IconButton( icon: Icon(Icons.tune_rounded, color: theme.colorScheme.primary), onPressed: () => ref.read(bottomSheetsNotifierProvider.notifier).showQuickSettings(), ), ), const Gap(8), Semantics( key: const ValueKey("profile_add_button"), label: t.pages.profiles.add, child: IconButton( icon: Icon(Icons.add_rounded, color: theme.colorScheme.primary), onPressed: () => ref.read(bottomSheetsNotifierProvider.notifier).showAddProfile(), ), ), const Gap(8), ], ), body: Container( decoration: BoxDecoration( image: DecorationImage( image: const AssetImage('assets/images/world_map.png'), // Replace with your image path fit: BoxFit.cover, opacity: 0.09, colorFilter: theme.brightness == Brightness.dark ? ColorFilter.mode(Colors.white.withValues(alpha: .15), BlendMode.srcIn) // : ColorFilter.mode( Colors.grey.withValues(alpha: 1), BlendMode.srcATop, ), // Apply white tint in dark mode ), ), child: Stack( alignment: Alignment.center, children: [ Center( child: ConstrainedBox( constraints: const BoxConstraints( maxWidth: 600, // Set the maximum width here ), child: CustomScrollView( slivers: [ // switch (activeProfile) { // AsyncData(value: final profile?) => MultiSliver( children: [ // const Gap(100), switch (activeProfile) { AsyncData(value: final profile?) => ProfileTile( profile: profile, isMain: true, margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), color: Theme.of(context).colorScheme.surfaceContainer, ), _ => const Text(""), }, const SliverFillRemaining( hasScrollBody: false, child: Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( child: Column( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center, children: [ConnectionButton(), ActiveProxyDelayIndicator()], ), ), ActiveProxyFooter(), ], ), ), ], ), // AsyncData() => switch (hasAnyProfile) { // AsyncData(value: true) => const EmptyActiveProfileHomeBody(), // _ => const EmptyProfilesHomeBody(), // }, // AsyncError(:final error) => SliverErrorBodyPlaceholder(t.presentShortError(error)), // _ => const SliverToBoxAdapter(), // }, ], ), ), ), ], ), ), ); } } class AppVersionLabel extends HookConsumerWidget { const AppVersionLabel({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; final theme = Theme.of(context); final version = ref.watch(appInfoProvider).requireValue.presentVersion; if (version.isBlank) return const SizedBox(); return Semantics( label: t.common.version, button: false, child: Container( decoration: BoxDecoration(color: theme.colorScheme.secondaryContainer, borderRadius: BorderRadius.circular(4)), padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 1), child: Text( version, textDirection: TextDirection.ltr, style: theme.textTheme.bodySmall?.copyWith(color: theme.colorScheme.onSecondaryContainer), ), ), ); } } ================================================ FILE: lib/features/home/widget/new_con_button.dart ================================================ import 'package:flutter/material.dart'; import 'package:flutter_animate/flutter_animate.dart'; import 'package:gap/gap.dart'; import 'package:hiddify/core/widget/animated_text.dart'; class CircleDesignWidget extends StatelessWidget { final double animationValue; final Color color; final VoidCallback onTap; final bool enabled; final String label; const CircleDesignWidget({ Key? key, required this.animationValue, required this.color, required this.onTap, required this.enabled, required this.label, }) : super(key: key); // GestureDetector( // onTap: onTap, // child: CustomPaint( // size: const Size(168, 168), // painter: CirclePainter( // animationValue: animationValue, // baseColor: color, // ), // ) @override Widget build(BuildContext context) { return Column( mainAxisAlignment: MainAxisAlignment.center, children: [ // CircleDesignWidget(newButtonColor: newButtonColor, onTap: onTap, animated: animated), Semantics( button: true, enabled: enabled, label: label, child: Container( clipBehavior: Clip.antiAlias, decoration: const BoxDecoration( shape: BoxShape.circle, // boxShadow: [ // BoxShadow( // blurRadius: 16, // color: color.withOpacity(0.5), // ), // ], ), width: 168, height: 168, child: Material( key: const ValueKey("home_connection_button"), shape: const CircleBorder(), // color: Colors.white, child: InkWell( onTap: onTap, child: Padding( padding: const EdgeInsets.all(0), child: TweenAnimationBuilder( tween: ColorTween(end: color), duration: const Duration(milliseconds: 250), builder: (context, value, child) { return CustomPaint( painter: CirclePainter(animationValue: animationValue, baseColor: value!), ); // return Assets.images.logo.svg( // colorFilter: ColorFilter.mode( // value!, // BlendMode.srcIn, // ), // ); }, ), ), ), ).animate(target: enabled ? 0 : 1).blurXY(end: 1), ).animate(target: enabled ? 0 : 1).scaleXY(end: .88, curve: Curves.easeIn), ), const Gap(16), ExcludeSemantics(child: AnimatedText(label, style: Theme.of(context).textTheme.titleMedium)), ], ); } } class CirclePainter extends CustomPainter { final double animationValue; final Color baseColor; CirclePainter({required this.animationValue, required this.baseColor}); @override void paint(Canvas canvas, Size size) { final double cx = size.width / 2; final double cy = size.height / 2; final innerCircleColor = [baseColor.withAlpha(230), baseColor]; // Outer circle (pulsing animation for connecting state) final Paint outerCirclePaint = Paint() ..color = baseColor.withOpacity(0.15) ..style = PaintingStyle.fill; final double outerRadius = 84 * animationValue; canvas.drawCircle(Offset(cx, cy), outerRadius, outerCirclePaint); // Middle circle final Paint middleCirclePaint = Paint() ..color = baseColor.withOpacity(.3) ..style = PaintingStyle.fill; final double middleRadius = 60 * animationValue + (1 - animationValue) / 3; canvas.drawCircle(Offset(cx, cy), middleRadius, middleCirclePaint); // Inner circle with gradient final Paint innerCirclePaint = Paint() ..shader = LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: innerCircleColor, ).createShader(Rect.fromCircle(center: Offset(cx, cy), radius: 36)); final double innerRadius = 36; canvas.drawCircle(Offset(cx, cy), innerRadius, innerCirclePaint); // Draw path and vertical line (same as original) final Paint pathPaint = Paint() ..color = Colors.white ..style = PaintingStyle.stroke ..strokeWidth = 3.80952 ..strokeCap = StrokeCap.round ..strokeJoin = StrokeJoin.round; final Path curvePath = Path() ..moveTo(92.4867, 75.52) ..cubicTo(94.1645, 77.1984, 95.307, 79.3366, 95.7697, 81.6643) ..cubicTo(96.2324, 83.9919, 95.9946, 86.4045, 95.0862, 88.597) ..cubicTo(94.1778, 90.7895, 92.6397, 92.6634, 90.6664, 93.9818) ..cubicTo(88.6931, 95.3002, 86.3732, 96.0039, 84, 96.0039) ..cubicTo(81.6268, 96.0039, 79.3069, 95.3002, 77.3336, 93.9818) ..cubicTo(75.3603, 92.6634, 73.8222, 90.7895, 72.9138, 88.597) ..cubicTo(72.0055, 86.4045, 71.7676, 83.9919, 72.2303, 81.6643) ..cubicTo(72.693, 79.3366, 73.8355, 77.1984, 75.5133, 75.52); canvas.drawPath(curvePath, pathPaint); final Path linePath = Path() ..moveTo(84.0066, 72) ..lineTo(84.0066, 82.6667); // Draw the vertical line canvas.drawPath(linePath, pathPaint); } @override bool shouldRepaint(CustomPainter oldDelegate) => true; } ================================================ FILE: lib/features/home/widget/new_connection_button.dart ================================================ import 'package:flutter/material.dart'; enum ConnectionStateStatus { disconnected, connecting, connected, error } class CircleDesignWidget extends StatefulWidget { @override _CircleDesignWidgetState createState() => _CircleDesignWidgetState(); } class _CircleDesignWidgetState extends State with SingleTickerProviderStateMixin { ConnectionStateStatus _currentState = ConnectionStateStatus.disconnected; late AnimationController _controller; late Animation _animation; @override void initState() { super.initState(); // Animation controller for the connecting state _controller = AnimationController(duration: const Duration(seconds: 4), vsync: this)..repeat(reverse: true); _animation = Tween(begin: 0.8, end: 1).animate(_controller) ..addListener(() { setState(() {}); }); } @override void dispose() { _controller.dispose(); super.dispose(); } void changeState(ConnectionStateStatus state) { setState(() { _currentState = state; if (state == ConnectionStateStatus.connecting || state == ConnectionStateStatus.connected) { _controller.repeat(reverse: true); } else { _controller.stop(); } }); } @override Widget build(BuildContext context) { return GestureDetector( onTap: () { // Change the state for demo purposes switch (_currentState) { case ConnectionStateStatus.disconnected: changeState(ConnectionStateStatus.connecting); break; case ConnectionStateStatus.connecting: changeState(ConnectionStateStatus.connected); break; case ConnectionStateStatus.connected: changeState(ConnectionStateStatus.error); break; case ConnectionStateStatus.error: changeState(ConnectionStateStatus.disconnected); break; } }, child: CustomPaint( size: const Size(168, 168), painter: CirclePainter(currentState: _currentState, animationValue: _animation.value), ), ); } } class CirclePainter extends CustomPainter { final ConnectionStateStatus currentState; final double animationValue; CirclePainter({required this.currentState, required this.animationValue}); @override void paint(Canvas canvas, Size size) { final double cx = size.width / 2; final double cy = size.height / 2; Color baseColor; var innerCircleColor = [const Color(0xFF455FE9), const Color(0xFF3446A5)]; if (currentState == ConnectionStateStatus.connected) { baseColor = Colors.green.shade900; } else if (currentState == ConnectionStateStatus.error) { baseColor = Colors.red.shade900; } else { // baseColor = const Color(0xFFB8C7F4); baseColor = const Color(0xFF3446A5); } innerCircleColor = [ baseColor.withAlpha(230), baseColor, // Color.fromARGB(73, 27, 80, 43), // Color.fromARGB(71, 12, 48, 21), ]; // Outer circle (pulsing animation for connecting state) final Paint outerCirclePaint = Paint() ..color = baseColor.withOpacity(0.15) ..style = PaintingStyle.fill; // double outerRadius = (size.width / 2) * (currentState == ConnectionStateStatus.connecting ? animationValue : 1); final double outerRadius = 84 * ([ConnectionStateStatus.connecting, ConnectionStateStatus.connected].contains(currentState) ? animationValue : 1); canvas.drawCircle(Offset(cx, cy), outerRadius, outerCirclePaint); // Middle circle final Paint middleCirclePaint = Paint() ..color = baseColor.withOpacity(.3) ..style = PaintingStyle.fill; final double middleRadius = 60 * ([ConnectionStateStatus.connecting, ConnectionStateStatus.connected].contains(currentState) ? animationValue + (1 - animationValue) / 3 : 1); canvas.drawCircle(Offset(cx, cy), middleRadius, middleCirclePaint); // Inner circle with gradient final Paint innerCirclePaint = Paint() ..shader = LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: innerCircleColor, ).createShader(Rect.fromCircle(center: Offset(cx, cy), radius: 36)); final double innerRadius = 36; //* (currentState == ConnectionStateStatus.connecting ? animationValue : 1); canvas.drawCircle(Offset(cx, cy), innerRadius, innerCirclePaint); final Paint pathPaint = Paint() ..color = Colors.white ..style = PaintingStyle.stroke ..strokeWidth = 3.80952 ..strokeCap = StrokeCap.round ..strokeJoin = StrokeJoin.round; final Path curvePath = Path() ..moveTo(92.4867, 75.52) ..cubicTo(94.1645, 77.1984, 95.307, 79.3366, 95.7697, 81.6643) ..cubicTo(96.2324, 83.9919, 95.9946, 86.4045, 95.0862, 88.597) ..cubicTo(94.1778, 90.7895, 92.6397, 92.6634, 90.6664, 93.9818) ..cubicTo(88.6931, 95.3002, 86.3732, 96.0039, 84, 96.0039) ..cubicTo(81.6268, 96.0039, 79.3069, 95.3002, 77.3336, 93.9818) ..cubicTo(75.3603, 92.6634, 73.8222, 90.7895, 72.9138, 88.597) ..cubicTo(72.0055, 86.4045, 71.7676, 83.9919, 72.2303, 81.6643) ..cubicTo(72.693, 79.3366, 73.8355, 77.1984, 75.5133, 75.52); canvas.drawPath(curvePath, pathPaint); // Vertical line final Path linePath = Path() ..moveTo(84.0066, 72) ..lineTo(84.0066, 82.6667); // Draw the vertical line canvas.drawPath(linePath, pathPaint); } @override bool shouldRepaint(CustomPainter oldDelegate) => true; } ================================================ FILE: lib/features/intro/widget/intro_page.dart ================================================ import 'dart:async'; import 'dart:io'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.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/http_client/dio_http_client.dart'; import 'package:hiddify/core/localization/locale_preferences.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/model/constants.dart'; import 'package:hiddify/core/model/region.dart'; import 'package:hiddify/core/preferences/general_preferences.dart'; import 'package:hiddify/features/common/general_pref_tiles.dart'; import 'package:hiddify/features/settings/data/config_option_repository.dart'; import 'package:hiddify/features/settings/widget/preference_tile.dart'; import 'package:hiddify/gen/assets.gen.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class IntroPage extends HookConsumerWidget with PresLogger { const IntroPage({super.key}); static bool locationInfoLoaded = false; // for focus management KeyEventResult _handleKeyEvent(KeyEvent event, String key) { if (KeyboardConst.select.contains(event.logicalKey) && event is KeyUpEvent) { UriUtils.tryLaunch(Uri.parse(IntroConst.url[key]!)); return KeyEventResult.handled; } return KeyEventResult.ignored; } @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; final theme = Theme.of(context); final isStarting = useState(false); if (!locationInfoLoaded) { autoSelectRegion(ref).then((value) => loggy.debug("Auto Region selection finished!")); locationInfoLoaded = true; } // for focus management final focusStates = >{ IntroConst.termsAndConditionsKey: useState(false), IntroConst.githubKey: useState(false), IntroConst.licenseKey: useState(false), }; final focusNodes = { IntroConst.termsAndConditionsKey: useFocusNode(), IntroConst.githubKey: useFocusNode(), IntroConst.licenseKey: useFocusNode(), }; useEffect(() { for (final entry in focusNodes.entries) { entry.value.addListener(() => focusStates[entry.key]!.value = entry.value.hasPrimaryFocus); } return null; }, []); return Scaffold( body: Center( child: ScrollConfiguration( behavior: ScrollConfiguration.of(context).copyWith(scrollbars: false), child: SingleChildScrollView( child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 620), child: Column( mainAxisSize: MainAxisSize.min, children: [ LayoutBuilder( builder: (context, constraints) { final width = constraints.maxWidth > IntroConst.maxwidth ? IntroConst.maxwidth : constraints.maxWidth; final size = width * 0.4; return Assets.images.logo.svg(width: size, height: size); }, ), const Gap(16), Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: Text( t.intro.banner, style: theme.textTheme.bodyLarge, maxLines: 1, overflow: TextOverflow.ellipsis, ), ), const Gap(24), const LocalePrefTile(), ChoicePreferenceWidget( selected: ref.watch(ConfigOptions.region), preferences: ref.watch(ConfigOptions.region.notifier), choices: Region.values, title: t.pages.settings.routing.region, showFlag: true, icon: Icons.place_rounded, presentChoice: (value) => value.present(t), onChanged: (val) async { await ref.read(ConfigOptions.directDnsAddress.notifier).reset(); }, ), const EnableAnalyticsPrefTile(), const Gap(24), Focus( focusNode: focusNodes[IntroConst.termsAndConditionsKey], onKeyEvent: (node, event) => _handleKeyEvent(event, IntroConst.termsAndConditionsKey), child: Text.rich( t.intro.termsAndPolicyCaution( tap: (text) => TextSpan( text: text, style: TextStyle( color: focusStates[IntroConst.termsAndConditionsKey]!.value ? Colors.green : Colors.blue, ), recognizer: TapGestureRecognizer() ..onTap = () async { await UriUtils.tryLaunch(Uri.parse(Constants.termsAndConditionsUrl)); }, ), ), style: theme.textTheme.bodySmall, ), ), const Gap(8), Focus( focusNode: focusNodes[IntroConst.githubKey], onKeyEvent: (node, event) => _handleKeyEvent(event, IntroConst.githubKey), child: Text.rich( t.intro.info( tap_source: (text) => TextSpan( text: text, style: TextStyle( color: focusStates[IntroConst.githubKey]!.value ? Colors.green : Colors.blue, ), recognizer: TapGestureRecognizer() ..onTap = () async { await UriUtils.tryLaunch(Uri.parse(Constants.githubUrl)); }, ), tap_license: (text) => TextSpan( text: text, style: TextStyle( color: focusStates[IntroConst.githubKey]!.value ? Colors.green : Colors.blue, ), recognizer: TapGestureRecognizer() ..onTap = () async { await UriUtils.tryLaunch(Uri.parse(Constants.licenseUrl)); }, ), ), style: theme.textTheme.bodySmall, ), ), // only for managing license node focus Focus( focusNode: focusNodes[IntroConst.licenseKey], onKeyEvent: (node, event) => _handleKeyEvent(event, IntroConst.licenseKey), child: const Gap(88), ), ], ), ), ), ), ), floatingActionButton: FloatingActionButton.extended( icon: isStarting.value ? const SizedBox(width: 24, height: 24, child: CircularProgressIndicator()) : const Icon(Icons.rocket_launch), label: Text(t.common.start, style: theme.textTheme.titleMedium), onPressed: () async { if (isStarting.value) return; isStarting.value = true; if (!ref.read(analyticsControllerProvider).requireValue) { loggy.info("disabling analytics per user request"); try { await ref.read(analyticsControllerProvider.notifier).disableAnalytics(); } catch (error, stackTrace) { loggy.error("could not disable analytics", error, stackTrace); } } await ref.read(Preferences.introCompleted.notifier).update(true); }, ), ); } Future autoSelectRegion(WidgetRef ref) async { try { final countryCode = RegionDetector.detect(); final regionLocale = _getRegionLocale(countryCode); loggy.debug('Timezone Region: ${regionLocale.region} Locale: ${regionLocale.locale}'); await ref.read(ConfigOptions.region.notifier).update(regionLocale.region); await ref.watch(ConfigOptions.directDnsAddress.notifier).reset(); await ref.read(localePreferencesProvider.notifier).changeLocale(regionLocale.locale); return; } catch (e) { loggy.warning('Could not get the local country code based on timezone', e); } try { final DioHttpClient client = DioHttpClient( timeout: const Duration(seconds: 2), userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:123.0) Gecko/20100101 Firefox/123.0", debug: true, ); final response = await client.get>('https://api.ip.sb/geoip/'); if (response.statusCode == 200) { final jsonData = response.data!; final regionLocale = _getRegionLocale(jsonData['country_code']?.toString() ?? ""); loggy.debug('Region: ${regionLocale.region} Locale: ${regionLocale.locale}'); await ref.read(ConfigOptions.region.notifier).update(regionLocale.region); await ref.read(localePreferencesProvider.notifier).changeLocale(regionLocale.locale); } else { loggy.warning('Request failed with status: ${response.statusCode}'); } } catch (e) { loggy.warning('Could not get the local country code from ip'); } } RegionLocale _getRegionLocale(String country) { switch (country.toUpperCase()) { case "IR": return RegionLocale(Region.ir, AppLocale.fa); case "CN": return RegionLocale(Region.cn, AppLocale.zhCn); case "RU": return RegionLocale(Region.ru, AppLocale.ru); case "AF": return RegionLocale(Region.af, AppLocale.fa); case "BR": return RegionLocale(Region.br, AppLocale.ptBr); case "TR": return RegionLocale(Region.tr, AppLocale.tr); default: return RegionLocale(Region.other, AppLocale.en); } } } class RegionLocale { final Region region; final AppLocale locale; RegionLocale(this.region, this.locale); } class RegionDetector { /// Returns: 'IR' | 'AF' | 'CN' | 'TR' | 'RU' | 'BR' | 'US' static String detect() { final now = DateTime.now(); final offset = now.timeZoneOffset.inMinutes; final tz = now.timeZoneName.toLowerCase().trim(); if (offset == 210) return 'IR'; if (offset == 270) { final (_, country) = _parseLocale(); return country == 'IR' ? 'IR' : 'AF'; } final fromName = _fromTzName(tz, offset); if (fromName != null) return fromName; final candidates = _candidatesForOffset(offset); if (candidates.isEmpty) return 'US'; return _resolveByLocale(candidates); } static String? _fromTzName(String tz, int offset) { if (tz.contains('/')) { final city = tz.split('/').last.replaceAll(' ', '_'); final r = _ianaCities[city]; if (r != null) return r; } if (tz == 'irst' || tz == 'irdt' || tz.contains('iran')) return 'IR'; if (tz == 'aft' || tz.contains('afghanistan')) return 'AF'; if (tz == 'trt' || tz.contains('turkey') || tz.contains('istanbul')) { return 'TR'; } if (tz.contains('china') || tz.contains('beijing')) return 'CN'; if (tz == 'cst' && offset == 480) return 'CN'; if (_matchesRussiaTz(tz)) return 'RU'; if (_matchesBrazilTz(tz)) return 'BR'; return null; } static bool _matchesRussiaTz(String tz) { if (tz.contains('russia') || tz.contains('moscow')) return true; const abbrs = {'msk', 'yekt', 'omst', 'krat', 'irkt', 'yakt', 'vlat', 'magt', 'pett', 'sakt', 'sret'}; if (abbrs.contains(tz)) return true; const winKeys = [ 'ekaterinburg', 'kaliningrad', 'yakutsk', 'vladivostok', 'magadan', 'sakhalin', 'kamchatka', 'astrakhan', 'saratov', 'volgograd', 'altai', 'tomsk', 'transbaikal', 'n. central asia', 'north asia', ]; return winKeys.any(tz.contains); } static bool _matchesBrazilTz(String tz) { if (tz == 'brt' || tz == 'brst') return true; if (tz.contains('brazil') || tz.contains('brasilia')) return true; const winKeys = ['e. south america', 'central brazilian', 'tocantins', 'bahia']; return winKeys.any(tz.contains); } static Set _candidatesForOffset(int offset) { final c = {}; if (offset == 180) c.add('TR'); if (offset == 480) c.add('CN'); if (_ruOffsets.contains(offset)) c.add('RU'); if (_brOffsets.contains(offset)) c.add('BR'); return c; } static const _ruOffsets = {120, 180, 240, 300, 360, 420, 480, 540, 600, 660, 720}; static const _brOffsets = {-120, -180, -240, -300}; static String _resolveByLocale(Set candidates) { final (lang, country) = _parseLocale(); if (country != null && candidates.contains(country)) { return country; } final regionFromLang = _langToRegion[lang]; if (regionFromLang != null && candidates.contains(regionFromLang)) { return regionFromLang; } return 'US'; } static (String, String?) _parseLocale() { try { final parts = Platform.localeName.split(RegExp(r'[_\-.]')); final lang = parts.first.toLowerCase(); String? country; for (final p in parts.skip(1)) { if (p.length == 2) { country = p.toUpperCase(); break; } } return (lang, country); } catch (_) { return ('en', null); } } static const _langToRegion = {'fa': 'IR', 'ps': 'AF', 'tr': 'TR', 'zh': 'CN', 'ru': 'RU', 'pt': 'BR'}; static const _ianaCities = { 'tehran': 'IR', 'kabul': 'AF', 'istanbul': 'TR', 'shanghai': 'CN', 'chongqing': 'CN', 'urumqi': 'CN', 'harbin': 'CN', 'moscow': 'RU', 'kaliningrad': 'RU', 'samara': 'RU', 'yekaterinburg': 'RU', 'omsk': 'RU', 'novosibirsk': 'RU', 'barnaul': 'RU', 'tomsk': 'RU', 'krasnoyarsk': 'RU', 'irkutsk': 'RU', 'chita': 'RU', 'yakutsk': 'RU', 'vladivostok': 'RU', 'magadan': 'RU', 'sakhalin': 'RU', 'kamchatka': 'RU', 'anadyr': 'RU', 'volgograd': 'RU', 'saratov': 'RU', 'astrakhan': 'RU', 'sao_paulo': 'BR', 'fortaleza': 'BR', 'recife': 'BR', 'manaus': 'BR', 'belem': 'BR', 'cuiaba': 'BR', 'bahia': 'BR', 'rio_branco': 'BR', 'noronha': 'BR', 'porto_velho': 'BR', 'campo_grande': 'BR', }; } ================================================ FILE: lib/features/log/data/log_data_providers.dart ================================================ 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/hiddifycore/hiddify_core_service_provider.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'log_data_providers.g.dart'; @Riverpod(keepAlive: true) Future logRepository(LogRepositoryRef ref) async { final repo = LogRepositoryImpl( singbox: ref.watch(hiddifyCoreServiceProvider), logPathResolver: ref.watch(logPathResolverProvider), ); await repo.init().getOrElse((l) => throw l).run(); return repo; } @Riverpod(keepAlive: true) LogPathResolver logPathResolver(LogPathResolverRef ref) { return LogPathResolver(ref.watch(appDirectoriesProvider).requireValue.workingDir); } ================================================ FILE: lib/features/log/data/log_parser.dart ================================================ // ignore_for_file: parameter_assignments import 'package:dartx/dartx.dart'; import 'package:hiddify/features/log/model/log_entity.dart'; import 'package:hiddify/features/log/model/log_level.dart'; import 'package:hiddify/hiddifycore/generated/v2/hcore/hcore.pb.dart' as pb; import 'package:tint/tint.dart'; abstract class LogParser { static LogEntity parseLogProto(pb.LogMessage message) { final level = switch (message.level) { pb.LogLevel.DEBUG => LogLevel.debug, pb.LogLevel.INFO => LogLevel.info, pb.LogLevel.WARNING => LogLevel.warn, pb.LogLevel.ERROR => LogLevel.error, pb.LogLevel.FATAL => LogLevel.fatal, _ => LogLevel.debug, }; return LogEntity(level: level, time: message.time.toDateTime(), message: message.message); } static LogEntity parseSingbox(String log) { log = log.strip(); DateTime? time; if (log.length > 25) { time = DateTime.tryParse(log.substring(6, 25)); } if (time != null) { log = log.substring(26); } final level = LogLevel.values.firstOrNullWhere((e) { if (log.startsWith(e.name.toUpperCase())) { log = log.removePrefix(e.name.toUpperCase()); return true; } return false; }); return LogEntity(level: level, time: time, message: log.trim()); } } ================================================ FILE: lib/features/log/data/log_path_resolver.dart ================================================ import 'dart:io'; import 'package:path/path.dart' as p; class LogPathResolver { const LogPathResolver(this._workingDir); final Directory _workingDir; Directory get directory => _workingDir; File coreFile() { return File(p.join(directory.path, "box.log")); } File appFile() { return File(p.join(directory.path, "app.log")); } } ================================================ FILE: lib/features/log/data/log_repository.dart ================================================ import 'package:flutter/foundation.dart'; import 'package:fpdart/fpdart.dart'; import 'package:hiddify/core/utils/exception_handler.dart'; import 'package:hiddify/features/log/data/log_parser.dart'; import 'package:hiddify/features/log/data/log_path_resolver.dart'; import 'package:hiddify/features/log/model/log_entity.dart'; import 'package:hiddify/features/log/model/log_failure.dart'; import 'package:hiddify/hiddifycore/hiddify_core_service.dart'; import 'package:hiddify/utils/custom_loggers.dart'; abstract interface class LogRepository { TaskEither init(); Stream>> watchLogs(); TaskEither clearLogs(); } class LogRepositoryImpl with ExceptionHandler, InfraLogger implements LogRepository { LogRepositoryImpl({required this.singbox, required this.logPathResolver}); final HiddifyCoreService singbox; final LogPathResolver logPathResolver; @override TaskEither init() { return exceptionHandler(() async { if (!kIsWeb) { if (!await logPathResolver.directory.exists()) { await logPathResolver.directory.create(recursive: true); } if (await logPathResolver.coreFile().exists()) { await logPathResolver.coreFile().writeAsString(""); } else { await logPathResolver.coreFile().create(recursive: true); } if (await logPathResolver.appFile().exists()) { await logPathResolver.appFile().writeAsString(""); } else { await logPathResolver.appFile().create(recursive: true); } } return right(unit); }, LogUnexpectedFailure.new); } @override Stream>> watchLogs() { return singbox .watchLogs(logPathResolver.coreFile().path) .map((event) => event.map(LogParser.parseLogProto).toList()) .handleExceptions((error, stackTrace) { loggy.warning("error watching logs", error, stackTrace); return LogFailure.unexpected(error, stackTrace); }); } @override TaskEither clearLogs() { return exceptionHandler(() => singbox.clearLogs().mapLeft(LogFailure.unexpected).run(), LogFailure.unexpected); } } ================================================ FILE: lib/features/log/model/log_entity.dart ================================================ import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:hiddify/features/log/model/log_level.dart'; part 'log_entity.freezed.dart'; @freezed class LogEntity with _$LogEntity { const factory LogEntity({LogLevel? level, DateTime? time, required String message}) = _LogEntity; } ================================================ FILE: lib/features/log/model/log_failure.dart ================================================ import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/model/failures.dart'; part 'log_failure.freezed.dart'; @freezed sealed class LogFailure with _$LogFailure, Failure { const LogFailure._(); @With() const factory LogFailure.unexpected([Object? error, StackTrace? stackTrace]) = LogUnexpectedFailure; @override ({String type, String? message}) present(TranslationsEn t) { return switch (this) { LogUnexpectedFailure() => (type: t.errors.unexpected, message: null), }; } } ================================================ FILE: lib/features/log/model/log_level.dart ================================================ import 'package:dart_mappable/dart_mappable.dart'; import 'package:dartx/dartx.dart'; import 'package:flutter/material.dart'; part 'log_level.mapper.dart'; @MappableEnum() enum LogLevel { trace, debug, info, warn, error, fatal, panic; /// [LogLevel] selectable by user as preference static List get choices => values.takeFirst(4); Color? get color => switch (this) { trace => Colors.lightBlueAccent, debug => Colors.grey, info => Colors.lightGreen, warn => Colors.orange, error => Colors.redAccent, fatal => Colors.red, panic => Colors.red, }; } ================================================ FILE: lib/features/log/overview/logs_overview_notifier.dart ================================================ import 'dart:async'; import 'package:hiddify/features/log/data/log_data_providers.dart'; import 'package:hiddify/features/log/model/log_entity.dart'; import 'package:hiddify/features/log/model/log_level.dart'; import 'package:hiddify/features/log/overview/logs_overview_state.dart'; import 'package:hiddify/hiddifycore/init_signal.dart'; import 'package:hiddify/utils/riverpod_utils.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:rxdart/rxdart.dart'; part 'logs_overview_notifier.g.dart'; @riverpod class LogsOverviewNotifier extends _$LogsOverviewNotifier with AppLogger { @override LogsOverviewState build() { ref.disposeDelay(const Duration(seconds: 20)); state = const LogsOverviewState(); ref.onDispose(() { loggy.debug("disposing"); _listener?.cancel(); _listener = null; }); ref.onCancel(() { if (_listener?.isPaused != true) { loggy.debug("pausing"); _listener?.pause(); } }); ref.onResume(() { if (!state.paused && (_listener?.isPaused ?? false)) { loggy.debug("resuming"); _listener?.resume(); } }); _addListeners(); return const LogsOverviewState(); } StreamSubscription? _listener; Future _addListeners() async { loggy.debug("adding listeners"); ref.watch(coreRestartSignalProvider); await _listener?.cancel(); _listener = ref .read(logRepositoryProvider) .requireValue .watchLogs() .throttle((_) => Stream.value(_listener?.isPaused ?? false), leading: false, trailing: true) .throttleTime(const Duration(milliseconds: 250), leading: false, trailing: true) .asyncMap((event) async { await event.fold( (f) { _logs = []; state = state.copyWith(logs: AsyncError(f, StackTrace.current)); }, (a) async { _logs = a.reversed; state = state.copyWith(logs: AsyncData(await _computeLogs())); }, ); }) .listen((event) {}); } Iterable _logs = []; final _debouncer = CallbackDebouncer(const Duration(milliseconds: 200)); LogLevel? _levelFilter; String _filter = ""; Future> _computeLogs() async { if (_levelFilter == null && _filter.isEmpty) return _logs.toList(); return _logs.where((e) { return (_filter.isEmpty || e.message.contains(_filter)) && (_levelFilter == null || e.level == null || e.level!.index >= _levelFilter!.index); }).toList(); } void pause() { loggy.debug("pausing"); _listener?.pause(); state = state.copyWith(paused: true); } void resume() { loggy.debug("resuming"); _listener?.resume(); state = state.copyWith(paused: false); } Future clear() async { loggy.debug("clearing"); await ref .read(logRepositoryProvider) .requireValue .clearLogs() .match( (l) { loggy.warning("error clearing logs", l); }, (_) { _logs = []; state = state.copyWith(logs: const AsyncData([])); }, ) .run(); } void filterMessage(String? filter) { _filter = filter ?? ''; _debouncer(() async { if (state.logs case AsyncData()) { state = state.copyWith(filter: _filter, logs: AsyncData(await _computeLogs())); } }); } Future filterLevel(LogLevel? level) async { _levelFilter = level; if (state.logs case AsyncData()) { state = state.copyWith(levelFilter: _levelFilter, logs: AsyncData(await _computeLogs())); } } } ================================================ FILE: lib/features/log/overview/logs_overview_state.dart ================================================ import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:hiddify/features/log/model/log_entity.dart'; import 'package:hiddify/features/log/model/log_level.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'logs_overview_state.freezed.dart'; @freezed class LogsOverviewState with _$LogsOverviewState { const LogsOverviewState._(); const factory LogsOverviewState({ @Default(AsyncLoading()) AsyncValue> logs, @Default(false) bool paused, @Default("") String filter, LogLevel? levelFilter, }) = _LogsOverviewState; } ================================================ FILE: lib/features/log/overview/logs_page.dart ================================================ import 'package:fluentui_system_icons/fluentui_system_icons.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:fpdart/fpdart.dart'; import 'package:gap/gap.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/model/failures.dart'; import 'package:hiddify/core/preferences/general_preferences.dart'; import 'package:hiddify/core/widget/adaptive_icon.dart'; import 'package:hiddify/features/log/data/log_data_providers.dart'; import 'package:hiddify/features/log/model/log_level.dart'; import 'package:hiddify/features/log/overview/logs_overview_notifier.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:sliver_tools/sliver_tools.dart'; class LogsPage extends HookConsumerWidget with PresLogger { const LogsPage({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; final state = ref.watch(logsOverviewNotifierProvider); final notifier = ref.watch(logsOverviewNotifierProvider.notifier); final debug = ref.watch(debugModeNotifierProvider); final pathResolver = ref.watch(logPathResolverProvider); final filterController = useTextEditingController(text: state.filter); final List popupButtons = debug || PlatformUtils.isDesktop ? [ PopupMenuItem( child: Text(t.pages.logs.shareCoreLogs), onTap: () async { await UriUtils.tryShareOrLaunchFile( Uri.parse(pathResolver.coreFile().path), fileOrDir: pathResolver.directory.uri, ); }, ), PopupMenuItem( child: Text(t.pages.logs.shareAppLogs), onTap: () async { await UriUtils.tryShareOrLaunchFile( Uri.parse(pathResolver.appFile().path), fileOrDir: pathResolver.directory.uri, ); }, ), ] : []; return Scaffold( appBar: AppBar( title: Text(t.pages.logs.title), actions: [ if (state.paused) IconButton( onPressed: notifier.resume, icon: const Icon(FluentIcons.play_20_regular), tooltip: t.common.resume, iconSize: 20, ) else IconButton( onPressed: notifier.pause, icon: const Icon(FluentIcons.pause_20_regular), tooltip: t.common.pause, iconSize: 20, ), IconButton( onPressed: notifier.clear, icon: const Icon(FluentIcons.delete_lines_20_regular), tooltip: t.common.clear, iconSize: 20, ), if (popupButtons.isNotEmpty) PopupMenuButton( icon: Icon(AdaptiveIcon(context).more), itemBuilder: (context) { return popupButtons; }, ), const Gap(8), ], ), body: NestedScrollView( headerSliverBuilder: (context, innerBoxIsScrolled) { return [ SliverOverlapAbsorber( handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context), sliver: MultiSliver( children: [ // NestedAppBar( // forceElevated: innerBoxIsScrolled, // ), SliverPinnedHeader( child: DecoratedBox( decoration: BoxDecoration(color: Theme.of(context).colorScheme.surface), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), child: Row( children: [ Flexible( child: TextFormField( controller: filterController, onChanged: notifier.filterMessage, decoration: InputDecoration(isDense: true, hintText: t.common.filter), ), ), const Gap(16), DropdownButton>( value: optionOf(state.levelFilter), onChanged: (v) { if (v == null) return; notifier.filterLevel(v.toNullable()); }, padding: const EdgeInsets.symmetric(horizontal: 8), borderRadius: BorderRadius.circular(4), items: [ DropdownMenuItem(value: none(), child: Text(t.common.all)), ...LogLevel.choices.map((e) => DropdownMenuItem(value: some(e), child: Text(e.name))), ], ), ], ), ), ), ), ], ), ), ]; }, body: Builder( builder: (context) { return CustomScrollView( primary: false, reverse: true, slivers: [ switch (state.logs) { AsyncData(value: final logs) => SliverList.builder( itemCount: logs.length, itemBuilder: (context, index) { final log = logs[index]; return Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ if (log.level != null) Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( log.level!.name.toUpperCase(), style: Theme.of( context, ).textTheme.labelMedium?.copyWith(color: log.level!.color), ), if (log.time != null) Text(log.time!.toString(), style: Theme.of(context).textTheme.labelSmall), ], ), Text(extractMessage(log.message), style: Theme.of(context).textTheme.bodySmall), ], ), ), if (index != 0) const Divider(indent: 16, endIndent: 16, height: 4), ], ); }, ), AsyncError(:final error) => SliverErrorBodyPlaceholder(t.presentShortError(error)), _ => const SliverLoadingBodyPlaceholder(), }, SliverOverlapInjector(handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context)), ], ); }, ), ), ); } } String extractMessage(String message) { final parts = message.split(' '); return parts.length <= 2 ? parts.last : parts.sublist(2).join(' '); } ================================================ FILE: lib/features/per_app_proxy/data/app_proxy_data_source.dart ================================================ import 'package:drift/drift.dart'; import 'package:hiddify/core/db/db.dart'; import 'package:hiddify/features/per_app_proxy/model/per_app_proxy_backup.dart'; import 'package:hiddify/features/per_app_proxy/model/per_app_proxy_mode.dart'; import 'package:hiddify/features/per_app_proxy/model/pkg_flag.dart'; import 'package:hiddify/utils/custom_loggers.dart'; part 'app_proxy_data_source.g.dart'; abstract interface class AppProxyDataSource { Future updatePkg({required String pkg, required AppProxyMode mode}); Stream> watchAll({required AppProxyMode mode}); Stream> watchFilterForDisplay({required Set phonePkgs, required AppProxyMode mode}); Stream> watchActivePackages({required Set phonePkgs, required AppProxyMode mode}); Future> getPkgsByFlag({required PkgFlag flag, required AppProxyMode mode}); Future importPkgs({required PerAppProxyBackup backup}); Future applyAutoSelection({required Set autoList, required AppProxyMode mode}); Future clearAutoSelected({required AppProxyMode mode}); Future revertForceDeselection({required AppProxyMode mode}); Future clearAll({required AppProxyMode mode}); } @DriftAccessor(tables: [AppProxyEntries]) class AppProxyDao extends DatabaseAccessor with _$AppProxyDaoMixin, InfraLogger implements AppProxyDataSource { AppProxyDao(super.db); @override Future updatePkg({required String pkg, required AppProxyMode mode}) { return transaction(() async { final entry = await (select( appProxyEntries, )..where((tbl) => tbl.mode.equalsValue(mode) & tbl.pkgName.equals(pkg))).getSingleOrNull(); if (entry == null) { await into( appProxyEntries, ).insert(AppProxyEntriesCompanion.insert(mode: mode, pkgName: pkg, flags: Value(PkgFlag.userSelection.add(0)))); return; } final flag = entry.flags; final isAutoSelection = PkgFlag.autoSelection.check(flag); if (!isAutoSelection) { await (delete(appProxyEntries)..where((tbl) => tbl.mode.equalsValue(mode) & tbl.pkgName.equals(pkg))).go(); return; } int newFlag; if (PkgFlag.forceDeselection.check(flag)) { newFlag = PkgFlag.forceDeselection.remove(PkgFlag.userSelection.remove(flag)); } else if (PkgFlag.userSelection.check(flag)) { newFlag = PkgFlag.forceDeselection.add(flag); } else { newFlag = PkgFlag.userSelection.add(flag); } await (update(appProxyEntries)..where((tbl) => tbl.mode.equalsValue(mode) & tbl.pkgName.equals(pkg))).write( AppProxyEntriesCompanion(flags: Value(newFlag)), ); }); } @override Stream> watchAll({required AppProxyMode mode}) { final query = select(appProxyEntries)..where((tbl) => tbl.mode.equalsValue(mode)); return query.watch(); } @override Stream> watchFilterForDisplay({required Set phonePkgs, required AppProxyMode mode}) { if (phonePkgs.isEmpty) return Stream.value([]); return (select(appProxyEntries)..where((tbl) { final modeFilter = tbl.mode.equalsValue(mode); final packageFilter = tbl.pkgName.isIn(phonePkgs); return modeFilter & packageFilter; })) .watch(); } @override Stream> watchActivePackages({required Set phonePkgs, required AppProxyMode mode}) { if (phonePkgs.isEmpty) return Stream.value([]); final query = selectOnly(appProxyEntries)..addColumns([appProxyEntries.pkgName]); final modeFilter = appProxyEntries.mode.equalsValue(mode); final packageFilter = appProxyEntries.pkgName.isIn(phonePkgs); final isForceDeselectionSet = appProxyEntries.flags .bitwiseAnd(Constant(PkgFlag.forceDeselection.value)) .equals(PkgFlag.forceDeselection.value); final combinedFilter = modeFilter & packageFilter & isForceDeselectionSet.not(); query.where(combinedFilter); return query.watch().map((rows) { return rows.map((row) => row.read(appProxyEntries.pkgName)!).toList(); }); } @override Future> getPkgsByFlag({required PkgFlag flag, required AppProxyMode mode}) { final query = selectOnly(appProxyEntries)..addColumns([appProxyEntries.pkgName]); final filter = appProxyEntries.mode.equalsValue(mode) & (appProxyEntries.flags.bitwiseAnd(Constant(flag.value)).equals(flag.value)); query.where(filter); return query.map((row) => row.read(appProxyEntries.pkgName)!).get(); } @override Future importPkgs({required PerAppProxyBackup backup}) { return transaction(() async { await (delete(appProxyEntries)..where((tbl) => tbl.flags.equals(0))).go(); await db.batch((b) { b ..update( appProxyEntries, AppProxyEntriesCompanion.custom( flags: appProxyEntries.flags ..bitwiseAnd(Constant(~PkgFlag.userSelection.value)) ..bitwiseAnd(Constant(~PkgFlag.forceDeselection.value)), ), ) ..deleteWhere(appProxyEntries, (tbl) => tbl.flags.equals(0)) ..insertAll( db.appProxyEntries, [ ...backup.include.selected.map( (pkg) => AppProxyEntriesCompanion.insert( mode: AppProxyMode.include, pkgName: pkg, flags: Value(PkgFlag.userSelection.add(0)), ), ), ...backup.include.deselected.map( (pkg) => AppProxyEntriesCompanion.insert( mode: AppProxyMode.include, pkgName: pkg, flags: Value(PkgFlag.forceDeselection.add(0)), ), ), ...backup.exclude.selected.map( (pkg) => AppProxyEntriesCompanion.insert( mode: AppProxyMode.exclude, pkgName: pkg, flags: Value(PkgFlag.userSelection.add(0)), ), ), ...backup.exclude.deselected.map( (pkg) => AppProxyEntriesCompanion.insert( mode: AppProxyMode.exclude, pkgName: pkg, flags: Value(PkgFlag.forceDeselection.add(0)), ), ), ], onConflict: DoUpdate.withExcluded((AppProxyEntries old, AppProxyEntries e) { return AppProxyEntriesCompanion.custom(flags: old.flags.bitwiseOr(e.flags)); }), ); }); }); } @override Future applyAutoSelection({required Set autoList, required AppProxyMode mode}) { return transaction(() async { // removing all items that have only auto selection await (delete( appProxyEntries, )..where((tbl) => tbl.mode.equalsValue(mode) & tbl.flags.equals(PkgFlag.autoSelection.value))).go(); // removing auto selection flag from items final entriesToUpdate = await (db.select(db.appProxyEntries)..where((tbl) => tbl.mode.equalsValue(mode))).get(); if (entriesToUpdate.isNotEmpty) { final updatedCompanions = entriesToUpdate.map((entry) { return entry.copyWith(flags: PkgFlag.autoSelection.remove(entry.flags)).toCompanion(false); }).toList(); await db.batch((b) { b.replaceAll(db.appProxyEntries, updatedCompanions); }); } // adding auto selected if (autoList.isNotEmpty) { await db.batch((b) { b.insertAll( db.appProxyEntries, autoList.map( (pkg) => AppProxyEntriesCompanion.insert(mode: mode, pkgName: pkg, flags: Value(PkgFlag.autoSelection.add(0))), ), onConflict: DoUpdate((AppProxyEntries old) { return AppProxyEntriesCompanion.custom(flags: old.flags.bitwiseOr(Constant(PkgFlag.autoSelection.value))); }), ); }); } }); } @override Future clearAutoSelected({required AppProxyMode mode}) { return transaction(() async { // removing all items that have only auto selection await (delete( appProxyEntries, )..where((tbl) => tbl.mode.equalsValue(mode) & (appProxyEntries.flags.equals(PkgFlag.autoSelection.value)))).go(); // removing auto selection flag from items await (update(appProxyEntries)..where((tbl) => tbl.mode.equalsValue(mode))).write( AppProxyEntriesCompanion.custom( flags: appProxyEntries.flags.bitwiseAnd(Constant(~PkgFlag.autoSelection.value)), ), ); }); } @override Future revertForceDeselection({required AppProxyMode mode}) { return transaction(() async { // remove forceDeselection flag from flags await (update(appProxyEntries)..where((tbl) => tbl.mode.equalsValue(mode))).write( AppProxyEntriesCompanion.custom( flags: appProxyEntries.flags.bitwiseAnd(Constant(~PkgFlag.forceDeselection.value)), ), ); // romve extra items await (delete(appProxyEntries)..where((tbl) => tbl.mode.equalsValue(mode) & tbl.flags.equals(0))).go(); }); } @override Future clearAll({required AppProxyMode mode}) { return (delete(appProxyEntries)..where((tbl) => tbl.mode.equalsValue(mode))).go(); } } ================================================ FILE: lib/features/per_app_proxy/data/auto_selection_repository.dart ================================================ import 'package:dio/dio.dart'; import 'package:hiddify/core/http_client/dio_http_client.dart'; import 'package:hiddify/core/http_client/http_client_provider.dart'; import 'package:hiddify/core/model/region.dart'; import 'package:hiddify/core/preferences/general_preferences.dart'; import 'package:hiddify/features/per_app_proxy/model/per_app_proxy_mode.dart'; import 'package:hiddify/features/settings/data/config_option_repository.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; enum AutoSelectionResult { success, failure, notFound; bool isSuccess() => this == success; bool isFailure() => this == failure; bool isNotFound() => this == notFound; } abstract interface class AutoSelectionRepository { Future<(Set?, AutoSelectionResult)> getByAppProxyMode({AppProxyMode? mode, Region? region}); Future<(Set?, AutoSelectionResult)> getInclude({Region? region}); Future<(Set?, AutoSelectionResult)> getExclude({Region? region}); } class AutoSelectionRepositoryImpl with AppLogger implements AutoSelectionRepository { AutoSelectionRepositoryImpl({required Ref ref}) : _ref = ref; final Ref _ref; static const _baseUrl = 'https://raw.githubusercontent.com/hiddify/Android-GFW-Apps/refs/heads/master/'; @override Future<(Set?, AutoSelectionResult)> getByAppProxyMode({AppProxyMode? mode, Region? region}) async => await _makeRequest(mode: mode ?? _getMode(), region: region ?? _getRegion()); @override Future<(Set?, AutoSelectionResult)> getExclude({Region? region}) async => await _makeRequest(mode: AppProxyMode.exclude, region: region ?? _getRegion()); @override Future<(Set?, AutoSelectionResult)> getInclude({Region? region}) async => await _makeRequest(mode: AppProxyMode.include, region: region ?? _getRegion()); Future<(Set?, AutoSelectionResult)> _makeRequest({required AppProxyMode mode, Region? region}) async { try { final rs = await _getHttp().get(_genUrl(mode, region ?? _getRegion())); if (rs.statusCode == 200) { return (_parseToListOfString(rs.data), AutoSelectionResult.success); } loggy.error("Auto selection failed. status code : ${rs.statusCode}"); return (null, AutoSelectionResult.failure); } on DioException catch (e, st) { if (e.response?.statusCode == 404) { loggy.error("Auto selection region not found. region : ${region?.name ?? _getRegion().name}", e, st); return (null, AutoSelectionResult.notFound); } else { loggy.error("Failed to fetch auto selection", e, st); return (null, AutoSelectionResult.failure); } } catch (e, st) { loggy.error("Failed to fetch auto selection with unexpected error", e, st); return (null, AutoSelectionResult.failure); } } String _genUrl(AppProxyMode mode, Region region) => switch (mode) { AppProxyMode.include => '${_baseUrl}proxy_${region.name}', AppProxyMode.exclude => '${_baseUrl}direct_${region.name}', }; Set _parseToListOfString(dynamic data) => data.toString().split('\n').map((e) => e.trim()).where((element) => element.isNotEmpty).toSet(); AppProxyMode _getMode() => _ref.read(Preferences.perAppProxyMode).toAppProxy()!; Region _getRegion() => _ref.read(ConfigOptions.region); DioHttpClient _getHttp() => _ref.read(httpClientProvider); } ================================================ FILE: lib/features/per_app_proxy/data/auto_selection_repository_provider.dart ================================================ import 'package:hiddify/features/per_app_proxy/data/auto_selection_repository.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'auto_selection_repository_provider.g.dart'; @riverpod AutoSelectionRepository autoSelectionRepo(Ref ref) => AutoSelectionRepositoryImpl(ref: ref); ================================================ FILE: lib/features/per_app_proxy/data/selected_data_provider.dart ================================================ import 'package:hiddify/core/db/provider/db_providers.dart'; import 'package:hiddify/features/per_app_proxy/data/app_proxy_data_source.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'selected_data_provider.g.dart'; @riverpod AppProxyDataSource appProxyDataSource(Ref ref) => AppProxyDao(ref.watch(dbProvider)); ================================================ FILE: lib/features/per_app_proxy/model/app_package_info.dart ================================================ import 'dart:typed_data'; class AppPackageInfo { const AppPackageInfo({required this.packageName, required this.name, required this.icon}); final String packageName; final String name; final Uint8List? icon; } ================================================ FILE: lib/features/per_app_proxy/model/per_app_proxy_backup.dart ================================================ import 'package:freezed_annotation/freezed_annotation.dart'; part 'per_app_proxy_backup.freezed.dart'; part 'per_app_proxy_backup.g.dart'; @freezed abstract class PerAppProxyBackup with _$PerAppProxyBackup { const factory PerAppProxyBackup({ @Default(PerAppProxyBackupMode()) PerAppProxyBackupMode include, @Default(PerAppProxyBackupMode()) PerAppProxyBackupMode exclude, }) = _PerAppProxyBackup; factory PerAppProxyBackup.fromJson(Map json) => _$PerAppProxyBackupFromJson(json); } @freezed abstract class PerAppProxyBackupMode with _$PerAppProxyBackupMode { const factory PerAppProxyBackupMode({@Default([]) List selected, @Default([]) List deselected}) = _PerAppProxyBackupMode; factory PerAppProxyBackupMode.fromJson(Map json) => _$PerAppProxyBackupModeFromJson(json); } ================================================ FILE: lib/features/per_app_proxy/model/per_app_proxy_mode.dart ================================================ import 'package:hiddify/core/localization/translations.dart'; enum PerAppProxyMode { off, include, exclude; bool get enabled => this != off; ({String title, String message}) present(TranslationsEn t) => switch (this) { off => ( title: t.pages.settings.routing.perAppProxy.modes.all, message: t.pages.settings.routing.perAppProxy.modes.allMsg, ), include => ( title: t.pages.settings.routing.perAppProxy.modes.proxy, message: t.pages.settings.routing.perAppProxy.modes.proxyMsg, ), exclude => ( title: t.pages.settings.routing.perAppProxy.modes.bypass, message: t.pages.settings.routing.perAppProxy.modes.bypassMsg, ), }; AppProxyMode? toAppProxy() => switch (this) { PerAppProxyMode.off => null, PerAppProxyMode.include => AppProxyMode.include, PerAppProxyMode.exclude => AppProxyMode.exclude, }; } enum AppProxyMode { include, exclude; PerAppProxyMode toPerAppProxy() => switch (this) { AppProxyMode.include => PerAppProxyMode.include, AppProxyMode.exclude => PerAppProxyMode.exclude, }; ({String title, String message}) present(Translations t) => switch (this) { include => ( title: t.pages.settings.routing.perAppProxy.modes.proxy, message: t.pages.settings.routing.perAppProxy.modes.proxyMsg, ), exclude => ( title: t.pages.settings.routing.perAppProxy.modes.bypass, message: t.pages.settings.routing.perAppProxy.modes.bypassMsg, ), }; } ================================================ FILE: lib/features/per_app_proxy/model/pkg_flag.dart ================================================ enum PkgFlag { userSelection(1 << 0), forceDeselection(1 << 1), autoSelection(1 << 2); final int value; const PkgFlag(this.value); bool check(int value) => (value & this.value) == this.value; int add(int value) { int nValue = value; if (this == userSelection) { nValue = forceDeselection.remove(nValue); } else if (this == forceDeselection) { nValue = userSelection.remove(nValue); } return nValue | this.value; } int remove(int value) => value & ~this.value; int toggle(int value) => value ^ this.value; static bool? checkboxValue(int flag) => switch (flag) { _ when forceDeselection.check(flag) => false, _ when autoSelection.check(flag) && !userSelection.check(flag) => null, _ when userSelection.check(flag) => true, _ => null, }; } ================================================ FILE: lib/features/per_app_proxy/overview/per_app_proxy_loading_notifier.dart ================================================ import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'per_app_proxy_loading_notifier.g.dart'; @Riverpod() class AppProxyLoading extends _$AppProxyLoading { @override bool build() => false; Future doAsync(Future Function() operation) async { state = true; final T? result = await operation(); state = false; return result; } } ================================================ FILE: lib/features/per_app_proxy/overview/per_app_proxy_notifier.dart ================================================ import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'package:dartx/dartx_io.dart'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/services.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/model/region.dart'; import 'package:hiddify/core/notification/in_app_notification_controller.dart'; import 'package:hiddify/core/preferences/general_preferences.dart'; import 'package:hiddify/core/router/dialog/dialog_notifier.dart'; import 'package:hiddify/features/per_app_proxy/data/auto_selection_repository.dart'; import 'package:hiddify/features/per_app_proxy/data/auto_selection_repository_provider.dart'; import 'package:hiddify/features/per_app_proxy/data/selected_data_provider.dart'; import 'package:hiddify/features/per_app_proxy/model/per_app_proxy_backup.dart'; import 'package:hiddify/features/per_app_proxy/model/per_app_proxy_mode.dart'; import 'package:hiddify/features/per_app_proxy/model/pkg_flag.dart'; import 'package:hiddify/features/settings/data/config_option_repository.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:installed_apps/index.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'per_app_proxy_notifier.g.dart'; @riverpod class PerAppProxy extends _$PerAppProxy with AppLogger { late final AppProxyMode? _mode; @override Stream> build(AppProxyMode? mode) { _mode = mode; if (_mode == null) return Stream.value({}); final appsInfo = InstalledApps.getInstalledApps(false); return Stream.fromFuture(appsInfo).asyncExpand((appsInfo) { final phonePkgs = appsInfo.map((e) => e.packageName).toSet(); return ref.watch(appProxyDataSourceProvider).watchFilterForDisplay(phonePkgs: phonePkgs, mode: _mode).map(( entryList, ) { return {for (final entry in entryList) entry.pkgName: entry.flags}; }); }); } Future updatePkg(String pkg) async { loggy.info('Updationg $pkg status'); await ref.read(appProxyDataSourceProvider).updatePkg(pkg: pkg, mode: _mode!); } Future applyAutoSelection() async { loggy.info('Performming auto selection'); final t = ref.watch(translationsProvider).requireValue; final region = ref.watch(ConfigOptions.region); final rs = await ref.watch(autoSelectionRepoProvider).getByAppProxyMode(mode: _mode); switch (rs.$2) { case AutoSelectionResult.success: final autoList = rs.$1!; await ref.read(appProxyDataSourceProvider).applyAutoSelection(autoList: autoList, mode: _mode!); await ref.read(Preferences.autoAppsSelectionRegion.notifier).update(region); await ref.read(Preferences.autoAppsSelectionLastUpdate.notifier).update(DateTime.now()); return true; case AutoSelectionResult.failure: ref .read(inAppNotificationControllerProvider) .showErrorToast(t.pages.settings.routing.perAppProxy.autoSelection.toast.failure); return false; case AutoSelectionResult.notFound: ref .read(inAppNotificationControllerProvider) .showInfoToast( t.pages.settings.routing.perAppProxy.autoSelection.toast.regionNotFound( region: ref.watch(ConfigOptions.region).name, ), duration: const Duration(seconds: 5), ); return false; } } Future revertForceDeselection() async { loggy.info('Reverting force deselection'); await ref.read(appProxyDataSourceProvider).revertForceDeselection(mode: _mode!); } Future clearAutoSelected() async { loggy.info('Clearing auto selected'); await ref.read(appProxyDataSourceProvider).clearAutoSelected(mode: _mode!); await ref.watch(Preferences.autoAppsSelectionRegion.notifier).update(null); await ref.read(Preferences.autoAppsSelectionLastUpdate.notifier).update(null); } Future clearAll() async { loggy.info('Clearing all items'); await ref.read(appProxyDataSourceProvider).clearAll(mode: _mode!); await ref.watch(Preferences.autoAppsSelectionRegion.notifier).update(null); } Future importClipboard() async { final t = ref.read(translationsProvider).requireValue; try { final input = await Clipboard.getData(Clipboard.kTextPlain).then((value) => value?.text); await _importJson(input!); ref.read(inAppNotificationControllerProvider).showSuccessToast(t.common.msg.import.success); return true; } catch (e, st) { loggy.warning("error importing from clipboard", e, st); ref.read(inAppNotificationControllerProvider).showErrorToast(t.common.msg.import.failure); return false; } } Future importFile() async { final t = ref.read(translationsProvider).requireValue; try { final result = await FilePicker.platform.pickFiles(type: FileType.custom, allowedExtensions: ['json']); final file = File(result!.files.single.path!); if (!await file.exists()) throw Exception('File does not exist: path = ${file.path}'); final bytes = await file.readAsBytes(); await _importJson(jsonDecode(utf8.decode(bytes)).toString()); ref.read(inAppNotificationControllerProvider).showSuccessToast(t.common.msg.import.success); return true; } catch (e, st) { loggy.warning("error importing config options from json file", e, st); ref.read(inAppNotificationControllerProvider).showErrorToast(t.common.msg.import.failure); return false; } } Future exportClipboard() async { final t = ref.watch(translationsProvider).requireValue; try { final json = await _exportJson(); await Clipboard.setData(ClipboardData(text: json)); ref.read(inAppNotificationControllerProvider).showSuccessToast(t.common.msg.export.clipboard.success); return true; } on PlatformException { ref .read(inAppNotificationControllerProvider) .showInfoToast(t.common.msg.export.clipboard.contentTooLarge, duration: const Duration(seconds: 5)); return false; } catch (e, st) { loggy.warning("error exporting to clipboard", e, st); ref.read(inAppNotificationControllerProvider).showErrorToast(t.common.msg.export.clipboard.failure); return false; } } Future exportFile() async { final t = ref.watch(translationsProvider).requireValue; try { final json = await _exportJson(); final bytes = utf8.encode(jsonEncode(json)); final outputFile = await FilePicker.platform.saveFile( fileName: 'per-app proxy.json', type: FileType.custom, allowedExtensions: ['json'], bytes: bytes, ); if (outputFile == null) return false; if (PlatformUtils.isDesktop) { final file = File(outputFile); if (file.extension != '.json') return false; if (!await file.exists()) await file.parent.create(recursive: true); await file.writeAsBytes(bytes); } ref.read(inAppNotificationControllerProvider).showSuccessToast(t.common.msg.export.file.success); return true; } catch (e, st) { loggy.warning("error exporting config options to json file", e, st); ref.read(inAppNotificationControllerProvider).showErrorToast(t.common.msg.export.file.failure); return false; } } Future shareOnGithub() async { final t = ref.watch(translationsProvider).requireValue; final region = ref.watch(ConfigOptions.region); final mode = ref.watch(Preferences.perAppProxyMode).toAppProxy()!; assert(region != Region.other); final rs = await ref.read(autoSelectionRepoProvider).getByAppProxyMode(mode: mode, region: region); if (rs.$2 != AutoSelectionResult.success) return false; final autoList = rs.$1!; final userSelected = (await ref.read(appProxyDataSourceProvider).getPkgsByFlag(mode: mode, flag: PkgFlag.userSelection)) ..removeWhere((pkg) => autoList.contains(pkg)); final forceDeselected = (await ref.read(appProxyDataSourceProvider).getPkgsByFlag(mode: mode, flag: PkgFlag.forceDeselection)) ..removeWhere((pkg) => !autoList.contains(pkg)); if (userSelected.isNotEmpty || forceDeselected.isNotEmpty) { final agree = await ref .read(dialogNotifierProvider.notifier) .showConfirmation( title: t.dialogs.confirmation.perAppProxy.shareOnGithub.title, message: t.dialogs.confirmation.perAppProxy.shareOnGithub.msg, positiveBtnTxt: t.common.kContinue, ); if (agree != true) return false; final title = '${region.name} | ${mode.present(t).title}'; var body = const JsonEncoder.withIndent( ' ', ).convert({'addedPkgs': userSelected.toList(), 'removedPkgs': forceDeselected.toList()}); body = '```\n$body\n```'; UriUtils.tryLaunch(Uri.parse('https://github.com/hiddify/Android-GFW-Apps/issues/new?title=$title&body=$body')); return true; } else { ref .read(inAppNotificationControllerProvider) .showInfoToast( t.pages.settings.routing.perAppProxy.autoSelection.toast.alreadyInAuto, duration: const Duration(seconds: 5), ); return false; } } Future _importJson(String input) async { final backup = PerAppProxyBackup.fromJson((jsonDecode(input) as Map).cast()); await ref.read(appProxyDataSourceProvider).importPkgs(backup: backup); } Future _exportJson() async { final ds = ref.read(appProxyDataSourceProvider); final backup = PerAppProxyBackup( include: PerAppProxyBackupMode( selected: await ds.getPkgsByFlag(mode: AppProxyMode.include, flag: PkgFlag.userSelection), deselected: await ds.getPkgsByFlag(mode: AppProxyMode.include, flag: PkgFlag.forceDeselection), ), exclude: PerAppProxyBackupMode( selected: await ds.getPkgsByFlag(mode: AppProxyMode.exclude, flag: PkgFlag.userSelection), deselected: await ds.getPkgsByFlag(mode: AppProxyMode.exclude, flag: PkgFlag.forceDeselection), ), ); return const JsonEncoder.withIndent(' ').convert(backup.toJson()); } } ================================================ FILE: lib/features/per_app_proxy/overview/per_app_proxy_page.dart ================================================ import 'package:dartx/dartx.dart'; import 'package:fluentui_system_icons/fluentui_system_icons.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:gap/gap.dart'; import 'package:go_router/go_router.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/model/region.dart'; import 'package:hiddify/core/preferences/general_preferences.dart'; import 'package:hiddify/core/router/bottom_sheets/bottom_sheets_notifier.dart'; import 'package:hiddify/core/router/dialog/dialog_notifier.dart'; import 'package:hiddify/features/per_app_proxy/model/app_package_info.dart'; import 'package:hiddify/features/per_app_proxy/model/per_app_proxy_mode.dart'; import 'package:hiddify/features/per_app_proxy/model/pkg_flag.dart'; import 'package:hiddify/features/per_app_proxy/overview/per_app_proxy_loading_notifier.dart'; import 'package:hiddify/features/per_app_proxy/overview/per_app_proxy_notifier.dart'; import 'package:hiddify/features/settings/data/config_option_repository.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:installed_apps/index.dart'; class PerAppProxyPage extends HookConsumerWidget with PresLogger { const PerAppProxyPage({super.key}); int _getPriority(AppPackageInfo app, Map selected) { final flag = selected[app.packageName]; if (flag == null) return 4; if (PkgFlag.userSelection.check(flag)) { return 1; } else if (PkgFlag.autoSelection.check(flag) && !PkgFlag.forceDeselection.check(flag)) { return 2; } else { return 3; } } Future> getApps(bool hideSystem) async { if (!PlatformUtils.isAndroid) return {}; return (await InstalledApps.getInstalledApps( hideSystem, true, )).map((e) => AppPackageInfo(packageName: e.packageName, name: e.name, icon: e.icon)).toSet(); } @override Widget build(BuildContext context, WidgetRef ref) { final theme = Theme.of(context); final t = ref.watch(translationsProvider).requireValue; final localizations = MaterialLocalizations.of(context); final mode = ref.watch(Preferences.perAppProxyMode).toAppProxy(); final selectedApps = ref.watch(PerAppProxyProvider(mode)); final hideSystemApps = useState(false); final isSearching = useState(false); final searchQuery = useState(""); final sortListener = useState(false); final asyncApps = useFuture(useMemoized(() => getApps(false))); final asyncAppsHideSys = useFuture(useMemoized(() => getApps(true))); final asyncFilteredApps = hideSystemApps.value ? asyncAppsHideSys : asyncApps; final displayedApps = useMemoized>>( () { if (!(selectedApps.hasValue && selectedApps is AsyncData && asyncFilteredApps.hasData && asyncFilteredApps.connectionState == ConnectionState.done)) return const AsyncValue.loading(); final appsList = asyncFilteredApps.requireData.toList(); if (searchQuery.value.isBlank) { appsList.sort((a, b) { final priorityA = _getPriority(a, selectedApps.requireValue); final priorityB = _getPriority(b, selectedApps.requireValue); return priorityA.compareTo(priorityB); }); return AsyncValue.data(appsList); } final filteredAppsList = appsList .filter((e) => e.name.toLowerCase().contains(searchQuery.value.toLowerCase())) .toList(); return AsyncValue.data(filteredAppsList); }, [ asyncFilteredApps.connectionState == ConnectionState.done, hideSystemApps.value, selectedApps.hasValue, searchQuery.value, sortListener.value, ], ); if (mode != null) { ref.listen(PerAppProxyProvider(mode), (previous, next) { if (previous != null) { if ((previous, next) case (AsyncData(value: final prevData), AsyncData(value: final nextData))) { if (nextData.isNotEmpty) { if ((nextData.length - prevData.length).abs() > 1) sortListener.value = !sortListener.value; } } } }); } final scrollController = useScrollController(); const double scrollThreshold = 300.0; final showScrollToTop = useState(false); useEffect(() { void listener() { showScrollToTop.value = scrollController.offset > scrollThreshold; } scrollController.addListener(listener); return () => scrollController.removeListener(listener); }, []); useEffect(() { showScrollToTop.value = false; return null; }, [displayedApps]); return Scaffold( appBar: isSearching.value ? AppBar( title: TextFormField( onChanged: (value) => searchQuery.value = value, autofocus: true, decoration: InputDecoration( hintText: "${localizations.searchFieldLabel}...", isDense: true, filled: false, border: InputBorder.none, focusedBorder: InputBorder.none, enabledBorder: InputBorder.none, errorBorder: InputBorder.none, focusedErrorBorder: InputBorder.none, disabledBorder: InputBorder.none, ), ), leading: IconButton( onPressed: () { searchQuery.value = ""; isSearching.value = false; }, icon: const Icon(Icons.close), tooltip: localizations.cancelButtonLabel, ), ) : AppBar( title: Text(t.pages.settings.routing.perAppProxy.title), actions: [ IconButton( icon: const Icon(FluentIcons.search_24_regular), onPressed: () => isSearching.value = true, tooltip: localizations.searchFieldLabel, ), MenuAnchor( menuChildren: [ SubmenuButton( menuChildren: [ MenuItemButton( child: Text(t.pages.settings.routing.perAppProxy.options.import.clipboard), onPressed: () async => await ref .read(dialogNotifierProvider.notifier) .showConfirmation( title: t.common.msg.import.confirm, message: t.dialogs.confirmation.perAppProxy.import.msg, ) .then((shouldImport) async { if (shouldImport) await ref.read(PerAppProxyProvider(mode).notifier).importClipboard(); }), ), MenuItemButton( child: Text(t.pages.settings.routing.perAppProxy.options.import.file), onPressed: () async => await ref .read(dialogNotifierProvider.notifier) .showConfirmation( title: t.pages.settings.routing.perAppProxy.options.import.file, message: t.pages.settings.routing.perAppProxy.options.import.msg, ) .then((shouldImport) async { if (shouldImport) await ref.read(PerAppProxyProvider(mode).notifier).importFile(); }), ), ], child: Text(t.common.import), ), SubmenuButton( menuChildren: [ MenuItemButton( child: Text(t.pages.settings.routing.perAppProxy.options.export.clipboard), onPressed: () async => await ref.read(PerAppProxyProvider(mode).notifier).exportClipboard(), ), MenuItemButton( child: Text(t.pages.settings.routing.perAppProxy.options.export.file), onPressed: () async => await ref.read(PerAppProxyProvider(mode).notifier).exportFile(), ), ], child: Text(t.common.export), ), if (ref.watch(ConfigOptions.region) != Region.other) MenuItemButton( child: Text(t.pages.settings.routing.perAppProxy.options.shareToAll), onPressed: () async => await ref .read(appProxyLoadingProvider.notifier) .doAsync(ref.read(PerAppProxyProvider(mode).notifier).shareOnGithub), ), const PopupMenuDivider(), MenuItemButton( child: Text(t.pages.settings.routing.perAppProxy.options.clearAllSelections), onPressed: () => ref.read(PerAppProxyProvider(mode).notifier).clearAll(), ), ], builder: (context, controller, child) => AnimatedSwitcher( duration: const Duration(milliseconds: 300), child: ref.watch(appProxyLoadingProvider) ? const Padding( padding: EdgeInsets.all(8), child: SizedBox(width: 32, height: 32, child: CircularProgressIndicator()), ) : IconButton( onPressed: () { if (controller.isOpen) { controller.close(); } else { controller.open(); } }, icon: const Icon(Icons.more_vert_rounded), ), ), ), ], bottom: PreferredSize( preferredSize: const Size.fromHeight(48), child: Expanded( child: ListView( scrollDirection: Axis.horizontal, padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), children: [ PopupMenuButton( borderRadius: BorderRadius.circular(8), position: PopupMenuPosition.under, tooltip: (mode?.toPerAppProxy() ?? PerAppProxyMode.off).present(t).message, initialValue: mode?.toPerAppProxy() ?? PerAppProxyMode.off, onSelected: (e) async { if (ref.read(Preferences.autoAppsSelectionRegion) != null) await ref.read(PerAppProxyProvider(mode).notifier).clearAutoSelected(); if (e == PerAppProxyMode.off && context.mounted) context.pop(); await ref.read(Preferences.perAppProxyMode.notifier).update(e); }, itemBuilder: (context) => PerAppProxyMode.values .map((e) => PopupMenuItem(value: e, child: Text(e.present(t).message))) .toList(), child: Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(8), color: theme.colorScheme.surface, border: Border.all(color: theme.colorScheme.outlineVariant), ), child: Row( children: [ const Gap(16), Text(mode?.present(t).title ?? ''), const Gap(4), Icon(Icons.arrow_drop_down_rounded, color: theme.colorScheme.onSurfaceVariant), const Gap(8), ], ), ), ), const Gap(8), ChoiceChip( label: Text(t.pages.settings.routing.perAppProxy.hideSysApps), selected: hideSystemApps.value, onSelected: (value) => hideSystemApps.value = value, ), ], ), ), ), ), floatingActionButton: showScrollToTop.value ? FloatingActionButton( onPressed: () => scrollController.animateTo(0.0, duration: const Duration(milliseconds: 500), curve: Curves.easeOut), child: const Icon(Icons.keyboard_arrow_up_rounded), ) : (ref.watch(ConfigOptions.region) != Region.other) ? FloatingActionButton.extended( onPressed: () async => await ref.read(bottomSheetsNotifierProvider.notifier).showAutoAppsSelection(mode: mode!), label: Text(t.pages.settings.routing.perAppProxy.autoSelection.title), icon: Icon( ref.watch(Preferences.autoAppsSelectionRegion) == null ? Icons.toggle_off_outlined : Icons.toggle_on_rounded, ), ) : null, body: displayedApps.when( data: (packages) => ListView.builder( padding: const EdgeInsets.only(bottom: 88), controller: scrollController, itemBuilder: (context, index) { final package = packages[index]; final flag = selectedApps.requireValue[package.packageName]; return CheckboxListTile.adaptive( title: Row( children: [ Flexible(child: Text(package.name, maxLines: 1, overflow: TextOverflow.ellipsis)), if (flag != null && PkgFlag.forceDeselection.check(flag)) ...[ const Gap(6), Container( width: 6, height: 6, decoration: BoxDecoration(color: theme.colorScheme.error, shape: BoxShape.circle), ), ], ], ), subtitle: Text( package.packageName, style: Theme.of(context).textTheme.bodySmall, maxLines: 1, overflow: TextOverflow.ellipsis, ), value: flag == null ? false : PkgFlag.checkboxValue(flag), tristate: true, onChanged: (_) => ref.read(PerAppProxyProvider(mode).notifier).updatePkg(package.packageName), secondary: package.icon == null ? null : Image.memory(package.icon!, width: 48, height: 48, cacheWidth: 48, cacheHeight: 48), ); }, itemCount: packages.length, ), error: (error, _) => SliverErrorBodyPlaceholder(error.toString()), loading: () => const Center(child: CircularProgressIndicator()), ), ); } } ================================================ FILE: lib/features/per_app_proxy/overview/per_app_proxy_service_notifier.dart ================================================ import 'dart:async'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/notification/in_app_notification_controller.dart'; import 'package:hiddify/core/preferences/general_preferences.dart'; import 'package:hiddify/features/per_app_proxy/data/selected_data_provider.dart'; import 'package:hiddify/features/per_app_proxy/model/per_app_proxy_mode.dart'; import 'package:hiddify/features/per_app_proxy/overview/per_app_proxy_notifier.dart'; import 'package:installed_apps/index.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'per_app_proxy_service_notifier.g.dart'; @riverpod class PerAppProxyService extends _$PerAppProxyService { StreamSubscription? _includeSubscription; StreamSubscription? _excludeSubscription; Timer? _timer; @override Future build() async { final phonePkgs = (await InstalledApps.getInstalledApps(false)).map((e) => e.packageName).toSet(); _includeSubscription = ref .read(appProxyDataSourceProvider) .watchActivePackages(phonePkgs: phonePkgs, mode: AppProxyMode.include) .listen((pkgs) => ref.read(Preferences.includeApps.notifier).update(pkgs)); _excludeSubscription = ref .read(appProxyDataSourceProvider) .watchActivePackages(phonePkgs: phonePkgs, mode: AppProxyMode.exclude) .listen((pkgs) => ref.read(Preferences.excludeApps.notifier).update(pkgs)); _timer = Timer.periodic(const Duration(days: 1), (_) async => await _autoSelectionUpdate()); ref.onDispose(() { _includeSubscription?.cancel(); _excludeSubscription?.cancel(); _timer?.cancel(); }); await _autoSelectionUpdate(); } Future _autoSelectionUpdate() async { final autoRegion = ref.read(Preferences.autoAppsSelectionRegion); if (autoRegion == null) return; final mode = ref.read(Preferences.perAppProxyMode).toAppProxy(); final lastUpdate = ref.read(Preferences.autoAppsSelectionLastUpdate); final days = ref.read(Preferences.autoAppsSelectionUpdateInterval).round(); final interval = Duration(days: days); if (mode != null && (lastUpdate == null || DateTime.now().difference(lastUpdate) > interval)) { final rs = await ref.read(PerAppProxyProvider(mode).notifier).applyAutoSelection(); if (rs) { final t = ref.read(translationsProvider).requireValue; ref .read(inAppNotificationControllerProvider) .showSuccessToast(t.pages.settings.routing.perAppProxy.autoSelection.toast.success); } } } } ================================================ FILE: lib/features/platform_specific/android_quick_settings_tile.dart ================================================ // void setTileLable(Tile tile, WidgetRef ref) { // final t = ref.watch(translationsProvider).requireValue; // final connectionStatus = ref.watch(connectionNotifierProvider); // final activeProfile = ref.watch(activeProfileProvider).valueOrNull; // tile.label = activeProfile?.name ?? t.general.appTitle; // final activeProxy = ref.watch(activeProxyNotifierProvider); // final delay = activeProxy.valueOrNull?.urlTestDelay ?? 0; // tile.subtitle = switch (connectionStatus) { // AsyncData(value: Connected()) when delay <= 0 || delay >= 65000 => t.connection.connecting, // AsyncData(value: Connected()) => "{delay}ms", // _ => "", // }; // } // // QuickSettings setup // void setupQuickSettings(WidgetRef ref) { // final connectionStatus = ref.read(connectionNotifierProvider.notifier); // @pragma("vm:entry-point") // Tile onTileClicked(Tile tile) { // final t = ref.read(translationsProvider).requireValue; // final oldStatus = tile.tileStatus; // connectionStatus.toggleConnection(); // if (oldStatus == TileStatus.active) { // tile.tileStatus = TileStatus.inactive; // tile.subtitle = t.connection.disconnecting; // } else { // tile.tileStatus = TileStatus.active; // tile.subtitle = t.connection.connecting; // } // return tile; // } // @pragma("vm:entry-point") // Tile onTileAdded(Tile tile) { // setTileLable(tile, ref); // return tile; // } // @pragma("vm:entry-point") // void onTileRemoved() { // print("Tile removed"); // } // QuickSettings.setup( // onTileClicked: onTileClicked, // onTileAdded: onTileAdded, // onTileRemoved: onTileRemoved, // ); // final t = ref.read(translationsProvider).requireValue; // QuickSettings.addTileToQuickSettings(label: t.general.appTitle, drawableName: "ic_launcher"); // } ================================================ FILE: lib/features/profile/add/add_profile_modal.dart ================================================ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:gap/gap.dart'; import 'package:go_router/go_router.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/model/constants.dart'; import 'package:hiddify/features/profile/add/widgets/free_btns.dart'; import 'package:hiddify/features/profile/add/widgets/widgets.dart'; import 'package:hiddify/features/profile/model/profile_entity.dart'; import 'package:hiddify/features/profile/notifier/profile_notifier.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class AddProfileModal extends HookConsumerWidget { const AddProfileModal({super.key, this.url}); // static const warpConsentGiven = "warp_consent_given"; final String? url; @override Widget build(BuildContext context, WidgetRef ref) { final isLoading = ref.watch(addProfileNotifierProvider).isLoading; final currentWidget = ref.watch(addProfilePageNotifierProvider); ref.listen(freeSwitchNotifierProvider, (_, _) {}); ref.listen(addProfileNotifierProvider, (previous, next) { if (next case AsyncData(value: final _?)) { WidgetsBinding.instance.addPostFrameCallback((_) { if (context.mounted && context.canPop()) context.pop(); }); } }); useMemoized(() async { await Future.delayed(const Duration(milliseconds: 200)); if (url != null && context.mounted) { if (isLoading) return; ref.read(addProfileNotifierProvider.notifier).addClipboard(url!); } }); return SafeArea( child: isLoading ? const ProfileLoading() : switch (currentWidget) { AddProfilePages.options => const AddProfileOptions(), AddProfilePages.manual => const AddProfileManual(), }, ); } } class AddProfileOptions extends HookConsumerWidget { const AddProfileOptions({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { // final isLoadingProfile = ref.watch(addProfileNotifierProvider).isLoading; final freeSwitch = ref.watch(freeSwitchNotifierProvider); final isDesktop = PlatformUtils.isDesktop; return LayoutBuilder( builder: (context, constraints) { final fixBtnsHeight = (constraints.maxWidth - AddProfileModalConst.fixBtnsGap * AddProfileModalConst.fixBtnsGapCount) / AddProfileModalConst.fixBtnsItemCount; final fullHeight = fixBtnsHeight + AddProfileModalConst.navBarHeight + 32; final initial = !freeSwitch ? fullHeight : fullHeight + 180; var min = !freeSwitch ? fullHeight : fullHeight + 100; var max = !freeSwitch ? fullHeight / constraints.maxHeight : 0.85; if (isDesktop) { min = initial; max = initial / constraints.maxHeight; } return DraggableScrollableSheet( initialChildSize: initial / constraints.maxHeight, minChildSize: min / constraints.maxHeight, maxChildSize: max, expand: false, builder: (context, scrollController) => Column( children: [ const Gap(AddProfileModalConst.fixBtnsGap), FixBtns(height: fixBtnsHeight), if (freeSwitch) Expanded(child: FreeBtns(scrollController: scrollController)) else const Spacer(), const NavBar(), ], ), ); }, ); } } class AddProfileManual extends HookConsumerWidget { const AddProfileManual({super.key}); String _genSliderText(Translations t, int sliderValue) { if (sliderValue == 0) { return t.common.auto; } else if (sliderValue < 24) { return t.common.interval.hour(n: sliderValue); } final day = t.common.interval.day(n: sliderValue ~/ 24); final hour = t.common.interval.hour(n: sliderValue % 24); return '$day $hour'; } @override Widget build(BuildContext context, WidgetRef ref) { final theme = Theme.of(context); final t = ref.watch(translationsProvider).requireValue; final formKey = useMemoized(() => GlobalKey()); final nameTextController = useTextEditingController(); final urlTextController = useTextEditingController(); final isAutoUpdateDisable = useState(false); final updateInterval = useState(.0); final sliderFocusNode = useFocusNode( onKeyEvent: (node, event) { if (KeyboardConst.verticalArrows.contains(event.logicalKey) && event is KeyDownEvent) { if (event.logicalKey == LogicalKeyboardKey.arrowUp) { node.previousFocus(); } else { node.nextFocus(); } return KeyEventResult.handled; } return KeyEventResult.ignored; }, ); return Form( key: formKey, child: Column( mainAxisSize: MainAxisSize.min, children: [ Padding( padding: const EdgeInsetsDirectional.fromSTEB(16, 8, 8, 12), child: Row( children: [ Expanded(child: Text(t.common.manually, style: theme.textTheme.headlineMedium)), IconButton( icon: const Icon(Icons.close), onPressed: () => ref.read(addProfilePageNotifierProvider.notifier).goOptions(), ), ], ), ), Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: CustomTextFormField( maxLines: 1, controller: nameTextController, validator: (value) => (value?.isEmpty ?? true) ? t.pages.profileDetails.form.emptyName : null, label: t.common.name, hint: t.pages.profileDetails.form.nameHint, ), ), const Gap(16), Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: CustomTextFormField( maxLines: 1, controller: urlTextController, validator: (value) => (value != null && !isUrl(value)) ? t.pages.profileDetails.form.invalidUrl : null, label: t.common.url, hint: t.pages.profileDetails.form.urlHint, ), ), const Gap(12), SwitchListTile.adaptive( title: Text( t.pages.profileDetails.form.disableAutoUpdate, style: theme.textTheme.titleSmall!.copyWith(color: theme.colorScheme.onSurface), ), value: isAutoUpdateDisable.value, onChanged: (value) => isAutoUpdateDisable.value = value, ), AnimatedSize( alignment: Alignment.topCenter, duration: const Duration(milliseconds: 300), curve: Curves.easeInOut, child: !isAutoUpdateDisable.value ? Column( children: [ const Divider(indent: 16, endIndent: 16), const Gap(12), Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: Row( children: [ Expanded( child: Text( t.pages.profileDetails.form.autoUpdateInterval, style: theme.textTheme.titleSmall!.copyWith(color: theme.colorScheme.onSurface), ), ), Text( _genSliderText(t, updateInterval.value.round()), style: theme.textTheme.labelSmall!.copyWith(color: theme.colorScheme.onSurfaceVariant), ), ], ), ), const Gap(4), Padding( padding: const EdgeInsets.symmetric(horizontal: 10), child: Slider( focusNode: sliderFocusNode, value: updateInterval.value, max: 96, divisions: 96, label: updateInterval.value.round().toString(), onChanged: (double value) => updateInterval.value = value, ), ), ], ) : const SizedBox.shrink(), ), Padding( padding: const EdgeInsets.symmetric(horizontal: 16).copyWith(bottom: 8), child: Row( children: [ Expanded( child: FilledButton( child: Text(t.common.add), onPressed: () async { if (formKey.currentState!.validate()) { final i = updateInterval.value.toInt(); final interval = i > 0 ? i : null; await ref .read(addProfileNotifierProvider.notifier) .addManual( url: urlTextController.text.trim(), userOverride: UserOverride( name: nameTextController.text.trim(), isAutoUpdateDisable: isAutoUpdateDisable.value, updateInterval: interval, ), ); } }, ), ), ], ), ), // const Gap(16), ], ), ); } } ================================================ FILE: lib/features/profile/add/model/free_profiles_model.dart ================================================ // This file is "main.dart" import 'package:flutter/foundation.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; part 'free_profiles_model.freezed.dart'; part 'free_profiles_model.g.dart'; @freezed class FreeProfilesModel with _$FreeProfilesModel { const factory FreeProfilesModel({required List profiles}) = _FreeProfilesModel; factory FreeProfilesModel.fromJson(Map json) => _$FreeProfilesModelFromJson(json); } @freezed class FreeProfile with _$FreeProfile { const factory FreeProfile({ required List region, required StringByLocale title, required String sublink, required ListOfStringByLocale tags, required StringByLocale consent, @JsonKey(name: 'needed_features') List? neededFeatures, }) = _FreeProfile; factory FreeProfile.fromJson(Map json) => _$FreeProfileFromJson(json); } @freezed class StringByLocale with _$StringByLocale { const factory StringByLocale({required String en, required String fa}) = _StringByLocale; factory StringByLocale.fromJson(Map json) => _$StringByLocaleFromJson(json); } @freezed class ListOfStringByLocale with _$ListOfStringByLocale { const factory ListOfStringByLocale({required List en, required List fa}) = _ListOfStringByLocale; factory ListOfStringByLocale.fromJson(Map json) => _$ListOfStringByLocaleFromJson(json); } ================================================ FILE: lib/features/profile/add/widgets/fix_btn.dart ================================================ import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; import 'package:hiddify/core/router/go_router/helper/active_breakpoint_notifier.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class FixBtn extends ConsumerWidget { const FixBtn({super.key, required this.height, required this.title, required this.icon, required this.onTap}); final double height; final String title; final IconData icon; final GestureTapCallback onTap; @override Widget build(BuildContext context, WidgetRef ref) { final theme = Theme.of(context); final isMobile = Breakpoint(context).isMobile(); final color = theme.colorScheme.primary; final borderRadius = BorderRadius.circular(18); return Expanded( child: InkWell( onTap: onTap, borderRadius: borderRadius, child: Container( alignment: Alignment.center, height: height, padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 4), decoration: BoxDecoration( borderRadius: borderRadius, border: Border.all(color: theme.colorScheme.outlineVariant), ), child: Column( mainAxisSize: MainAxisSize.min, children: [ Icon(icon, size: isMobile ? 32 : 40, color: color), Gap(isMobile ? 4 : 8), Text( title, style: isMobile ? theme.textTheme.titleSmall!.copyWith(color: color) : theme.textTheme.titleMedium!.copyWith(color: color), overflow: TextOverflow.ellipsis, maxLines: 1, ), ], ), ), ), ); } } ================================================ FILE: lib/features/profile/add/widgets/fix_btns.dart ================================================ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:gap/gap.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/model/constants.dart'; import 'package:hiddify/core/router/dialog/dialog_notifier.dart'; import 'package:hiddify/features/profile/add/widgets/widgets.dart'; import 'package:hiddify/features/profile/notifier/profile_notifier.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class FixBtns extends ConsumerWidget { const FixBtns({super.key, required this.height}); final double height; @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; final isDesktop = PlatformUtils.isDesktop; return Row( children: [ if (!isDesktop) ...[ const Gap(AddProfileModalConst.fixBtnsGap), FixBtn( key: const ValueKey('add_by_qr_code_button'), height: height, title: t.common.scanQr, icon: Icons.qr_code_scanner, onTap: () async { final cr = await ref.read(dialogNotifierProvider.notifier).showQrScanner(); if (cr == null) return; ref.read(addProfileNotifierProvider.notifier).addClipboard(cr); }, ), ], const Gap(AddProfileModalConst.fixBtnsGap), FixBtn( key: const ValueKey('add_from_clipboard_button'), height: height, title: t.common.clipboard, icon: Icons.content_paste, onTap: () async { final cr = await Clipboard.getData(Clipboard.kTextPlain).then((value) => value?.text ?? ''); ref.read(addProfileNotifierProvider.notifier).addClipboard(cr); }, ), const Gap(AddProfileModalConst.fixBtnsGap), FixBtn( key: const ValueKey('add_manually_button'), height: height, title: t.common.manually, icon: Icons.add, onTap: () { ref.read(addProfilePageNotifierProvider.notifier).goManual(); }, ), const Gap(AddProfileModalConst.fixBtnsGap), ], ); } } ================================================ FILE: lib/features/profile/add/widgets/free_btn.dart ================================================ import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; import 'package:hiddify/core/localization/locale_preferences.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/features/common/custom_text_scroll.dart'; import 'package:hiddify/features/profile/add/model/free_profiles_model.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class FreeBtn extends ConsumerWidget { const FreeBtn({super.key, required this.freeProfile, required this.onTap}); final FreeProfile freeProfile; final GestureTapCallback onTap; @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; final locale = ref.watch(localePreferencesProvider); final isFa = locale.name == AppLocale.fa.name; final theme = Theme.of(context); final borderRadius = BorderRadius.circular(18); return Material( borderRadius: borderRadius, child: InkWell( onTap: onTap, borderRadius: borderRadius, child: Container( height: 72, padding: const EdgeInsets.all(10), decoration: BoxDecoration( borderRadius: borderRadius, border: Border.all(color: theme.colorScheme.outlineVariant), ), child: Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ Expanded( child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( child: Text( isFa ? freeProfile.title.fa : freeProfile.title.en, maxLines: 1, overflow: TextOverflow.ellipsis, style: theme.textTheme.titleSmall!.copyWith(color: theme.colorScheme.onSurface), ), ), if (freeProfile.neededFeatures != null) Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ if (freeProfile.neededFeatures!.contains('warp_over_proxies')) Feature(title: t.common.warp, icon: Icons.add_moderator), if (freeProfile.neededFeatures!.contains('fragment')) Feature(title: t.common.fragment, icon: Icons.content_cut), ], ), ], ), ), CustomTextScroll( isFa ? freeProfile.tags.fa.join(' · ') : freeProfile.tags.en.join(' · '), style: theme.textTheme.labelMedium!.copyWith(color: theme.colorScheme.onSurfaceVariant), ), ], ), ), ), ); } } class Feature extends ConsumerWidget { const Feature({super.key, required this.title, required this.icon}); final String title; final IconData icon; @override Widget build(BuildContext context, WidgetRef ref) { final theme = Theme.of(context); final color = theme.colorScheme.primary; return Row( children: [ Icon(icon, size: 12, color: theme.colorScheme.primary), const Gap(4), Text(title, style: theme.textTheme.labelSmall!.copyWith(color: color)), ], ); } } ================================================ FILE: lib/features/profile/add/widgets/free_btns.dart ================================================ import 'package:flutter/material.dart'; import 'package:hiddify/core/localization/locale_preferences.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/model/constants.dart'; import 'package:hiddify/core/router/dialog/dialog_notifier.dart'; import 'package:hiddify/features/profile/add/widgets/free_btn.dart'; import 'package:hiddify/features/profile/model/profile_entity.dart'; import 'package:hiddify/features/profile/notifier/profile_notifier.dart'; import 'package:hiddify/features/settings/data/config_option_repository.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class FreeBtns extends ConsumerWidget { const FreeBtns({super.key, required this.scrollController}); final ScrollController scrollController; @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; final freeProfiles = ref.watch(freeProfilesNotifierProvider); final freeProfilesFilteredByRegion = ref.watch(freeProfilesFilteredByRegionProvider); final theme = Theme.of(context); final locale = ref.watch(localePreferencesProvider); final isFa = locale.name == AppLocale.fa.name; return freeProfilesFilteredByRegion.when( data: (data) => data.isNotEmpty ? ScrollConfiguration( behavior: ScrollConfiguration.of(context).copyWith(scrollbars: false), child: GridView.builder( controller: scrollController, padding: const EdgeInsets.all(16).copyWith(bottom: 0), itemCount: freeProfiles.value!.length, gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: MediaQuery.of(context).size.width <= BottomSheetConst.maxWidth || freeProfiles.value!.length < 2 ? 1 : 2, mainAxisSpacing: 8, crossAxisSpacing: 8, mainAxisExtent: 72, ), itemBuilder: (context, index) { final profile = freeProfiles.value![index]; return FreeBtn( freeProfile: profile, onTap: () async { final title = isFa ? profile.title.fa : profile.title.en; final consent = isFa ? profile.consent.fa : profile.consent.en; final result = await ref .read(dialogNotifierProvider.notifier) .showFreeProfileConsent(title: title, consent: consent); if (result == true) { await ref .read(addProfileNotifierProvider.notifier) .addManual( url: profile.sublink, userOverride: UserOverride( name: title, updateInterval: 12, enableWarp: profile.neededFeatures?.contains('warp_over_proxies'), enableFragment: profile.neededFeatures?.contains('fragment'), ), ); } }, ); }, ), ) : Center( child: Text( (freeProfiles.value?.isEmpty ?? true) ? t.pages.profiles.freeSubNotFound : t.pages.profiles.freeSubNotFoundForRegion(region: ref.watch(ConfigOptions.region).name), style: theme.textTheme.bodySmall!.copyWith(color: theme.colorScheme.onSurface), ), ), error: (error, stackTrace) => Center( child: Text( t.pages.profiles.failedToLoad, style: theme.textTheme.bodyMedium!.copyWith(color: theme.colorScheme.onSurface), ), ), loading: () => const Center(child: CircularProgressIndicator()), ); } } ================================================ FILE: lib/features/profile/add/widgets/loading.dart ================================================ import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/features/profile/notifier/profile_notifier.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class ProfileLoading extends ConsumerWidget { const ProfileLoading({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; final theme = Theme.of(context); return Padding( padding: const EdgeInsets.symmetric(horizontal: 64, vertical: 64), child: Column( mainAxisSize: MainAxisSize.min, children: [ Text( t.pages.profileDetails.form.loading, style: theme.textTheme.bodyMedium!.copyWith(color: theme.colorScheme.onSurface), ), const Gap(20), const LinearProgressIndicator(backgroundColor: Colors.transparent), const Gap(8), TextButton( onPressed: () { ref.invalidate(addProfileNotifierProvider); }, child: Text(MaterialLocalizations.of(context).cancelButtonLabel), ), ], ), ); } } ================================================ FILE: lib/features/profile/add/widgets/nav_bar.dart ================================================ import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/model/constants.dart'; import 'package:hiddify/core/router/dialog/dialog_notifier.dart'; import 'package:hiddify/features/profile/notifier/profile_notifier.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class NavBar extends ConsumerWidget { const NavBar({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final theme = Theme.of(context); final t = ref.watch(translationsProvider).requireValue; final freeSwitch = ref.watch(freeSwitchNotifierProvider); final textColor = theme.colorScheme.onSurface; return Padding( padding: const EdgeInsets.all( AddProfileModalConst.navBarGap, ).copyWith(bottom: AddProfileModalConst.navBarBottomGap), child: Row( children: [ Row( key: const ValueKey('free'), children: [ Text(t.common.free, style: theme.textTheme.titleMedium!.copyWith(color: textColor)), const Gap(8), Switch(value: freeSwitch, onChanged: ref.read(freeSwitchNotifierProvider.notifier).onChange), ], ), const Spacer(), ActionChip( key: const ValueKey("help"), label: Text(t.common.help, style: theme.textTheme.labelLarge!.copyWith(color: textColor)), avatar: Icon(Icons.help_outline, color: theme.colorScheme.onSurfaceVariant), onPressed: () async => await ref.read(dialogNotifierProvider.notifier).showNoActiveProfile(), ), ], ), ); } } ================================================ FILE: lib/features/profile/add/widgets/widgets.dart ================================================ export 'fix_btn.dart'; export 'fix_btns.dart'; export 'loading.dart'; export 'nav_bar.dart'; ================================================ FILE: lib/features/profile/data/profile_data_mapper.dart ================================================ import 'dart:convert'; import 'package:drift/drift.dart'; import 'package:hiddify/core/db/db.dart'; import 'package:hiddify/features/profile/model/profile_entity.dart'; extension ProfileEntityMapper on ProfileEntity { ProfileEntriesCompanion toInsertEntry() => map( remote: (rp) => ProfileEntriesCompanion.insert( id: rp.id, type: ProfileType.remote, active: rp.active, name: rp.name, url: Value(rp.url), lastUpdate: rp.lastUpdate, updateInterval: Value(rp.options?.updateInterval), populatedHeaders: Value(jsonEncode(rp.populatedHeaders)), profileOverride: Value(rp.profileOverride), userOverride: Value(rp.userOverride?.toStr()), upload: Value(rp.subInfo?.upload), download: Value(rp.subInfo?.download), total: Value(rp.subInfo?.total), expire: Value(rp.subInfo?.expire), webPageUrl: Value(rp.subInfo?.webPageUrl), supportUrl: Value(rp.subInfo?.supportUrl), ), local: (lp) => ProfileEntriesCompanion.insert( id: lp.id, type: ProfileType.local, active: lp.active, name: lp.name, lastUpdate: lp.lastUpdate, populatedHeaders: Value(jsonEncode(lp.populatedHeaders)), profileOverride: Value(lp.profileOverride), userOverride: Value(lp.userOverride?.toStr()), ), ); ProfileEntriesCompanion toUpdateEntry() => map( remote: (rp) => ProfileEntriesCompanion( name: Value(rp.name), lastUpdate: Value(rp.lastUpdate), updateInterval: Value(rp.options?.updateInterval), populatedHeaders: Value(jsonEncode(rp.populatedHeaders)), profileOverride: Value(rp.profileOverride), userOverride: Value(rp.userOverride?.toStr()), upload: Value(rp.subInfo?.upload), download: Value(rp.subInfo?.download), total: Value(rp.subInfo?.total), expire: Value(rp.subInfo?.expire), webPageUrl: Value(rp.subInfo?.webPageUrl), supportUrl: Value(rp.subInfo?.supportUrl), ), local: (lp) => ProfileEntriesCompanion( name: Value(lp.name), lastUpdate: Value(lp.lastUpdate), populatedHeaders: Value(jsonEncode(lp.populatedHeaders)), profileOverride: Value(lp.profileOverride), userOverride: Value(lp.userOverride?.toStr()), ), ); } extension ProfileEntryMapper on ProfileEntry { ProfileEntity toEntity() { ProfileOptions? options; if (updateInterval != null) { options = ProfileOptions(updateInterval: updateInterval!); } SubscriptionInfo? subInfo; if (upload != null && download != null && total != null && expire != null) { subInfo = SubscriptionInfo( upload: upload!, download: download!, total: total!, expire: expire!, webPageUrl: webPageUrl, supportUrl: supportUrl, ); } Map? mPopulatedHeaders; if (populatedHeaders != null) { final m = jsonDecode(populatedHeaders!) as Map; mPopulatedHeaders = m.cast(); } return switch (type) { ProfileType.remote => RemoteProfileEntity( id: id, active: active, name: name, url: url!, lastUpdate: lastUpdate, options: options, subInfo: subInfo, populatedHeaders: mPopulatedHeaders, profileOverride: profileOverride, userOverride: UserOverride.fromStr(userOverride), ), ProfileType.local => LocalProfileEntity( id: id, active: active, name: name, lastUpdate: lastUpdate, populatedHeaders: mPopulatedHeaders, profileOverride: profileOverride, userOverride: UserOverride.fromStr(userOverride), ), }; } } ================================================ FILE: lib/features/profile/data/profile_data_providers.dart ================================================ import 'package:hiddify/core/db/provider/db_providers.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_parser.dart'; import 'package:hiddify/features/profile/data/profile_path_resolver.dart'; import 'package:hiddify/features/profile/data/profile_repository.dart'; import 'package:hiddify/features/settings/data/config_option_data_providers.dart'; import 'package:hiddify/hiddifycore/hiddify_core_service_provider.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'profile_data_providers.g.dart'; @Riverpod(keepAlive: true) Future profileRepository(Ref ref) async { final repo = ProfileRepositoryImpl( profileDataSource: ref.watch(profileDataSourceProvider), profilePathResolver: ref.watch(profilePathResolverProvider), singbox: ref.watch(hiddifyCoreServiceProvider), configOptionRepository: ref.watch(configOptionRepositoryProvider), profileParser: ref.watch(profileParserProvider), ); await repo.init().getOrElse((l) => throw l).run(); return repo; } @Riverpod(keepAlive: true) ProfileDataSource profileDataSource(Ref ref) { return ProfileDao(ref.watch(dbProvider)); } @Riverpod(keepAlive: true) ProfilePathResolver profilePathResolver(Ref ref) { return ProfilePathResolver(ref.watch(appDirectoriesProvider).requireValue.workingDir); } @Riverpod(keepAlive: true) ProfileParser profileParser(Ref ref) { return ProfileParser(ref: ref, httpClient: ref.watch(httpClientProvider)); } ================================================ FILE: lib/features/profile/data/profile_data_source.dart ================================================ import 'package:drift/drift.dart'; import 'package:fpdart/fpdart.dart'; import 'package:hiddify/core/db/db.dart'; import 'package:hiddify/features/profile/model/profile_sort_enum.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:loggy/loggy.dart'; part 'profile_data_source.g.dart'; abstract interface class ProfileDataSource { Future getById(String id); Future getByUrl(String url); Future getByName(String name); Stream watchActiveProfile(); Stream watchProfilesCount(); Stream> watchAll({required ProfilesSort sort, required SortMode sortMode}); Future insert(ProfileEntriesCompanion entry); Future edit(String id, ProfileEntriesCompanion entry); Future deleteById(String id, bool isActive); } Map orderMap = {SortMode.ascending: OrderingMode.asc, SortMode.descending: OrderingMode.desc}; @DriftAccessor(tables: [ProfileEntries]) class ProfileDao extends DatabaseAccessor with _$ProfileDaoMixin, InfraLogger implements ProfileDataSource { ProfileDao(super.db); @override Future getById(String id) async { return await (profileEntries.select()..where((tbl) => tbl.id.equals(id))).getSingleOrNull(); } @override Future getByUrl(String url) async { return await (select(profileEntries) ..where((tbl) => tbl.url.like('%$url%')) ..limit(1)) .getSingleOrNull(); } @override Future getByName(String name) async { return await (select(profileEntries) ..where((tbl) => tbl.name.equals(name)) ..limit(1)) .getSingleOrNull(); } @override Stream watchActiveProfile() { return (profileEntries.select() ..where((tbl) => tbl.active.equals(true)) ..limit(1)) .watchSingleOrNull() .distinct(); } @override Stream watchProfilesCount() { final count = profileEntries.id.count(); return (profileEntries.selectOnly()..addColumns([count])).map((exp) => exp.read(count)!).watchSingle().distinct(); } @override Stream> watchAll({required ProfilesSort sort, required SortMode sortMode}) { return (profileEntries.select()..orderBy([ (tbl) => OrderingTerm(expression: tbl.active, mode: OrderingMode.desc), (tbl) { final trafficRatio = (tbl.download + tbl.upload) / tbl.total; final isExpired = tbl.expire.isSmallerOrEqualValue(DateTime.now()); return OrderingTerm( expression: (trafficRatio.isNull() | trafficRatio.isSmallerThanValue(1)) & (isExpired.isNull() | isExpired.equals(false)), mode: OrderingMode.desc, ); }, switch (sort) { ProfilesSort.name => (tbl) => OrderingTerm(expression: tbl.name, mode: orderMap[sortMode]!), ProfilesSort.lastUpdate => (tbl) => OrderingTerm(expression: tbl.lastUpdate, mode: orderMap[sortMode]!), }, ])) .watch(); } @override Future insert(ProfileEntriesCompanion entry) async { await transaction(() async { if (entry.active.present && entry.active.value) { await update(profileEntries).write(const ProfileEntriesCompanion(active: Value(false))); } final name = StringBuffer(entry.name.value); while (await getByName(name.toString()) != null) { name.write('${randomInt(0, 9).run()}'); } await into(profileEntries).insert(entry.copyWith(name: Value(name.toString()))); }); } @override Future edit(String id, ProfileEntriesCompanion entry) async { await transaction(() async { final profile = await (profileEntries.select()..where((tbl) => tbl.id.equals(id))).getSingleOrNull(); if (profile == null) { loggy.log(LogLevel.info, 'profile with id : [$id] deleted'); return; } if (entry.active.present && entry.active.value) { await update(profileEntries).write(const ProfileEntriesCompanion(active: Value(false))); } await (update(profileEntries)..where((tbl) => tbl.id.equals(id))).write(entry); }); } @override Future deleteById(String id, bool isActive) async { await transaction(() async { await (delete(profileEntries)..where((tbl) => tbl.id.equals(id))).go(); if (isActive) { final profiles = await (profileEntries.select()..where((tbl) => tbl.id.equals(id).not())).get(); if (profiles.isEmpty) return; final prof = profiles.first; await (update( profileEntries, )..where((tbl) => tbl.id.equals(prof.id))).write(const ProfileEntriesCompanion(active: Value(true))); } }); } } ================================================ FILE: lib/features/profile/data/profile_parser.dart ================================================ import 'dart:convert'; import 'dart:io'; import 'package:dartx/dartx.dart'; import 'package:dio/dio.dart'; import 'package:fpdart/fpdart.dart'; import 'package:hiddify/core/db/db.dart'; import 'package:hiddify/core/http_client/dio_http_client.dart'; import 'package:hiddify/features/profile/data/profile_data_mapper.dart'; import 'package:hiddify/features/profile/model/profile_entity.dart'; import 'package:hiddify/features/profile/model/profile_failure.dart'; import 'package:hiddify/features/settings/data/config_option_repository.dart'; import 'package:hiddify/singbox/model/singbox_proxy_type.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:meta/meta.dart'; /// parse profile subscription url and headers for data /// /// ***name parser hierarchy:*** /// - UserOverride.name /// - `profile-title` header /// - `content-disposition` header /// - url fragment (example: `https://example.com/config#user`) -> name=`user` /// - url filename extension (example: `https://example.com/config.json`) -> name=`config` /// - if none of these methods return a non-blank string, switch(profileType) /// - remote: fallback to `Remote Profile` /// - local: fallback to protocol, extracted from content by protocol() class ProfileParser { static const infiniteTrafficThreshold = 920_233_720_368; static const infiniteTimeThreshold = 92_233_720_368; static const allowedOverrideConfigs = [ 'connection-test-url', 'direct-dns-address', 'remote-dns-address', 'warp', 'warp2', 'tls-tricks', ]; static const allowedProfileHeaders = [ 'profile-title', 'content-disposition', 'subscription-userinfo', 'profile-update-interval', 'support-url', 'profile-web-page-url', 'enable-warp', 'enable-fragment', ]; final Ref _ref; final DioHttpClient _httpClient; ProfileParser({required Ref ref, required DioHttpClient httpClient}) : _ref = ref, _httpClient = httpClient; TaskEither addLocal({ required String id, required String content, required String tempFilePath, required UserOverride? userOverride, }) { return TaskEither.tryCatch(() async { await expandRemoteLinesInParallel( tempFilePath: tempFilePath, httpClient: _httpClient, cancelToken: CancelToken(), ref: _ref, ); }, (_, __) => ProfileFailure.unexpected()) .flatMap((_) => TaskEither.fromEither(populateHeaders(content: content))) .flatMap( (populatedHeaders) => TaskEither.fromEither( parse( tempFilePath: tempFilePath, profile: ProfileEntity.local( id: id, active: true, name: '', lastUpdate: DateTime.now(), userOverride: userOverride, populatedHeaders: populatedHeaders, ), ).flatMap((profEntity) => Either.tryCatch(() => profEntity.toInsertEntry(), ProfileFailure.unexpected)), ), ); } TaskEither addRemote({ required String id, required String url, required String tempFilePath, required UserOverride? userOverride, CancelToken? cancelToken, }) => _downloadProfile(url, tempFilePath, cancelToken).flatMap( (remoteHeaders) => TaskEither.fromEither( populateHeaders(content: File(tempFilePath).readAsStringSync(), remoteHeaders: remoteHeaders), ).flatMap( (populatedHeaders) => TaskEither.fromEither( parse( tempFilePath: tempFilePath, profile: ProfileEntity.remote( id: id, active: true, name: '', url: url, lastUpdate: DateTime.now(), userOverride: userOverride, populatedHeaders: populatedHeaders, ), ).flatMap((profEntity) => Either.tryCatch(() => profEntity.toInsertEntry(), ProfileFailure.unexpected)), ), ), ); TaskEither updateRemote({ required RemoteProfileEntity rp, required String tempFilePath, CancelToken? cancelToken, }) => _downloadProfile(rp.url, tempFilePath, cancelToken).flatMap( (remoteHeaders) => TaskEither.fromEither( populateHeaders(content: File(tempFilePath).readAsStringSync(), remoteHeaders: remoteHeaders), ).flatMap( (populatedHeaders) => TaskEither.fromEither( parse( tempFilePath: tempFilePath, profile: rp.copyWith(populatedHeaders: populatedHeaders), ).flatMap((profEntity) => Either.tryCatch(() => profEntity.toUpdateEntry(), ProfileFailure.unexpected)), ), ), ); Either offlineUpdate({ required ProfileEntity profile, required String tempFilePath, }) => profile .map( remote: (rp) => parse(profile: rp, tempFilePath: tempFilePath), local: (lp) => parse(tempFilePath: tempFilePath, profile: lp), ) .flatMap((profEntity) => Either.tryCatch(() => profEntity.toUpdateEntry(), ProfileFailure.unexpected)); TaskEither> _downloadProfile( String url, String tempFilePath, CancelToken? cancelToken, ) => TaskEither.tryCatch(() async { // if (url.startsWith("http://")) // throw const ProfileFailure.invalidUrl('HTTP is not supported. Please use HTTPS for secure connection.'); final rs = await _httpClient .download( url.trim(), tempFilePath, cancelToken: cancelToken, userAgent: _ref.read(ConfigOptions.useXrayCoreWhenPossible) ? _httpClient.userAgent.replaceAll("HiddifyNext", "HiddifyNextX") : null, ) .catchError((err) { if (CancelToken.isCancel(err as DioException)) { throw const ProfileFailure.cancelByUser('HTTP request for getting profile content canceled by user.'); } throw err; }); await expandRemoteLinesInParallel( tempFilePath: tempFilePath, httpClient: _httpClient, cancelToken: cancelToken ?? CancelToken(), ref: _ref, ); // fixing headers before return return rs.headers.map.map((key, value) { if (value.length == 1) return MapEntry(key, value.first); return MapEntry(key, value); }); }, (err, st) => err is ProfileFailure ? err : ProfileFailure.unexpected(err, st)); Future expandRemoteLinesInParallel({ required String tempFilePath, required DioHttpClient httpClient, required CancelToken cancelToken, required Ref ref, int parallelism = 4, }) async { final content = await File(tempFilePath).readAsString(); final lines = content.split('\n'); final results = List.filled(lines.length, null); int index = 0; Future worker() async { while (true) { if (cancelToken.isCancelled) return; final currentIndex = index++; if (currentIndex >= lines.length) return; final line = lines[currentIndex]; // Non-URL if (!line.startsWith('http://') && !line.startsWith('https://')) { results[currentIndex] = line.trim(); continue; } try { final tmpPath = '$tempFilePath.$currentIndex'; await httpClient.download( line, tmpPath, cancelToken: cancelToken, userAgent: ref.read(ConfigOptions.useXrayCoreWhenPossible) ? httpClient.userAgent.replaceAll('HiddifyNext', 'HiddifyNextX') : null, ); results[currentIndex] = (await File(tmpPath).readAsString()).trim(); } catch (err) { if (err is DioException && CancelToken.isCancel(err)) { return; } results[currentIndex] = ''; } } } // Start workers await Future.wait(List.generate(parallelism, (_) => worker())); if (results.any((e) => e != null)) { final newContent = results.join("\n"); await File(tempFilePath).writeAsString(newContent); } } static Either> populateHeaders({ required String content, Map? remoteHeaders, }) => Either.tryCatch(() { final contentHeaders = _parseHeadersFromContent(content); return _mergeAndValidateHeaders(contentHeaders, remoteHeaders ?? {}); }, ProfileFailure.unexpected); static Map _mergeAndValidateHeaders( Map contentHeaders, Map remoteHeaders, ) { for (final entry in contentHeaders.entries) { if (!remoteHeaders.keys.contains(entry.key)) { remoteHeaders[entry.key] = entry.value; } } final headers = {}; for (final entry in remoteHeaders.entries) { if (allowedProfileHeaders.contains(entry.key) && entry.value != null && entry.value.toString().isNotEmpty) { headers[entry.key] = entry.value; } } return headers; } static Map _parseHeadersFromContent(String content) { final headers = {}; final content_ = safeDecodeBase64(content); final lines = content_.split("\n"); final linesToProcess = lines.length < 10 ? lines.length : 10; for (int i = 0; i < linesToProcess; i++) { final line = lines[i]; if (line.startsWith("#") || line.startsWith("//")) { final index = line.indexOf(':'); if (index == -1) continue; final key = line.substring(0, index).replaceFirst(RegExp("^#|//"), "").trim().toLowerCase(); final value = line.substring(index + 1).trim(); headers[key] = value; } } return headers; } static SubscriptionInfo? _parseSubscriptionInfo(String subInfoStr) { final values = subInfoStr.split(';'); final map = {for (final v in values) v.split('=').first.trim(): num.tryParse(v.split('=').second.trim())?.toInt()}; if (map case {"upload": final upload?, "download": final download?, "total": final total, "expire": var expire}) { final total1 = (total == null || total == 0) ? infiniteTrafficThreshold + 1 : total; expire = (expire == null || expire == 0) ? infiniteTimeThreshold : expire; return SubscriptionInfo( upload: upload, download: download, total: total1, expire: DateTime.fromMillisecondsSinceEpoch(expire * 1000), ); } return null; } @visibleForTesting static Either parse({required String tempFilePath, required ProfileEntity profile}) => Either.tryCatch(() { final headers = Map.from(profile.populatedHeaders ?? {}); var name = ''; if (profile.userOverride?.name case final String oName when oName.isNotEmpty) { name = oName; } if (headers['profile-title'] case final String titleHeader when name.isEmpty) { if (titleHeader.startsWith("base64:")) { name = utf8.decode(base64.decode(titleHeader.replaceFirst("base64:", ""))); } else { name = titleHeader.trim(); } } if (headers['content-disposition'] case final String contentDispositionHeader when name.isEmpty) { final regExp = RegExp('filename="([^"]*)"'); final match = regExp.firstMatch(contentDispositionHeader); if (match != null && match.groupCount >= 1) { name = match.group(1) ?? ''; } } if (profile case RemoteProfileEntity(:final url)) { if (Uri.parse(url).fragment case final fragment when name.isEmpty) { name = fragment; } if (url.split("/").lastOrNull case final part? when name.isEmpty) { final pattern = RegExp(r"\.(json|yaml|yml|txt)[\s\S]*"); name = part.replaceFirst(pattern, ""); } } if (name.isBlank) { switch (profile) { case RemoteProfileEntity(): name = "Remote Profile"; case LocalProfileEntity(): name = protocol(File(tempFilePath).readAsStringSync()); } } if (headers['enable-warp'].toString() == 'true' || profile.userOverride?.enableWarp == true) { final value = {'enable': true, 'mode': 'warp_over_proxy'}; headers['warp'] = value; headers['warp2'] = value; } if (headers['enable-fragment'].toString() == 'true' || profile.userOverride?.enableFragment == true) { headers['tls-tricks'] = {'enable-fragment': true}; } final isAutoUpdateDisable = profile.userOverride?.isAutoUpdateDisable ?? false; ProfileOptions? options; if (profile.userOverride?.updateInterval case final int updateInterval when updateInterval > 0 && !isAutoUpdateDisable) { options = ProfileOptions(updateInterval: Duration(hours: updateInterval)); } if (headers['profile-update-interval'] case final String updateIntervalStr when options == null && !isAutoUpdateDisable) { final updateInterval = Duration(hours: int.parse(updateIntervalStr)); options = ProfileOptions(updateInterval: updateInterval); } SubscriptionInfo? subInfo; if (headers['subscription-userinfo'] case final String subInfoStr) { subInfo = _parseSubscriptionInfo(subInfoStr); } if (subInfo != null) { if (headers['profile-web-page-url'] case final String profileWebPageUrl when isUrl(profileWebPageUrl)) { subInfo = subInfo.copyWith(webPageUrl: profileWebPageUrl); } if (headers['support-url'] case final String profileSupportUrl when isUrl(profileSupportUrl)) { subInfo = subInfo.copyWith(supportUrl: profileSupportUrl); } } headers.removeWhere( (key, value) => !allowedOverrideConfigs.contains(key) || value == null || value.toString().isEmpty, ); final profileOverrideStr = jsonEncode({for (final key in headers.keys) key: headers[key]}); return profile.map( remote: (rp) => rp.copyWith( name: name, lastUpdate: DateTime.now(), options: options, subInfo: subInfo, profileOverride: profileOverrideStr, ), local: (lp) => lp.copyWith(name: name, lastUpdate: DateTime.now(), profileOverride: profileOverrideStr), ); }, ProfileFailure.unexpected); static String protocol(String content) { if (content.contains("[Interface]")) { return ProxyType.wireguard.label; } final lines = content.split('\n'); String? name; for (final line in lines) { final uri = Uri.tryParse(line); if (uri == null) continue; final fragment = uri.hasFragment ? Uri.decodeComponent(uri.fragment.split(" -> ")[0]) : null; name ??= switch (uri.scheme) { 'ss' => fragment ?? ProxyType.shadowsocks.label, 'ssconf' => fragment ?? ProxyType.shadowsocks.label, 'vmess' => ProxyType.vmess.label, 'vless' => fragment ?? ProxyType.vless.label, 'trojan' => fragment ?? ProxyType.trojan.label, 'tuic' => fragment ?? ProxyType.tuic.label, 'hy2' || 'hysteria2' => fragment ?? ProxyType.hysteria2.label, 'hy' || 'hysteria' => fragment ?? ProxyType.hysteria.label, 'ssh' => fragment ?? ProxyType.ssh.label, 'wg' => fragment ?? ProxyType.wireguard.label, 'awg' => fragment ?? ProxyType.awg.label, 'shadowtls' => fragment ?? ProxyType.shadowtls.label, 'mieru' => fragment ?? ProxyType.mieru.label, 'warp' => fragment ?? ProxyType.warp.label, _ => null, }; } return name ?? ProxyType.unknown.label; } static Map applyProfileOverride(Map main, String? profileOverride) { if (profileOverride == null) return main; if (profileOverride.contains("{")) { final profileOverrideMap = jsonDecode(profileOverride) as Map; return _mergeJson(main, profileOverrideMap); } else { return main; } } static Map _mergeJson(Map main, Map override) { override.forEach((key, value) { if (main.containsKey(key)) { if (main[key] is Map && value is Map) { main[key] = _mergeJson(main[key] as Map, value); } else { main[key] = value; } } else { main[key] = value; } }); return main; } } ================================================ FILE: lib/features/profile/data/profile_path_resolver.dart ================================================ import 'dart:io'; import 'package:path/path.dart' as p; class ProfilePathResolver { const ProfilePathResolver(this._workingDir); final Directory _workingDir; Directory get directory => Directory(p.join(_workingDir.path, "configs")); File file(String fileName) { return File(p.join(directory.path, "$fileName.json")); } File tempFile(String fileName) => file("$fileName.tmp"); } ================================================ FILE: lib/features/profile/data/profile_repository.dart ================================================ import 'package:dio/dio.dart'; import 'package:drift/drift.dart'; import 'package:flutter/foundation.dart'; import 'package:fpdart/fpdart.dart'; import 'package:hiddify/core/db/db.dart'; import 'package:hiddify/core/utils/exception_handler.dart'; import 'package:hiddify/features/profile/data/profile_data_mapper.dart'; import 'package:hiddify/features/profile/data/profile_data_source.dart'; import 'package:hiddify/features/profile/data/profile_parser.dart'; import 'package:hiddify/features/profile/data/profile_path_resolver.dart'; import 'package:hiddify/features/profile/model/profile_entity.dart'; import 'package:hiddify/features/profile/model/profile_failure.dart'; import 'package:hiddify/features/profile/model/profile_sort_enum.dart'; import 'package:hiddify/features/settings/data/config_option_repository.dart'; import 'package:hiddify/hiddifycore/hiddify_core_service.dart'; import 'package:hiddify/utils/custom_loggers.dart'; import 'package:uuid/uuid.dart'; abstract interface class ProfileRepository { TaskEither init(); TaskEither getById(String id); TaskEither setAsActive(String id); TaskEither deleteById(String id, bool isActive); Stream> watchActiveProfile(); Stream> watchHasAnyProfile(); Stream>> watchAll({ ProfilesSort sort = ProfilesSort.lastUpdate, SortMode sortMode = SortMode.ascending, }); TaskEither upsertRemote(String url, {UserOverride? userOverride, CancelToken? cancelToken}); TaskEither addLocal(String content, {UserOverride? userOverride}); TaskEither offlineUpdate(ProfileEntity nProfile, String nContent); TaskEither validateConfig(String path, String tempPath, String? profileOverride, bool debug); TaskEither generateConfig(String id); TaskEither getRawConfig(String id); } class ProfileRepositoryImpl with ExceptionHandler, InfraLogger implements ProfileRepository { ProfileRepositoryImpl({ required ProfileDataSource profileDataSource, required ProfilePathResolver profilePathResolver, required HiddifyCoreService singbox, required ConfigOptionRepository configOptionRepository, required ProfileParser profileParser, }) : _profileParser = profileParser, _configOptionRepo = configOptionRepository, _singbox = singbox, _profilePathResolver = profilePathResolver, _profileDataSource = profileDataSource; final ProfileDataSource _profileDataSource; final ProfilePathResolver _profilePathResolver; final HiddifyCoreService _singbox; final ConfigOptionRepository _configOptionRepo; final ProfileParser _profileParser; @override TaskEither init() { return exceptionHandler(() async { if (!kIsWeb) { if (!await _profilePathResolver.directory.exists()) { await _profilePathResolver.directory.create(recursive: true); } } return right(unit); }, ProfileUnexpectedFailure.new); } @override TaskEither getById(String id) { return TaskEither.tryCatch( () => _profileDataSource.getById(id).then((value) => value?.toEntity()), ProfileUnexpectedFailure.new, ); } @override TaskEither setAsActive(String id) { return TaskEither.tryCatch(() async { await _profileDataSource.edit(id, const ProfileEntriesCompanion(active: Value(true))); return unit; }, ProfileUnexpectedFailure.new); } @override TaskEither deleteById(String id, bool isActive) { return TaskEither.tryCatch(() async { await _profileDataSource.deleteById(id, isActive); await _profilePathResolver.file(id).delete(); return unit; }, ProfileUnexpectedFailure.new); } @override Stream> watchActiveProfile() { return _profileDataSource.watchActiveProfile().map((event) => event?.toEntity()).handleExceptions(( error, stackTrace, ) { loggy.error("error watching active profile", error, stackTrace); return ProfileUnexpectedFailure(error, stackTrace); }); } @override Stream> watchHasAnyProfile() { return _profileDataSource .watchProfilesCount() .map((event) => event != 0) .handleExceptions(ProfileUnexpectedFailure.new); } @override Stream>> watchAll({ ProfilesSort sort = ProfilesSort.lastUpdate, SortMode sortMode = SortMode.ascending, }) { return _profileDataSource .watchAll(sort: sort, sortMode: sortMode) .map((event) => event.map((e) => e.toEntity()).toList()) .handleExceptions(ProfileUnexpectedFailure.new); } @override TaskEither upsertRemote(String url, {UserOverride? userOverride, CancelToken? cancelToken}) => TaskEither.tryCatch( () async => await _profileDataSource.getByUrl(url).then((profEntry) => profEntry?.toEntity()), ProfileFailure.unexpected, ).flatMap((profEntity) { // if profile is null, generate id final id = profEntity?.id ?? const Uuid().v4(); final file = _profilePathResolver.file(id); final tempFile = _profilePathResolver.tempFile(id); try { if (profEntity != null && profEntity is RemoteProfileEntity) { // Update if (userOverride != null) { profEntity = profEntity.copyWith(userOverride: userOverride); } return _profileParser .updateRemote(rp: profEntity, tempFilePath: tempFile.path, cancelToken: cancelToken) .flatMap( (profEntity) => validateConfig(file.path, tempFile.path, profEntity.profileOverride.value, false).flatMap( (unit) => TaskEither.tryCatch(() async { await _profileDataSource.edit(id, profEntity); return unit; }, ProfileFailure.unexpected), ), ); } else { // Add return _profileParser .addRemote( id: id, url: url, tempFilePath: tempFile.path, userOverride: userOverride, cancelToken: cancelToken, ) .flatMap( (profEntity) => validateConfig(file.path, tempFile.path, profEntity.profileOverride.value, false).flatMap( (unit) => TaskEither.tryCatch(() async { await _profileDataSource.insert(profEntity); return unit; }, ProfileFailure.unexpected), ), ); } } finally { if (tempFile.existsSync()) tempFile.deleteSync(); } }); @override TaskEither addLocal(String content, {UserOverride? userOverride}) => TaskEither.tryCatch(() async { final id = const Uuid().v4(); final file = _profilePathResolver.file(id); final tempFile = _profilePathResolver.tempFile(id); try { await tempFile.writeAsString(content); final task = _profileParser .addLocal(id: id, content: content, tempFilePath: tempFile.path, userOverride: userOverride) .flatMap( (profEntity) => validateConfig(file.path, tempFile.path, profEntity.profileOverride.value, false).flatMap( (unit) => TaskEither.tryCatch(() async { await _profileDataSource.insert(profEntity); return unit; }, ProfileFailure.unexpected), ), ); return (await task.run()).getOrElse((l) => throw l); } finally { if (tempFile.existsSync()) tempFile.deleteSync(); } }, ProfileFailure.unexpected); @override TaskEither offlineUpdate(ProfileEntity profile, String nContent) => TaskEither.tryCatch( () async => await _profileDataSource.getById(profile.id).then((profEntry) => profEntry?.toEntity()), ProfileFailure.unexpected, ).flatMap((oProfile) { if (oProfile == null || oProfile.runtimeType != profile.runtimeType) throw const ProfileFailure.notFound(); if (profile.userOverride == null) loggy.warning('Updaing profile content with "userOverride" == null'); final id = oProfile.id; final file = _profilePathResolver.file(id); final tempFile = _profilePathResolver.tempFile(id); try { return TaskEither.tryCatch( () async => await tempFile.writeAsString(nContent), ProfileFailure.unexpected, ).flatMap( (_) => TaskEither.fromEither( _profileParser.offlineUpdate( profile: oProfile.copyWith(userOverride: profile.userOverride), tempFilePath: tempFile.path, ), ).flatMap( (profEntity) => validateConfig(file.path, tempFile.path, profEntity.profileOverride.value, false).flatMap( (unit) => TaskEither.tryCatch(() async { await _profileDataSource.edit(id, profEntity); return unit; }, ProfileFailure.unexpected), ), ), ); } finally { if (tempFile.existsSync()) tempFile.deleteSync(); } }); @override TaskEither validateConfig(String path, String tempPath, String? profileOverride, bool debug) => TaskEither.fromEither(_configOptionRepo.fullOptionsOverrided(profileOverride)) .mapLeft((configOptionFailure) => ProfileFailure.invalidConfig(null, configOptionFailure)) .flatMap( (overridedOptions) => _singbox .changeOptions(overridedOptions) .mapLeft(ProfileFailure.invalidConfig) .flatMap( (_) => _singbox.validateConfigByPath(path, tempPath, debug).mapLeft(ProfileFailure.invalidConfig), ), ); @override TaskEither generateConfig(String id) => TaskEither.fromEither( Either.tryCatch(() => _profilePathResolver.file(id), ProfileFailure.unexpected), ).flatMap((configFile) => _singbox.generateFullConfigByPath(configFile.path).mapLeft(ProfileFailure.unexpected)); @override TaskEither getRawConfig(String id) { return TaskEither.fromEither( Either.tryCatch(() => _profilePathResolver.file(id), ProfileFailure.unexpected), ).flatMap((configFile) => TaskEither.tryCatch(() => configFile.readAsString(), ProfileFailure.unexpected)); } } ================================================ FILE: lib/features/profile/details/json_editor.dart ================================================ library json_editor_flutter; import 'dart:convert'; import 'dart:async'; import 'dart:math'; import 'dart:ui'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; const _space = 18.0; const _textStyle = TextStyle(fontSize: 16); const _options = Icon(Icons.more_horiz, size: 16); const _expandIconWidth = 10.0; const _rowHeight = 30.0; const _popupMenuHeight = 30.0; const _popupMenuItemPadding = 20.0; const _textSpacer = SizedBox(width: 5); const _newKey = "new_key_added"; const _downArrow = SizedBox(width: _expandIconWidth, child: Icon(FontAwesomeIcons.caretDown, size: 14)); const _rightArrow = SizedBox(width: _expandIconWidth, child: Icon(FontAwesomeIcons.caretRight, size: 14)); const _newDataValue = {"string": "", "bool": false, "num": 0}; bool _enableMoreOptions = true; bool _enableKeyEdit = true; bool _enableValueEdit = true; // enum _OptionItems { map, list, string, bool, num, delete, protocols, configElement } typedef _OptionItems = String; enum _SearchActions { next, prev } /// Supported editors for JSON Editor. enum Editors { tree, text } const Map> protocolSchemaValues = { "xray": { "type": "xray", "tag": "xray-out", "xray_outbound_raw": {}, "xray_fragment": {"packets": "tlshello", "interval": "1-10", "length": "1-10"}, }, "warp": { "type": "warp", "key": "", "host": "", "port": 808, "noise": { "fake_packets": {"enabled": true, "count": "1-10", "delay": "1-10", "mode": "m4"}, }, }, "mieru": { "type": "mieru", "tag": "mieru-out", "server": "127.0.0.1", "portBindings": [ {"protocol": "tcp", "port": 1080}, {"protocol": "udp", "portRanges": "1080-1090"}, ], "multiplexing": "high", "handshake": "no_wait", }, "naive": { "type": "naive", "tag": "naive-out", "server": "127.0.0.1", "server_port": 1080, "username": "", "password": "", "tls": {"enabled": true}, }, "dnstt": { "type": "dnstt", "tag": "dnstt-out", "domain": "dnstt.hiddify.com", "publicKey": "publickey", "resolvers": ["8.8.8.8:53", "8.8.4.4:53"], "tunnel_per_resolver": 4, }, "vless": { "type": "vless", "tag": "vless-out", "server": "127.0.0.1", "server_port": 1080, "uuid": "bf000d23-0752-40b4-affe-68f7707a9661", "flow": "xtls-rprx-vision", "packet_encoding": "", }, "vmess": { "type": "vmess", "tag": "vmess-out", "server": "127.0.0.1", "server_port": 1080, "uuid": "bf000d23-0752-40b4-affe-68f7707a9661", "security": "auto", "global_padding": false, "authenticated_length": true, "packet_encoding": "", }, "trojan": { "type": "trojan", "tag": "trojan-out", "server": "127.0.0.1", "server_port": 1080, "password": "8JCsPssfgS8tiRwiMlhARg==", }, "hysteria": { "type": "hysteria", "tag": "hysteria-out", "server": "127.0.0.1", "server_port": 1080, "up": "100 Mbps", "up_mbps": 100, "down": "100 Mbps", "down_mbps": 100, "obfs": "daylight", "auth": "", "auth_str": "password", "recv_window_conn": 0, "recv_window": 0, "disable_mtu_discovery": false, "tls": {"enabled": true}, }, "hysteria2": { "type": "hysteria2", "tag": "hy2-out", "server": "127.0.0.1", "server_port": 1080, "up_mbps": 100, "down_mbps": 100, "obfs": {"type": "salamander", "password": "cry_me_a_r1ver"}, "password": "goofy_ahh_password", "tls": {"enabled": true}, }, "shadowsocks": { "type": "shadowsocks", "tag": "ss-out", "server": "127.0.0.1", "server_port": 1080, "method": "2022-blake3-aes-128-gcm", "password": "8JCsPssfgS8tiRwiMlhARg==", "plugin": "", "plugin_opts": "", "udp_over_tcp": false, }, "socks": { "type": "socks", "tag": "socks-out", "server": "127.0.0.1", "server_port": 1080, "version": "5", "username": "", "password": "", "udp_over_tcp": false, }, "http": { "type": "http", "tag": "http-out", "server": "127.0.0.1", "server_port": 1080, "username": "", "password": "", "path": "", "headers": {}, "tls": {"enabled": false}, }, "wireguard": { "type": "wireguard", "tag": "wireguard-out", "server": "127.0.0.1", "server_port": 1080, "system_interface": false, "gso": false, "interface_name": "wg0", "local_address": ["10.0.0.2/32"], "private_key": "YNXtAzepDqRv9H52osJVDQnznT5AM11eCK3ESpwSt04=", "peer_public_key": "Z1XXLsKYkYxuiYjJIkRvtIKFepCYHTgON+GwPq7SOV4=", "pre_shared_key": "31aIhAPwktDGpH4JDhA8GNvjFXEf/a6+UaQRyOAiyfM=", "reserved": [0, 0, 0], "workers": 4, "mtu": 1408, "fake_packets": "1-10", "fake_packets_size": "1-10", "fake_packets_delay": "1-10", "fake_packets_mode": "m4", }, "tuic": { "type": "tuic", "tag": "tuic-out", "server": "127.0.0.1", "server_port": 1080, "uuid": "2DD61D93-75D8-4DA4-AC0E-6AECE7EAC365", "password": "hello", "congestion_control": "cubic", "udp_relay_mode": "native", "udp_over_stream": false, "zero_rtt_handshake": false, "heartbeat": "10s", "tls": {"enabled": true}, }, "ssh": { "type": "ssh", "tag": "ssh-out", "server": "127.0.0.1", "server_port": 22, "user": "root", "password": "admin", "private_key": "", "private_key_passphrase": "", "host_key": [""], "client_version": "SSH-2.0-OpenSSH_7.4p1", }, }; const Map>> exampleSchemaValues = { "config.outbounds.transport": { "browser user-agent": { "header": {"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:128.0) Gecko/20100101 Firefox/128.0"}, }, }, "config.outbounds.tls": { "fragment": { "tls_fragment": {"enabled": true, "size": "1-10", "sleep": "1-10"}, }, "utls": { "utls": {"enabled": true, "fingerprint": "chrome"}, }, }, "config.outbounds": { "multiplex": { "multiplex": { "enabled": true, "protocol": "smux", "max_connections": 4, "min_streams": 4, "max_streams": 0, "padding": false, "brutal": {"enabled": true, "up_mbps": 100, "down_mbps": 100}, }, }, "reality": { "tls": { "enabled": true, "server_name": "", "min_version": "", "max_version": "", "utls": {"enabled": true, "fingerprint": "chrome"}, "reality": {"enabled": true, "public_key": "", "short_id": ""}, }, }, "tls": { "tls": { "enabled": true, "utls": {"enabled": true, "fingerprint": "chrome"}, "disable_sni": false, "insecure": false, "server_name": "", "alpn": [], "min_version": "", "max_version": "", "tls_fragment": {"enabled": false, "size": "1-10", "sleep": "1-10"}, }, }, "websocket": { "transport": {"type": "ws", "path": "", "headers": {}, "max_early_data": 0, "early_data_header_name": ""}, }, "grpc": { "transport": { "type": "grpc", "service_name": "TunService", "idle_timeout": "15s", "ping_timeout": "15s", "permit_without_stream": false, }, }, "quic": { "transport": {"type": "quic"}, }, "http": { "transport": { "type": "http", "host": [], "path": "", "method": "", "headers": {}, "idle_timeout": "15s", "ping_timeout": "15s", }, }, "httpupgrade": { "transport": {"type": "httpupgrade", "host": "", "path": "", "headers": {}}, }, "xhttp": { "transport": {"type": "xhttp", "host": "", "path": "", "headers": {}}, }, }, }; const Map> possibleValues = { "config.outbounds.flow": ["", "xtls-rprx-vision"], "config.outbounds.security": ["", "auto", "none", "zero", "aes-128-gcm", "chacha20-poly1305"], "config.outbounds.method": [ "", "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305", "none", "aes-128-gcm", "aes-192-gcm", "aes-256-gcm", "chacha20-ietf-poly1305", "xchacha20-ietf-poly1305", ], "config.outbounds.plugin": ["", "obfs-local", "v2ray-plugin"], "config.outbounds.network": ["", "udp", "tcp"], "config.endpoints.network": ["", "udp", "tcp"], "config.outbounds.multiplex.protocol": ["", "smux", "yamux", "h2mux"], "config.outbounds.tls.min_version": ["", "1.0", "1.1", "1.2", "1.3"], "config.outbounds.tls.max_version": ["", "1.0", "1.1", "1.2", "1.3"], "config.outbounds.tls.utls.fingerprint": [ "", "chrome", "chrome_psk", "chrome_psk_shuffle", "chrome_padding_psk_shuffle", "chrome_pq", "chrome_pq_psk", "firefox", "edge", "safari", "360", "qq", "ios", "android", "random", "randomized", ], "config.outbounds.packet_encoding": ["", "(none)", "xudp", "packetaddr"], "config.outbounds.transport.type": ["", "http", "ws", "grpc", "quic", "httpupgrade"], "config.outbounds.type": [ "vless", "dnstt", "vmess", "trojan", "xray", "shadowsocks", "wireguard", "hysteria", "hysteria2", "tuic", "ssh", "shadowtls", "custom", "direct", "block", "socks", "http", "mieru", "naive", "anytls", ], "config.endpoints.type": ["wireguard", "warp"], }; /// Edit your JSON object with this Widget. Create, edit and format objects /// using this user friendly widget. class JsonEditor extends StatefulWidget { /// JSON can be edited in two ways, Tree editor or text editor. You can disable /// either of them. /// /// When UI editor is active, you can disable adding/deleting keys by using /// [enableMoreOptions]. Editing keys and values can also be disabled by using /// [enableKeyEdit] and [enableValueEdit]. /// /// When text editor is active, it will simply ignore [enableMoreOptions], /// [enableKeyEdit] and [enableValueEdit]. /// /// [duration] is the debounce time for [onChanged] function. Defaults to /// 500 milliseconds. /// /// [editors] is the supported list of editors. First element will be /// used as default editor. Defaults to `[Editors.tree, Editors.text]`. const JsonEditor({ super.key, required this.json, required this.onChanged, this.duration = const Duration(milliseconds: 500), this.enableMoreOptions = true, this.enableKeyEdit = true, this.enableValueEdit = true, this.editors = const [Editors.tree, Editors.text], this.themeColor, this.actions = const [], this.enableHorizontalScroll = false, this.searchDuration = const Duration(milliseconds: 500), this.hideEditorsMenuButton = false, this.expandedObjects = const [], }) : assert(editors.length > 0, "editors list cannot be empty"); /// JSON string to be edited. final String json; /// Callback function that will be called with the new [dynamic] data. final ValueChanged onChanged; /// Debounce duration for [onChanged] function. final Duration duration; /// Enables more options like adding or deleting data. Defaults to `true`. final bool enableMoreOptions; /// Enables editing of keys. Defaults to `true`. final bool enableKeyEdit; /// Enables editing of values. Defaults to `true`. final bool enableValueEdit; /// Theme color for the editor. Changes the border color and header color. final Color? themeColor; /// List of supported editors. First element will be used as default editor. final List editors; /// A list of Widgets to display in a row at the end of header. final List actions; /// Enables horizontal scroll for the tree view. Defaults to `false`. final bool enableHorizontalScroll; /// Debounce duration for search function. final Duration searchDuration; /// Hides the option of changing editor. Defaults to `false`. final bool hideEditorsMenuButton; /// [expandedObjects] refers to the objects that will be expanded by /// default. Index can be provided when the data is a List. /// /// Examples: /// ```dart /// data = { /// "hobbies": ["Reading books", "Playing Cricket"], /// "education": [ /// {"name": "Bachelor of Engineering", "marks": 75}, /// {"name": "Master of Engineering", "marks": 72}, /// ], /// } /// ``` /// /// For the given data /// 1. To expand education pass => `["education"]` /// 2. To expand hobbies and education pass => `["hobbies", "education"]` /// 3. To expand the first element (index 0) of education list, this means /// we need to expand education too. In this case you need not to pass /// "education" separately. Just pass a list of all nested objects => /// `[["education", 0]]` /// /// ```dart /// JsonEditor( /// expandedObjects: const [ /// "hobbies", /// ["education", 0] // expands nested object in education /// ], /// onChanged: (_) {}, /// json: jsonEncode(data), /// ) /// ``` final List expandedObjects; @override State createState() => _JsonEditorState(); } class _JsonEditorState extends State { Timer? _timer; Timer? _searchTimer; late dynamic _data; late final _themeColor = widget.themeColor ?? Theme.of(context).primaryColor; late Editors _editor = widget.editors.first; bool _onError = false; bool? allExpanded; late final _controller = TextEditingController()..text = _stringifyData(_data, 0, true); late final _scrollController = ScrollController(); final _matchedKeys = {}; final _matchedKeysLocation = []; int? _focusedKey; int? _results; late final _expandedObjects = { ["config"].toString(): true, if (widget.expandedObjects.isNotEmpty) ...getExpandedParents(), }; Map getExpandedParents() { final map = {}; for (var key in widget.expandedObjects) { if (key is List) { final newExpandList = ["config", ...key]; for (int i = newExpandList.length - 1; i > 0; i--) { map[newExpandList.toString()] = true; newExpandList.removeLast(); } } else { map[["config", key].toString()] = true; } } return map; } void callOnChanged() { if (_timer?.isActive ?? false) _timer?.cancel(); _timer = Timer(widget.duration, () { widget.onChanged(jsonDecode(jsonEncode(_data))); }); } void parseData(String value) { if (_timer?.isActive ?? false) _timer?.cancel(); _timer = Timer(widget.duration, () { try { _data = jsonDecode(value); widget.onChanged(_data); setState(() { _onError = false; }); } catch (_) { setState(() { _onError = true; }); } }); } void copyData() async { await Clipboard.setData(ClipboardData(text: const JsonEncoder.withIndent(' ').convert(_data))); } bool updateParentObjects(List newExpandList) { bool needsRebuilding = false; for (int i = newExpandList.length - 1; i >= 0; i--) { if (_expandedObjects[newExpandList.toString()] == null) { _expandedObjects[newExpandList.toString()] = true; needsRebuilding = true; } newExpandList.removeLast(); } return needsRebuilding; } void findMatchingKeys(data, String text, List nestedParents) { if (data is Map) { final keys = data.keys.toList(); for (var key in keys) { final keyName = key.toString(); if (keyName.toLowerCase().contains(text) || (data[key] is String && data[key].toString().toLowerCase().contains(text))) { _results = _results! + 1; _matchedKeys[keyName] = true; _matchedKeysLocation.add([...nestedParents, key]); } if (data[key] is Map) { findMatchingKeys(data[key], text, [...nestedParents, key]); } else if (data[key] is List) { findMatchingKeys(data[key], text, [...nestedParents, key]); } } } else if (data is List) { for (int i = 0; i < data.length; i++) { final item = data[i]; if (item is Map) { findMatchingKeys(item, text, [...nestedParents, i]); } else if (item is List) { findMatchingKeys(item, text, [...nestedParents, i]); } } } } void onSearch(String text) { if (_searchTimer?.isActive ?? false) _searchTimer?.cancel(); _searchTimer = Timer(widget.searchDuration, () async { _matchedKeys.clear(); _matchedKeysLocation.clear(); _focusedKey = null; if (text.isEmpty) { setState(() { _results = null; }); } else { _results = 0; findMatchingKeys(_data, text.toLowerCase(), ["config"]); setState(() {}); if (_matchedKeys.isNotEmpty) { _focusedKey = 0; scrollTo(0); } } }); } int getOffset(List toFind) { int offset = 1; bool keyFound = false; void calculateOffset(data, List parents, List toFind) { if (keyFound) return; if (data is Map) { for (var entry in data.entries) { if (keyFound) return; offset++; final newList = [...parents, entry.key]; if (entry.key == toFind.last && newList.toString() == toFind.toString()) { keyFound = true; return; } if (entry.value is Map || entry.value is List) { if (_expandedObjects[newList.toString()] == true && !keyFound) { calculateOffset(entry.value, newList, toFind); } } } } else if (data is List) { for (int i = 0; i < data.length; i++) { if (keyFound) return; offset++; if (data[i] is Map || data[i] is List) { final newList = [...parents, i]; if (_expandedObjects[newList.toString()] == true && !keyFound) { calculateOffset(data[i], newList, toFind); } } } } } calculateOffset(_data, ["config"], toFind); return offset; } void scrollTo(int index) { final toFind = [..._matchedKeysLocation[index]]; final needsRebuilding = updateParentObjects([..._matchedKeysLocation[index]]..removeLast()); if (needsRebuilding) setState(() {}); Future.delayed(const Duration(milliseconds: 150), () { _scrollController.animateTo( (getOffset(toFind) * _rowHeight) - 90, duration: const Duration(milliseconds: 200), curve: Curves.easeInOut, ); }); } void onSearchAction(_SearchActions action) { if (_matchedKeys.isEmpty) return; if (action == _SearchActions.next) { if (_focusedKey != null && _matchedKeysLocation.length - 1 > _focusedKey!) { _focusedKey = _focusedKey! + 1; } else { _focusedKey = 0; } } else { if (_focusedKey != null && _focusedKey! > 0) { _focusedKey = _focusedKey! - 1; } else { _focusedKey = _matchedKeysLocation.length - 1; } } scrollTo(_focusedKey!); } void expandAllObjects(data, List expandedList) { if (data is Map) { for (var entry in data.entries) { if (entry.value is Map || entry.value is List) { final newList = [...expandedList, entry.key]; _expandedObjects[newList.toString()] = true; expandAllObjects(entry.value, newList); } } } else if (data is List) { for (int i = 0; i < data.length; i++) { if (data[i] is Map || data[i] is List) { final newList = [...expandedList, i]; _expandedObjects[newList.toString()] = true; expandAllObjects(data[i], newList); } } } } Widget wrapWithHorizontolScroll(Widget child) { if (widget.enableHorizontalScroll) { return SingleChildScrollView(scrollDirection: Axis.horizontal, child: child); } return child; } @override void initState() { super.initState(); _data = jsonDecode(widget.json); _enableMoreOptions = widget.enableMoreOptions; _enableKeyEdit = widget.enableKeyEdit; _enableValueEdit = widget.enableValueEdit; } @override void dispose() { _timer?.cancel(); _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Directionality( textDirection: TextDirection.ltr, child: DecoratedBox( decoration: BoxDecoration( border: Border.all(width: _onError ? 2 : 1, color: _onError ? Colors.red : _themeColor), ), child: SizedBox( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ DecoratedBox( decoration: BoxDecoration( color: _themeColor, border: _onError ? const Border(bottom: BorderSide(color: Colors.red, width: 2)) : null, ), child: Padding( padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 10), child: Row( children: [ const Text('Config Editor: '), if (!widget.hideEditorsMenuButton) PopupMenuButton( initialValue: _editor, tooltip: 'Change editor', padding: EdgeInsets.zero, onSelected: (value) { if (value == Editors.text) { _controller.text = _stringifyData(_data, 0, true); } setState(() { _editor = value; }); }, position: PopupMenuPosition.under, enabled: widget.editors.length > 1, constraints: const BoxConstraints(minWidth: 50, maxWidth: 150), itemBuilder: (context) { return >[ PopupMenuItem( height: _popupMenuHeight, padding: const EdgeInsets.symmetric(horizontal: 12), enabled: widget.editors.contains(Editors.tree), value: Editors.tree, child: const Text("Tree"), ), PopupMenuItem( height: _popupMenuHeight, padding: const EdgeInsets.symmetric(horizontal: 12), enabled: widget.editors.contains(Editors.text), value: Editors.text, child: const Text("Text"), ), ]; }, child: Row( mainAxisSize: MainAxisSize.min, children: [ Text(_editor.name, style: _textStyle), const Icon(Icons.arrow_drop_down, size: 20), ], ), ), const Spacer(), if (_editor == Editors.text) ...[ const SizedBox(width: 20), InkWell( onTap: () { _controller.text = _stringifyData(_data, 0, true); }, child: const Tooltip(message: 'Format', child: Icon(Icons.format_align_left, size: 20)), ), ] else ...[ const SizedBox(width: 20), if (_results != null) ...[Text("$_results results"), const SizedBox(width: 5)], _SearchField(onSearch, onSearchAction), const SizedBox(width: 20), InkWell( onTap: () { _expandedObjects[["config"].toString()] = true; expandAllObjects(_data, ["config"]); setState(() {}); }, child: const Tooltip(message: 'Expand All', child: Icon(Icons.expand, size: 20)), ), const SizedBox(width: 20), InkWell( onTap: () { _expandedObjects.clear(); setState(() {}); }, child: const Tooltip(message: 'Collapse All', child: Icon(Icons.compress, size: 20)), ), ], const SizedBox(width: 20), InkWell( onTap: copyData, child: const Tooltip(message: 'Copy', child: Icon(Icons.copy, size: 20)), ), if (widget.actions.isNotEmpty) const SizedBox(width: 20), ...widget.actions, ], ), ), ), if (_editor == Editors.tree) Expanded( child: SingleChildScrollView( controller: _scrollController, physics: const ClampingScrollPhysics(), child: wrapWithHorizontolScroll( _Holder( key: UniqueKey(), data: _data, keyName: "config", paddingLeft: _space, onChanged: callOnChanged, parentObject: {"config": _data}, setState: setState, matchedKeys: _matchedKeys, allParents: const ["config"], expandedObjects: _expandedObjects, ), ), ), ), if (_editor == Editors.text) Expanded( child: TextFormField( style: _textStyle, controller: _controller, onChanged: parseData, maxLines: null, minLines: null, expands: true, textAlignVertical: TextAlignVertical.top, decoration: const InputDecoration( border: InputBorder.none, contentPadding: EdgeInsets.only(left: 5, top: 8, bottom: 8), ), ), ), ], ), ), ), ); } } class _Holder extends StatefulWidget { const _Holder({ super.key, this.keyName, required this.data, required this.paddingLeft, required this.onChanged, required this.parentObject, required this.setState, required this.matchedKeys, required this.allParents, required this.expandedObjects, }); final dynamic keyName; final dynamic data; final double paddingLeft; final VoidCallback onChanged; final dynamic parentObject; final StateSetter setState; final Map matchedKeys; final List allParents; final Map expandedObjects; String getKeyPath() { final basePath = allParents.whereType().join('.'); // final typePath = (parentObject['type'] != null && // parentObject['type'] != keyName) // ? '.${parentObject['type']}' // : ''; return '$basePath'; } @override State<_Holder> createState() => _HolderState(); } class _HolderState extends State<_Holder> { late bool isExpanded = widget.expandedObjects[widget.allParents.toString()] == true; void _toggleState() { if (!isExpanded) { widget.expandedObjects[widget.allParents.toString()] = true; } else { widget.expandedObjects.remove(widget.allParents.toString()); } setState(() { isExpanded = !isExpanded; }); } void onSelected(_OptionItems selectedItem) { if (selectedItem == "delete") { if (widget.parentObject is Map) { widget.parentObject.remove(widget.keyName); } else { widget.parentObject.removeAt(widget.keyName); } widget.setState(() {}); } else if (selectedItem == "map") { if (widget.data is Map) { widget.data[_newKey] = Map(); } else { widget.data.add(Map()); } setState(() {}); widget.onChanged(); } else if (exampleSchemaValues.containsKey(selectedItem.split("___")[0])) { final jsonItem = exampleSchemaValues[selectedItem.split("___")[0]]![selectedItem.split("___")[1]]!; for (final key in jsonItem.keys) { widget.data[key] = jsonDecode(jsonEncode(jsonItem[key])); } setState(() {}); } else if (protocolSchemaValues.containsKey(selectedItem)) { final jsonItem = protocolSchemaValues[selectedItem]!; widget.data.add(jsonDecode(jsonEncode(jsonItem))); setState(() {}); } else if (selectedItem == "list") { if (widget.data is Map) { widget.data[_newKey] = []; } else { widget.data.add([]); } setState(() {}); } else { if (widget.data is Map) { widget.data[_newKey] = _newDataValue[selectedItem]; } else { widget.data.add(_newDataValue[selectedItem]); } setState(() {}); } widget.onChanged(); } void onKeyChanged(Object key) { final val = widget.parentObject.remove(widget.keyName); widget.parentObject[key] = val; widget.onChanged(); widget.setState(() {}); } void onValueChanged(Object value) { widget.parentObject[widget.keyName] = value; widget.onChanged(); } Widget wrapWithColoredBox(Widget child, String key) { if (widget.matchedKeys[key] == true) { return ColoredBox(color: Theme.of(context).colorScheme.secondaryContainer, child: child); } return child; } String getChildSummary(_Holder widget) { final data = widget.data; var res = "{"; if (data is Map) { if (widget.expandedObjects[widget.allParents.toString()] ?? false) return ""; final content = data as Map; //res += "${data.length}"; if (content["type"] != null) { res += "${content["type"]}"; } if (content["tag"] != null) { res += " [${content["tag"]}]"; } else { final d = "$content"; res += " [${d.substring(0, min(20, d.length))}...]"; } } else if (data is List) { final content = data as List; res += "${content.length}"; } return res + "}"; } @override Widget build(BuildContext context) { if (widget.data is Map) { final mapWidget = []; final widgetData = widget.data as Map; final List keys = widgetData.keys.toList(); for (var key in keys) { mapWidget.add( _Holder( key: Key(key), data: widget.data[key], keyName: key, onChanged: widget.onChanged, parentObject: widget.data, paddingLeft: widget.paddingLeft + _space, setState: setState, matchedKeys: widget.matchedKeys, allParents: [...widget.allParents, key], expandedObjects: widget.expandedObjects, ), ); } return Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ SizedBox( height: _rowHeight, child: Row( children: [ const SizedBox(width: _expandIconWidth), if (_enableMoreOptions) _Options(onSelected, widget.getKeyPath()), SizedBox(width: widget.paddingLeft), InkWell( hoverColor: Colors.transparent, splashColor: Colors.transparent, onTap: _toggleState, child: isExpanded ? _downArrow : _rightArrow, ), const SizedBox(width: _expandIconWidth), if (_enableKeyEdit && widget.parentObject is! List) ...[ _ReplaceTextWithField( key: Key(widget.keyName.toString()), initialValue: widget.keyName, isKey: true, onChanged: onKeyChanged, setState: setState, isHighlighted: widget.matchedKeys["${widget.keyName}"] == true, ), _textSpacer, Text(getChildSummary(widget), style: _textStyle), ] else InkWell( hoverColor: Colors.transparent, splashColor: Colors.transparent, onTap: _toggleState, child: Row( mainAxisSize: MainAxisSize.min, children: [ wrapWithColoredBox(Text("${widget.keyName}", style: _textStyle), "${widget.keyName}"), _textSpacer, Text(getChildSummary(widget), style: _textStyle), ], ), ), ], ), ), if (isExpanded) Column(crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: mapWidget), ], ); } else if (widget.data is List) { final listWidget = []; final widgetData = widget.data as List; for (int i = 0; i < widgetData.length; i++) { listWidget.add( _Holder( key: Key("$i"), keyName: i, data: widgetData[i], onChanged: widget.onChanged, parentObject: widget.data, paddingLeft: widget.paddingLeft + _space, setState: setState, matchedKeys: widget.matchedKeys, allParents: [...widget.allParents, i], expandedObjects: widget.expandedObjects, ), ); } return Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ SizedBox( height: _rowHeight, child: Row( mainAxisSize: MainAxisSize.min, children: [ const SizedBox(width: _expandIconWidth), if (_enableMoreOptions) _Options(onSelected, widget.getKeyPath()), SizedBox(width: widget.paddingLeft), InkWell( hoverColor: Colors.transparent, splashColor: Colors.transparent, onTap: _toggleState, child: isExpanded ? _downArrow : _rightArrow, ), const SizedBox(width: _expandIconWidth), if (_enableKeyEdit && widget.parentObject is! List) ...[ _ReplaceTextWithField( key: Key(widget.keyName.toString()), initialValue: widget.keyName, isKey: true, onChanged: onKeyChanged, setState: setState, isHighlighted: widget.matchedKeys["${widget.keyName}"] == true, ), _textSpacer, Text("[${widget.data.length}]", style: _textStyle), ] else InkWell( hoverColor: Colors.transparent, splashColor: Colors.transparent, onTap: _toggleState, child: Row( mainAxisSize: MainAxisSize.min, children: [ wrapWithColoredBox(Text("${widget.keyName}", style: _textStyle), "${widget.keyName}"), _textSpacer, Text("[${widget.data.length}]", style: _textStyle), ], ), ), ], ), ), if (isExpanded) Column(crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: listWidget), ], ); } else { return SizedBox( height: _rowHeight, child: Row( mainAxisSize: MainAxisSize.min, children: [ const SizedBox(width: _expandIconWidth), if (_enableMoreOptions) _Options(onSelected, widget.getKeyPath()), SizedBox(width: widget.paddingLeft + (_expandIconWidth * 2)), Row( mainAxisSize: MainAxisSize.min, children: [ if (_enableKeyEdit) ...[ _ReplaceTextWithField( key: Key(widget.keyName.toString()), initialValue: widget.keyName, isKey: true, onChanged: onKeyChanged, setState: setState, isHighlighted: widget.matchedKeys["${widget.keyName}"] == true, ), const Text(' :', style: _textStyle), ] else Row( mainAxisSize: MainAxisSize.min, children: [ wrapWithColoredBox(Text("${widget.keyName}", style: _textStyle), "${widget.keyName}"), _textSpacer, const Text(" :", style: _textStyle), ], ), _textSpacer, if (_enableValueEdit) ...[ _ReplaceTextWithField( key: UniqueKey(), initialValue: widget.data, keyPath: widget.getKeyPath(), onChanged: onValueChanged, setState: setState, ), _textSpacer, ] else ...[ Text(widget.data.toString(), style: _textStyle), _textSpacer, ], ], ), ], ), ); } } } class _ReplaceTextWithField extends StatefulWidget { const _ReplaceTextWithField({ super.key, required this.initialValue, required this.onChanged, required this.setState, this.isKey = false, this.isHighlighted = false, this.keyPath = "", }); final String keyPath; final dynamic initialValue; final bool isKey; final ValueChanged onChanged; final StateSetter setState; final bool isHighlighted; @override State<_ReplaceTextWithField> createState() => _ReplaceTextWithFieldState(); } class _ReplaceTextWithFieldState extends State<_ReplaceTextWithField> { late final _focusNode = FocusNode(); bool _isFocused = false; bool _value = false; String _text = ""; late final BoxConstraints _constraints; void handleChange() { if (!_focusNode.hasFocus) { _text = _text.trim(); final val = num.tryParse(_text); if (val == null) { widget.onChanged(_text); } else { widget.onChanged(val); } setState(() { _isFocused = false; }); } } Widget wrapWithColoredBox(String keyName) { if (widget.isHighlighted) { return ColoredBox( color: Theme.of(context).colorScheme.errorContainer, child: Text(keyName, style: _textStyle), ); } return Text(keyName, style: _textStyle); } @override void initState() { super.initState(); if (widget.initialValue is bool) { _value = widget.initialValue as bool; } else { if (widget.initialValue == _newKey) { _text = ""; _isFocused = true; _focusNode.requestFocus(); } else { _text = widget.initialValue.toString(); } } if (widget.isKey) { _constraints = const BoxConstraints(minWidth: 20, maxWidth: 100); } else if (widget.initialValue is num) { _constraints = const BoxConstraints(minWidth: 20, maxWidth: 80); } else { _constraints = const BoxConstraints(minWidth: 20, maxWidth: 400); } _focusNode.addListener(handleChange); } @override void dispose() { _focusNode.removeListener(handleChange); _focusNode.dispose(); super.dispose(); } @override Widget build(BuildContext context) { if (possibleValues.containsKey(widget.keyPath)) { final options = possibleValues[widget.keyPath]!; return Row( mainAxisSize: MainAxisSize.min, children: [ Transform.scale( scale: 0.75, child: DropdownButton( hint: Text('Select ${widget.keyPath.replaceAll("config.outbounds", "")}'), value: _text, icon: const Icon(Icons.arrow_downward), iconSize: 24, elevation: 16, underline: Container(height: 2), onChanged: (String? newValue) { widget.onChanged(newValue!); _text = newValue; setState(() { _text = newValue; }); }, items: options.map>((String value) { return DropdownMenuItem(value: value, child: Text(value)); }).toList(), ), ), ], ); } else if (widget.initialValue is bool) { return Row( mainAxisSize: MainAxisSize.min, children: [ Transform.scale( scale: 0.75, child: Checkbox( visualDensity: const VisualDensity(horizontal: -4, vertical: -4), value: _value, onChanged: (value) { widget.onChanged(value!); setState(() { _value = value; }); }, ), ), Text(_value.toString(), style: _textStyle), ], ); } else { if (_isFocused) { return TextFormField( initialValue: _text, focusNode: _focusNode, onChanged: (value) => _text = value, autocorrect: false, cursorWidth: 1, style: _textStyle, cursorHeight: 12, decoration: InputDecoration( constraints: _constraints, border: InputBorder.none, fillColor: Colors.transparent, filled: true, isDense: true, contentPadding: const EdgeInsets.all(3), focusedBorder: const OutlineInputBorder( borderRadius: BorderRadius.zero, borderSide: BorderSide(width: 0.3), ), ), ); } else { return InkWell( onTap: () { setState(() { _isFocused = true; }); _focusNode.requestFocus(); }, mouseCursor: WidgetStateMouseCursor.textable, child: widget.initialValue is String && _text.isEmpty ? const SizedBox(width: 400, height: 18) : wrapWithColoredBox(_text), ); } } } } class _Options extends StatelessWidget { const _Options(this.onSelected, this.keyPath); final String keyPath; final void Function(_OptionItems) onSelected; @override Widget build(BuildContext context) { return PopupMenuButton<_OptionItems>( tooltip: 'Add new object', padding: EdgeInsets.zero, onSelected: onSelected, itemBuilder: (context) { return >[ if (keyPath != "config" && T == Map) const _PopupMenuWidget( Row( mainAxisSize: MainAxisSize.min, children: [ SizedBox(width: 5), Icon(Icons.add), SizedBox(width: 10), Text("Insert", style: TextStyle(fontSize: 14)), ], ), ), if (keyPath != "config" && T == List) const _PopupMenuWidget( Row( mainAxisSize: MainAxisSize.min, children: [ SizedBox(width: 5), Icon(Icons.add), SizedBox(width: 10), Text("Append", style: TextStyle(fontSize: 14)), ], ), ), if (keyPath != "config" && (T == Map || T == List)) ...[ if ((keyPath == "config.outbounds" || keyPath == "config.endpoints") && T == List) ...[ for (final String key in protocolSchemaValues.keys) ...{ PopupMenuItem<_OptionItems>( height: _popupMenuHeight, padding: const EdgeInsets.only(left: _popupMenuItemPadding), value: key, child: Row( mainAxisSize: MainAxisSize.min, children: [ const Icon(Icons.data_object), const SizedBox(width: 10), Text(key, style: const TextStyle(fontSize: 14)), ], ), ), }, const PopupMenuDivider(height: 1), ], if (T == Map) ...[ for (final String key in exampleSchemaValues.keys) ...{ if (keyPath == key) for (final String key2 in exampleSchemaValues[key]!.keys) ...{ PopupMenuItem<_OptionItems>( height: _popupMenuHeight, padding: const EdgeInsets.only(left: _popupMenuItemPadding), value: key + "___" + key2, child: Row( mainAxisSize: MainAxisSize.min, children: [ const Icon(Icons.data_object), const SizedBox(width: 10), Text(key2, style: const TextStyle(fontSize: 14)), ], ), ), }, const PopupMenuDivider(height: 1), }, ], if (keyPath != "config" && !(T == List && (keyPath == "config.outbounds" || keyPath == "config.endpoints"))) ...[ const PopupMenuItem<_OptionItems>( height: _popupMenuHeight, padding: EdgeInsets.only(left: _popupMenuItemPadding), value: "string", child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.abc), SizedBox(width: 10), Text("String", style: TextStyle(fontSize: 14)), ], ), ), const PopupMenuItem<_OptionItems>( height: _popupMenuHeight, padding: EdgeInsets.only(left: _popupMenuItemPadding), value: "num", child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.onetwothree), SizedBox(width: 10), Text("Number", style: TextStyle(fontSize: 14)), ], ), ), const PopupMenuItem<_OptionItems>( height: _popupMenuHeight, padding: EdgeInsets.only(left: _popupMenuItemPadding), value: "bool", child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.check_rounded), SizedBox(width: 10), Text("Boolean", style: TextStyle(fontSize: 14)), ], ), ), const PopupMenuItem<_OptionItems>( height: _popupMenuHeight, padding: EdgeInsets.only(left: _popupMenuItemPadding), value: "map", child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.data_object), SizedBox(width: 10), Text("object", style: TextStyle(fontSize: 14)), ], ), ), const PopupMenuItem<_OptionItems>( height: _popupMenuHeight, padding: EdgeInsets.only(left: _popupMenuItemPadding), value: "list", child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.data_array), SizedBox(width: 10), Text("List", style: TextStyle(fontSize: 14)), ], ), ), ], ], const PopupMenuDivider(height: 1), if (keyPath != "config" && !(T == List && (keyPath == "config.outbounds" || keyPath == "config.endpoints"))) const PopupMenuItem<_OptionItems>( height: _popupMenuHeight, padding: EdgeInsets.only(left: 5), value: "delete", child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.delete), SizedBox(width: 10), Text("Delete", style: TextStyle(fontSize: 14)), ], ), ), ]; }, child: _options, ); } } class _PopupMenuWidget extends PopupMenuEntry { const _PopupMenuWidget(this.child); final Widget child; @override final double height = _popupMenuHeight; @override bool represents(_) => false; @override State<_PopupMenuWidget> createState() => _PopupMenuWidgetState(); } class _PopupMenuWidgetState extends State<_PopupMenuWidget> { @override Widget build(BuildContext context) { return widget.child; } } class _SearchField extends StatelessWidget { final ValueChanged onChanged; final ValueChanged<_SearchActions> onAction; const _SearchField(this.onChanged, this.onAction); @override Widget build(BuildContext context) { return ColoredBox( color: Theme.of(context).searchBarTheme.backgroundColor?.resolve({}) ?? Colors.black, child: Row( mainAxisSize: MainAxisSize.min, children: [ const SizedBox(width: 2), const Icon(Icons.search, size: 20), const SizedBox(width: 5), TextField( onChanged: onChanged, autocorrect: false, autofocus: true, cursorWidth: 1, // style: _textStyle, cursorHeight: 12, decoration: InputDecoration( hintText: "Search", hintStyle: Theme.of(context).textTheme.bodySmall, constraints: const BoxConstraints(maxWidth: 100), border: InputBorder.none, // fillColor: Colors.transparent, // filled: true, isDense: true, contentPadding: const EdgeInsets.all(3), focusedBorder: InputBorder.none, // hoverColor: Colors.transparent, ), ), const SizedBox(width: 5), InkWell( onTap: () { onAction(_SearchActions.next); }, child: const Tooltip(message: 'Next', child: Icon(Icons.keyboard_arrow_down_rounded, size: 20)), ), const SizedBox(width: 2), InkWell( onTap: () { onAction(_SearchActions.prev); }, child: const Tooltip(message: 'Previous', child: Icon(Icons.keyboard_arrow_up_rounded, size: 20)), ), const SizedBox(width: 5), ], ), ); } } List _getSpace(int count) { if (count == 0) return ['', ' ']; String space = ''; for (int i = 0; i < count; i++) { space += ' '; } return [space, '$space ']; } String _stringifyData(data, int spacing, [bool isLast = false]) { String str = ''; final spaceList = _getSpace(spacing); final objectSpace = spaceList[0]; final dataSpace = spaceList[1]; if (data is Map) { str += '$objectSpace{'; str += '\n'; final keys = data.keys.toList(); for (int i = 0; i < keys.length; i++) { str += '$dataSpace"${keys[i]}": ${_stringifyData(data[keys[i]], spacing + 1, i == keys.length - 1)}'; str += '\n'; } str += '$objectSpace}'; if (!isLast) str += ','; } else if (data is List) { str += '$objectSpace['; str += '\n'; for (int i = 0; i < data.length; i++) { final item = data[i]; if (item is Map || item is List) { str += _stringifyData(item, spacing + 1, i == data.length - 1); } else { str += '$dataSpace${_stringifyData(item, spacing + 1, i == data.length - 1)}'; } str += '\n'; } str += '$objectSpace]'; if (!isLast) str += ','; } else { if (data is String) { str = '"$data"'; } else { str = '$data'; } if (!isLast) str += ','; } return str; } ================================================ FILE: lib/features/profile/details/profile_details_notifier.dart ================================================ import 'dart:convert'; import 'dart:developer'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/model/failures.dart'; import 'package:hiddify/core/notification/in_app_notification_controller.dart'; import 'package:hiddify/core/router/dialog/dialog_notifier.dart'; import 'package:hiddify/features/profile/data/profile_data_providers.dart'; import 'package:hiddify/features/profile/data/profile_repository.dart'; import 'package:hiddify/features/profile/details/profile_details_state.dart'; import 'package:hiddify/features/profile/model/profile_entity.dart'; import 'package:hiddify/features/profile/model/profile_failure.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'profile_details_notifier.g.dart'; @riverpod class ProfileDetailsNotifier extends _$ProfileDetailsNotifier with AppLogger { ProfileRepository get _profilesRepo => ref.read(profileRepositoryProvider).requireValue; @override Future build(String id) async { final prof = (await _profilesRepo.getById(id).run()).match((l) => throw l, (prof) { // _originalProfile = prof; if (prof == null) { loggy.warning('profile with id: [$id] does not exist'); throw const ProfileNotFoundFailure(); } return prof; }); var profContent = ""; try { profContent = (await _profilesRepo.generateConfig(id).run()).match( (l) => throw Exception('Failed to generate config: $l'), (content) => content, ); } catch (e, st) { loggy.error('Error generating config for profile $id', e, st); // Optionally, you can set profContent to an empty string or keep the original content profContent = await _profilesRepo.getRawConfig(id).run().then((e) => e.getOrElse((f) => "")); } try { final jsonObject = jsonDecode(profContent); final List> outbounds = []; if (jsonObject is Map && jsonObject['outbounds'] is List) { for (final outbound in jsonObject['outbounds'] as List) { if (outbound is Map && outbound['type'] != null && !['selector', 'urltest', 'dns', 'block'].contains(outbound['type']) && !['direct', 'bypass', 'direct-fragment'].contains(outbound['tag'])) { outbounds.add(outbound); } } } else { // print('No outbounds found in the config'); } final endpoints = jsonObject['endpoints'] as List? ?? []; profContent = '{"outbounds": ${json.encode(outbounds)},"endpoints":${json.encode(endpoints)} }'; loggy.info(profContent); } catch (e, st) { loggy.error('Error parsing profile-content JSON', e, st); // rethrow; } return ProfileDetailsState( loadingState: const AsyncData(null), profile: prof, configContent: profContent, isDetailsChanged: false, ); } Future doAsync(Future Function() operation) async { if (state case AsyncData(value: final ProfileDetailsState data)) { state = AsyncData(data.copyWith(loadingState: const AsyncLoading())); final T? result = await operation(); state = AsyncData(data.copyWith(loadingState: const AsyncData(null))); return result; } return null; } void setUserOverride(UserOverride userOverride) { if (state case AsyncData(value: final ProfileDetailsState data)) { state = AsyncData( data.copyWith(profile: data.profile.copyWith(userOverride: userOverride), isDetailsChanged: true), ); } } void setContent(String configContent) { if (state case AsyncData(value: final ProfileDetailsState data)) { state = AsyncData(data.copyWith(configContent: configContent, isDetailsChanged: true)); } } Future save() async { bool success = false; if (state case AsyncData(:final value)) { if (value.loadingState case AsyncLoading()) return false; success = await doAsync(() async { final t = await ref.read(translationsProvider.future); return (await _profilesRepo.offlineUpdate(value.profile, value.configContent).run()).match( (l) async { await ref .read(dialogNotifierProvider.notifier) .showCustomAlertFromErr( t.presentError(l, action: t.pages.profiles.msg.update.failureNamed(name: value.profile.name)), ); return false; }, (r) { ref.read(inAppNotificationControllerProvider).showSuccessToast(t.pages.profiles.msg.update.success); return true; }, ); }) ?? false; } return success; } } ================================================ FILE: lib/features/profile/details/profile_details_page.dart ================================================ import 'dart:convert'; import 'package:fluentui_system_icons/fluentui_system_icons.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:gap/gap.dart'; import 'package:go_router/go_router.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/model/constants.dart'; import 'package:hiddify/core/model/failures.dart'; import 'package:hiddify/core/notification/in_app_notification_controller.dart'; import 'package:hiddify/features/profile/details/json_editor.dart'; import 'package:hiddify/features/profile/details/profile_details_notifier.dart'; import 'package:hiddify/features/profile/model/profile_entity.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class ProfileDetailsPage extends HookConsumerWidget with PresLogger { const ProfileDetailsPage({super.key, required this.id}); final String id; String _genSliderText(Translations t, int sliderValue) { if (sliderValue == 0) { return t.common.auto; } else if (sliderValue < 24) { return t.common.interval.hour(n: sliderValue); } final day = t.common.interval.day(n: sliderValue ~/ 24); final hour = t.common.interval.hour(n: sliderValue % 24); return '$day $hour'; } @override Widget build(BuildContext context, WidgetRef ref) { final theme = Theme.of(context); final t = ref.watch(translationsProvider).requireValue; final formKey = useMemoized(() => GlobalKey()); final provider = profileDetailsNotifierProvider(id); return ref .watch(ProfileDetailsNotifierProvider(id)) .when( data: (data) { final isLoading = data.loadingState is AsyncLoading; final userOverride = data.profile.userOverride ?? const UserOverride(); final sliderFocusNode = useFocusNode( onKeyEvent: (node, event) { if (KeyboardConst.verticalArrows.contains(event.logicalKey) && event is KeyDownEvent) { if (event.logicalKey == LogicalKeyboardKey.arrowUp) { node.previousFocus(); } else { node.nextFocus(); } return KeyEventResult.handled; } return KeyEventResult.ignored; }, ); return Scaffold( appBar: AppBar( title: Text(t.pages.profileDetails.title), actions: [ TextButton.icon( onPressed: isLoading || !data.isDetailsChanged ? null : () async { if (formKey.currentState!.validate()) { await ref.read(provider.notifier).save().then((success) { ref .read(inAppNotificationControllerProvider) .showSuccessToast(t.pages.profiles.msg.save.success); if (success && context.mounted) context.pop(); }); } }, icon: const Icon(Icons.check), label: Text(t.common.save), ), const Gap(8), ], ), body: ListView( children: [ Form( key: formKey, child: Column( children: [ Padding( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), child: CustomTextFormField( maxLines: 1, initialValue: userOverride.name ?? data.profile.name, validator: (value) => (value?.isEmpty ?? true) ? t.pages.profileDetails.form.emptyName : null, onChanged: (value) => ref .read(ProfileDetailsNotifierProvider(id).notifier) .setUserOverride(userOverride.copyWith(name: value)), label: t.common.name, hint: t.pages.profileDetails.form.nameHint, ), ), if (data.profile case RemoteProfileEntity(:final url)) Padding( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( t.common.url, style: theme.textTheme.labelMedium!.copyWith(color: theme.colorScheme.onSurface), maxLines: 1, overflow: TextOverflow.ellipsis, ), const Gap(4), SelectableText( url, style: theme.textTheme.bodySmall!.copyWith(color: theme.colorScheme.onSurfaceVariant), ), ], ), ), const Divider(indent: 16, endIndent: 16), if (data.profile case RemoteProfileEntity(:final options)) ...[ SwitchListTile.adaptive( title: Text( t.pages.profileDetails.form.disableAutoUpdate, style: theme.textTheme.titleSmall!.copyWith(color: theme.colorScheme.onSurface), ), value: userOverride.isAutoUpdateDisable, onChanged: (value) => ref .read(ProfileDetailsNotifierProvider(id).notifier) .setUserOverride(userOverride.copyWith(isAutoUpdateDisable: value)), ), AnimatedSize( alignment: Alignment.topCenter, duration: const Duration(milliseconds: 300), curve: Curves.easeInOut, child: !userOverride.isAutoUpdateDisable ? Column( children: [ const Divider(indent: 16, endIndent: 16), const Gap(12), Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: Row( children: [ Expanded( child: Text( t.pages.profileDetails.form.autoUpdateInterval, style: theme.textTheme.titleSmall!.copyWith( color: theme.colorScheme.onSurface, ), ), ), Text( _genSliderText(t, userOverride.updateInterval ?? 0), style: theme.textTheme.labelSmall!.copyWith( color: theme.colorScheme.onSurfaceVariant, ), ), ], ), ), const Gap(4), Padding( padding: const EdgeInsets.symmetric(horizontal: 10), child: Slider( focusNode: sliderFocusNode, value: userOverride.updateInterval?.toDouble() ?? options?.updateInterval.inHours.toDouble() ?? 0.0, max: 96, divisions: 96, label: (userOverride.updateInterval ?? 0).toString(), onChanged: (double value) => ref .read(ProfileDetailsNotifierProvider(id).notifier) .setUserOverride(userOverride.copyWith(updateInterval: value.toInt())), ), ), ], ) : const SizedBox.shrink(), ), const Divider(indent: 16, endIndent: 16), ], ListTile( title: Text(t.pages.profileDetails.lastUpdate), leading: const Icon(FluentIcons.history_24_regular), subtitle: Text(data.profile.lastUpdate.format()), dense: true, ), if (data.profile case RemoteProfileEntity(:final subInfo?)) ...[ const Divider(indent: 16, endIndent: 16), Align( alignment: AlignmentDirectional.centerStart, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 8), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text.rich( style: Theme.of(context).textTheme.bodySmall, TextSpan( children: [ _buildSubProp( FluentIcons.arrow_upload_16_regular, subInfo.upload.size(), t.components.subscriptionInfo.upload, ), const TextSpan(text: " "), _buildSubProp( FluentIcons.arrow_download_16_regular, subInfo.download.size(), t.components.subscriptionInfo.download, ), const TextSpan(text: " "), _buildSubProp( FluentIcons.arrow_bidirectional_up_down_16_regular, subInfo.total.size(), t.components.subscriptionInfo.total, ), ], ), ), const Gap(12), Text.rich( style: Theme.of(context).textTheme.bodySmall, TextSpan( children: [ _buildSubProp( FluentIcons.clock_dismiss_20_regular, subInfo.expire.format(), t.components.subscriptionInfo.expireDate, ), ], ), ), ], ), ), ), ], const Divider(), ], ), ), SizedBox( height: MediaQuery.of(context).size.height * 0.7, child: isJson(data.configContent) ? JsonEditor( expandedObjects: const ["outbounds", "endpoints"], onChanged: (value) { if (value == null) return; try { const encoder = JsonEncoder.withIndent(' '); ref.read(provider.notifier).setContent(encoder.convert(value)); } catch (e) { ref.read(provider.notifier).setContent("$value"); } }, enableHorizontalScroll: true, json: data.configContent, ) : TextFormField( onChanged: (value) { ref.read(provider.notifier).setContent(value); }, maxLines: null, minLines: null, expands: true, textAlignVertical: TextAlignVertical.top, decoration: const InputDecoration( border: InputBorder.none, contentPadding: EdgeInsets.only(left: 5, top: 8, bottom: 8), ), ), ), ], ), ); }, error: (error, stackTrace) => Scaffold( appBar: AppBar(title: Text(t.pages.profileDetails.title)), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(FluentIcons.error_circle_12_filled), Text(t.presentShortError(error)), Text(error.toString()), ], ), ), ), loading: () => Scaffold( appBar: AppBar(title: Text(t.pages.profileDetails.title)), body: const Center(child: CircularProgressIndicator()), ), ); } InlineSpan _buildSubProp(IconData icon, String text, String semanticLabel) { return TextSpan( children: [ WidgetSpan(child: Icon(icon, size: 16, semanticLabel: semanticLabel)), const TextSpan(text: " "), TextSpan(text: text), ], ); } } bool isJson(String value) { try { jsonDecode(value); return true; } catch (_) { return false; } } ================================================ FILE: lib/features/profile/details/profile_details_state.dart ================================================ import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:hiddify/features/profile/model/profile_entity.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; part 'profile_details_state.freezed.dart'; @freezed class ProfileDetailsState with _$ProfileDetailsState { const ProfileDetailsState._(); const factory ProfileDetailsState({ required AsyncValue loadingState, required ProfileEntity profile, required String configContent, required bool isDetailsChanged, }) = _ProfileDetailsState; bool get isLoading => loadingState is AsyncLoading; } ================================================ FILE: lib/features/profile/model/profile_entity.dart ================================================ import 'dart:convert'; import 'dart:math'; import 'package:dartx/dartx.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; part 'profile_entity.freezed.dart'; part 'profile_entity.g.dart'; enum ProfileType { remote, local } @freezed sealed class ProfileEntity with _$ProfileEntity { const ProfileEntity._(); const factory ProfileEntity.remote({ required String id, required bool active, required String name, required String url, required DateTime lastUpdate, ProfileOptions? options, SubscriptionInfo? subInfo, Map? populatedHeaders, String? profileOverride, UserOverride? userOverride, }) = RemoteProfileEntity; const factory ProfileEntity.local({ required String id, required bool active, required String name, required DateTime lastUpdate, Map? populatedHeaders, String? profileOverride, UserOverride? userOverride, }) = LocalProfileEntity; } @freezed class ProfileOptions with _$ProfileOptions { const factory ProfileOptions({@Default(Duration.zero) Duration updateInterval}) = _ProfileOptions; } @freezed class SubscriptionInfo with _$SubscriptionInfo { const SubscriptionInfo._(); const factory SubscriptionInfo({ required int upload, required int download, required int total, required DateTime expire, String? webPageUrl, String? supportUrl, }) = _SubscriptionInfo; bool get isExpired => expire <= DateTime.now(); int get consumption => upload + download; int get remainingBW => total - consumption; double get remainingBWratio => (remainingBW / total).clamp(0, 1); double get ratio => (consumption / total).clamp(0, 1); Duration get remaining => expire.difference(DateTime.now()); double get remainingRatio => min(remaining.inDays, 30) / 30; } const int latestUserOverrideVersion = 1; @freezed abstract class UserOverride with _$UserOverride { const UserOverride._(); const factory UserOverride({ @Default(latestUserOverrideVersion) int version, String? name, @Default(false) bool isAutoUpdateDisable, // hours int? updateInterval, bool? enableWarp, bool? enableFragment, }) = _UserOverride; factory UserOverride.fromJson(Map json) => _$UserOverrideFromJson(json); String toStr() => jsonEncode(toJson()); static UserOverride? fromStr(String? str) { if (str != null) { final m = (jsonDecode(str) as Map).cast(); return UserOverride.fromJson(_migrate(m)); } return null; } static Map _migrate(Map json) { final version = json['version'] as int? ?? 1; if (version < 2) { // Migration 1 to 2 } json['version'] = latestUserOverrideVersion; return json; } } ================================================ FILE: lib/features/profile/model/profile_failure.dart ================================================ import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/model/failures.dart'; import 'package:hiddify/features/settings/model/config_option_failure.dart'; part 'profile_failure.freezed.dart'; @freezed sealed class ProfileFailure with _$ProfileFailure, Failure { const ProfileFailure._(); @With() const factory ProfileFailure.unexpected([Object? error, StackTrace? stackTrace]) = ProfileUnexpectedFailure; const factory ProfileFailure.notFound() = ProfileNotFoundFailure; @With() const factory ProfileFailure.invalidUrl([String? message]) = ProfileInvalidUrlFailure; @With() const factory ProfileFailure.invalidConfig([String? message, ConfigOptionFailure? configOptionFailure]) = ProfileInvalidConfigFailure; @With() const factory ProfileFailure.cancelByUser([String? message]) = ProfileCancelByUserFailure; @override ({String type, String? message}) present(TranslationsEn t) { return switch (this) { ProfileUnexpectedFailure() => (type: t.errors.profiles.unexpected, message: null), ProfileNotFoundFailure() => (type: t.errors.profiles.notFound, message: null), ProfileInvalidUrlFailure(:final message) => (type: t.errors.profiles.invalidUrl, message: message), ProfileInvalidConfigFailure(:final message, :final configOptionFailure) => configOptionFailure?.present(t) ?? (type: t.errors.profiles.invalidConfig, message: message), ProfileCancelByUserFailure(:final message) => (type: t.errors.profiles.canceledByUser, message: message), }; } } ================================================ FILE: lib/features/profile/model/profile_sort_enum.dart ================================================ import 'package:fluentui_system_icons/fluentui_system_icons.dart'; import 'package:flutter/material.dart'; import 'package:hiddify/core/localization/translations.dart'; enum ProfilesSort { lastUpdate, name; String present(TranslationsEn t) { return switch (this) { lastUpdate => t.dialogs.sortProfiles.sort.name, name => t.dialogs.sortProfiles.sort.lastUpdate, }; } IconData get icon => switch (this) { lastUpdate => FluentIcons.history_24_regular, name => FluentIcons.text_sort_ascending_24_regular, }; } enum SortMode { ascending, descending } ================================================ FILE: lib/features/profile/notifier/active_profile_notifier.dart ================================================ import 'package:hiddify/features/profile/data/profile_data_mapper.dart'; import 'package:hiddify/features/profile/data/profile_data_providers.dart'; import 'package:hiddify/features/profile/model/profile_entity.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'active_profile_notifier.g.dart'; @Riverpod(keepAlive: true) class ActiveProfile extends _$ActiveProfile with AppLogger { @override Stream build() { loggy.debug("watching active profile"); return ref.watch(profileDataSourceProvider).watchActiveProfile().map((event) => event?.toEntity()); } } // TODO: move to specific feature @Riverpod(keepAlive: true) Stream hasAnyProfile(Ref ref) { return ref.watch(profileDataSourceProvider).watchProfilesCount().map((event) => event != 0).distinct(); } ================================================ FILE: lib/features/profile/notifier/profile_notifier.dart ================================================ import 'dart:async'; import 'dart:convert'; import 'package:dio/dio.dart'; import 'package:fpdart/fpdart.dart'; import 'package:hiddify/core/haptic/haptic_service.dart'; import 'package:hiddify/core/http_client/http_client_provider.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/model/failures.dart'; import 'package:hiddify/core/notification/in_app_notification_controller.dart'; import 'package:hiddify/core/router/dialog/dialog_notifier.dart'; import 'package:hiddify/features/connection/notifier/connection_notifier.dart'; import 'package:hiddify/features/profile/add/model/free_profiles_model.dart'; import 'package:hiddify/features/profile/data/profile_data_providers.dart'; import 'package:hiddify/features/profile/data/profile_repository.dart'; import 'package:hiddify/features/profile/model/profile_entity.dart'; import 'package:hiddify/features/profile/model/profile_failure.dart'; import 'package:hiddify/features/profile/notifier/active_profile_notifier.dart'; import 'package:hiddify/features/settings/data/config_option_repository.dart'; import 'package:hiddify/utils/riverpod_utils.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'profile_notifier.g.dart'; @riverpod class AddProfileNotifier extends _$AddProfileNotifier with AppLogger { @override AsyncValue build() { ref.disposeDelay(const Duration(minutes: 1)); ref.onDispose(() { loggy.debug("disposing"); _cancelToken?.cancel(); }); listenSelf((previous, next) { final t = ref.read(translationsProvider).requireValue; final notification = ref.read(inAppNotificationControllerProvider); switch (next) { case AsyncData(value: final _?): notification.showSuccessToast(t.pages.profiles.msg.save.success); case AsyncError(:final error): if (error case ProfileInvalidUrlFailure()) { notification.showErrorToast(t.pages.profiles.msg.invalidUrl); } else if (error case ProfileCancelByUserFailure()) { return; } else { ref .read(dialogNotifierProvider.notifier) .showCustomAlertFromErr(t.presentError(error, action: t.pages.profiles.msg.add.failure)); } } }); ref.onDispose(() { if (!(_cancelToken?.isCancelled ?? true)) _cancelToken?.cancel(); }); return const AsyncData(null); } ProfileRepository get _profilesRepo => ref.read(profileRepositoryProvider).requireValue; CancelToken? _cancelToken; Future addClipboard(String rawInput) async { if (state.isLoading) return; state = const AsyncLoading(); state = await AsyncValue.guard(() async { // final activeProfile = await ref.read(activeProfileProvider.future); // final markAsActive = activeProfile == null || ref.read(Preferences.markNewProfileActive); final TaskEither task; if (LinkParser.parse(rawInput) case (final rs)?) { loggy.debug("adding profile, url: [${rs.url}]"); task = _profilesRepo.upsertRemote( rs.url, userOverride: rs.name.isNotEmpty ? UserOverride(name: rs.name) : null, cancelToken: _cancelToken = CancelToken(), ); } else { loggy.debug("adding profile, content"); task = _profilesRepo.addLocal(safeDecodeBase64(rawInput)); } return await task .match( (err) { loggy.warning("failed to add profile", err); throw err; }, (_) { loggy.info("successfully added profile"); return unit; }, ) .run(); }); } Future addManual({required String url, required UserOverride userOverride}) async { if (state.isLoading) return; state = const AsyncLoading(); state = await AsyncValue.guard(() async { final task = _profilesRepo.upsertRemote(url, userOverride: userOverride); return await task .match( (err) { loggy.warning("failed to add profile", err); throw err; }, (r) { loggy.info("successfully added profile, mark as active? [true]"); return r; }, ) .run(); }); } } @riverpod class UpdateProfileNotifier extends _$UpdateProfileNotifier with AppLogger { @override AsyncValue build(String id) { ref.disposeDelay(const Duration(minutes: 1)); listenSelf((previous, next) { final t = ref.read(translationsProvider).requireValue; final notification = ref.read(inAppNotificationControllerProvider); switch (next) { case AsyncData(value: final _?): notification.showSuccessToast(t.pages.profiles.msg.update.success); case AsyncError(:final error): ref .read(dialogNotifierProvider.notifier) .showCustomAlertFromErr(t.presentError(error, action: t.pages.profiles.msg.update.failure)); } }); return const AsyncData(null); } ProfileRepository get _profilesRepo => ref.read(profileRepositoryProvider).requireValue; Future updateProfile(RemoteProfileEntity profile) async { if (state.isLoading) return; state = const AsyncLoading(); await ref.read(hapticServiceProvider.notifier).lightImpact(); state = await AsyncValue.guard(() async { return await _profilesRepo .upsertRemote(profile.url) .match( (err) { loggy.warning("failed to update profile", err); throw err; }, (_) async { loggy.info('successfully updated profile'); await ref.read(activeProfileProvider.future).then((active) async { if (active != null && active.id == profile.id) { await ref.read(connectionNotifierProvider.notifier).reconnect(profile); } }); return unit; }, ) .run(); }); } } @riverpod class FreeSwitchNotifier extends _$FreeSwitchNotifier { @override bool build() { return false; } Future onChange(bool value) async => state = value; } @riverpod class AddProfilePageNotifier extends _$AddProfilePageNotifier { @override AddProfilePages build() => AddProfilePages.options; void goOptions() => state = AddProfilePages.options; void goManual() => state = AddProfilePages.manual; } enum AddProfilePages { options, manual } @riverpod class FreeProfilesNotifier extends _$FreeProfilesNotifier { @override Future> build() async { final httpClient = ref.watch(httpClientProvider); final res = await httpClient.get( 'https://raw.githubusercontent.com/hiddify/hiddify-app/refs/heads/main/test.configs/free_configs', ); if (res.statusCode == 200) { return FreeProfilesModel.fromJson(jsonDecode(res.data.toString()) as Map).profiles; } return []; } } @riverpod Future> freeProfilesFilteredByRegion(Ref ref) async { final freeProfiles = await ref.watch(freeProfilesNotifierProvider.future); final region = ref.watch(ConfigOptions.region); return freeProfiles.where((e) => e.region.contains(region.name) || e.region.isEmpty).toList(); } ================================================ FILE: lib/features/profile/notifier/profiles_update_notifier.dart ================================================ import 'package:dartx/dartx.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/notification/in_app_notification_controller.dart'; import 'package:hiddify/core/preferences/general_preferences.dart'; import 'package:hiddify/core/preferences/preferences_provider.dart'; import 'package:hiddify/features/profile/data/profile_data_providers.dart'; import 'package:hiddify/features/profile/model/profile_entity.dart'; import 'package:hiddify/utils/custom_loggers.dart'; import 'package:meta/meta.dart'; import 'package:neat_periodic_task/neat_periodic_task.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'profiles_update_notifier.g.dart'; typedef ProfileUpdateStatus = ({String name, bool success}); @Riverpod(keepAlive: true) class ForegroundProfilesUpdateNotifier extends _$ForegroundProfilesUpdateNotifier with AppLogger { static const prefKey = "profiles_update_check"; static const interval = Duration(minutes: 15); @override Stream build() { var cycleCount = 0; _scheduler = NeatPeriodicTaskScheduler( name: 'profiles update worker', interval: interval, timeout: const Duration(minutes: 5), task: () async { loggy.debug("cycle [${cycleCount++}]"); await updateProfiles(); }, ); ref.onDispose(() async { await _scheduler?.stop(); _scheduler = null; }); if (ref.watch(Preferences.introCompleted)) { loggy.debug("intro done, starting"); _scheduler?.start(); } else { loggy.debug("intro in process, skipping"); } return const Stream.empty(); } NeatPeriodicTaskScheduler? _scheduler; bool _forceNextRun = false; Future trigger() async { loggy.debug("triggering update"); _forceNextRun = true; await _scheduler?.trigger(); } @visibleForTesting Future updateProfiles() async { var force = false; if (_forceNextRun) { force = true; _forceNextRun = false; } try { final previousRun = DateTime.tryParse(ref.read(sharedPreferencesProvider).requireValue.getString(prefKey) ?? ""); if (!force && previousRun != null && previousRun.add(interval) > DateTime.now()) { loggy.debug("too soon! previous run: [$previousRun]"); return; } loggy.debug("${force ? "[FORCED] " : ""}running, previous run: [$previousRun]"); final remoteProfiles = await ref .read(profileRepositoryProvider) .requireValue .watchAll() .map( (event) => event.getOrElse((f) { loggy.error("error getting profiles"); throw f; }).whereType(), ) .first; await for (final profile in Stream.fromIterable(remoteProfiles)) { final updateInterval = profile.options?.updateInterval; if (force || updateInterval != null && updateInterval <= DateTime.now().difference(profile.lastUpdate)) { final t = ref.read(translationsProvider).requireValue; await ref .read(profileRepositoryProvider) .requireValue .upsertRemote(profile.url) .mapLeft((l) { loggy.debug("error updating profile [${profile.id}]", l); ref .read(inAppNotificationControllerProvider) .showErrorToast(t.pages.profiles.msg.update.failureNamed(name: profile.name)); state = AsyncData((name: profile.name, success: false)); }) .map((_) { loggy.debug("profile [${profile.id}] updated successfully"); ref .read(inAppNotificationControllerProvider) .showSuccessToast(t.pages.profiles.msg.update.successNamed(name: profile.name)); state = AsyncData((name: profile.name, success: true)); }) .run(); } else { loggy.debug( "skipping profile [${profile.id}] update. last successful update: [${profile.lastUpdate}] - interval: [${profile.options?.updateInterval}]", ); } } } finally { await ref.read(sharedPreferencesProvider).requireValue.setString(prefKey, DateTime.now().toIso8601String()); } } } ================================================ FILE: lib/features/profile/overview/profiles_modal.dart ================================================ import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; import 'package:go_router/go_router.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/model/failures.dart'; import 'package:hiddify/core/router/dialog/dialog_notifier.dart'; import 'package:hiddify/features/profile/notifier/profiles_update_notifier.dart'; import 'package:hiddify/features/profile/overview/profiles_notifier.dart'; import 'package:hiddify/features/profile/widget/profile_tile.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class ProfilesModal extends HookConsumerWidget { const ProfilesModal({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; final asyncProfiles = ref.watch(profilesNotifierProvider); ref.listen(profilesNotifierProvider, (_, next) { if (next.hasValue && next.value!.isEmpty) { if (context.canPop()) context.pop(); } }); final initialSize = PlatformUtils.isDesktop ? .60 : .35; return SafeArea( child: asyncProfiles.when( data: (data) => DraggableScrollableSheet( initialChildSize: initialSize, maxChildSize: 0.85, expand: false, builder: (context, scrollController) => ScrollConfiguration( behavior: ScrollConfiguration.of(context).copyWith(scrollbars: false), child: Column( children: [ Expanded( child: ListView.separated( padding: const EdgeInsets.all(12), separatorBuilder: (context, index) => const Gap(12), // shrinkWrap: true, controller: scrollController, itemBuilder: (context, index) => ProfileTile(profile: data[index]), itemCount: data.length, ), ), Padding( padding: const EdgeInsets.symmetric(horizontal: 16).copyWith(bottom: 8), child: Wrap( spacing: 8, runSpacing: 8, alignment: WrapAlignment.center, crossAxisAlignment: WrapCrossAlignment.center, children: [ FilledButton.icon( label: Text(t.common.sort, maxLines: 1, overflow: TextOverflow.ellipsis), icon: const Icon(Icons.sort_rounded), onPressed: () => ref.read(dialogNotifierProvider.notifier).showSortProfiles(), ), FilledButton.icon( label: Text(t.pages.profiles.updateSubscriptions, maxLines: 1, overflow: TextOverflow.ellipsis), icon: const Icon(Icons.update_rounded), onPressed: () => ref.read(foregroundProfilesUpdateNotifierProvider.notifier).trigger(), ), ], ), ), ], ), ), ), loading: () => const Center(child: CircularProgressIndicator()), error: (error, stackTrace) => Text(t.presentShortError(error)), ), ); } } ================================================ FILE: lib/features/profile/overview/profiles_notifier.dart ================================================ import 'dart:async'; import 'package:flutter/services.dart'; import 'package:fpdart/fpdart.dart'; import 'package:hiddify/core/haptic/haptic_service.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/notification/in_app_notification_controller.dart'; import 'package:hiddify/features/connection/notifier/connection_notifier.dart'; import 'package:hiddify/features/profile/data/profile_data_providers.dart'; import 'package:hiddify/features/profile/data/profile_repository.dart'; import 'package:hiddify/features/profile/model/profile_entity.dart'; import 'package:hiddify/features/profile/model/profile_sort_enum.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'profiles_notifier.g.dart'; @riverpod class ProfilesSortNotifier extends _$ProfilesSortNotifier with AppLogger { @override ({ProfilesSort by, SortMode mode}) build() { return (by: ProfilesSort.lastUpdate, mode: SortMode.descending); } void changeSort(ProfilesSort sortBy) => state = (by: sortBy, mode: state.mode); void toggleMode() => state = (by: state.by, mode: state.mode == SortMode.ascending ? SortMode.descending : SortMode.ascending); } @riverpod class ProfilesNotifier extends _$ProfilesNotifier with AppLogger { @override Stream> build() { final sort = ref.watch(profilesSortNotifierProvider); return _profilesRepo.watchAll(sort: sort.by, sortMode: sort.mode).map((event) => event.getOrElse((l) => throw l)); } ProfileRepository get _profilesRepo => ref.read(profileRepositoryProvider).requireValue; Future selectActiveProfile(String id) async { loggy.debug('changing active profile to: [$id]'); await ref.read(hapticServiceProvider.notifier).lightImpact(); return _profilesRepo.setAsActive(id).getOrElse((err) { loggy.warning('failed to set [$id] as active profile', err); throw err; }).run(); } Future deleteProfile(ProfileEntity profile) async { loggy.debug('deleting profile: ${profile.name}'); if (profile.active) await ref.read(connectionNotifierProvider.notifier).abortConnection(); await _profilesRepo .deleteById(profile.id, profile.active) .match( (err) { loggy.warning('failed to delete profile', err); throw err; }, (_) { loggy.info('successfully deleted profile, was active? [${profile.active}]'); final t = ref.read(translationsProvider).requireValue; ref.read(inAppNotificationControllerProvider).showSuccessToast(t.pages.profiles.msg.delete.success); return unit; }, ) .run(); } Future exportConfigToClipboard(ProfileEntity profile) async { await _profilesRepo .generateConfig(profile.id) .match( (err) { loggy.warning('error generating config', err); throw err; }, (configJson) async { await Clipboard.setData(ClipboardData(text: configJson)); final t = ref.read(translationsProvider).requireValue; ref.read(inAppNotificationControllerProvider).showSuccessToast(t.common.msg.export.clipboard.success); }, ) .run(); } } ================================================ FILE: lib/features/profile/overview/profiles_page.dart ================================================ import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; import 'package:go_router/go_router.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/model/failures.dart'; import 'package:hiddify/core/router/bottom_sheets/bottom_sheets_notifier.dart'; import 'package:hiddify/core/router/dialog/dialog_notifier.dart'; import 'package:hiddify/features/profile/notifier/profiles_update_notifier.dart'; import 'package:hiddify/features/profile/overview/profiles_notifier.dart'; import 'package:hiddify/features/profile/widget/profile_tile.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class ProfilesPage extends HookConsumerWidget { const ProfilesPage({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; final asyncProfiles = ref.watch(profilesNotifierProvider); ref.listen(profilesNotifierProvider, (_, next) { if (next.hasValue && next.value!.isEmpty) { context.goNamed('home'); } }); return Scaffold( appBar: AppBar( title: Text(t.pages.profiles.title), actions: [ IconButton( onPressed: () => ref.read(foregroundProfilesUpdateNotifierProvider.notifier).trigger(), icon: const Icon(Icons.update_rounded), tooltip: t.pages.profiles.updateSubscriptions, ), IconButton( onPressed: () => ref.read(dialogNotifierProvider.notifier).showSortProfiles(), icon: const Icon(Icons.sort_rounded), tooltip: t.common.sort, ), const Gap(8), ], ), floatingActionButton: FloatingActionButton.extended( onPressed: () async => await ref.read(bottomSheetsNotifierProvider.notifier).showAddProfile(), label: Text(t.pages.profiles.add), icon: const Icon(Icons.add_rounded), ), body: asyncProfiles.when( data: (data) => ListView.separated( padding: const EdgeInsets.all(12).copyWith(bottom: 84), separatorBuilder: (context, index) => const Gap(12), itemBuilder: (context, index) => ProfileTile(profile: data[index]), itemCount: data.length, ), loading: () => const Center(child: CircularProgressIndicator()), error: (error, stackTrace) => Text(t.presentShortError(error)), ), ); } } ================================================ FILE: lib/features/profile/widget/profile_tile.dart ================================================ import 'package:fluentui_system_icons/fluentui_system_icons.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; import 'package:gap/gap.dart'; import 'package:go_router/go_router.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/model/constants.dart'; import 'package:hiddify/core/model/failures.dart'; import 'package:hiddify/core/notification/in_app_notification_controller.dart'; import 'package:hiddify/core/router/bottom_sheets/bottom_sheets_notifier.dart'; import 'package:hiddify/core/router/dialog/dialog_notifier.dart'; import 'package:hiddify/core/router/go_router/helper/active_breakpoint_notifier.dart'; import 'package:hiddify/core/widget/adaptive_icon.dart'; import 'package:hiddify/core/widget/adaptive_menu.dart'; import 'package:hiddify/features/profile/model/profile_entity.dart'; import 'package:hiddify/features/profile/notifier/profile_notifier.dart'; import 'package:hiddify/features/profile/overview/profiles_notifier.dart'; import 'package:hiddify/gen/fonts.gen.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:url_launcher/url_launcher.dart'; class ProfileTile extends HookConsumerWidget { const ProfileTile({super.key, required this.profile, this.isMain = false, this.margin = EdgeInsets.zero, this.color}); final ProfileEntity profile; /// home screen active profile card final bool isMain; final EdgeInsets margin; final Color? color; @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; final theme = Theme.of(context); final selectActiveMutation = useMutation( initialOnFailure: (err) { CustomToast.error(t.presentShortError(err)).show(context); }, initialOnSuccess: () { if (context.mounted && context.canPop()) context.pop(); }, ); final subInfo = switch (profile) { RemoteProfileEntity(:final subInfo) => subInfo, _ => null, }; final showActionButton = profile is RemoteProfileEntity || !isMain; // final effectiveMargin = isMain ? const EdgeInsets.symmetric(horizontal: 16, vertical: 8) : const EdgeInsets.only(left: 12, right: 12, bottom: 12); // final double effectiveElevation = profile.active ? 12 : 4; // final effectiveOutlineColor = profile.active ? theme.colorScheme.outline : Colors.transparent; return Card( // margin: effectiveMargin, // elevation: effectiveElevation, margin: margin, shape: RoundedRectangleBorder( side: profile.active ? BorderSide(color: theme.colorScheme.outline) : BorderSide.none, borderRadius: ProfileTileConst.cardBorderRadius, ), // color: color ?? theme.colorScheme.secondaryContainer, elevation: profile.active ? 0 : 1, // shadowColor: Colors.transparent, child: IntrinsicHeight( child: ConstrainedBox( constraints: const BoxConstraints(minHeight: 48), child: Row( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ if (showActionButton) ...[ SizedBox( width: 48, child: Semantics(sortKey: const OrdinalSortKey(1), child: ProfileActionButton(profile, !isMain)), ), if (profile.active) VerticalDivider(width: 1, color: theme.colorScheme.outline) else const Gap(1), ], Expanded( child: Semantics( button: true, sortKey: isMain ? const OrdinalSortKey(0) : null, focused: isMain, liveRegion: isMain, namesRoute: isMain, label: isMain ? t.pages.profiles.viewAllProfiles : null, child: InkWell( borderRadius: showActionButton ? ProfileTileConst.endBorderRadius(Directionality.of(context)) : ProfileTileConst.cardBorderRadius, onTap: () { if (isMain) { if (Breakpoint(context).isMobile()) { ref.read(bottomSheetsNotifierProvider.notifier).showProfilesOverview(); } else { context.goNamed('profiles'); } } else { if (selectActiveMutation.state.isInProgress) return; // if (profile.active) return; selectActiveMutation.setFuture( ref.read(profilesNotifierProvider.notifier).selectActiveProfile(profile.id), ); if (context.canPop()) { context.pop(); } else { context.goNamed('home'); } } }, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4), child: Column( // mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: [ if (isMain) Padding( padding: const EdgeInsets.symmetric(vertical: 4), child: Material( borderRadius: BorderRadius.circular(8), color: Colors.transparent, clipBehavior: Clip.antiAlias, child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Flexible( child: Text( profile.name, maxLines: 2, overflow: TextOverflow.ellipsis, style: theme.textTheme.titleMedium?.copyWith( fontFamily: PlatformUtils.isWindows ? FontFamily.emoji : null, ), semanticsLabel: t.pages.profiles.activeProfileName(name: profile.name), ), ), const Icon(Icons.arrow_drop_down_rounded), ], ), ), ) else Text( profile.name, maxLines: 2, overflow: TextOverflow.ellipsis, style: theme.textTheme.titleMedium?.copyWith( fontFamily: PlatformUtils.isWindows ? FontFamily.emoji : null, ), semanticsLabel: profile.active ? t.pages.profiles.activeProfileName(name: profile.name) : t.pages.profiles.nonActiveProfileName(name: profile.name), ), if (subInfo != null) ...[ const Gap(4), RemainingTrafficIndicator(subInfo.ratio), const Gap(4), ProfileSubscriptionInfo(subInfo), const Gap(4), ], ], ), ), ), ), ), ], ), ), ), ); } } class ProfileActionButton extends HookConsumerWidget { const ProfileActionButton(this.profile, this.showAllActions, {super.key}); final ProfileEntity profile; final bool showAllActions; @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; if (profile case RemoteProfileEntity() when !showAllActions) { return Semantics( button: true, enabled: !ref.watch(updateProfileNotifierProvider(profile.id)).isLoading, child: Tooltip( message: t.pages.profiles.update, child: InkWell( borderRadius: ProfileTileConst.startBorderRadius(Directionality.of(context)), onTap: () { if (ref.read(updateProfileNotifierProvider(profile.id)).isLoading) { return; } ref .read(updateProfileNotifierProvider(profile.id).notifier) .updateProfile(profile as RemoteProfileEntity); }, child: const Icon(Icons.update_rounded), ), ), ); } return ProfileActionsMenu(profile, (context, toggleVisibility, _) { return Semantics( button: true, child: Tooltip( message: MaterialLocalizations.of(context).showMenuTooltip, child: InkWell( borderRadius: ProfileTileConst.startBorderRadius(Directionality.of(context)), onTap: toggleVisibility, child: Icon(AdaptiveIcon(context).more), ), ), ); }); } } class ProfileActionsMenu extends HookConsumerWidget { const ProfileActionsMenu(this.profile, this.builder, {super.key, this.child}); final ProfileEntity profile; final AdaptiveMenuBuilder builder; final Widget? child; @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; final menuItems = [ if (profile case RemoteProfileEntity()) AdaptiveMenuItem( title: t.common.update, icon: Icons.update_rounded, onTap: () { if (ref.read(updateProfileNotifierProvider(profile.id)).isLoading) { return; } ref.read(updateProfileNotifierProvider(profile.id).notifier).updateProfile(profile as RemoteProfileEntity); }, ), AdaptiveMenuItem( title: t.common.share, icon: AdaptiveIcon(context).share, subItems: [ if (profile case RemoteProfileEntity(:final url, :final name)) ...[ AdaptiveMenuItem( title: t.pages.profiles.share.urlToClipboard, onTap: () async { final link = LinkParser.generateSubShareLink(url, name); if (link.isNotEmpty) { await Clipboard.setData(ClipboardData(text: link)); if (context.mounted) { ref .read(inAppNotificationControllerProvider) .showSuccessToast(t.common.msg.export.clipboard.success); } } }, ), AdaptiveMenuItem( title: t.pages.profiles.share.showUrlQr, onTap: () async { final link = LinkParser.generateSubShareLink(url, name); if (link.isNotEmpty) { await ref.read(dialogNotifierProvider.notifier).showQrCode(link, message: name); } }, ), ], AdaptiveMenuItem( title: t.pages.profiles.share.jsonToClipboard, onTap: () async => await ref.read(profilesNotifierProvider.notifier).exportConfigToClipboard(profile), ), ], ), AdaptiveMenuItem( icon: Icons.edit_rounded, title: t.common.edit, onTap: () { if (Breakpoint(context).isMobile()) context.pop(); context.goNamed('profileDetails', pathParameters: {'id': profile.id}); }, ), // if (!profile.active) AdaptiveMenuItem( icon: Icons.delete_outline_rounded, title: t.common.delete, onTap: () async => await ref .read(dialogNotifierProvider.notifier) .showConfirmation( title: t.dialogs.confirmation.profile.delete.title, message: t.dialogs.confirmation.profile.delete.msg, ) .then((deleteConfirmed) async { if (!deleteConfirmed) return; await ref.read(profilesNotifierProvider.notifier).deleteProfile(profile); }), ), ]; return AdaptiveMenu(builder: builder, items: menuItems, child: child); } } // TODO add support url class ProfileSubscriptionInfo extends HookConsumerWidget { const ProfileSubscriptionInfo(this.subInfo, {super.key}); final SubscriptionInfo subInfo; (String, Color?) remainingText(TranslationsEn t, ThemeData theme) { if (subInfo.isExpired) { return (t.components.subscriptionInfo.expired, theme.colorScheme.error); } else if (subInfo.ratio >= 1) { return (t.components.subscriptionInfo.noTraffic, theme.colorScheme.error); } else if (subInfo.remaining.inDays > 365) { return (t.components.subscriptionInfo.remainingDuration(duration: "∞"), null); } else { return (t.components.subscriptionInfo.remainingDuration(duration: subInfo.remaining.inDays), null); } } @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; final theme = Theme.of(context); final remaining = remainingText(t, theme); return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Directionality( textDirection: TextDirection.ltr, child: Flexible( child: Text( subInfo.total > 10 * 1099511627776 //10TB ? "∞ GiB" : subInfo.consumption.sizeOf(subInfo.total), semanticsLabel: t.components.subscriptionInfo.remainingTrafficSemanticLabel( consumed: subInfo.consumption.sizeGB(), total: subInfo.total.sizeGB(), ), style: theme.textTheme.bodySmall, overflow: TextOverflow.ellipsis, ), ), ), Flexible( child: Text( remaining.$1, style: theme.textTheme.bodySmall?.copyWith(color: remaining.$2), overflow: TextOverflow.ellipsis, ), ), ], ); } } // TODO add support url class NewTrafficSubscriptionInfo extends HookConsumerWidget { const NewTrafficSubscriptionInfo(this.subInfo, {super.key}); final SubscriptionInfo subInfo; @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; return Column( children: [ const Icon(Icons.assessment_rounded, color: Colors.blue), Text(t.components.subscriptionInfo.remainingTraffic), const SizedBox(height: 4), Row( mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.min, children: [ Directionality( textDirection: TextDirection.ltr, child: Text( subInfo.total > 10 * 1099511627776 //10TB ? "∞ GiB" : subInfo.consumption.sizeOf(subInfo.total), semanticsLabel: t.components.subscriptionInfo.remainingTrafficSemanticLabel( consumed: subInfo.consumption.sizeGB(), total: subInfo.total.sizeGB(), ), // style: theme.textTheme.body, overflow: TextOverflow.ellipsis, ), ), ], ), ], ); } } // TODO add support url class NewDaySubscriptionInfo extends HookConsumerWidget { const NewDaySubscriptionInfo(this.subInfo, {super.key}); final SubscriptionInfo subInfo; (String, Color?) remainingText(TranslationsEn t, ThemeData theme) { if (subInfo.isExpired) { return (t.components.subscriptionInfo.expired, theme.colorScheme.error); } else if (subInfo.ratio >= 1) { return (t.components.subscriptionInfo.noTraffic, theme.colorScheme.error); } else if (subInfo.remaining.inDays > 365) { return (t.components.subscriptionInfo.remainingDurationNew(duration: "∞"), null); } else { return (t.components.subscriptionInfo.remainingDurationNew(duration: subInfo.remaining.inDays), null); } } @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; final theme = Theme.of(context); final remaining = remainingText(t, theme); return Column( children: [ const Icon(Icons.timer, color: Colors.blue), Text(t.components.subscriptionInfo.remainingTime), const SizedBox(height: 4), Row( mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.min, children: [ Flexible( child: Text( remaining.$1, // style: theme.textTheme.bodySmall?.copyWith(color: remaining.$2), overflow: TextOverflow.ellipsis, ), ), ], ), ], ); } } // TODO add support url class NewDayTrafficSubscriptionInfo extends HookConsumerWidget { const NewDayTrafficSubscriptionInfo(this.subInfo, {super.key}); final SubscriptionInfo subInfo; (String, Color?) remainingText(TranslationsEn t, ThemeData theme) { if (subInfo.isExpired) { return (t.components.subscriptionInfo.expired, theme.colorScheme.error); } else if (subInfo.ratio >= 1) { return (t.components.subscriptionInfo.noTraffic, theme.colorScheme.error); } else if (subInfo.remaining.inDays > 365) { return (t.components.subscriptionInfo.remainingDurationNew(duration: "∞"), null); } else { return (t.components.subscriptionInfo.remainingDurationNew(duration: subInfo.remaining.inDays), null); } } @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; final theme = Theme.of(context); final remaining = remainingText(t, theme); return Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.assessment_rounded, color: Colors.blue), Text(t.components.subscriptionInfo.remainingUsage), const SizedBox(height: 4), Text( remaining.$1, // style: theme.textTheme.bodySmall?.copyWith(color: remaining.$2), overflow: TextOverflow.ellipsis, ), Directionality( textDirection: TextDirection.ltr, child: Text( subInfo.total > 10 * 1099511627776 //10TB ? "∞ GiB" : subInfo.consumption.sizeOf(subInfo.total), semanticsLabel: t.components.subscriptionInfo.remainingTrafficSemanticLabel( consumed: subInfo.consumption.sizeGB(), total: subInfo.total.sizeGB(), ), // style: theme.textTheme.body, overflow: TextOverflow.ellipsis, ), ), ], ); } } class NewSiteSubscriptionInfo extends HookConsumerWidget { const NewSiteSubscriptionInfo(this.subInfo, {super.key}); final SubscriptionInfo subInfo; @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; final uri = Uri.parse(subInfo.webPageUrl ?? ""); var host = uri.host; if (["telegram.me", "t.me"].contains(host)) { host = "@${uri.path.split("/").last}"; } return InkWell( onTap: () => launchUrl(Uri.parse(subInfo.webPageUrl ?? "")), child: Column( children: [ const Icon(FluentIcons.globe_person_24_filled, size: 24, color: Colors.blue), Text(t.components.subscriptionInfo.profileSite), const SizedBox(height: 4), Row( mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.min, children: [ Flexible( child: Text( host, // style: theme.textTheme.bodySmall?.copyWith(color: remaining.$2), overflow: TextOverflow.ellipsis, ), ), ], ), ], ), ); } } // TODO change colors class RemainingTrafficIndicator extends StatelessWidget { const RemainingTrafficIndicator(this.ratio, {super.key}); final double ratio; @override Widget build(BuildContext context) { // final startColor = ratio < 0.25 // ? const Color.fromRGBO(93, 205, 251, 1.0) // : ratio < 0.65 // ? const Color.fromRGBO(205, 199, 64, 1.0) // : const Color.fromRGBO(241, 82, 81, 1.0); // final endColor = ratio < 0.25 // ? const Color.fromRGBO(49, 146, 248, 1.0) // : ratio < 0.65 // ? const Color.fromRGBO(98, 115, 32, 1.0) // : const Color.fromRGBO(139, 30, 36, 1.0); return LinearProgressIndicator(value: ratio, borderRadius: BorderRadius.circular(16), minHeight: 6); // return HorizontalPercentIndicator( // height: 6, // borderRadius: 16, // loadingPercent: ratio, // // inactiveTrackColor: Color.fromRGBO(r, g, b, opacity), // activeTrackColor: [startColor, endColor], // ); // return LinearPercentIndicator( // // percent: ratio, // // animation: false, // // padding: EdgeInsets.zero, // // lineHeight: 6, // // barRadius: const Radius.circular(16), // // linearGradient: LinearGradient( // // colors: [startColor, endColor], // // ), // ); } } ================================================ FILE: lib/features/profile/widget/profile_tile_main.dart ================================================ import 'package:fluentui_system_icons/fluentui_system_icons.dart'; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:gap/gap.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/router/dialog/dialog_notifier.dart'; import 'package:hiddify/features/profile/model/profile_entity.dart'; import 'package:hiddify/features/profile/notifier/profile_notifier.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:url_launcher/url_launcher.dart'; class ProfileTileMain extends HookConsumerWidget { const ProfileTileMain({super.key, required this.profile, this.isMain = false}); final ProfileEntity profile; final bool isMain; static const verifiedDomains = [ 'hiddify.com', // 't.me', // 'telegram.me', // 'instagram.com', // 'x.com', // 'facebook.com', ]; static const verifiedLinks = [ 'https://t.me/hiddify', 'https://t.me/hiddify_board', 'https://instagram.com/hiddify_com', 'https://x.com/hiddify_com', 'https://facebook.com/hiddify', ]; Future _launchUrlWithCheck(BuildContext context, WidgetRef ref, String url) async { final uri = Uri.parse(url); final host = uri.host.toLowerCase(); if (verifiedDomains.any((p) => host == p || host.endsWith(".$p")) || verifiedLinks.any((p) => url == p)) { await launchUrl(uri); return; } // Show warning dialog for unknown domains final shouldLaunch = await ref.read(dialogNotifierProvider.notifier).showUnknownDomainsWarning(url: url); if (shouldLaunch == true) { await launchUrl(uri); } } @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; final theme = Theme.of(context); final subInfo = switch (profile) { RemoteProfileEntity(:final subInfo) => subInfo, _ => null, }; if (!isMain) return const Card(); return Material( color: Colors.transparent, child: Column( mainAxisSize: MainAxisSize.min, children: [ Padding( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), child: InkWell( onTap: () => ref .read(updateProfileNotifierProvider(profile.id).notifier) .updateProfile(profile as RemoteProfileEntity), borderRadius: BorderRadius.circular(12), child: Row( mainAxisSize: MainAxisSize.min, children: [ Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: theme.colorScheme.primaryContainer.withOpacity(0.1), borderRadius: BorderRadius.circular(8), ), child: Icon(FluentIcons.arrow_sync_24_filled, color: theme.colorScheme.primary, size: 20), ), const Gap(6), Text( profile.name, style: theme.textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w600), maxLines: 1, overflow: TextOverflow.ellipsis, ), ], ), ), ), if (subInfo != null) Container( width: 350, child: Column( mainAxisSize: MainAxisSize.min, children: [ IntrinsicHeight( // Add this to ensure equal height child: Row( children: [ if (subInfo.total > 0) _BandwithUsageRow(subInfo), // if (subInfo.total > 0 && subInfo.remaining.inDays > 0) // const VerticalDivider( // // Add divider between items // width: 1, // thickness: 1, // indent: 12, // endIndent: 12, // ), if (subInfo.remaining.inDays > 0) // Add Expanded _UsageRow( icon: null, //FluentIcons.timer_24_regular, title: subInfo.remaining.inDays > 365 ? "∞ days remaining" : "${subInfo.remaining.inDays}/30 days remaining", progress: subInfo.remaining.inDays > 365 ? 0 : subInfo.remaining.inDays / 30, color: _getProgressColor(1 - (subInfo.remaining.inDays / 30)), ), ], ), ), if ((subInfo.webPageUrl != null || subInfo.supportUrl != null)) Padding( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), child: Row( children: [ if (subInfo.webPageUrl != null) Expanded( child: InkWell( onTap: () => _launchUrlWithCheck(context, ref, subInfo.webPageUrl!), borderRadius: BorderRadius.circular(8), child: _InfoItem( icon: _getLinkIcon(subInfo.webPageUrl!, FluentIcons.building_shop_24_regular), label: t.components.subscriptionInfo.profileSite, value: _formatSupportLink(subInfo.webPageUrl!), ), ), ), if (subInfo.supportUrl != null) ...[ const Gap(12), Expanded( child: InkWell( onTap: () => _launchUrlWithCheck(context, ref, subInfo.supportUrl!), borderRadius: BorderRadius.circular(8), child: _InfoItem( icon: _getLinkIcon(subInfo.supportUrl!, FontAwesomeIcons.headset), label: t.components.subscriptionInfo.profileSupport, value: _formatSupportLink(subInfo.supportUrl!), ), ), ), ], ], ), ), ], ), ), ], ), ); } IconData _getLinkIcon(String url, [IconData? icon]) { final uri = Uri.parse(url); final host = uri.host.toLowerCase(); if (host.endsWith('telegram.me') || host.endsWith('t.me')) { return FontAwesomeIcons.telegram; } if (host.endsWith('instagram.com')) { return FontAwesomeIcons.instagram; } if (host.endsWith('twitter.com')) { return FontAwesomeIcons.xTwitter; } if (host.endsWith('facebook.com')) { return FontAwesomeIcons.facebook; } if (host.endsWith('hiddify.com')) { // return IconData(); } return icon ?? FluentIcons.link_24_regular; } String _formatSupportLink(String url) { final uri = Uri.parse(url); final host = uri.host.toLowerCase(); if (host.endsWith('telegram.me') || host.endsWith('t.me')) { return "@${uri.pathSegments.last}"; } if (host.endsWith('instagram.com')) { return "@${uri.pathSegments.first}"; } if (host.endsWith('twitter.com')) { return "@${uri.pathSegments.first}"; } if (host.endsWith('facebook.com')) { return uri.pathSegments.lastWhere((e) => e.isNotEmpty, orElse: () => ''); } if (host.endsWith('hiddify.com')) { return "Hiddify"; } return uri.host; } Color _getProgressColor(double ratio) { if (ratio < 0.25) return Colors.red; if (ratio < 0.45) return Colors.orange; return Colors.green; } Widget _BandwithUsageRow(SubscriptionInfo subInfo) { return _UsageRow( icon: FluentIcons.data_usage_24_filled, title: subInfo.total.isInfinitSize() ? "∞ GB remaining" : "${subInfo.remainingBWratio * 100}% remaining", progress: subInfo.total.isInfinitSize() ? 1 : subInfo.remainingBWratio, color: _getProgressColor(subInfo.remainingBWratio), ); } } // Rest of the widget classes remain the same... class _UsageRow extends StatelessWidget { const _UsageRow({required this.icon, required this.title, required this.progress, required this.color}); final IconData? icon; final String title; final double progress; final Color color; @override Widget build(BuildContext context) { return Expanded( child: Padding( padding: const EdgeInsets.all(12), child: Row( children: [ if (icon != null) ...[Icon(icon, size: 20, color: color), const Gap(12)], Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(title, style: Theme.of(context).textTheme.bodyMedium), const Gap(4), ClipRRect( borderRadius: BorderRadius.circular(4), child: LinearProgressIndicator( value: progress, backgroundColor: Theme.of(context).colorScheme.surfaceVariant, valueColor: AlwaysStoppedAnimation(color), minHeight: 4, ), ), ], ), ), ], ), ), ); } } class _InfoItem extends StatelessWidget { const _InfoItem({required this.icon, required this.label, required this.value}); final IconData icon; final String label; final String value; @override Widget build(BuildContext context) { final theme = Theme.of(context); return Container( padding: const EdgeInsets.all(12), // decoration: BoxDecoration( // color: theme.colorScheme.surfaceVariant.withOpacity(0.5), // borderRadius: BorderRadius.circular(12), // ), child: Row( children: [ Icon(icon, size: 20, color: theme.colorScheme.onSurfaceVariant), const Gap(12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( label, style: theme.textTheme.bodySmall?.copyWith( color: theme.colorScheme.onSurfaceVariant.withOpacity(0.7), ), ), Text(value, style: theme.textTheme.bodyMedium, maxLines: 1, overflow: TextOverflow.ellipsis), ], ), ), ], ), ); } } ================================================ FILE: lib/features/proxy/active/active_proxy_card.dart ================================================ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/router/dialog/dialog_notifier.dart'; import 'package:hiddify/features/connection/model/connection_status.dart'; import 'package:hiddify/features/connection/notifier/connection_notifier.dart'; import 'package:hiddify/features/proxy/active/active_proxy_notifier.dart'; import 'package:hiddify/features/proxy/active/ip_widget.dart'; import 'package:hiddify/hiddifycore/generated/v2/hcore/hcore.pb.dart'; import 'package:hiddify/utils/custom_loggers.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class ActiveProxyFooter extends ConsumerWidget with InfraLogger { const ActiveProxyFooter({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final connectionState = ref.watch( connectionNotifierProvider.select((value) => value.valueOrNull ?? const Disconnected()), ); final activeProxy = ref.watch(activeProxyNotifierProvider.select((value) => value.valueOrNull)); final t = ref.watch(translationsProvider).requireValue; // Early return if required data is not available if (connectionState != const Connected() || activeProxy == null) { return const SizedBox.shrink(); } final theme = Theme.of(context); // Handle URL test in a way that won't trigger during build Future handleUrlTest() async { try { if (!context.mounted) return; await ref.read(activeProxyNotifierProvider.notifier).urlTest(""); } catch (e) { // Handle error here loggy.error("Error during URL test: $e"); } } return Container( margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 16), padding: const EdgeInsets.symmetric(vertical: 12), decoration: BoxDecoration( color: theme.colorScheme.background.withOpacity(1), borderRadius: BorderRadius.circular(20), boxShadow: [ BoxShadow(color: theme.colorScheme.secondary.withOpacity(.21), blurRadius: 10, offset: const Offset(0, 4)), ], ), child: InkWell( onTap: () { context.goNamed('proxies'); }, child: Row( children: [ InkWell( onTap: () async { await handleUrlTest(); await ref.read(dialogNotifierProvider.notifier).showProxyInfo(outboundInfo: activeProxy); }, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 8), child: IPCountryFlag( countryCode: activeProxy.ipinfo.countryCode, organization: activeProxy.ipinfo.org, size: 48, ), ), ), Expanded( child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: [ Semantics( label: t.pages.proxies.activeProxy, child: Text( // getRealOutboundTag(activeProxy), activeProxy.tagDisplay, style: theme.textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.bold), maxLines: 1, overflow: TextOverflow.ellipsis, ), ), const SizedBox(height: 4), Row( children: [ if (activeProxy.ipinfo.ip.isNotEmpty) IPText(ip: activeProxy.ipinfo.ip, onLongPress: handleUrlTest, constrained: true) else UnknownIPText(text: t.pages.proxies.unknownIp, onTap: handleUrlTest), const Spacer(), Text( // getRealOutboundTag(activeProxy), activeProxy.type, style: theme.textTheme.bodySmall?.copyWith(fontWeight: FontWeight.bold), maxLines: 1, overflow: TextOverflow.ellipsis, ), ], ), ], ), ), const Padding( padding: EdgeInsets.all(16.0), child: Icon(Icons.arrow_forward_ios, color: Colors.blue), ), ], ), ), ); } } String getRealOutboundTag(OutboundInfo group) { var tag = group.tagDisplay; if (group.groupSelectedTagDisplay != "" && group.groupSelectedTagDisplay != tag) { tag = "$tag → ${group.groupSelectedTagDisplay}"; } return tag; } // class _StatsColumn extends HookConsumerWidget { // const _StatsColumn(); // @override // Widget build(BuildContext context, WidgetRef ref) { // final t = ref.watch(translationsProvider).requireValue; // final stats = ref.watch(statsNotifierProvider).value; // return Directionality( // textDirection: TextDirection.values[(Directionality.of(context).index + 1) % TextDirection.values.length], // child: Flexible( // child: Column( // children: [ // _InfoProp( // icon: FluentIcons.arrow_bidirectional_up_down_20_regular, // text: (stats?.downlinkTotal ?? 0).size(), // semanticLabel: t.stats.totalTransferred, // ), // const Gap(8), // _InfoProp( // icon: FluentIcons.arrow_download_20_regular, // text: (stats?.downlink ?? 0).speed(), // semanticLabel: t.stats.speed, // ), // ], // ), // ), // ); // } // } // class _InfoProp extends StatelessWidget { // const _InfoProp({ // required this.icon, // required this.text, // this.semanticLabel, // }); // final IconData icon; // final String text; // final String? semanticLabel; // @override // Widget build(BuildContext context) { // return Semantics( // label: semanticLabel, // child: Row( // children: [ // Icon(icon), // const Gap(8), // Flexible( // child: Text( // text, // style: Theme.of(context).textTheme.labelMedium?.copyWith(fontFamily: FontFamily.emoji), // overflow: TextOverflow.ellipsis, // ), // ), // ], // ), // ); // } // } ================================================ FILE: lib/features/proxy/active/active_proxy_delay_indicator.dart ================================================ import 'package:fluentui_system_icons/fluentui_system_icons.dart'; import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/widget/shimmer_skeleton.dart'; import 'package:hiddify/features/proxy/active/active_proxy_notifier.dart'; import 'package:hiddify/utils/custom_loggers.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class ActiveProxyDelayIndicator extends HookConsumerWidget with InfraLogger { const ActiveProxyDelayIndicator({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; final activeProxy = ref.watch(activeProxyNotifierProvider); final theme = Theme.of(context); if (activeProxy is! AsyncData) { return const SizedBox(); // Avoid building widget if data is not available } final proxy = activeProxy.value!; final delay = proxy.urlTestDelay; final timeout = delay > 65000; return Center( child: InkWell( onTap: () async { try { await ref.read(activeProxyNotifierProvider.notifier).urlTest(""); } catch (e) { // Handle error here loggy.error("Error during URL test: $e"); } }, borderRadius: BorderRadius.circular(24), child: Padding( padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16), child: Row( mainAxisSize: MainAxisSize.min, children: [ const Icon(FluentIcons.wifi_1_24_regular), const Gap(8), if (delay > 0) Text.rich( semanticsLabel: timeout ? t.pages.proxies.delay.timeout : t.pages.proxies.delay.result(delay: delay), TextSpan( children: [ if (timeout) TextSpan( text: t.common.timeout, style: theme.textTheme.titleMedium?.copyWith( fontWeight: FontWeight.bold, color: theme.colorScheme.error, ), ) else ...[ TextSpan( text: delay.toString(), style: theme.textTheme.titleMedium?.copyWith(fontWeight: FontWeight.bold), ), const TextSpan(text: " ms"), ], ], ), ) else Semantics(label: t.pages.proxies.delay.testing, child: const ShimmerSkeleton(width: 48, height: 18)), ], ), ), ), ); } } ================================================ FILE: lib/features/proxy/active/active_proxy_footer_old.dart ================================================ // class ActiveProxyFooter extends HookConsumerWidget { // const ActiveProxyFooter({super.key}); // @override // Widget build(BuildContext context, WidgetRef ref) { // return Column(); // final t = ref.watch(translationsProvider).requireValue; // final activeProxy = ref.watch(activeProxyNotifierProvider); // // final ipInfo = ref.watch(ipInfoNotifierProvider); // return AnimatedVisibility( // axis: Axis.vertical, // visible: activeProxy is AsyncData, // child: switch (activeProxy) { // AsyncData(value: final proxy) => Padding( // padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16), // child: Row( // mainAxisAlignment: MainAxisAlignment.spaceBetween, // children: [ // Flexible( // child: Column( // crossAxisAlignment: CrossAxisAlignment.start, // children: [ // _InfoProp( // icon: FluentIcons.arrow_routing_20_regular, // text: proxy.tagDisplay, // semanticLabel: t.proxies.activeProxySemanticLabel, // ), // const Gap(8), // if (proxy.ipinfo.ip.isNotEmpty) // Row( // children: [ // IPCountryFlag(countryCode: proxy.ipinfo.countryCode), // const Gap(4), // OrganisationFlag(organization: proxy.ipinfo.org), // const Gap(4), // IPText( // ip: proxy.ipinfo.ip, // onLongPress: () async { // ref.read(ipInfoNotifierProvider.notifier).refresh(); // }, // ), // // const Gap(8), // ], // ) // else // Row( // children: [ // const Icon(FluentIcons.arrow_sync_20_regular), // const Gap(8), // UnknownIPText( // text: t.proxies.checkIp, // onTap: () async { // ref.read(ipInfoNotifierProvider.notifier).refresh(); // }, // ), // ], // ), // // switch (ipInfo) { // // AsyncData(value: final info) => Row( // // children: [ // // IPCountryFlag(countryCode: info.countryCode), // // const Gap(4), // // OrganisationFlag(organization: info.org ?? ""), // // const Gap(4), // // IPText( // // ip: info.ip, // // onLongPress: () async { // // ref.read(ipInfoNotifierProvider.notifier).refresh(); // // }, // // ), // // // const Gap(8), // // ], // // ), // // AsyncError(error: final UnknownIp _) => Row( // // children: [ // // const Icon(FluentIcons.arrow_sync_20_regular), // // const Gap(8), // // UnknownIPText( // // text: t.proxies.checkIp, // // onTap: () async { // // ref.read(ipInfoNotifierProvider.notifier).refresh(); // // }, // // ), // // ], // // ), // // AsyncError() => Row( // // children: [ // // const Icon(FluentIcons.error_circle_20_regular), // // const Gap(8), // // UnknownIPText( // // text: t.proxies.unknownIp, // // onTap: () async { // // ref.read(ipInfoNotifierProvider.notifier).refresh(); // // }, // // ), // // ], // // ), // // _ => const Row( // // children: [ // // Icon(FluentIcons.question_circle_20_regular), // // Gap(8), // // Flexible( // // child: ShimmerSkeleton( // // height: 16, // // widthFactor: 1, // // ), // // ), // // ], // // ), // // }, // ], // ), // ), // const _StatsColumn(), // ], // ), // ), // _ => const SizedBox(), // }, // ); // } // } // class _StatsColumn extends HookConsumerWidget { // const _StatsColumn(); // @override // Widget build(BuildContext context, WidgetRef ref) { // final t = ref.watch(translationsProvider).requireValue; // final stats = ref.watch(statsNotifierProvider).value; // return Directionality( // textDirection: TextDirection.values[(Directionality.of(context).index + 1) % TextDirection.values.length], // child: Flexible( // child: Column( // children: [ // _InfoProp( // icon: FluentIcons.arrow_bidirectional_up_down_20_regular, // text: (stats?.downlinkTotal.toInt() ?? 0).size(), // semanticLabel: t.stats.totalTransferred, // ), // const Gap(8), // _InfoProp( // icon: FluentIcons.arrow_download_20_regular, // text: (stats?.downlink.toInt() ?? 0).speed(), // semanticLabel: t.stats.speed, // ), // ], // ), // ), // ); // } // } // class _InfoProp extends StatelessWidget { // const _InfoProp({ // required this.icon, // required this.text, // this.semanticLabel, // }); // final IconData icon; // final String text; // final String? semanticLabel; // @override // Widget build(BuildContext context) { // return Semantics( // label: semanticLabel, // child: Row( // children: [ // Icon(icon), // const Gap(8), // Flexible( // child: Text( // text, // style: Theme.of(context).textTheme.labelMedium?.copyWith(fontFamily: FontFamily.emoji), // overflow: TextOverflow.ellipsis, // ), // ), // ], // ), // ); // } // } ================================================ FILE: lib/features/proxy/active/active_proxy_notifier.dart ================================================ import 'dart:async'; import 'package:dio/dio.dart'; import 'package:hiddify/core/haptic/haptic_service.dart'; import 'package:hiddify/core/preferences/general_preferences.dart'; import 'package:hiddify/core/utils/throttler.dart'; import 'package:hiddify/features/connection/notifier/connection_notifier.dart'; import 'package:hiddify/features/proxy/data/proxy_data_providers.dart'; import 'package:hiddify/features/proxy/model/ip_info_entity.dart' as oldipinfo; import 'package:hiddify/features/proxy/model/proxy_failure.dart'; import 'package:hiddify/hiddifycore/generated/v2/hcore/hcore.pb.dart'; import 'package:hiddify/hiddifycore/init_signal.dart'; import 'package:hiddify/utils/riverpod_utils.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'active_proxy_notifier.g.dart'; @riverpod class IpInfoNotifier extends _$IpInfoNotifier with AppLogger { @override Future build() async { ref.disposeDelay(const Duration(seconds: 20)); final cancelToken = CancelToken(); Timer? timer; ref.onDispose(() { loggy.debug("disposing"); cancelToken.cancel(); timer?.cancel(); }); ref.listen(serviceRunningProvider, (_, next) => _idle = false); final autoCheck = ref.watch(Preferences.autoCheckIp); final serviceRunning = await ref.watch(serviceRunningProvider.future); // loggy.debug( // "idle? [$_idle], forced? [$_forceCheck], connected? [$serviceRunning]", // ); if (!_forceCheck && !serviceRunning) { throw const ServiceNotRunning(); } else if ((_idle && !_forceCheck) || (!_forceCheck && serviceRunning && !autoCheck)) { throw const UnknownIp(); } _forceCheck = false; final info = await ref.watch(proxyRepositoryProvider).getCurrentIpInfo(cancelToken).getOrElse((err) { loggy.warning("error getting proxy ip info", err, StackTrace.current); // throw err; //hiddify: remove exception to be logged throw const UnknownIp(); }).run(); timer = Timer(const Duration(seconds: 10), () { loggy.debug("entering idle mode"); _idle = true; ref.invalidateSelf(); }); return info; } bool _idle = false; bool _forceCheck = false; Future refresh() async { if (state.isLoading) return; loggy.debug("refreshing"); state = const AsyncLoading(); await ref.read(hapticServiceProvider.notifier).lightImpact(); _forceCheck = true; ref.invalidateSelf(); } } @Riverpod(keepAlive: true) class ActiveProxyNotifier extends _$ActiveProxyNotifier with AppLogger { @override Stream build() async* { // ref.disposeDelay(const Duration(seconds: 20)); ref.watch(coreRestartSignalProvider); final serviceRunning = await ref.watch(serviceRunningProvider.future); if (!serviceRunning) { throw const ServiceNotRunning(); } final proxyprovider = ref.watch(proxyRepositoryProvider); yield* proxyprovider .watchActiveProxies() .map((event) => event.getOrElse((l) => List.empty())) .map((event) => event.firstOrNull?.items.first ?? OutboundInfo()); } final _urlTestThrottler = Throttler(const Duration(seconds: 1)); Future urlTest(String? groupTag_) async { final groupTag = groupTag_ ?? ""; _urlTestThrottler(() async { if (state case AsyncData()) { await ref.read(hapticServiceProvider.notifier).lightImpact(); await ref.read(proxyRepositoryProvider).urlTest(groupTag).getOrElse((err) { loggy.warning("error testing group", err); throw err; }).run(); } }); } } ================================================ FILE: lib/features/proxy/active/ip_widget.dart ================================================ import 'package:circle_flags/circle_flags.dart'; import 'package:fluentui_system_icons/fluentui_system_icons.dart'; import 'package:flutter/material.dart'; import 'package:hiddify/core/haptic/haptic_service.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/utils/ip_utils.dart'; import 'package:hiddify/gen/fonts.gen.dart'; import 'package:hiddify/utils/riverpod_utils.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import "package:simple_icons/simple_icons.dart"; final _showIp = StateProvider.autoDispose((ref) { ref.disposeDelay(const Duration(seconds: 20)); ref.listenSelf((previous, next) { if (previous == false && next == true) { ref.read(hapticServiceProvider.notifier).mediumImpact(); } }); return false; }); class IPText extends HookConsumerWidget { const IPText({required this.ip, required this.onLongPress, this.constrained = false, super.key}); final String ip; final VoidCallback onLongPress; final bool constrained; @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; final isVisible = ref.watch(_showIp); final textTheme = Theme.of(context).textTheme; final ipStyle = (constrained ? textTheme.labelMedium : textTheme.labelLarge)?.copyWith( fontFamily: FontFamily.emoji, ); return Semantics( label: t.pages.proxies.ipInfo.address, child: InkWell( onTap: () { ref.read(_showIp.notifier).state = !isVisible; }, onLongPress: onLongPress, borderRadius: BorderRadius.circular(12), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 2), child: AnimatedCrossFade( firstChild: Text(ip, style: ipStyle, textDirection: TextDirection.ltr, overflow: TextOverflow.ellipsis), secondChild: Padding( padding: constrained ? EdgeInsets.zero : const EdgeInsetsDirectional.only(end: 48), child: Text( obscureIp(ip), semanticsLabel: t.common.hidden, style: ipStyle, textDirection: TextDirection.ltr, overflow: TextOverflow.ellipsis, ), ), crossFadeState: isVisible ? CrossFadeState.showFirst : CrossFadeState.showSecond, duration: const Duration(milliseconds: 200), ), ), ), ); } } class UnknownIPText extends HookConsumerWidget { const UnknownIPText({required this.text, required this.onTap, this.constrained = false, super.key}); final String text; final VoidCallback onTap; final bool constrained; @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; final textTheme = Theme.of(context).textTheme; final style = constrained ? textTheme.bodySmall : textTheme.labelMedium; return Semantics( label: t.pages.proxies.ipInfo.address, child: InkWell( onTap: onTap, borderRadius: BorderRadius.circular(12), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 2), child: Text(text, style: style, overflow: TextOverflow.ellipsis), ), ), ); } } class IPCountryFlag extends HookConsumerWidget { const IPCountryFlag({ required this.countryCode, this.organization, this.size = 16, this.padding = EdgeInsets.zero, super.key, }); final String? countryCode; final double size; final EdgeInsetsGeometry padding; final String? organization; @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; return Semantics( label: t.pages.proxies.ipInfo.country, child: Padding( padding: padding, child: (countryCode?.isEmpty ?? true) ? Icon(FluentIcons.question_circle_20_regular, size: size) : SizedBox( width: size, height: size, child: Stack( textDirection: Directionality.of(context), alignment: Alignment.center, children: [ CircleFlag( // key: ValueKey(countryCode), countryCode!.toLowerCase() == "ir" ? "ir-shir" : countryCode!, size: size - 8, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), // Rounded effect ), ), if (organization != null) Positioned.directional( textDirection: Directionality.of(context), bottom: 0, end: 0, child: OrganisationFlag(organization: organization!, size: size / 2.5), ), ], ), ), ), ); } } class OrgIconData { final IconData icon; final Color color; const OrgIconData(this.icon, this.color); } // Map of organization keywords to icon and color const Map organizationData = { "cloudflare": OrgIconData(SimpleIcons.cloudflare, SimpleIconColors.cloudflare), "hetzner": OrgIconData(SimpleIcons.hetzner, SimpleIconColors.hetzner), "ovh": OrgIconData(SimpleIcons.ovh, SimpleIconColors.ovh), "azure": OrgIconData(SimpleIcons.microsoftazure, SimpleIconColors.microsoftazure), "amazon": OrgIconData(SimpleIcons.amazonaws, SimpleIconColors.amazonaws), "oracle": OrgIconData(SimpleIcons.oracle, SimpleIconColors.oracle), "fastly": OrgIconData(SimpleIcons.fastly, SimpleIconColors.fastly), "digitalocean": OrgIconData(SimpleIcons.digitalocean, SimpleIconColors.digitalocean), "alibaba": OrgIconData(SimpleIcons.alibabacloud, SimpleIconColors.alibabacloud), "google": OrgIconData(SimpleIcons.googlecloud, SimpleIconColors.googlecloud), "starlink": OrgIconData(SimpleIcons.satellite, SimpleIconColors.satellite), }; class OrganisationFlag extends HookConsumerWidget { const OrganisationFlag({required this.organization, this.size = 24, super.key}); final String organization; final double size; // Function to create flag widget with icon and color Widget getFlagWidget({ required Widget widget, required String organization, required double size, required String label, required Color color, }) { return Semantics( label: "$label $organization", child: Container( width: size, height: size, decoration: BoxDecoration(color: color, borderRadius: BorderRadius.circular(100)), // padding: const , child: widget, ), ); } @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; for (final entry in organizationData.entries) { if (organization.toLowerCase().contains(entry.key)) { return getFlagWidget( widget: Icon(entry.value.icon, color: Colors.white, size: size - 6), color: entry.value.color, organization: organization, size: size, label: t.pages.proxies.ipInfo.organization, ); } } // Return empty widget if no match is found return const SizedBox.shrink(); } } ================================================ FILE: lib/features/proxy/data/proxy_data_providers.dart ================================================ import 'package:hiddify/core/http_client/http_client_provider.dart'; import 'package:hiddify/features/proxy/data/proxy_repository.dart'; import 'package:hiddify/hiddifycore/hiddify_core_service_provider.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'proxy_data_providers.g.dart'; @Riverpod(keepAlive: true) ProxyRepository proxyRepository(Ref ref) { return ProxyRepositoryImpl(singbox: ref.watch(hiddifyCoreServiceProvider), client: ref.watch(httpClientProvider)); } ================================================ FILE: lib/features/proxy/data/proxy_repository.dart ================================================ import 'package:dio/dio.dart'; import 'package:fpdart/fpdart.dart'; import 'package:hiddify/core/http_client/dio_http_client.dart'; import 'package:hiddify/core/utils/exception_handler.dart'; import 'package:hiddify/features/proxy/model/ip_info_entity.dart' as oldipinfo; import 'package:hiddify/features/proxy/model/proxy_failure.dart'; import 'package:hiddify/hiddifycore/generated/v2/hcore/hcore.pb.dart'; import 'package:hiddify/hiddifycore/hiddify_core_service.dart'; import 'package:hiddify/utils/custom_loggers.dart'; abstract interface class ProxyRepository { // Stream>> watchProxies(); Stream> watchProxies(); Stream>> watchActiveProxies(); TaskEither getCurrentIpInfo(CancelToken cancelToken); TaskEither selectProxy(String groupTag, String outboundTag); TaskEither urlTest(String groupTag); } class ProxyRepositoryImpl with ExceptionHandler, InfraLogger implements ProxyRepository { ProxyRepositoryImpl({required this.singbox, required this.client}); final HiddifyCoreService singbox; final DioHttpClient client; // @override // Stream>> watchProxies() { // return singbox.watchGroups().map((event) { // // final groupWithSelected = { // // for (final group in event) group.tag: group.selected, // // }; // return event; // // .map( // // (e) => ProxyGroupEntity( // // tag: e.tag, // // type: e.type, // // selected: e.selected, // // items: e.items // // .map( // // (e) => ProxyItemEntity( // // tag: e.tag, // // type: e.type, // // urlTestDelay: e.urlTestDelay, // // selectedTag: groupWithSelected[e.tag], // // ), // // ) // // .filter((t) => t.isVisible) // // .toList(), // // ), // // ) // // .toList(); // }).handleExceptions( // (error, stackTrace) { // loggy.error("error watching proxies", error, stackTrace); // return ProxyUnexpectedFailure(error, stackTrace); // }, // ); // } @override Stream> watchProxies() { return singbox.watchGroup().handleExceptions((error, stackTrace) { loggy.error("error watching proxies", error, stackTrace); return ProxyUnexpectedFailure(error, stackTrace); }); } @override Stream>> watchActiveProxies() { return singbox.watchActiveGroups().handleExceptions((error, stackTrace) { loggy.error("error watching active proxies", error, stackTrace); return ProxyUnexpectedFailure(error, stackTrace); }); } @override TaskEither selectProxy(String groupTag, String outboundTag) { return exceptionHandler( () => singbox.selectOutbound(groupTag, outboundTag).mapLeft(ProxyUnexpectedFailure.new).run(), ProxyUnexpectedFailure.new, ); } @override TaskEither urlTest(String groupTag) { return exceptionHandler( () => singbox.urlTest(groupTag).mapLeft(ProxyUnexpectedFailure.new).run(), ProxyUnexpectedFailure.new, ); } static final Map response)> _ipInfoSources = { // "https://geolocation-db.com/json/": IpInfo.fromGeolocationDbComJson, //bug response is not json "https://ipwho.is/": oldipinfo.IpInfo.fromIpwhoIsJson, "https://api.ip.sb/geoip/": oldipinfo.IpInfo.fromIpSbJson, "https://ipapi.co/json/": oldipinfo.IpInfo.fromIpApiCoJson, "https://ipinfo.io/json/": oldipinfo.IpInfo.fromIpInfoIoJson, }; @override TaskEither getCurrentIpInfo(CancelToken cancelToken) { return TaskEither.tryCatch(() async { Object? error; for (final source in _ipInfoSources.entries) { try { loggy.debug("getting current ip info using [${source.key}]"); final response = await client.get>( source.key, cancelToken: cancelToken, proxyOnly: true, ); if (response.statusCode == 200 && response.data != null) { return source.value(response.data!); } } catch (e, s) { loggy.debug("failed getting ip info using [${source.key}]", e, s); error = e; continue; } } throw UnableToRetrieveIp(error, StackTrace.current); }, ProxyUnexpectedFailure.new); } } ================================================ FILE: lib/features/proxy/model/ip_info_entity.dart ================================================ import 'package:dart_mappable/dart_mappable.dart'; part 'ip_info_entity.mapper.dart'; @MappableClass() class IpInfo with IpInfoMappable { const IpInfo({ required this.ip, required this.countryCode, this.region, this.city, this.timezone, this.asn, this.org, }); final String ip; final String countryCode; final String? region; final String? city; final String? timezone; final String? asn; final String? org; static IpInfo fromIpInfoIoJson(Map json) { return switch (json) { { "ip": final String ip, "country": final String country, // "region": final String region, //sometime is not available // "city": final String city,//sometime is not available "timezone": final String timezone, "org": final String org, } => IpInfo( ip: ip, countryCode: country, // region: region, // city: city, timezone: timezone, org: org, ), _ => throw const FormatException("invalid json"), }; } static IpInfo fromIpApiCoJson(Map json) { return switch (json) { { "ip": final String ip, "country_code": final String countryCode, // "region": final String region, //sometime is not available // "city": final String city,//sometime is not available "timezone": final String timezone, "asn": final String asn, "org": final String org, } => IpInfo( ip: ip, countryCode: countryCode, // region: region, // city: city, timezone: timezone, asn: asn, org: org, ), _ => throw const FormatException("invalid json"), }; } static IpInfo fromIpSbJson(Map json) { return switch (json) { { "ip": final String ip, "country_code": final String countryCode, // "region": final String region, // "city": final String city, "timezone": final String timezone, "asn": final int asn, "asn_organization": final String org, } => IpInfo( ip: ip, countryCode: countryCode, // region: region, // city: city, timezone: timezone, asn: '$asn', org: org, ), _ => throw const FormatException("invalid json"), }; } static IpInfo fromIpwhoIsJson(Map json) { return switch (json) { { "ip": final String ip, "country_code": final String countryCode, // "region": final String region, // "city": final String city, // "timezone": final String timezone, // "asn": final int asn, "connection": final Map connection, } => IpInfo( ip: ip, countryCode: countryCode, // region: region, // city: city, // timezone: timezone, asn: '$connection["asn"]', org: '$connection["org"]', ), _ => throw const FormatException("invalid json"), }; } static IpInfo fromGeolocationDbComJson(Map json) { return switch (json) { { "ip": final String ip, "country_code": final String countryCode, // "state": final String region, "city": final String city, } => IpInfo( ip: ip, countryCode: countryCode, // region: region, city: city, ), _ => throw const FormatException("invalid json"), }; } } ================================================ FILE: lib/features/proxy/model/proxy_entity.dart ================================================ import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:hiddify/singbox/model/singbox_proxy_type.dart'; part 'proxy_entity.freezed.dart'; @freezed class ProxyGroupEntity with _$ProxyGroupEntity { const ProxyGroupEntity._(); const factory ProxyGroupEntity({ required String tag, required String type, required String selected, @Default([]) List items, }) = _ProxyGroupEntity; String get name => _sanitizedTag(tag); } @freezed class ProxyItemEntity with _$ProxyItemEntity { const ProxyItemEntity._(); const factory ProxyItemEntity({ required String tag, required String type, required int urlTestDelay, String? selectedTag, }) = _ProxyItemEntity; String get name => _sanitizedTag(tag); String? get selectedName => selectedTag == null ? null : _sanitizedTag(selectedTag!); bool get isVisible => !tag.contains("§hide§"); } String _sanitizedTag(String tag) => tag.replaceFirst(RegExp(r"\§[^]*"), "").trimRight(); // bool get isVisible => !tag.contains("§hide§"); ================================================ FILE: lib/features/proxy/model/proxy_failure.dart ================================================ import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/model/failures.dart'; part 'proxy_failure.freezed.dart'; @freezed sealed class ProxyFailure with _$ProxyFailure, Failure { const ProxyFailure._(); @With() const factory ProxyFailure.unexpected([Object? error, StackTrace? stackTrace]) = ProxyUnexpectedFailure; @With() const factory ProxyFailure.serviceNotRunning() = ServiceNotRunning; @With() const factory ProxyFailure.unknownIp() = UnknownIp; @With() const factory ProxyFailure.unableToRetrieveIp([Object? error, StackTrace? stackTrace]) = UnableToRetrieveIp; @override ({String type, String? message}) present(TranslationsEn t) { return switch (this) { ProxyUnexpectedFailure() => (type: t.errors.unexpected, message: null), ServiceNotRunning() => (type: t.errors.singbox.serviceNotRunning, message: null), UnknownIp() => (type: t.common.unknown, message: null), UnableToRetrieveIp() => (type: t.errors.unexpected, message: null), }; } } ================================================ FILE: lib/features/proxy/overview/proxies_overview_notifier.dart ================================================ import 'dart:async'; import 'package:dartx/dartx.dart'; import 'package:hiddify/core/haptic/haptic_service.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/preferences/preferences_provider.dart'; import 'package:hiddify/core/utils/preferences_utils.dart'; import 'package:hiddify/features/connection/notifier/connection_notifier.dart'; import 'package:hiddify/features/proxy/data/proxy_data_providers.dart'; import 'package:hiddify/features/proxy/model/proxy_failure.dart'; import 'package:hiddify/hiddifycore/generated/v2/hcore/hcore.pb.dart'; import 'package:hiddify/hiddifycore/init_signal.dart'; import 'package:hiddify/utils/riverpod_utils.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'proxies_overview_notifier.g.dart'; enum ProxiesSort { unsorted, name, delay, usage; String present(TranslationsEn t) => switch (this) { ProxiesSort.unsorted => t.pages.proxies.sortOptions.unsorted, ProxiesSort.name => t.pages.proxies.sortOptions.name, ProxiesSort.delay => t.pages.proxies.sortOptions.delay, ProxiesSort.usage => t.pages.proxies.sortOptions.usage, }; } @Riverpod(keepAlive: true) class ProxiesSortNotifier extends _$ProxiesSortNotifier with AppLogger { late final _pref = PreferencesEntry( preferences: ref.watch(sharedPreferencesProvider).requireValue, key: "proxies_sort_mode", defaultValue: ProxiesSort.delay, mapFrom: ProxiesSort.values.byName, mapTo: (value) => value.name, ); @override ProxiesSort build() { final sortBy = _pref.read(); loggy.info("sort proxies by: [${sortBy.name}]"); return sortBy; } Future update(ProxiesSort value) { state = value; return _pref.write(value); } } @riverpod class ProxiesOverviewNotifier extends _$ProxiesOverviewNotifier with AppLogger { @override Stream build() async* { ref.disposeDelay(const Duration(seconds: 15)); ref.watch(coreRestartSignalProvider); final serviceRunning = await ref.watch(serviceRunningProvider.future); if (!serviceRunning) { throw const ServiceNotRunning(); } final sortBy = ref.watch(proxiesSortNotifierProvider); // yield* ref // .watch(proxyRepositoryProvider) // .watchProxies() // .throttleTime( // const Duration(milliseconds: 100), // leading: false, // trailing: true, // ) // .map( // (event) => event.getOrElse( // (err) { // loggy.warning("error receiving proxies", err); // throw err; // }, // ), // ) // .asyncMap((proxies) async => _sortOutbounds(proxies, sortBy)); yield* ref .watch(proxyRepositoryProvider) .watchProxies() .map( (event) => event.getOrElse((err) { loggy.warning("error receiving proxies", err); throw err; }), ) .asyncMap((proxies) async => await _sortOutbounds(proxies, sortBy)); } // Future> _sortOutbounds( // List proxies, // ProxiesSort sortBy, // ) async { // final groupWithSelected = { // for (final o in proxies) o.tag: o.selected, // }; // final sortedProxies = []; // for (final group in proxies) { // final sortedItems = switch (sortBy) { // ProxiesSort.name => group.items.sortedWith((a, b) { // if (a.isGroup && !b.isGroup) return -1; // if (!a.isGroup && b.isGroup) return 1; // return a.tag.compareTo(b.tag); // }), // ProxiesSort.delay => group.items.sortedWith((a, b) { // if (a.isGroup && !b.isGroup) return -1; // if (!a.isGroup && b.isGroup) return 1; // final ai = a.urlTestDelay; // final bi = b.urlTestDelay; // if (ai == 0 && bi == 0) return -1; // if (ai == 0 && bi > 0) return 1; // if (ai > 0 && bi == 0) return -1; // return ai.compareTo(bi); // }), // ProxiesSort.unsorted => group.items, // }; // final items = []; // for (final item in sortedItems) { // // if (groupWithSelected.keys.contains(item.tag)) { // // items.add(item.copyWith(selectedTag: groupWithSelected[item.tag])); // // } else { // items.add(item); // // } // } // group.items.clear(); // group.items.addAll(items); // sortedProxies.add(group); // } // return sortedProxies; // } Future _sortOutbounds(OutboundGroup? proxies, ProxiesSort sortBy) async { if (proxies == null) return null; final sortedItems = switch (sortBy) { ProxiesSort.name => proxies.items.sortedWith((a, b) { if (a.isGroup && !b.isGroup) return -1; if (!a.isGroup && b.isGroup) return 1; return a.tag.compareTo(b.tag); }), ProxiesSort.delay => proxies.items.sortedWith((a, b) { if (a.isGroup && !b.isGroup) return -1; if (!a.isGroup && b.isGroup) return 1; final ai = a.urlTestDelay; final bi = b.urlTestDelay; if (ai == 0 && bi == 0) return -1; if (ai == 0 && bi > 0) return 1; if (ai > 0 && bi == 0) return -1; return ai.compareTo(bi); }), ProxiesSort.unsorted => proxies.items, ProxiesSort.usage => proxies.items.sortedWith((a, b) { if (a.isGroup && !b.isGroup) return -1; if (!a.isGroup && b.isGroup) return 1; return (b.upload + b.download).compareTo(a.upload + a.download); }), }; final items = []; for (final item in sortedItems) { // if (groupWithSelected.keys.contains(item.tag)) { // items.add(item.copyWith(selectedTag: groupWithSelected[item.tag])); // } else { items.add(item); // } } proxies.items.clear(); proxies.items.addAll(items); return proxies; } // Future changeProxy(String groupTag, String outboundTag) async { // loggy.debug( // "changing proxy, group: [$groupTag] - outbound: [$outboundTag]", // ); // if (state case AsyncData(value: final outbounds)) { // await ref.read(hapticServiceProvider.notifier).lightImpact(); // await ref.read(proxyRepositoryProvider).selectProxy(groupTag, outboundTag).getOrElse((err) { // loggy.warning("error selecting outbound", err); // throw err; // }).run(); // final outboundg = outbounds.where((e) => e.tag == groupTag).firstOrNull; // if (outboundg != null) { // final newselected = outboundg.items.where((e) => e.tag == outboundTag).firstOrNull; // if (newselected != null) { // newselected.isSelected = true; // outboundg.selected = newselected; // } // } // state = AsyncData( // [...outbounds], // ).copyWithPrevious(state); // } // } Future changeProxy(String groupTag, String outboundTag) async { loggy.debug("changing proxy, group: [$groupTag] - outbound: [$outboundTag]"); if (!state.hasValue) return; final outbounds = state.value!; await ref.read(hapticServiceProvider.notifier).lightImpact(); await ref.read(proxyRepositoryProvider).selectProxy(groupTag, outboundTag).getOrElse((err) { loggy.warning("error selecting outbound", err); throw err; }).run(); final newselected = outbounds.items.where((e) => e.tag == outboundTag).firstOrNull; if (newselected != null) { newselected.isSelected = true; outbounds.selected = newselected.tag; state = AsyncValue.data(outbounds); } } Future urlTest(String groupTag) async { loggy.debug("testing group: [$groupTag]"); if (state case AsyncData()) { await ref.read(hapticServiceProvider.notifier).lightImpact(); await ref.read(proxyRepositoryProvider).urlTest(groupTag).getOrElse((err) { loggy.error("error testing group", err); throw err; }).run(); } } } ================================================ FILE: lib/features/proxy/overview/proxies_overview_page.dart ================================================ import 'dart:math'; import 'package:fluentui_system_icons/fluentui_system_icons.dart'; import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/model/failures.dart'; import 'package:hiddify/features/proxy/overview/proxies_overview_notifier.dart'; import 'package:hiddify/features/proxy/widget/proxy_tile.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class ProxiesOverviewPage extends HookConsumerWidget with PresLogger { const ProxiesOverviewPage({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; final proxies = ref.watch(proxiesOverviewNotifierProvider); final sortBy = ref.watch(proxiesSortNotifierProvider); // final selectActiveProxyMutation = useMutation( // initialOnFailure: (error) => CustomToast.error(t.presentShortError(error)).show(context), // ); return Scaffold( appBar: AppBar( title: Text(t.pages.proxies.title), actions: [ PopupMenuButton( initialValue: sortBy, onSelected: ref.read(proxiesSortNotifierProvider.notifier).update, icon: const Icon(FluentIcons.arrow_sort_24_regular), tooltip: t.pages.proxies.sort, itemBuilder: (context) { return [...ProxiesSort.values.map((e) => PopupMenuItem(value: e, child: Text(e.present(t))))]; }, ), const Gap(8), ], ), floatingActionButton: FloatingActionButton( onPressed: () async => await ref.read(proxiesOverviewNotifierProvider.notifier).urlTest("select"), tooltip: t.pages.proxies.testDelay, child: const Icon(FluentIcons.flash_24_filled), ), body: proxies.when( data: (group) => group != null ? LayoutBuilder( builder: (context, constraints) { final width = constraints.maxWidth; final crossAxisCount = PlatformUtils.isMobile && width < 600 ? 1 : max(1, (width / 268).floor()); return GridView.builder( padding: const EdgeInsets.only(bottom: 86), itemCount: group.items.length, gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: crossAxisCount, mainAxisExtent: 72, ), itemBuilder: (context, index) { final proxy = group.items[index]; return ProxyTile( proxy, selected: group.selected == proxy.tag, onTap: () async { await ref.read(proxiesOverviewNotifierProvider.notifier).changeProxy(group.tag, proxy.tag); // if (selectActiveProxyMutation.state.isInProgress) return; // selectActiveProxyMutation.setFuture( // ); }, ); }, ); }, ) : Center(child: Text(t.pages.proxies.empty)), error: (error, stackTrace) => Center(child: Text(t.presentShortError(error))), loading: () => const Center(child: CircularProgressIndicator()), ), ); } } ================================================ FILE: lib/features/proxy/widget/proxy_tile.dart ================================================ import 'package:flutter/material.dart'; import 'package:hiddify/core/router/dialog/dialog_notifier.dart'; import 'package:hiddify/features/proxy/active/ip_widget.dart'; import 'package:hiddify/gen/fonts.gen.dart'; import 'package:hiddify/hiddifycore/generated/v2/hcore/hcore.pb.dart'; import 'package:hiddify/utils/custom_loggers.dart'; import 'package:hiddify/utils/platform_utils.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class ProxyTile extends HookConsumerWidget with PresLogger { const ProxyTile(this.proxy, {super.key, required this.selected, required this.onTap}); final OutboundInfo proxy; final bool selected; final GestureTapCallback? onTap; @override Widget build(BuildContext context, WidgetRef ref) { final theme = Theme.of(context); return ListTile( // shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), title: Text( proxy.tagDisplay, overflow: TextOverflow.ellipsis, style: PlatformUtils.isWindows ? const TextStyle(fontFamily: FontFamily.emoji) : null, ), leading: IPCountryFlag( countryCode: proxy.ipinfo.countryCode, organization: proxy.ipinfo.org, size: 40, padding: const EdgeInsetsDirectional.only(end: 8), ), subtitle: Text.rich( TextSpan( text: proxy.type, children: [ if (proxy.isGroup) TextSpan( text: ' (${proxy.groupSelectedTagDisplay.trim()})', style: Theme.of(context).textTheme.bodySmall, ), ], ), maxLines: 1, overflow: TextOverflow.ellipsis, ), trailing: Column( children: [ if (proxy.urlTestDelay != 0) Text( proxy.urlTestDelay > 65000 ? "×" : proxy.urlTestDelay.toString(), style: TextStyle(color: delayColor(context, proxy.urlTestDelay)), ), if (proxy.download > 0) Text("⬩", style: Theme.of(context).textTheme.bodySmall), ], ), selected: selected, selectedTileColor: theme.colorScheme.primaryContainer, onTap: onTap, onLongPress: () async => await ref.read(dialogNotifierProvider.notifier).showProxyInfo(outboundInfo: proxy), horizontalTitleGap: 4, ); } Color delayColor(BuildContext context, int delay) { if (Theme.of(context).brightness == Brightness.dark) { return switch (delay) { < 800 => Colors.lightGreen, < 1500 => Colors.orange, _ => Colors.redAccent, }; } return switch (delay) { < 800 => Colors.green, < 1500 => Colors.deepOrangeAccent, _ => Colors.red, }; } } ================================================ FILE: lib/features/route_rules/notifier/android_apps_notifier.dart ================================================ // ignore: unused_import import 'package:dio/dio.dart'; // ignore: depend_on_referenced_packages import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:hiddify/features/route_rules/notifier/rule_notifier.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:installed_apps/app_info.dart'; import 'package:installed_apps/installed_apps.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'android_apps_notifier.g.dart'; @riverpod Future> apps(Ref ref) async { if (!PlatformUtils.isAndroid) return []; return await InstalledApps.getInstalledApps(false, true); } @riverpod Future> appsHideSystem(Ref ref) async { if (!PlatformUtils.isAndroid) return []; return await InstalledApps.getInstalledApps(true, true); } @riverpod Future> appPackages(Ref ref) async { if (!PlatformUtils.isAndroid) return []; return (await InstalledApps.getInstalledApps(false)).map((e) => e.packageName).toList(); } @riverpod Future> filteredByHideSystem(Ref ref) async { final hideSystem = ref.watch(hideSystemNotifierProvider); return hideSystem ? await ref.watch(appsHideSystemProvider.future) : await ref.watch(appsProvider.future); } @riverpod Future> uninstalledPackages(Ref ref, int? ruleListOrder) async { final allPackages = await ref.watch(appPackagesProvider.future); final selectedPackages = ref.read(SelectedPackagesNotifierProvider(ruleListOrder)); return selectedPackages.toSet().difference(allPackages.toSet()).toList(); } @riverpod Future> filterBySearch(Ref ref, int? ruleListOrder) async { final searchQuery = ref.watch(searchQueryNotifierProvider); final filteredByHideSystem = await ref.watch(filteredByHideSystemProvider.future); if (searchQuery.isEmpty) { final selected = ref.read(SelectedPackagesNotifierProvider(ruleListOrder)); final uninstalledPackages = await ref.read(UninstalledPackagesProvider(ruleListOrder).future); final fullList = [...uninstalledPackages, ...filteredByHideSystem]; fullList.sort((a, b) { final aValue = (a is String) ? a : (a as AppInfo).packageName; final bValue = (b is String) ? b : (b as AppInfo).packageName; final aIndex = selected.indexOf(aValue); final bIndex = selected.indexOf(bValue); if (aIndex == -1 && bIndex == -1) return 0; if (aIndex == -1) return 1; if (bIndex == -1) return -1; return aIndex.compareTo(bIndex); }); return fullList; } else { return filteredByHideSystem.where((app) => app.name.toLowerCase().contains(searchQuery.toLowerCase())).toList(); } } @riverpod class HideSystemNotifier extends _$HideSystemNotifier { @override bool build() { return false; } void show() => state = false; void hide() => state = true; } @riverpod class SearchQueryNotifier extends _$SearchQueryNotifier { @override String build() => ''; void setQuery(String query) => state = query.trim(); void clear() => state = ''; } @riverpod class SelectedPackagesNotifier extends _$SelectedPackagesNotifier { late int? _ruleListOrder; final _ruleEnum = RuleEnum.packageName; @override List build(int? ruleListOrder) { _ruleListOrder = ruleListOrder; final value = ref.read(ruleNotifierProvider(ruleListOrder)).writeToJsonMap()['${_ruleEnum.getIndex()}']; if (value is List) return value.cast(); return []; } void onChanged(String packageName) { if (state.contains(packageName)) { state = List.from(state)..removeWhere((element) => element == packageName); } else { state = [...state, packageName]; } _save(); } void clearSelection() { state = []; _save(); } void _save() => ref.read(ruleNotifierProvider(_ruleListOrder).notifier).update>(_ruleEnum, state); } ================================================ FILE: lib/features/route_rules/notifier/generic_list_notifier.dart ================================================ import 'package:hiddify/core/notification/in_app_notification_controller.dart'; import 'package:hiddify/features/route_rules/notifier/rule_notifier.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'generic_list_notifier.g.dart'; @riverpod class GenericListNotifier extends _$GenericListNotifier { late int? _ruleListOrder; late RuleEnum _ruleEnum; @override List build(int? ruleListOrder, RuleEnum ruleEnum) { _ruleListOrder = ruleListOrder; _ruleEnum = ruleEnum; final value = ref.read(ruleNotifierProvider(ruleListOrder)).writeToJsonMap()['${ruleEnum.getIndex()}']; if (value is List) return value; return []; } void add(dynamic value) { if (!_isValid(value)) return; state = [...state, value]; _save(); } void update(int index, dynamic value) { if (!_isValid(value)) return; state = List.from(state)..[index] = value; _save(); } void remove(int index) { state = List.from(state)..removeAt(index); _save(); } void reset() { state = []; _save(); } void _save() => ref.read(ruleNotifierProvider(_ruleListOrder).notifier).update>(_ruleEnum, state); bool _isValid(dynamic value) { if (value == null) return false; if (state.contains(value)) { ref.read(inAppNotificationControllerProvider).showErrorToast('Value is exist'); return false; } return true; } } ================================================ FILE: lib/features/route_rules/notifier/rule_notifier.dart ================================================ import 'dart:convert'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/features/route_rules/notifier/rules_notifier.dart'; import 'package:hiddify/hiddifycore/generated/v2/config/route_rule.pb.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:protobuf/protobuf.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'rule_notifier.g.dart'; enum RuleEnum { listOrder, enabled, name, outbound, ruleSet, packageName, processName, processPath, network, portRange, sourcePortRange, protocol, ipCidr, sourceIpCidr, domain, domainSuffix, domainKeyword, domainRegex; int getIndex() => index + 1; String present(Translations t) => switch (this) { listOrder => this.name, enabled => this.name, name => t.pages.settings.routing.routeRule.rule.tileTitle['name']!, outbound => t.pages.settings.routing.routeRule.rule.tileTitle['outbound']!, ruleSet => t.pages.settings.routing.routeRule.rule.tileTitle['rule_set']!, packageName => t.pages.settings.routing.routeRule.rule.tileTitle['package_name']!, processName => t.pages.settings.routing.routeRule.rule.tileTitle['process_name']!, processPath => t.pages.settings.routing.routeRule.rule.tileTitle['process_path']!, network => t.pages.settings.routing.routeRule.rule.tileTitle['network']!, portRange => t.pages.settings.routing.routeRule.rule.tileTitle['port_range']!, sourcePortRange => t.pages.settings.routing.routeRule.rule.tileTitle['source_port_range']!, protocol => t.pages.settings.routing.routeRule.rule.tileTitle['protocol']!, ipCidr => t.pages.settings.routing.routeRule.rule.tileTitle['ip_cidr']!, sourceIpCidr => t.pages.settings.routing.routeRule.rule.tileTitle['source_ip_cidr']!, domain => t.pages.settings.routing.routeRule.rule.tileTitle['domain']!, domainSuffix => t.pages.settings.routing.routeRule.rule.tileTitle['domain_suffixe']!, domainKeyword => t.pages.settings.routing.routeRule.rule.tileTitle['domain_keyword']!, domainRegex => t.pages.settings.routing.routeRule.rule.tileTitle['domain_regex']!, }; } @riverpod class RuleNotifier extends _$RuleNotifier { bool isEditMode = false; @override Rule build(int? listOrder) { if (listOrder == null) { return Rule(name: 'Rule Name', outbound: Outbound.direct, network: Network.all); } else { isEditMode = true; return ref.read(rulesNotifierProvider).where((rule) => rule.listOrder == listOrder).first; } } void update(RuleEnum key, T value) { final map = state.writeToJsonMap(); map['${key.getIndex()}'] = value is ProtobufEnum ? '${value.value}' : value is List ? value.map((e) => '${e.value}').toList() : value; state = Rule.fromJson(jsonEncode(map)); } void save() { assert(state.hasName() && state.hasOutbound()); if (isEditMode) { assert(state.hasListOrder() && state.hasEnabled()); ref.read(rulesNotifierProvider.notifier).updateRule(state); } else { ref.read(rulesNotifierProvider.notifier).addRule(state); } } } @riverpod bool isRuleEdited(Ref ref, int? listOrder) { if (listOrder == null) return true; return ref.watch(RuleNotifierProvider(listOrder)) != ref.watch(rulesNotifierProvider.select((value) => value.where((rule) => rule.listOrder == listOrder))).first; } @riverpod class DialogCheckboxNotifier extends _$DialogCheckboxNotifier { @override List build(List selected) { return selected; } void update(ProtobufEnum value) { state = state.contains(value) ? state.where((element) => element != value).toList() : [...state, value]; } } ================================================ FILE: lib/features/route_rules/notifier/rules_notifier.dart ================================================ import 'dart:convert'; import 'dart:io'; import 'package:dartx/dartx_io.dart'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/services.dart'; import 'package:hiddify/core/directories/directories_provider.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/notification/in_app_notification_controller.dart'; import 'package:hiddify/hiddifycore/generated/v2/config/route_rule.pb.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'rules_notifier.g.dart'; @riverpod class RulesNotifier extends _$RulesNotifier with AppLogger { late File file; @override List build() { final directories = ref.watch(appDirectoriesProvider).requireValue; file = File('${directories.baseDir.path}/route_rule.proto'); if (file.existsSync()) { return RouteRule.fromBuffer(file.readAsBytesSync()).rules; } else { return []; } } Future addRule(Rule rule) async { final current = state; assert(rule.hasName() && rule.hasOutbound()); rule ..listOrder = current.length ..enabled = true; state = [...current, rule]; await _updateFile(); } Future updateRule(Rule rule) async { final current = state; final index = current.indexWhere((element) => element.listOrder == rule.listOrder); if (index == -1) return; current[index] = rule; state = current.toList(); await _updateFile(); } Future deleteRule(int listOrder) async { final current = state; state = _updateListOrder(current.where((element) => element.listOrder != listOrder).toList()); await _updateFile(); } Future reorder(int oldIndex, int newIndex) async { final current = state; final rule = current.removeAt(oldIndex); current.insert(oldIndex < newIndex ? newIndex - 1 : newIndex, rule); state = _updateListOrder(current).toList(); await _updateFile(); } Future updateEnabled(bool enabled, int listOrder) async { final current = state; current.firstWhere((rule) => rule.listOrder == listOrder).enabled = enabled; state = current.toList(); await _updateFile(); } //export Clipboard Future exportJsonToClipboard() async { final t = ref.read(translationsProvider).requireValue; try { final routeRules = RouteRule(rules: state); await Clipboard.setData(ClipboardData(text: jsonEncode(routeRules.writeToJson()))); ref.read(inAppNotificationControllerProvider).showSuccessToast(t.common.msg.export.clipboard.success); return true; } on PlatformException { ref .read(inAppNotificationControllerProvider) .showInfoToast(t.common.msg.export.clipboard.contentTooLarge, duration: const Duration(seconds: 5)); return false; } catch (e, st) { loggy.warning("error exporting route rules to clipboard", e, st); ref.read(inAppNotificationControllerProvider).showErrorToast(t.common.msg.export.clipboard.failure); return false; } } //import Clipboard Future importRulesFromClipboard() async { final t = ref.read(translationsProvider).requireValue; try { final input = await Clipboard.getData(Clipboard.kTextPlain).then((value) => value?.text); if (input == null) return false; final routeRules = RouteRule.fromJson(jsonDecode(input) as String); state = routeRules.rules; await _updateFile(); ref.read(inAppNotificationControllerProvider).showSuccessToast(t.common.msg.import.success); return true; } catch (e, st) { loggy.warning("error importing route rules from clipboard", e, st); ref.read(inAppNotificationControllerProvider).showErrorToast(t.common.msg.import.failure); return false; } } //export JSON Future saveRulesAsJsonFile() async { final t = ref.read(translationsProvider).requireValue; try { final bytes = utf8.encode(RouteRule(rules: state).writeToJson()); final outputFile = await FilePicker.platform.saveFile( fileName: 'route_rules.json', type: FileType.custom, allowedExtensions: ['json'], bytes: bytes, ); if (outputFile == null) return false; if (PlatformUtils.isDesktop) { final file = File(outputFile); if (file.extension != '.json') return false; if (!await file.exists()) await file.parent.create(recursive: true); await file.writeAsBytes(bytes); } ref.read(inAppNotificationControllerProvider).showSuccessToast(t.common.msg.export.file.success); return true; } catch (e, st) { loggy.warning("error exporting route rules to json file", e, st); ref.read(inAppNotificationControllerProvider).showErrorToast(t.common.msg.export.file.failure); return false; } } //import JSON Future importRulesFromJsonFile() async { final t = ref.read(translationsProvider).requireValue; try { final result = await FilePicker.platform.pickFiles(type: FileType.custom, allowedExtensions: ['json']); if (result == null) return false; final file = File(result.files.single.path!); if (!await file.exists()) return false; final bytes = await file.readAsBytes(); final routeRules = RouteRule.fromJson(utf8.decode(bytes)); state = routeRules.rules; await _updateFile(); ref.read(inAppNotificationControllerProvider).showSuccessToast(t.common.msg.import.success); return true; } catch (e, st) { loggy.warning("error importing route rules from json file", e, st); ref.read(inAppNotificationControllerProvider).showErrorToast(t.common.msg.import.failure); return false; } } Future resetRules() async { if (await file.exists()) { await file.delete(recursive: true); state = []; } } Future _updateFile() async { if (!await file.exists()) { await file.parent.create(recursive: true); } final sortedRules = state..sort((a, b) => a.listOrder.compareTo(b.listOrder)); final routeRules = RouteRule(rules: sortedRules); await file.writeAsBytes(routeRules.writeToBuffer()); } List _updateListOrder(List rules) { for (var i = 0; i < rules.length; i++) { rules[i].listOrder = i; } return rules; } } ================================================ FILE: lib/features/route_rules/overview/android_apps_page.dart ================================================ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:gap/gap.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/features/route_rules/notifier/android_apps_notifier.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:installed_apps/app_info.dart'; class AndroidAppsPage extends HookConsumerWidget { const AndroidAppsPage({super.key, this.ruleListOrder}); final int? ruleListOrder; @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; final theme = Theme.of(context); final searchController = useTextEditingController(); ref.listen(searchQueryNotifierProvider, (_, next) => searchController.text = next); final focusNode = useFocusNode(); ref.watch(appPackagesProvider); final selectedNotifier = SelectedPackagesNotifierProvider(ruleListOrder); final selected = ref.watch(selectedNotifier); final combinedList = ref.watch(FilterBySearchProvider(ruleListOrder)); final menuItems = [ if (ref.watch(hideSystemNotifierProvider)) PopupMenuItem( onTap: ref.read(hideSystemNotifierProvider.notifier).show, child: Text(t.pages.settings.routing.routeRule.androidApps.showSystemApps), ) else PopupMenuItem( onTap: ref.read(hideSystemNotifierProvider.notifier).hide, child: Text(t.pages.settings.routing.routeRule.androidApps.hideSystemApps), ), PopupMenuItem( onTap: ref.read(selectedNotifier.notifier).clearSelection, child: Text(t.pages.settings.routing.routeRule.androidApps.clearSelection), ), ]; return Scaffold( appBar: AppBar( title: Text(t.pages.settings.routing.routeRule.androidApps.pageTitle), actions: [ PopupMenuButton( icon: const Icon(Icons.more_vert_rounded), itemBuilder: (_) => selected.isEmpty ? [menuItems.first] : menuItems, ), ], bottom: PreferredSize( preferredSize: const Size.fromHeight(kMinInteractiveDimension), child: TextField( focusNode: focusNode, controller: searchController, decoration: InputDecoration( contentPadding: const EdgeInsets.symmetric(horizontal: 16), label: Text(t.common.search), suffixIcon: searchController.text.isNotEmpty ? IconButton( onPressed: () { ref.read(searchQueryNotifierProvider.notifier).clear(); focusNode.unfocus(); }, icon: const Icon(Icons.cancel_outlined), ) : null, ), onChanged: (value) => ref.read(searchQueryNotifierProvider.notifier).setQuery(value), ), ), ), body: combinedList.when( data: (items) => ListView.builder( itemCount: items.length, itemBuilder: (context, index) { final item = items[index]; if (item is AppInfo) { final appInfo = item; return CheckboxListTile( title: Row( children: [ SizedBox( width: 40, height: 40, child: CircleAvatar(backgroundColor: Colors.transparent, child: Image.memory(appInfo.icon!)), ), const Gap(16), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(appInfo.name, style: theme.textTheme.bodyLarge, overflow: TextOverflow.ellipsis), Text(appInfo.packageName, style: theme.textTheme.bodySmall, overflow: TextOverflow.ellipsis), ], ), ), ], ), value: selected.contains(item.packageName), onChanged: (_) => ref.read(selectedNotifier.notifier).onChanged(item.packageName), ); } else if (item is String) { final packageName = item; return CheckboxListTile( title: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ const Icon(size: 16, Icons.warning_rounded, color: Colors.amber), const Gap(4), Text( t.pages.settings.routing.routeRule.androidApps.uninstalled, style: theme.textTheme.bodyLarge, overflow: TextOverflow.ellipsis, ), ], ), Text(packageName, style: theme.textTheme.bodySmall, overflow: TextOverflow.ellipsis), ], ), value: selected.contains(packageName), onChanged: (_) => ref.read(selectedNotifier.notifier).onChanged(packageName), ); } else { throw Exception('Data type is not supported'); } }, ), loading: () => const Center(child: CircularProgressIndicator()), error: (error, stack) => Center(child: Text('Error: $error')), ), ); } } ================================================ FILE: lib/features/route_rules/overview/generic_list_page.dart ================================================ import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/router/dialog/dialog_notifier.dart'; import 'package:hiddify/features/route_rules/notifier/generic_list_notifier.dart'; import 'package:hiddify/features/route_rules/notifier/rule_notifier.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:text_scroll/text_scroll.dart'; class GenericListPage extends HookConsumerWidget { const GenericListPage({super.key, this.ruleListOrder, required this.ruleEnum, this.validator}); final int? ruleListOrder; final RuleEnum ruleEnum; final FormFieldValidator? validator; @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; final provider = genericListNotifierProvider(ruleListOrder, ruleEnum); final list = ref.watch(provider); Future addNewValue() async { final result = await ref .read(dialogNotifierProvider.notifier) .showSettingText(lable: t.pages.settings.routing.routeRule.genericList.addNew, validator: validator); if (result is String) ref.read(provider.notifier).add(result); } return Scaffold( appBar: AppBar( title: Text(ruleEnum.present(t)), actions: [ IconButton( onPressed: list.isEmpty ? null : () async { final result = await ref .read(dialogNotifierProvider.notifier) .showConfirmation( title: t.pages.settings.routing.routeRule.genericList.clearList, message: t.pages.settings.routing.routeRule.genericList.clearListMsg, ); if (result == true) ref.read(provider.notifier).reset(); }, icon: const Icon(Icons.clear_all), ), const Gap(8), ], ), floatingActionButton: list.isNotEmpty ? FloatingActionButton(onPressed: addNewValue, child: const Icon(Icons.add_rounded)) : FloatingActionButton.extended( onPressed: addNewValue, label: Text(t.pages.settings.routing.routeRule.genericList.addNew), icon: const Icon(Icons.add_rounded), ), body: ListView.builder( itemBuilder: (context, index) => GenericListTile( value: list[index], onRemove: () => ref.read(provider.notifier).remove(index), onUpdate: () async { final result = await ref .read(dialogNotifierProvider.notifier) .showSettingText( lable: t.pages.settings.routing.routeRule.genericList.update, value: '${list[index]}', validator: validator, ); if (result is String) ref.read(provider.notifier).update(index, result); }, ), itemCount: list.length, ), ); } } class GenericListTile extends ConsumerWidget { const GenericListTile({super.key, required this.value, required this.onRemove, required this.onUpdate}); final dynamic value; final VoidCallback? onRemove; final VoidCallback? onUpdate; @override Widget build(BuildContext context, WidgetRef ref) { return ListTile( onTap: onUpdate, title: TextScroll( '$value', mode: TextScrollMode.bouncing, velocity: const Velocity(pixelsPerSecond: Offset(30, 0)), pauseOnBounce: const Duration(seconds: 2), pauseBetween: const Duration(seconds: 2), ), trailing: IconButton(onPressed: onRemove, icon: const Icon(Icons.remove)), ); } } ================================================ FILE: lib/features/route_rules/overview/rule_page.dart ================================================ import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/router/dialog/dialog_notifier.dart'; import 'package:hiddify/features/route_rules/notifier/rule_notifier.dart'; import 'package:hiddify/features/route_rules/overview/android_apps_page.dart'; import 'package:hiddify/features/route_rules/overview/generic_list_page.dart'; import 'package:hiddify/features/route_rules/widget/setting_checkbox.dart'; import 'package:hiddify/features/route_rules/widget/setting_divider.dart'; import 'package:hiddify/features/route_rules/widget/setting_generic_list.dart'; import 'package:hiddify/features/route_rules/widget/setting_radio.dart'; import 'package:hiddify/features/route_rules/widget/setting_text.dart'; import 'package:hiddify/hiddifycore/generated/v2/config/route_rule.pb.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:protobuf/protobuf.dart'; import 'package:recase/recase.dart'; class RulePage extends HookConsumerWidget { const RulePage({super.key, this.ruleListOrder}); final int? ruleListOrder; String getTitle(Map t, RuleEnum key) => t[key.name.snakeCase] ?? key.name; @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; final isRuleEdited = ref.watch(IsRuleEditedProvider(ruleListOrder)); // TODO(): PopScope logic must be transferred to onExit method of go_router return PopScope( canPop: !isRuleEdited, onPopInvokedWithResult: (didPop, result) async { if (didPop) return; if (isRuleEdited) { final shouldSave = await ref .read(dialogNotifierProvider.notifier) .showSave( title: t.pages.settings.routing.routeRule.rule.ruleChanged, description: t.pages.settings.routing.routeRule.rule.ruleChangedMsg, ); if (shouldSave == null) return; if (shouldSave == true) { ref.read(ruleNotifierProvider(ruleListOrder).notifier).save(); if (context.mounted) Navigator.of(context).pop(); } else { if (context.mounted) Navigator.of(context).pop(); } } }, child: Scaffold( appBar: AppBar( title: Text(t.pages.settings.routing.routeRule.rule.title), actions: [ IconButton( onPressed: isRuleEdited ? () { ref.read(ruleNotifierProvider(ruleListOrder).notifier).save(); Navigator.of(context).pop(); } : null, icon: const Icon(Icons.check), ), const Gap(8), ], ), body: SingleChildScrollView( child: Column( children: [ SettingText( title: RuleEnum.name.present(t), value: ref.watch(ruleNotifierProvider(ruleListOrder).select((value) => value.name)), setValue: (value) => ref.read(ruleNotifierProvider(ruleListOrder).notifier).update(RuleEnum.name, value), ), SettingRadio( title: RuleEnum.outbound.present(t), values: Outbound.values, value: ref.watch(ruleNotifierProvider(ruleListOrder).select((value) => value.outbound)), setValue: (value) => ref.read(ruleNotifierProvider(ruleListOrder).notifier).update(RuleEnum.outbound, value), defaultValue: Outbound.direct, t: t.pages.settings.routing.routeRule.rule.outbound, ), const SettingDivider(), SettingGenericList( title: RuleEnum.ruleSet.present(t), values: ref.watch(ruleNotifierProvider(ruleListOrder).select((value) => value.ruleSets)), onTap: () => Navigator.of(context).push( MaterialPageRoute( builder: (context) => GenericListPage( ruleListOrder: ruleListOrder, ruleEnum: RuleEnum.ruleSet, validator: (value) { if (isUrl('$value')) return null; return t.pages.settings.routing.routeRule.rule.validUrl; }, ), fullscreenDialog: true, ), ), useEllipsis: true, ), SettingDivider(title: t.pages.settings.routing.routeRule.rule.onlyTunMode), SettingGenericList( title: RuleEnum.packageName.present(t), values: ref.watch(ruleNotifierProvider(ruleListOrder).select((value) => value.packageNames)), onTap: () => Navigator.of(context).push( MaterialPageRoute( builder: (context) => AndroidAppsPage(ruleListOrder: ruleListOrder), fullscreenDialog: true, ), ), isPackageName: true, showPlatformWarning: !PlatformUtils.isAndroid, ), SettingGenericList( title: RuleEnum.processName.present(t), values: ref.watch(ruleNotifierProvider(ruleListOrder).select((value) => value.processNames)), onTap: () => Navigator.of(context).push( MaterialPageRoute( builder: (context) => GenericListPage( ruleListOrder: ruleListOrder, ruleEnum: RuleEnum.processName, validator: (value) { if (isProcessName('$value')) return null; return t.pages.settings.routing.routeRule.rule.validProcessName; }, ), fullscreenDialog: true, ), ), showPlatformWarning: !PlatformUtils.isDesktop, ), SettingGenericList( title: RuleEnum.processPath.present(t), values: ref.watch(ruleNotifierProvider(ruleListOrder).select((value) => value.processPaths)), onTap: () => Navigator.of(context).push( MaterialPageRoute( builder: (context) => GenericListPage( ruleListOrder: ruleListOrder, ruleEnum: RuleEnum.processPath, validator: (value) { if (isProcessPath('$value')) return null; return t.pages.settings.routing.routeRule.rule.validProcessPath; }, ), fullscreenDialog: true, ), ), showPlatformWarning: !PlatformUtils.isDesktop, ), const SettingDivider(), SettingRadio( title: RuleEnum.network.present(t), values: Network.values, value: ref.watch(ruleNotifierProvider(ruleListOrder).select((value) => value.network)), setValue: (value) => ref.read(ruleNotifierProvider(ruleListOrder).notifier).update(RuleEnum.network, value), defaultValue: Network.all, t: t.pages.settings.routing.routeRule.rule.network, ), SettingGenericList( title: RuleEnum.portRange.present(t), values: ref.watch(ruleNotifierProvider(ruleListOrder).select((value) => value.portRanges)), onTap: () => Navigator.of(context).push( MaterialPageRoute( builder: (context) => GenericListPage( ruleListOrder: ruleListOrder, ruleEnum: RuleEnum.portRange, validator: (value) { if (isPortOrPortRange('$value')) return null; return t.pages.settings.routing.routeRule.rule.validPortRange; }, ), fullscreenDialog: true, ), ), ), SettingGenericList( title: RuleEnum.sourcePortRange.present(t), values: ref.watch(ruleNotifierProvider(ruleListOrder).select((value) => value.sourcePortRanges)), onTap: () => Navigator.of(context).push( MaterialPageRoute( builder: (context) => GenericListPage( ruleListOrder: ruleListOrder, ruleEnum: RuleEnum.sourcePortRange, validator: (value) { if (isPortOrPortRange('$value')) return null; return t.pages.settings.routing.routeRule.rule.validPortRange; }, ), fullscreenDialog: true, ), ), ), SettingCheckbox( title: RuleEnum.protocol.present(t), values: Protocol.values, selectedValues: ref.watch(ruleNotifierProvider(ruleListOrder).select((value) => value.protocols)), setValue: (value) => ref .read(ruleNotifierProvider(ruleListOrder).notifier) .update>(RuleEnum.protocol, value), t: t.pages.settings.routing.routeRule.rule.protocol, ), const SettingDivider(), SettingGenericList( title: RuleEnum.ipCidr.present(t), values: ref.watch(ruleNotifierProvider(ruleListOrder).select((value) => value.ipCidrs)), onTap: () => Navigator.of(context).push( MaterialPageRoute( builder: (context) => GenericListPage( ruleListOrder: ruleListOrder, ruleEnum: RuleEnum.ipCidr, validator: (value) { if (isIpCidr('$value')) return null; return t.pages.settings.routing.routeRule.rule.validIpCidr; }, ), fullscreenDialog: true, ), ), ), SettingGenericList( title: RuleEnum.sourceIpCidr.present(t), values: ref.watch(ruleNotifierProvider(ruleListOrder).select((value) => value.sourceIpCidrs)), onTap: () => Navigator.of(context).push( MaterialPageRoute( builder: (context) => GenericListPage( ruleListOrder: ruleListOrder, ruleEnum: RuleEnum.sourceIpCidr, validator: (value) { if (isIpCidr('$value')) return null; return t.pages.settings.routing.routeRule.rule.validIpCidr; }, ), fullscreenDialog: true, ), ), ), const SettingDivider(), SettingGenericList( title: RuleEnum.domain.present(t), values: ref.watch(ruleNotifierProvider(ruleListOrder).select((value) => value.domains)), onTap: () => Navigator.of(context).push( MaterialPageRoute( builder: (context) => GenericListPage( ruleListOrder: ruleListOrder, ruleEnum: RuleEnum.domain, validator: (value) { if (isDomain('$value')) return null; return t.pages.settings.routing.routeRule.rule.validDomain; }, ), fullscreenDialog: true, ), ), ), SettingGenericList( title: RuleEnum.domainSuffix.present(t), values: ref.watch(ruleNotifierProvider(ruleListOrder).select((value) => value.domainSuffixes)), onTap: () => Navigator.of(context).push( MaterialPageRoute( builder: (context) => GenericListPage( ruleListOrder: ruleListOrder, ruleEnum: RuleEnum.domainSuffix, validator: (value) { if (isDomainSuffix('$value')) return null; return t.pages.settings.routing.routeRule.rule.validDomainSuffix; }, ), fullscreenDialog: true, ), ), ), SettingGenericList( title: RuleEnum.domainKeyword.present(t), values: ref.watch(ruleNotifierProvider(ruleListOrder).select((value) => value.domainKeywords)), onTap: () => Navigator.of(context).push( MaterialPageRoute( builder: (context) => GenericListPage(ruleListOrder: ruleListOrder, ruleEnum: RuleEnum.domainKeyword), fullscreenDialog: true, ), ), ), SettingGenericList( title: RuleEnum.domainRegex.present(t), values: ref.watch(ruleNotifierProvider(ruleListOrder).select((value) => value.domainRegexes)), onTap: () => Navigator.of(context).push( MaterialPageRoute( builder: (context) => GenericListPage(ruleListOrder: ruleListOrder, ruleEnum: RuleEnum.domainRegex), fullscreenDialog: true, ), ), ), ], ), ), ), ); } } ================================================ FILE: lib/features/route_rules/overview/rules_page.dart ================================================ import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/features/route_rules/notifier/rules_notifier.dart'; import 'package:hiddify/features/route_rules/overview/rule_page.dart'; import 'package:hiddify/features/route_rules/widget/rule_tile.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class RulesPage extends HookConsumerWidget { const RulesPage({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; final rules = ref.watch(rulesNotifierProvider); final menuItems = [ PopupMenuItem( onTap: ref.read(rulesNotifierProvider.notifier).importRulesFromClipboard, child: Text(t.pages.settings.routing.routeRule.options.import.clipboard), ), PopupMenuItem( onTap: ref.read(rulesNotifierProvider.notifier).importRulesFromJsonFile, child: Text(t.pages.settings.routing.routeRule.options.import.file), ), const PopupMenuDivider(), PopupMenuItem( onTap: () async => await ref.read(rulesNotifierProvider.notifier).exportJsonToClipboard(), child: Text(t.pages.settings.routing.routeRule.options.export.clipboard), ), PopupMenuItem( onTap: () async => await ref.read(rulesNotifierProvider.notifier).saveRulesAsJsonFile(), child: Text(t.pages.settings.routing.routeRule.options.export.file), ), const PopupMenuDivider(), PopupMenuItem( onTap: ref.read(rulesNotifierProvider.notifier).resetRules, child: Text(t.pages.settings.routing.routeRule.options.reset), ), ]; return Scaffold( appBar: AppBar( title: Text(t.pages.settings.routing.routeRule.title), actions: [ PopupMenuButton( icon: const Icon(Icons.more_vert_rounded), itemBuilder: (_) => rules.isEmpty ? menuItems.getRange(0, 2).toList() : menuItems, ), const Gap(8), ], ), floatingActionButton: rules.isNotEmpty ? FloatingActionButton( onPressed: () => Navigator.of(context).push(MaterialPageRoute(builder: (context) => const RulePage())), child: const Icon(Icons.add_rounded), ) : FloatingActionButton.extended( onPressed: () => Navigator.of(context).push(MaterialPageRoute(builder: (context) => const RulePage())), label: Text(t.pages.settings.routing.routeRule.createRule), icon: const Icon(Icons.add_rounded), ), body: ReorderableListView.builder( buildDefaultDragHandles: false, onReorder: ref.read(rulesNotifierProvider.notifier).reorder, itemBuilder: (context, index) => RuleTile(key: Key('$index'), index: index, rule: rules[index]), itemCount: rules.length, ), ); } } ================================================ FILE: lib/features/route_rules/widget/rule_tile.dart ================================================ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/router/dialog/dialog_notifier.dart'; import 'package:hiddify/features/route_rules/notifier/rules_notifier.dart'; import 'package:hiddify/features/route_rules/overview/rule_page.dart'; import 'package:hiddify/features/route_rules/widget/setting_detail_chips.dart'; import 'package:hiddify/hiddifycore/generated/v2/config/route_rule.pb.dart'; import 'package:hiddify/utils/platform_utils.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:protobuf/protobuf.dart'; class RuleTile extends HookConsumerWidget { const RuleTile({super.key, required this.index, required this.rule}); final Rule rule; final int index; Map detailChipsValue() { final map = rule.toProto3Json()! as Map; map.removeWhere((key, value) => ['list_order', 'enabled', 'name', 'outbound'].contains(key)); map.updateAll( (key, value) => value is List ? value.length : value is ProtobufEnum ? value.name : value, ); return map; } Map mergeTranslation(List> translations) { return Map.fromEntries(translations.expand((map) => map.entries).toList()); } Future handleDelete(BuildContext context, WidgetRef ref) async { final t = ref.watch(translationsProvider).requireValue; final result = await ref .read(dialogNotifierProvider.notifier) .showConfirmation( title: t.dialogs.confirmation.routeRule.delete.title, message: t.dialogs.confirmation.routeRule.delete.msg(rulename: rule.name), positiveBtnTxt: t.common.delete, ); if (result == true) { await ref.read(rulesNotifierProvider.notifier).deleteRule(rule.listOrder); } } @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; final scrollController = useScrollController(); ref.listen(rulesNotifierProvider, (_, _) { if (scrollController.offset > 0) scrollController.jumpTo(0); }); return Material( child: InkWell( onTap: () => Navigator.of( context, ).push(MaterialPageRoute(builder: (context) => RulePage(ruleListOrder: rule.listOrder))), onLongPress: () async => await handleDelete(context, ref), onSecondaryTapUp: PlatformUtils.isDesktop ? (details) { final offset = details.globalPosition; showMenu( context: context, position: RelativeRect.fromLTRB(offset.dx, offset.dy, offset.dx, offset.dy), items: [ PopupMenuItem( child: Text(t.pages.settings.routing.routeRule.deleteRule), onTap: () async => await handleDelete(context, ref), ), ], ); } : null, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ ListTile( title: Text( t.pages.settings.routing.routeRule.rule.outbound[rule.outbound.name] ?? rule.outbound.name, style: Theme.of(context).textTheme.labelMedium, ), subtitle: Text(rule.name, style: Theme.of(context).textTheme.bodyLarge), leading: ReorderableDragStartListener(index: index, child: const Icon(Icons.drag_handle_rounded)), trailing: Switch( value: rule.enabled, onChanged: (value) async => await ref.read(rulesNotifierProvider.notifier).updateEnabled(value, rule.listOrder), ), ), SettingDetailChips( values: detailChipsValue().entries.toList(), scrollController: scrollController, t: mergeTranslation([ t.pages.settings.routing.routeRule.rule.tileTitle, t.pages.settings.routing.routeRule.rule.network, t.pages.settings.routing.routeRule.rule.outbound, ]), ), ], ), ), ); } } ================================================ FILE: lib/features/route_rules/widget/setting_checkbox.dart ================================================ import 'package:flutter/material.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/router/dialog/dialog_notifier.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:protobuf/protobuf.dart'; class SettingCheckbox extends ConsumerWidget { const SettingCheckbox({ super.key, required this.title, required this.values, required this.selectedValues, required this.setValue, this.defaultValue, this.t, }); final String title; final List values; final List selectedValues; final Function(List value) setValue; final List? defaultValue; final Map? t; String textWithTranslation(List e, WidgetRef ref) { if (t == null) { return e.map((e) => '$e').toList().join(', '); } else { if (e.isEmpty) return t![''] ?? ref.watch(translationsProvider).requireValue.common.empty; return e.map((e) => t!['$e'] ?? '$e').toList().join(', '); } } @override Widget build(BuildContext context, WidgetRef ref) { return ListTile( title: Text(title), subtitle: Text(textWithTranslation(selectedValues, ref)), onTap: () async { final result = await ref .read(dialogNotifierProvider.notifier) .showSettingCheckbox( title: title, values: values, selectedValues: selectedValues, defaultValue: defaultValue, t: t, ); if (result is List) setValue(result); }, ); } } ================================================ FILE: lib/features/route_rules/widget/setting_detail_chips.dart ================================================ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:gap/gap.dart'; import 'package:hiddify/utils/platform_utils.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:installed_apps/app_info.dart'; import 'package:installed_apps/installed_apps.dart'; class SettingDetailChips extends HookConsumerWidget { const SettingDetailChips({ super.key, required this.values, this.t, this.scrollController, this.useEllipsis = false, this.isPackageName = false, }); final List values; final Map? t; final ScrollController? scrollController; final bool useEllipsis; final bool isPackageName; @override Widget build(BuildContext context, WidgetRef ref) { final controller = scrollController ?? useScrollController(); final showStartBtn = useState(false); final showEndBtn = useState(true); useEffect(() { void listener() { showStartBtn.value = controller.position.pixels > controller.position.minScrollExtent; showEndBtn.value = controller.position.pixels < controller.position.maxScrollExtent; } controller.addListener(listener); return () => controller.removeListener(listener); }, [controller]); void scrollToEnd() { controller.animateTo( controller.offset + 150, duration: const Duration(milliseconds: 500), curve: Curves.easeInOut, ); } void scrollToStart() { controller.animateTo( controller.offset - 150, duration: const Duration(milliseconds: 500), curve: Curves.easeInOut, ); } return LayoutBuilder( builder: (context, constraints) { WidgetsBinding.instance.addPostFrameCallback((_) { if (controller.position.maxScrollExtent <= 0) { showStartBtn.value = false; showEndBtn.value = false; } else { showStartBtn.value = controller.position.pixels > controller.position.minScrollExtent; showEndBtn.value = controller.position.pixels < controller.position.maxScrollExtent; } }); return Container( padding: const EdgeInsets.only(bottom: 8), height: 32, child: PlatformUtils.isDesktop ? Stack( children: [ ListView.separated( controller: controller, padding: const EdgeInsets.symmetric(horizontal: 16), itemCount: values.length, scrollDirection: Axis.horizontal, itemBuilder: (context, index) => SettingDetailChip( value: values[index], t: t, useEllipsis: useEllipsis, isPackageName: isPackageName, ), separatorBuilder: (context, index) => const Gap(8), ), Row( children: [ if (showStartBtn.value) ScrollBtn(isStart: true, onTap: scrollToStart), const Spacer(), if (showEndBtn.value) ScrollBtn(isStart: false, onTap: scrollToEnd), ], ), ], ) : ListView.separated( controller: controller, padding: const EdgeInsets.symmetric(horizontal: 16), itemCount: values.length, scrollDirection: Axis.horizontal, itemBuilder: (context, index) => SettingDetailChip( value: values[index], t: t, useEllipsis: useEllipsis, isPackageName: isPackageName, ), separatorBuilder: (context, index) => const Gap(8), ), ); }, ); } } class SettingDetailChip extends ConsumerWidget { const SettingDetailChip({ super.key, required this.value, required this.t, required this.useEllipsis, required this.isPackageName, }); final T value; final Map? t; final bool useEllipsis; final bool isPackageName; Widget valueByType(T value, ThemeData theme) { if (value is MapEntry) { return Row( children: [ tText('${value.key}', theme), VerticalDivider(width: 12, color: theme.colorScheme.onSurfaceVariant, indent: 3, endIndent: 3), tText('${value.value}', theme), ], ); } else { return tText('$value', theme); } } Widget tText(String value, ThemeData theme) { String text = value; if (useEllipsis && value.length > 20) { text = '${value.substring(0, 10)}...${value.substring(value.length - 10)}'; } return Text(t == null ? text : t![text] ?? text, style: theme.textTheme.labelMedium); } @override Widget build(BuildContext context, WidgetRef ref) { final theme = Theme.of(context); return Container( decoration: BoxDecoration(color: theme.colorScheme.surfaceVariant, borderRadius: BorderRadius.circular(8)), child: isPackageName ? AndroidAppInfo(packageName: '$value') : Padding(padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), child: valueByType(value, theme)), ); } } class AndroidAppInfo extends HookConsumerWidget { const AndroidAppInfo({super.key, required this.packageName}); final String packageName; String useEllipsis() { if (packageName.length > 20) { return '${packageName.substring(0, 10)}...${packageName.substring(packageName.length - 10)}'; } else { return packageName; } } Future getAppInfo() async => await InstalledApps.getAppInfo(packageName, BuiltWith.flutter); @override Widget build(BuildContext context, WidgetRef ref) { final app = useFuture(useMemoized(() => getAppInfo(), [packageName])); if (app.hasData && app.data != null) { return Padding( padding: const EdgeInsetsDirectional.fromSTEB(4, 4, 8, 4), child: Row( children: [ AspectRatio( aspectRatio: 1.0, child: CircleAvatar(backgroundColor: Colors.transparent, child: Image.memory(app.data!.icon!)), ), const Gap(4), Text(app.data!.name, style: Theme.of(context).textTheme.labelMedium), ], ), ); } else if (app.hasError || (app.hasData && app.data == null)) { return Padding( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), child: Text(useEllipsis(), style: Theme.of(context).textTheme.labelMedium), ); } else { return const Padding( padding: EdgeInsets.all(4), child: AspectRatio(aspectRatio: 1.0, child: CircularProgressIndicator()), ); } } } class ScrollBtn extends ConsumerWidget { const ScrollBtn({super.key, required this.isStart, required this.onTap}); final bool isStart; final GestureTapCallback? onTap; @override Widget build(BuildContext context, WidgetRef ref) { final theme = Theme.of(context); const radius = Radius.circular(4); final borderRadius = isStart ? const BorderRadiusDirectional.only(topEnd: radius, bottomEnd: radius) : const BorderRadiusDirectional.only(topStart: radius, bottomStart: radius); return Material( borderRadius: borderRadius, child: InkWell( borderRadius: borderRadius.resolve(Directionality.of(context)), onTap: onTap, child: Container( width: 48, height: 24, decoration: BoxDecoration( color: theme.colorScheme.primaryContainer, borderRadius: borderRadius, boxShadow: [BoxShadow(color: theme.colorScheme.shadow, blurRadius: 12, offset: const Offset(0, 3))], ), child: Icon( isStart ? Icons.arrow_left_rounded : Icons.arrow_right_rounded, color: theme.colorScheme.onPrimaryContainer, ), ), ), ); } } ================================================ FILE: lib/features/route_rules/widget/setting_divider.dart ================================================ import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class SettingDivider extends ConsumerWidget { const SettingDivider({super.key, this.title}); final String? title; @override Widget build(BuildContext context, WidgetRef ref) { final theme = Theme.of(context); if (title == null) return const Divider(indent: 16, endIndent: 16, height: 1); return Row( children: [ const Expanded(child: Divider(indent: 16, endIndent: 8, height: 1)), const Icon(size: 16, Icons.warning_rounded, color: Colors.amber), const Gap(2), Text(title!, style: theme.textTheme.titleSmall!.copyWith(color: theme.colorScheme.onSurface)), const Expanded(child: Divider(indent: 8, endIndent: 16, height: 1)), ], ); } } ================================================ FILE: lib/features/route_rules/widget/setting_generic_list.dart ================================================ import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/features/route_rules/widget/setting_detail_chips.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class SettingGenericList extends ConsumerWidget { const SettingGenericList({ super.key, required this.title, required this.values, required this.onTap, this.useEllipsis = false, this.isPackageName = false, this.showPlatformWarning = false, }); final String title; final List values; final GestureTapCallback? onTap; final bool useEllipsis; final bool isPackageName; final bool showPlatformWarning; @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; final theme = Theme.of(context); return Material( child: InkWell( onTap: onTap, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ ListTile( title: showPlatformWarning ? Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ const Icon(size: 16, Icons.warning_rounded, color: Colors.amber), const Gap(2), Text( t.pages.settings.routing.routeRule.rule.notAvailabeInThisPlatform, style: theme.textTheme.labelSmall!.copyWith(color: theme.colorScheme.onSurfaceVariant), overflow: TextOverflow.ellipsis, maxLines: 1, ), ], ), Text(title), ], ) : Text(title), trailing: Text('${values.length}'), ), if (values.isNotEmpty) SettingDetailChips(values: values, useEllipsis: useEllipsis, isPackageName: isPackageName), ], ), ), ); } } ================================================ FILE: lib/features/route_rules/widget/setting_radio.dart ================================================ import 'package:flutter/material.dart'; import 'package:hiddify/core/router/dialog/dialog_notifier.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class SettingRadio extends ConsumerWidget { const SettingRadio({ super.key, required this.title, required this.values, required this.value, required this.setValue, this.defaultValue, this.t, }); final String title; final List values; final T value; final Function(T value) setValue; final T? defaultValue; final Map? t; String textWithTranslation(T e) { if (t == null) return '$e'; return t!['$e'] ?? '$e'; } @override Widget build(BuildContext context, WidgetRef ref) { return ListTile( title: Text(title), subtitle: Text(textWithTranslation(value)), onTap: () async { final result = await ref .read(dialogNotifierProvider.notifier) .showSettingRadio(title: title, values: values, value: value, defaultValue: defaultValue, t: t); if (result is T) setValue(result); }, ); } } ================================================ FILE: lib/features/route_rules/widget/setting_text.dart ================================================ import 'package:flutter/material.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/router/dialog/dialog_notifier.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class SettingText extends ConsumerWidget { const SettingText({ super.key, required this.title, required this.value, required this.setValue, this.defaultValue, this.validator, }); final String title; final String value; final Function(String value) setValue; final String? defaultValue; final FormFieldValidator? validator; @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; return ListTile( title: Text(title), subtitle: Text(value.isEmpty ? t.common.empty : value), onTap: () async { final result = await ref .read(dialogNotifierProvider.notifier) .showSettingText(lable: title, value: value, defaultValue: defaultValue, validator: validator); if (result is String) setValue(result); }, ); } } ================================================ FILE: lib/features/settings/data/battery_optimization_repository.dart ================================================ import 'package:flutter/services.dart'; import 'package:hiddify/core/utils/exception_handler.dart'; import 'package:hiddify/utils/custom_loggers.dart'; import 'package:loggy/loggy.dart'; abstract interface class BatteryOptimizationRepository { Future isIgnoringBatteryOptimizations(); Future requestIgnoreBatteryOptimizations(); } class BatteryOptimizationRepositoryImpl with ExceptionHandler, InfraLogger implements BatteryOptimizationRepository { final _methodChannel = const MethodChannel("com.hiddify.app/platform"); @override Future isIgnoringBatteryOptimizations() async { bool? result; try { loggy.debug("checking battery optimization status"); result = await _methodChannel.invokeMethod("is_ignoring_battery_optimizations"); loggy.debug("is ignoring battery optimizations? [$result]"); } catch (e) { loggy.log(LogLevel.error, e.toString()); } return result; } @override Future requestIgnoreBatteryOptimizations() async { bool? result; try { loggy.debug("requesting ignore battery optimization"); result = await _methodChannel.invokeMethod("request_ignore_battery_optimizations"); loggy.debug("ignore battery optimization result: [$result]"); } catch (e) { loggy.log(LogLevel.error, e.toString()); } return result; } } ================================================ FILE: lib/features/settings/data/config_option_data_providers.dart ================================================ import 'package:hiddify/core/preferences/preferences_provider.dart'; import 'package:hiddify/features/settings/data/config_option_repository.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'config_option_data_providers.g.dart'; @Riverpod(keepAlive: true) ConfigOptionRepository configOptionRepository(Ref ref) { return ConfigOptionRepository( preferences: ref.watch(sharedPreferencesProvider).requireValue, getConfigOptions: () => ref.read(ConfigOptions.singboxConfigOptions), ); } ================================================ FILE: lib/features/settings/data/config_option_repository.dart ================================================ import 'package:dartx/dartx.dart'; import 'package:fpdart/fpdart.dart'; import 'package:hiddify/core/model/optional_range.dart'; import 'package:hiddify/core/model/region.dart'; import 'package:hiddify/core/utils/exception_handler.dart'; import 'package:hiddify/core/utils/json_converters.dart'; import 'package:hiddify/core/utils/preferences_utils.dart'; import 'package:hiddify/features/log/model/log_level.dart'; import 'package:hiddify/features/profile/data/profile_parser.dart'; import 'package:hiddify/features/settings/model/config_option_failure.dart'; import 'package:hiddify/singbox/model/singbox_config_enum.dart'; import 'package:hiddify/singbox/model/singbox_config_option.dart'; import 'package:hiddify/singbox/model/singbox_rule.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:shared_preferences/shared_preferences.dart'; abstract class ConfigOptions { static final serviceMode = PreferencesNotifier.create( "service-mode", ServiceMode.defaultMode, mapFrom: (value) => ServiceMode.choices.firstWhere((e) => e.key == value), mapTo: (value) => value.key, ); static final balancerStrategy = PreferencesNotifier.create( "balancer-strategy", BalancerStrategy.roundRobin, mapFrom: (value) => BalancerStrategy.values.firstWhere((e) => e.key == value), mapTo: (value) => value.key, ); static final region = PreferencesNotifier.create( "region", Region.other, mapFrom: Region.values.byName, mapTo: (value) => value.name, ); static final useXrayCoreWhenPossible = PreferencesNotifier.create("use-xray-core-when-possible", false); static final blockAds = PreferencesNotifier.create("block-ads", false); static final logLevel = PreferencesNotifier.create( "log-level", LogLevel.warn, mapFrom: LogLevel.values.byName, mapTo: (value) => value.name, ); static final resolveDestination = PreferencesNotifier.create("resolve-destination", false); static final ipv6Mode = PreferencesNotifier.create( "ipv6-mode", IPv6Mode.disable, mapFrom: (value) => IPv6Mode.values.firstWhere((e) => e.key == value), mapTo: (value) => value.key, ); static final remoteDnsAddress = PreferencesNotifier.create( "remote-dns-address", "tcp://8.8.8.8", possibleValues: List.of([ "local", // "udp://223.5.5.5", // "udp://1.1.1.1", // "udp://1.1.1.2", "tcp://8.8.8.8", "tcp://1.1.1.1", "https://1.1.1.1/dns-query", "https://dns.cloudflare.com/dns-query", "tcp://4.4.2.2", ]), validator: (value) => value.isNotBlank, ); static final remoteDnsDomainStrategy = PreferencesNotifier.create( "remote-dns-domain-strategy", DomainStrategy.auto, mapFrom: (value) => DomainStrategy.values.firstWhere((e) => e.key == value), mapTo: (value) => value.key, ); static final directDnsAddress = PreferencesNotifier.create( "direct-dns-address", "udp://1.1.1.1", possibleValues: List.of([ "local", "udp://223.5.5.5", "udp://1.1.1.1", "udp://1.1.1.2", "tcp://1.1.1.1", "https://1.1.1.1/dns-query", "https://dns.cloudflare.com/dns-query", "4.4.2.2", "8.8.8.8", ]), defaultValueFunction: (ref) => ref.read(region) == Region.cn ? "223.5.5.5" : "1.1.1.1", validator: (value) => value.isNotBlank, ); static final directDnsDomainStrategy = PreferencesNotifier.create( "direct-dns-domain-strategy", DomainStrategy.auto, mapFrom: (value) => DomainStrategy.values.firstWhere((e) => e.key == value), mapTo: (value) => value.key, ); static final mixedPort = PreferencesNotifier.create( "mixed-port", 12334, validator: (value) => isPort(value.toString()), ); static final tproxyPort = PreferencesNotifier.create( "tproxy-port", 12335, validator: (value) => isPort(value.toString()), ); static final redirectPort = PreferencesNotifier.create( "redirect-port", 12336, validator: (value) => isPort(value.toString()), ); static final directPort = PreferencesNotifier.create( "direct-port", 12337, validator: (value) => isPort(value.toString()), ); static final tunImplementation = PreferencesNotifier.create( "tun-implementation", TunImplementation.gvisor, mapFrom: TunImplementation.values.byName, mapTo: (value) => value.name, ); static final mtu = PreferencesNotifier.create("mtu", 9000); static final strictRoute = PreferencesNotifier.create("strict-route", true); static final connectionTestUrl = PreferencesNotifier.create( "connection-test-url", "http://captive.apple.com/hotspot-detect.html", possibleValues: List.of([ "http://connectivitycheck.gstatic.com/generate_204", "http://www.gstatic.com/generate_204", "https://www.gstatic.com/generate_204", "http://cp.cloudflare.com", "http://kernel.org", "http://detectportal.firefox.com", "http://captive.apple.com/hotspot-detect.html", "https://1.1.1.1", "http://1.1.1.1", ]), validator: (value) => value.isNotBlank && isUrl(value), ); static final urlTestInterval = PreferencesNotifier.create( "url-test-interval", const Duration(minutes: 10), mapFrom: const IntervalInSecondsConverter().fromJson, mapTo: const IntervalInSecondsConverter().toJson, ); static final enableClashApi = PreferencesNotifier.create("enable-clash-api", true); static final clashApiPort = PreferencesNotifier.create( "clash-api-port", 16756, validator: (value) => isPort(value.toString()), ); static final bypassLan = PreferencesNotifier.create("bypass-lan", false); static final allowConnectionFromLan = PreferencesNotifier.create("allow-connection-from-lan", false); static final enableFakeDns = PreferencesNotifier.create("enable-fake-dns", false); // static final enableDnsRouting = PreferencesNotifier.create("enable-dns-routing", true); static final independentDnsCache = PreferencesNotifier.create("independent-dns-cache", true); static final enableTlsFragment = PreferencesNotifier.create("enable-tls-fragment", false); static final fragmentPackets = PreferencesNotifier.create( "fragment-packets", "tlshello", possibleValues: ["tlshello", "1-1", "1-2", "1-3", "1-4", "1-5"], ); static final tlsFragmentSize = PreferencesNotifier.create( "tls-fragment-size", const OptionalRange(min: 10, max: 30), mapFrom: OptionalRange.parse, mapTo: const OptionalRangeJsonConverter().toJson, ); static final tlsFragmentSleep = PreferencesNotifier.create( "tls-fragment-sleep", const OptionalRange(min: 2, max: 8), mapFrom: OptionalRange.parse, mapTo: const OptionalRangeJsonConverter().toJson, ); static final enableTlsMixedSniCase = PreferencesNotifier.create("enable-tls-mixed-sni-case", false); static final enableTlsPadding = PreferencesNotifier.create("enable-tls-padding", false); static final tlsPaddingSize = PreferencesNotifier.create( "tls-padding-size", const OptionalRange(min: 1, max: 1500), mapFrom: OptionalRange.parse, mapTo: const OptionalRangeJsonConverter().toJson, ); static final enableMux = PreferencesNotifier.create("enable-mux", false); static final muxPadding = PreferencesNotifier.create("mux-padding", false); static final muxMaxStreams = PreferencesNotifier.create( "mux-max-streams", 8, validator: (value) => value > 0, ); static final muxProtocol = PreferencesNotifier.create( "mux-protocol", MuxProtocol.h2mux, mapFrom: MuxProtocol.values.byName, mapTo: (value) => value.name, ); static final enableWarp = PreferencesNotifier.create("enable-warp", false); static final warpDetourMode = PreferencesNotifier.create( "warp-detour-mode", WarpDetourMode.warpOverProxy, mapFrom: WarpDetourMode.values.byName, mapTo: (value) => value.name, ); static final warpLicenseKey = PreferencesNotifier.create("warp-license-key", ""); static final warp2LicenseKey = PreferencesNotifier.create("warp2s-license-key", ""); static final warpAccountId = PreferencesNotifier.create("warp-account-id", ""); static final warp2AccountId = PreferencesNotifier.create("warp2-account-id", ""); static final warpAccessToken = PreferencesNotifier.create("warp-access-token", ""); static final warp2AccessToken = PreferencesNotifier.create("warp2-access-token", ""); static final warpCleanIp = PreferencesNotifier.create("warp-clean-ip", "auto"); static final warpPort = PreferencesNotifier.create( "warp-port", 0, validator: (value) => isPort(value.toString()), ); static final warpNoise = PreferencesNotifier.create( "warp-noise", const OptionalRange(min: 1, max: 3), mapFrom: (value) => OptionalRange.parse(value, allowEmpty: true), mapTo: const OptionalRangeJsonConverter().toJson, ); static final warpNoiseMode = PreferencesNotifier.create("warp-noise-mode", "m4"); static final warpNoiseDelay = PreferencesNotifier.create( "warp-noise-delay", const OptionalRange(min: 10, max: 30), mapFrom: (value) => OptionalRange.parse(value, allowEmpty: true), mapTo: const OptionalRangeJsonConverter().toJson, ); static final warpNoiseSize = PreferencesNotifier.create( "warp-noise-size", const OptionalRange(min: 10, max: 30), mapFrom: (value) => OptionalRange.parse(value, allowEmpty: true), mapTo: const OptionalRangeJsonConverter().toJson, ); static final warpWireguardConfig = PreferencesNotifier.create("warp-wireguard-config", ""); static final warp2WireguardConfig = PreferencesNotifier.create("warp2-wireguard-config", ""); static final hasExperimentalFeatures = Provider.autoDispose((ref) { // final mode = ref.watch(serviceMode); // if (PlatformUtils.isDesktop && mode == ServiceMode.tun) { // return true; // } // if (ref.watch(enableTlsFragment) || ref.watch(enableTlsMixedSniCase) || ref.watch(enableTlsPadding) || ref.watch(enableMux) || ref.watch(enableWarp) || ref.watch(bypassLan) || ref.watch(allowConnectionFromLan)) { // return true; // } return false; }); /// preferences to exclude from share and export static final privatePreferencesKeys = { "warp.license-key", "warp.access-token", "warp.account-id", "warp.wireguard-config", "warp2.license-key", "warp2.access-token", "warp2.account-id", "warp2.wireguard-config", }; static final Map> preferences = { "region": region, "balancer-strategy": balancerStrategy, "block-ads": blockAds, "use-xray-core-when-possible": useXrayCoreWhenPossible, "service-mode": serviceMode, "log-level": logLevel, "resolve-destination": resolveDestination, "ipv6-mode": ipv6Mode, "remote-dns-address": remoteDnsAddress, "remote-dns-domain-strategy": remoteDnsDomainStrategy, "direct-dns-address": directDnsAddress, "direct-dns-domain-strategy": directDnsDomainStrategy, "mixed-port": mixedPort, "tproxy-port": tproxyPort, "direct-port": directPort, "redirect-port": redirectPort, "tun-implementation": tunImplementation, "mtu": mtu, "strict-route": strictRoute, "connection-test-url": connectionTestUrl, "url-test-interval": urlTestInterval, "clash-api-port": clashApiPort, "bypass-lan": bypassLan, "allow-connection-from-lan": allowConnectionFromLan, // "enable-dns-routing": enableDnsRouting, // mux // "mux.enable": enableMux, // "mux.padding": muxPadding, // "mux.max-streams": muxMaxStreams, // "mux.protocol": muxProtocol, // tls-tricks "tls-tricks.enable-fragment": enableTlsFragment, "tls-tricks.fragment-packets": fragmentPackets, "tls-tricks.fragment-size": tlsFragmentSize, "tls-tricks.fragment-sleep": tlsFragmentSleep, "tls-tricks.mixed-sni-case": enableTlsMixedSniCase, "tls-tricks.enable-padding": enableTlsPadding, "tls-tricks.padding-size": tlsPaddingSize, // warp "warp.enable": enableWarp, "warp.mode": warpDetourMode, "warp.license-key": warpLicenseKey, "warp.account-id": warpAccountId, "warp.access-token": warpAccessToken, "warp.clean-ip": warpCleanIp, "warp.clean-port": warpPort, "warp.noise": warpNoise, "warp.noise-size": warpNoiseSize, "warp.noise-mode": warpNoiseMode, "warp.noise-delay": warpNoiseDelay, "warp.wireguard-config": warpWireguardConfig, "warp2.license-key": warp2LicenseKey, "warp2.account-id": warp2AccountId, "warp2.access-token": warp2AccessToken, "warp2.wireguard-config": warp2WireguardConfig, }; static final singboxConfigOptions = Provider((ref) { // final region = ref.watch(Preferences.region); final rules = []; // final rules = switch (region) { // Region.ir => [ // const SingboxRule( // domains: "domain:.ir,geosite:ir", // ip: "geoip:ir", // outbound: RuleOutbound.bypass, // ), // ], // Region.cn => [ // const SingboxRule( // domains: "domain:.cn,geosite:cn", // ip: "geoip:cn", // outbound: RuleOutbound.bypass, // ), // ], // Region.ru => [ // const SingboxRule( // domains: "domain:.ru", // ip: "geoip:ru", // outbound: RuleOutbound.bypass, // ), // ], // Region.af => [ // const SingboxRule( // domains: "domain:.af,geosite:af", // ip: "geoip:af", // outbound: RuleOutbound.bypass, // ), // ], // Region.id => [ // const SingboxRule( // domains: "domain:.id,geosite:id", // ip: "geoip:id", // outbound: RuleOutbound.bypass, // ), // ], // _ => [], // }; final mode = ref.watch(serviceMode); // final reg = ref.watch(Preferences.region.notifier).raw(); return SingboxConfigOption( region: ref.watch(region).name, balancerStrategy: ref.watch(balancerStrategy), blockAds: ref.watch(blockAds), useXrayCoreWhenPossible: ref.watch(useXrayCoreWhenPossible), executeConfigAsIs: false, logLevel: ref.watch(logLevel), resolveDestination: ref.watch(resolveDestination), ipv6Mode: ref.watch(ipv6Mode), remoteDnsAddress: ref.watch(remoteDnsAddress), remoteDnsDomainStrategy: ref.watch(remoteDnsDomainStrategy), directDnsAddress: ref.watch(directDnsAddress), directDnsDomainStrategy: ref.watch(directDnsDomainStrategy), mixedPort: ref.watch(mixedPort), tproxyPort: ref.watch(tproxyPort), directPort: ref.watch(directPort), redirectPort: ref.watch(redirectPort), tunImplementation: ref.watch(tunImplementation), mtu: ref.watch(mtu), strictRoute: ref.watch(strictRoute), connectionTestUrl: ref.watch(connectionTestUrl), urlTestInterval: ref.watch(urlTestInterval), enableClashApi: ref.watch(enableClashApi), clashApiPort: ref.watch(clashApiPort), enableTun: mode == ServiceMode.tun, // enableTunService: mode == false, //ServiceMode.tunService, setSystemProxy: mode == ServiceMode.systemProxy, bypassLan: ref.watch(bypassLan), allowConnectionFromLan: ref.watch(allowConnectionFromLan), enableFakeDns: ref.watch(enableFakeDns), // enableDnsRouting: ref.watch(enableDnsRouting), independentDnsCache: ref.watch(independentDnsCache), // mux: SingboxMuxOption( // enable: ref.watch(enableMux), // padding: ref.watch(muxPadding), // maxStreams: ref.watch(muxMaxStreams), // protocol: ref.watch(muxProtocol), // ), tlsTricks: SingboxTlsTricks( enableFragment: ref.watch(enableTlsFragment), fragmentSize: ref.watch(tlsFragmentSize), fragmentSleep: ref.watch(tlsFragmentSleep), mixedSniCase: ref.watch(enableTlsMixedSniCase), enablePadding: ref.watch(enableTlsPadding), paddingSize: ref.watch(tlsPaddingSize), ), warp: SingboxWarpOption( enable: ref.watch(enableWarp), mode: ref.watch(warpDetourMode), wireguardConfig: ref.watch(warpWireguardConfig), licenseKey: ref.watch(warpLicenseKey), accountId: ref.watch(warpAccountId), accessToken: ref.watch(warpAccessToken), cleanIp: ref.watch(warpCleanIp), cleanPort: ref.watch(warpPort), noise: ref.watch(warpNoise), noiseMode: ref.watch(warpNoiseMode), noiseSize: ref.watch(warpNoiseSize), noiseDelay: ref.watch(warpNoiseDelay), ), warp2: SingboxWarpOption( enable: ref.watch(enableWarp), mode: ref.watch(warpDetourMode), wireguardConfig: ref.watch(warp2WireguardConfig), licenseKey: ref.watch(warp2LicenseKey), accountId: ref.watch(warp2AccountId), accessToken: ref.watch(warp2AccessToken), cleanIp: ref.watch(warpCleanIp), cleanPort: ref.watch(warpPort), noise: ref.watch(warpNoise), noiseMode: ref.watch(warpNoiseMode), noiseSize: ref.watch(warpNoiseSize), noiseDelay: ref.watch(warpNoiseDelay), ), rules: rules, ); }); } class ConfigOptionRepository with ExceptionHandler, InfraLogger { ConfigOptionRepository({required this.preferences, required SingboxConfigOption Function() getConfigOptions}) : _getConfigOptions = getConfigOptions; final SharedPreferences preferences; final SingboxConfigOption Function() _getConfigOptions; Either fullOptions() => Either.tryCatch(() => _getConfigOptions(), ConfigOptionFailure.unexpected); Either fullOptionsOverrided(String? profileOverride) => Either.tryCatch(() => _getConfigOptions(), ConfigOptionFailure.unexpected).flatMap( (options) => Either.tryCatch(() { final json = ProfileParser.applyProfileOverride(options.toJson(), profileOverride); return SingboxConfigOption.fromJson(json); }, ConfigOptionFailure.unexpected), ); } ================================================ FILE: lib/features/settings/model/config_option_failure.dart ================================================ import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/model/failures.dart'; part 'config_option_failure.freezed.dart'; @freezed sealed class ConfigOptionFailure with _$ConfigOptionFailure, Failure { const ConfigOptionFailure._(); @With() const factory ConfigOptionFailure.unexpected([Object? error, StackTrace? stackTrace]) = ConfigOptionUnexpectedFailure; @With() const factory ConfigOptionFailure.missingWarp() = MissingWarpConfigFailure; @override ({String type, String? message}) present(TranslationsEn t) { return switch (this) { ConfigOptionUnexpectedFailure() => (type: t.errors.unexpected, message: null), MissingWarpConfigFailure() => (type: t.pages.settings.warp.missingConfig, message: null), }; } } ================================================ FILE: lib/features/settings/model/settings_failure.dart ================================================ import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/model/failures.dart'; part 'settings_failure.freezed.dart'; @freezed sealed class SettingsFailure with _$SettingsFailure, Failure { const SettingsFailure._(); @With() const factory SettingsFailure.unexpected([Object? error, StackTrace? stackTrace]) = SettingsUnexpectedFailure; @override ({String type, String? message}) present(TranslationsEn t) { return switch (this) { SettingsUnexpectedFailure() => (type: t.errors.unexpected, message: null), }; } } ================================================ FILE: lib/features/settings/notifier/battery_optimization/battery_optimizations_notifier.dart ================================================ import 'package:hiddify/features/settings/data/battery_optimization_repository.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'battery_optimizations_notifier.g.dart'; @riverpod class BatteryOptimizationNotifier extends _$BatteryOptimizationNotifier { @override Future build() async { return await BatteryOptimizationRepositoryImpl().isIgnoringBatteryOptimizations() ?? false; } Future requestToIgnore() async { state = const AsyncLoading(); await BatteryOptimizationRepositoryImpl().requestIgnoreBatteryOptimizations(); Future.delayed(const Duration(seconds: 1)); ref.invalidateSelf(); } } ================================================ FILE: lib/features/settings/notifier/config_option/config_option_notifier.dart ================================================ import 'dart:convert'; import 'dart:io'; import 'package:dartx/dartx_io.dart'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/services.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/notification/in_app_notification_controller.dart'; import 'package:hiddify/features/connection/data/connection_data_providers.dart'; import 'package:hiddify/features/connection/notifier/connection_notifier.dart'; import 'package:hiddify/features/profile/notifier/active_profile_notifier.dart'; import 'package:hiddify/features/proxy/active/active_proxy_notifier.dart'; import 'package:hiddify/features/settings/data/config_option_repository.dart'; import 'package:hiddify/utils/custom_loggers.dart'; import 'package:hiddify/utils/platform_utils.dart'; import 'package:json_path/json_path.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'config_option_notifier.g.dart'; @Riverpod(keepAlive: true) class ConfigOptionNotifier extends _$ConfigOptionNotifier with AppLogger { @override Future build() async { final serviceRunning = await ref.watch(serviceRunningProvider.future); final serviceSingboxOptions = ref.read(connectionRepositoryProvider).configOptionsSnapshot; ref.listen(ConfigOptions.singboxConfigOptions, (previous, next) async { if (!serviceRunning || previous == null) return; if (next != previous && next != serviceSingboxOptions) { if (_lastUpdate == null || DateTime.now().difference(_lastUpdate!) > const Duration(milliseconds: 100)) { _lastUpdate = DateTime.now(); if (serviceSingboxOptions?.enableTun != next.enableTun) { loggy.debug("tun option changed, reconnecting"); await ref.read(connectionNotifierProvider.notifier).toggleConnection(); await ref.read(connectionNotifierProvider.notifier).toggleConnection(); } else { final activeProfile = await ref.read(activeProfileProvider.future); return await ref.read(connectionNotifierProvider.notifier).reconnect(activeProfile); } state = AsyncData(false); } } }, fireImmediately: true); return false; } DateTime? _lastUpdate; Future _exportJson(bool excludePrivate) async { try { final options = ref.read(ConfigOptions.singboxConfigOptions); Map map = options.toJson(); if (excludePrivate) { for (final key in ConfigOptions.privatePreferencesKeys) { final query = key.split('.').map((e) => '["$e"]').join(); final res = JsonPath('\$$query').read(map).firstOrNull; if (res != null) { map = res.pointer.remove(map)! as Map; } } } const encoder = JsonEncoder.withIndent(' '); return encoder.convert(map); } catch (e, st) { loggy.warning("error creating config options json", e, st); return null; } } Future exportJsonClipboard({bool excludePrivate = true}) async { final t = ref.read(translationsProvider).requireValue; try { final json = await _exportJson(excludePrivate); if (json == null) return false; await Clipboard.setData(ClipboardData(text: json)); ref.read(inAppNotificationControllerProvider).showSuccessToast(t.common.msg.export.clipboard.success); return true; } on PlatformException { ref .read(inAppNotificationControllerProvider) .showInfoToast(t.common.msg.export.clipboard.contentTooLarge, duration: const Duration(seconds: 5)); return false; } catch (e, st) { loggy.warning("error exporting config options to clipboard", e, st); ref.read(inAppNotificationControllerProvider).showErrorToast(t.common.msg.export.clipboard.failure); return false; } } Future exportJsonFile({bool excludePrivate = true}) async { final t = ref.read(translationsProvider).requireValue; try { final json = await _exportJson(excludePrivate); if (json == null) return false; final bytes = utf8.encode(json); final outputFile = await FilePicker.platform.saveFile( fileName: 'options.json', type: FileType.custom, allowedExtensions: ['json'], bytes: bytes, ); if (outputFile == null) return false; if (PlatformUtils.isDesktop) { final file = File(outputFile); if (file.extension != '.json') return false; if (!await file.exists()) await file.parent.create(recursive: true); await file.writeAsBytes(bytes); } ref.read(inAppNotificationControllerProvider).showSuccessToast(t.common.msg.export.file.success); return true; } catch (e, st) { loggy.warning("error exporting config options to json file", e, st); ref.read(inAppNotificationControllerProvider).showErrorToast(t.common.msg.export.file.failure); return false; } } Future _importJson(String input) async { if (jsonDecode(input) case final Map map) { for (final option in ConfigOptions.preferences.entries) { final query = option.key.split('.').map((e) => '["$e"]').join(); final res = JsonPath('\$$query').read(map).firstOrNull; if (res?.value case final value?) { try { await ref.read(option.value.notifier).updateRaw(value); } catch (e) { loggy.debug("error updating [${option.key}]: $e", e); } } } } } Future importFromClipboard() async { final t = ref.read(translationsProvider).requireValue; try { final input = await Clipboard.getData(Clipboard.kTextPlain).then((value) => value?.text); if (input == null) return false; await _importJson(input); ref.read(inAppNotificationControllerProvider).showSuccessToast(t.common.msg.import.success); return true; } catch (e, st) { loggy.warning("error importing config options from clipboard", e, st); ref.read(inAppNotificationControllerProvider).showErrorToast(t.common.msg.import.failure); return false; } } Future importFromJsonFile() async { final t = ref.read(translationsProvider).requireValue; try { final result = await FilePicker.platform.pickFiles(type: FileType.custom, allowedExtensions: ['json']); if (result == null) return false; final file = File(result.files.single.path!); if (!await file.exists()) return false; final bytes = await file.readAsBytes(); await _importJson(utf8.decode(bytes)); ref.read(inAppNotificationControllerProvider).showSuccessToast(t.common.msg.import.success); return true; } catch (e, st) { loggy.warning("error importing config options from json file", e, st); ref.read(inAppNotificationControllerProvider).showErrorToast(t.common.msg.import.failure); return false; } } Future resetOption() async { for (final option in ConfigOptions.preferences.values) { await ref.read(option.notifier).reset(); } ref.invalidateSelf(); } } ================================================ FILE: lib/features/settings/notifier/reset_tunnel/reset_tunnel_notifier.dart ================================================ import 'package:hiddify/hiddifycore/hiddify_core_service_provider.dart'; import 'package:hiddify/utils/custom_loggers.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'reset_tunnel_notifier.g.dart'; @riverpod class ResetTunnelNotifier extends _$ResetTunnelNotifier with AppLogger { @override Future build() async {} Future run() async { state = const AsyncLoading(); state = await AsyncValue.guard( () => ref.read(hiddifyCoreServiceProvider).resetTunnel().getOrElse((err) { loggy.warning("error resetting tunnel", err); throw err; }).run(), ); } } ================================================ FILE: lib/features/settings/notifier/warp_option/warp_option_notifier.dart ================================================ import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/model/constants.dart'; import 'package:hiddify/core/notification/in_app_notification_controller.dart'; import 'package:hiddify/core/preferences/preferences_provider.dart'; import 'package:hiddify/features/settings/data/config_option_repository.dart'; import 'package:hiddify/features/settings/model/config_option_failure.dart'; import 'package:hiddify/hiddifycore/hiddify_core_service_provider.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:loggy/loggy.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:shared_preferences/shared_preferences.dart'; part 'warp_option_notifier.g.dart'; @riverpod class WarpOptionNotifier extends _$WarpOptionNotifier with AppLogger { SharedPreferences get _prefs => ref.read(sharedPreferencesProvider).requireValue; @override AsyncValue build() { bool hasWarpConfig = false; try { final accountId = _prefs.getString(WarpConst.warpAccountId); final accessToken = _prefs.getString(WarpConst.warpAccessToken); hasWarpConfig = accountId != null && accessToken != null; } catch (e) { loggy.warning(e); } return hasWarpConfig ? const AsyncValue.data("") : AsyncError(const MissingWarpConfigFailure(), StackTrace.current); } Future genWarps({bool showToast = true}) async { if (state is AsyncLoading) return; state = const AsyncLoading(); final t = ref.read(translationsProvider).requireValue; final warpLog = await _genWarpConfig(); final warpLog2 = await _genWarp2Config(); if (warpLog != null && warpLog2 != null) { loggy.log(LogLevel.info, 'generated warp log : $warpLog'); loggy.log(LogLevel.info, 'generated warp2 log : $warpLog2'); if (showToast) { ref .read(inAppNotificationControllerProvider) .showSuccessToast('${t.pages.settings.warp.configGenerated} $warpLog'); } state = AsyncValue.data(warpLog); } else { ref.read(inAppNotificationControllerProvider).showErrorToast(t.pages.settings.warp.missingConfig); state = AsyncError(const MissingWarpConfigFailure(), StackTrace.current); } } Future _genWarpConfig() async { final result = await AsyncValue.guard(() async { final warp = await ref .read(hiddifyCoreServiceProvider) .generateWarpConfig( licenseKey: ref.read(ConfigOptions.warpLicenseKey), previousAccountId: ref.read(ConfigOptions.warpAccountId), previousAccessToken: ref.read(ConfigOptions.warpAccessToken), ) .getOrElse((l) => throw l) .run(); await ref.read(ConfigOptions.warpAccountId.notifier).update(warp.accountId); await ref.read(ConfigOptions.warpAccessToken.notifier).update(warp.accessToken); await ref.read(ConfigOptions.warpWireguardConfig.notifier).update(warp.wireguardConfig); return warp.log; }); state = result; return result.value; } Future _genWarp2Config() async { final result = await AsyncValue.guard(() async { final warp = await ref .read(hiddifyCoreServiceProvider) .generateWarpConfig( licenseKey: ref.read(ConfigOptions.warpLicenseKey), previousAccountId: ref.read(ConfigOptions.warp2AccountId), previousAccessToken: ref.read(ConfigOptions.warp2AccessToken), ) .getOrElse((l) => throw l) .run(); await ref.read(ConfigOptions.warp2AccountId.notifier).update(warp.accountId); await ref.read(ConfigOptions.warp2AccessToken.notifier).update(warp.accessToken); await ref.read(ConfigOptions.warp2WireguardConfig.notifier).update(warp.wireguardConfig); return warp.log; }); return result.value; } } @riverpod class WarpLicenseNotifier extends _$WarpLicenseNotifier with AppLogger { SharedPreferences get _prefs => ref.read(sharedPreferencesProvider).requireValue; @override bool build() { final consent = _prefs.getBool(WarpConst.warpConsentGiven) ?? false; return consent; } Future agree() async { await _prefs.setBool(WarpConst.warpConsentGiven, true); await ref.read(warpOptionNotifierProvider.notifier).genWarps(showToast: false); state = true; } } ================================================ FILE: lib/features/settings/overview/sections/dns_options_page.dart ================================================ import 'package:flutter/material.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/features/settings/data/config_option_repository.dart'; import 'package:hiddify/features/settings/widget/preference_tile.dart'; import 'package:hiddify/singbox/model/singbox_config_enum.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class DnsOptionsPage extends HookConsumerWidget { const DnsOptionsPage({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; return Scaffold( appBar: AppBar(title: Text(t.pages.settings.dns.title)), body: ListView( children: [ ValuePreferenceWidget( value: ref.watch(ConfigOptions.remoteDnsAddress), icon: Icons.vpn_lock_rounded, preferences: ref.watch(ConfigOptions.remoteDnsAddress.notifier), title: t.pages.settings.dns.remoteDns, ), ChoicePreferenceWidget( selected: ref.watch(ConfigOptions.remoteDnsDomainStrategy), preferences: ref.watch(ConfigOptions.remoteDnsDomainStrategy.notifier), choices: DomainStrategy.values, title: t.pages.settings.dns.remoteDnsDomainStrategy, icon: Icons.sync_alt_rounded, presentChoice: (value) => value.present(t), ), SwitchListTile.adaptive( title: Text(t.pages.settings.dns.enableFakeDns), secondary: const Icon(Icons.private_connectivity_rounded), value: ref.watch(ConfigOptions.enableFakeDns), onChanged: ref.read(ConfigOptions.enableFakeDns.notifier).update, ), ValuePreferenceWidget( title: t.pages.settings.dns.directDns, icon: Icons.public_rounded, value: ref.watch(ConfigOptions.directDnsAddress), preferences: ref.watch(ConfigOptions.directDnsAddress.notifier), ), ChoicePreferenceWidget( selected: ref.watch(ConfigOptions.directDnsDomainStrategy), preferences: ref.watch(ConfigOptions.directDnsDomainStrategy.notifier), choices: DomainStrategy.values, title: t.pages.settings.dns.directDnsDomainStrategy, icon: Icons.sync_alt_rounded, presentChoice: (value) => value.present(t), ), // SwitchListTile.adaptive( // title: Text(t.pages.settings.dns.enableDnsRouting), // secondary: const Icon(Icons.private_connectivity_rounded), // value: ref.watch(ConfigOptions.enableDnsRouting), // onChanged: ref.read(ConfigOptions.enableDnsRouting.notifier).update, // ), ], ), ); } } ================================================ FILE: lib/features/settings/overview/sections/general_page.dart ================================================ import 'package:dartx/dartx.dart'; import 'package:flutter/material.dart'; import 'package:hiddify/core/haptic/haptic_service.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/preferences/general_preferences.dart'; import 'package:hiddify/core/router/dialog/dialog_notifier.dart'; import 'package:hiddify/features/auto_start/notifier/auto_start_notifier.dart'; import 'package:hiddify/features/common/general_pref_tiles.dart'; import 'package:hiddify/features/log/model/log_level.dart'; import 'package:hiddify/features/settings/data/config_option_repository.dart'; import 'package:hiddify/features/settings/widget/preference_tile.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:humanizer/humanizer.dart'; class GeneralPage extends HookConsumerWidget { const GeneralPage({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; return Scaffold( appBar: AppBar(title: Text(t.pages.settings.general.title)), body: ListView( children: [ const LocalePrefTile(), const ThemeModePrefTile(), const EnableAnalyticsPrefTile(), SwitchListTile.adaptive( title: Text(t.pages.settings.general.autoIpCheck), value: ref.watch(Preferences.autoCheckIp), secondary: const Icon(Icons.flag_rounded), onChanged: ref.read(Preferences.autoCheckIp.notifier).update, ), if (PlatformUtils.isAndroid) ...[ SwitchListTile.adaptive( title: Text(t.pages.settings.general.dynamicNotification), secondary: const Icon(Icons.speed_rounded), value: ref.watch(Preferences.dynamicNotification), onChanged: ref.read(Preferences.dynamicNotification.notifier).update, ), SwitchListTile.adaptive( title: Text(t.pages.settings.general.hapticFeedback), secondary: const Icon(Icons.vibration_rounded), value: ref.watch(hapticServiceProvider), onChanged: ref.read(hapticServiceProvider.notifier).updatePreference, ), ], if (PlatformUtils.isDesktop) ...[ const ClosingPrefTile(), SwitchListTile.adaptive( title: Text(t.pages.settings.general.autoStart), secondary: const Icon(Icons.auto_mode_rounded), value: ref.watch(autoStartNotifierProvider).asData!.value, onChanged: (value) async => value ? await ref.read(autoStartNotifierProvider.notifier).enable() : await ref.read(autoStartNotifierProvider.notifier).disable(), ), SwitchListTile.adaptive( title: Text(t.pages.settings.general.silentStart), secondary: const Icon(Icons.visibility_off_rounded), value: ref.watch(Preferences.silentStart), onChanged: ref.read(Preferences.silentStart.notifier).update, ), ], if (PlatformUtils.isAndroid) const BatteryOptimizationWidget(), SwitchListTile.adaptive( title: Text(t.pages.settings.general.memoryLimit), subtitle: Text(t.pages.settings.general.memoryLimitMsg), secondary: const Icon(Icons.memory_rounded), value: !ref.watch(Preferences.disableMemoryLimit), onChanged: (value) async => await ref.read(Preferences.disableMemoryLimit.notifier).update(!value), ), SwitchListTile.adaptive( title: Text(t.pages.settings.general.debugMode), secondary: const Icon(Icons.bug_report_rounded), value: ref.watch(debugModeNotifierProvider), onChanged: (value) async { if (value) await ref .read(dialogNotifierProvider.notifier) .showOk(t.pages.settings.general.debugMode, t.pages.settings.general.debugModeMsg); await ref.read(debugModeNotifierProvider.notifier).update(value); }, ), ChoicePreferenceWidget( selected: ref.watch(ConfigOptions.logLevel), preferences: ref.watch(ConfigOptions.logLevel.notifier), choices: LogLevel.choices, title: t.pages.settings.general.logLevel, icon: Icons.description_rounded, presentChoice: (value) => value.name.toUpperCase(), ), ValuePreferenceWidget( value: ref.watch(ConfigOptions.connectionTestUrl), preferences: ref.watch(ConfigOptions.connectionTestUrl.notifier), title: t.pages.settings.general.connectionTestUrl, icon: Icons.link_rounded, ), ListTile( title: Text(t.pages.settings.general.urlTestInterval), subtitle: Text(ref.watch(ConfigOptions.urlTestInterval).toApproximateTime(isRelativeToNow: false)), leading: const Icon(Icons.timer_rounded), onTap: () async => await ref .read(dialogNotifierProvider.notifier) .showSettingSlider( title: t.pages.settings.general.urlTestInterval, initialValue: ref.watch(ConfigOptions.urlTestInterval).inMinutes.coerceIn(0, 60).toDouble(), onReset: ref.read(ConfigOptions.urlTestInterval.notifier).reset, min: 1, max: 60, divisions: 60, labelGen: (value) => Duration(minutes: value.toInt()).toApproximateTime(isRelativeToNow: false), ) .then((value) async { if (value == null) return; await ref.read(ConfigOptions.urlTestInterval.notifier).update(Duration(minutes: value.toInt())); }), ), ValuePreferenceWidget( value: ref.watch(ConfigOptions.clashApiPort), preferences: ref.watch(ConfigOptions.clashApiPort.notifier), title: t.pages.settings.general.clashApiPort, icon: Icons.api_rounded, validateInput: isPort, digitsOnly: true, inputToValue: int.tryParse, ), SwitchListTile.adaptive( title: Text(t.pages.settings.general.useXrayCoreWhenPossible), subtitle: Text(t.pages.settings.general.useXrayCoreWhenPossibleMsg), secondary: const Icon(Icons.extension_rounded), value: ref.watch(ConfigOptions.useXrayCoreWhenPossible), onChanged: ref.read(ConfigOptions.useXrayCoreWhenPossible.notifier).update, ), ], ), ); } } ================================================ FILE: lib/features/settings/overview/sections/inbound_options_page.dart ================================================ import 'package:flutter/material.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/router/dialog/dialog_notifier.dart'; import 'package:hiddify/features/settings/data/config_option_repository.dart'; import 'package:hiddify/features/settings/widget/preference_tile.dart'; import 'package:hiddify/singbox/model/singbox_config_enum.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:network_info_plus/network_info_plus.dart'; class InboundOptionsPage extends HookConsumerWidget { const InboundOptionsPage({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; return Scaffold( appBar: AppBar(title: Text(t.pages.settings.inbound.title)), body: ListView( children: [ ChoicePreferenceWidget( selected: ref.watch(ConfigOptions.serviceMode), preferences: ref.watch(ConfigOptions.serviceMode.notifier), choices: ServiceMode.choices, title: t.pages.settings.inbound.serviceMode, icon: Icons.tune_rounded, presentChoice: (value) => value.present(t), ), SwitchListTile.adaptive( title: Text(t.pages.settings.inbound.strictRoute), secondary: const Icon(Icons.merge_rounded), value: ref.watch(ConfigOptions.strictRoute), onChanged: ref.read(ConfigOptions.strictRoute.notifier).update, ), ChoicePreferenceWidget( selected: ref.watch(ConfigOptions.tunImplementation), preferences: ref.watch(ConfigOptions.tunImplementation.notifier), choices: TunImplementation.values, title: t.pages.settings.inbound.tunImplementation, icon: Icons.trip_origin_rounded, presentChoice: (value) => value.name, ), ValuePreferenceWidget( value: ref.watch(ConfigOptions.mixedPort), preferences: ref.watch(ConfigOptions.mixedPort.notifier), title: t.pages.settings.inbound.mixedPort, icon: Icons.device_hub_rounded, inputToValue: int.tryParse, digitsOnly: true, validateInput: isPort, ), if (PlatformUtils.isLinux) ValuePreferenceWidget( value: ref.watch(ConfigOptions.tproxyPort), preferences: ref.watch(ConfigOptions.tproxyPort.notifier), title: t.pages.settings.inbound.tproxyPort, icon: Icons.device_hub_rounded, inputToValue: int.tryParse, digitsOnly: true, validateInput: isPort, ), if (PlatformUtils.isLinux || PlatformUtils.isMacOS) ValuePreferenceWidget( value: ref.watch(ConfigOptions.redirectPort), preferences: ref.watch(ConfigOptions.redirectPort.notifier), title: t.pages.settings.inbound.redirectPort, icon: Icons.device_hub_rounded, inputToValue: int.tryParse, digitsOnly: true, validateInput: isPort, ), ValuePreferenceWidget( value: ref.watch(ConfigOptions.directPort), preferences: ref.watch(ConfigOptions.directPort.notifier), title: t.pages.settings.inbound.directPort, icon: Icons.device_hub_rounded, inputToValue: int.tryParse, digitsOnly: true, validateInput: isPort, ), SwitchListTile.adaptive( title: Text(t.pages.settings.inbound.allowConnectionFromLan), secondary: const Icon(Icons.share_rounded), value: ref.watch(ConfigOptions.allowConnectionFromLan), onChanged: (bool value) async { await ref.read(ConfigOptions.allowConnectionFromLan.notifier).update(value); if (value == true) { final ip = await NetworkInfo().getWifiIP(); // final ipp = Networkinfo if (ip == null) return; final port = ref.read(ConfigOptions.mixedPort); final link = '#profile-title: LAN only\nsocks://$ip:$port#LAN only'; final message = 'socks://$ip:$port'; await ref.read(dialogNotifierProvider.notifier).showQrCode(link, message: message); } }, ), ], ), ); } } ================================================ FILE: lib/features/settings/overview/sections/route_options_page.dart ================================================ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/model/region.dart'; import 'package:hiddify/core/preferences/general_preferences.dart'; import 'package:hiddify/core/router/dialog/dialog_notifier.dart'; import 'package:hiddify/features/per_app_proxy/model/per_app_proxy_mode.dart'; import 'package:hiddify/features/per_app_proxy/overview/per_app_proxy_notifier.dart'; import 'package:hiddify/features/settings/data/config_option_repository.dart'; import 'package:hiddify/features/settings/widget/preference_tile.dart'; import 'package:hiddify/singbox/model/singbox_config_enum.dart'; import 'package:hiddify/utils/platform_utils.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class RouteOptionsPage extends HookConsumerWidget { const RouteOptionsPage({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; final perAppProxy = ref.watch(Preferences.perAppProxyMode).enabled; return Scaffold( appBar: AppBar(title: Text(t.pages.settings.routing.title)), body: ListView( children: [ if (PlatformUtils.isAndroid) ListTile( title: Text(t.pages.settings.routing.perAppProxy.title), leading: const Icon(Icons.apps_rounded), trailing: Switch( value: perAppProxy, onChanged: (value) async { final newMode = perAppProxy ? PerAppProxyMode.off : PerAppProxyMode.exclude; await ref.read(Preferences.perAppProxyMode.notifier).update(newMode); if (!perAppProxy && context.mounted) context.goNamed('perAppProxy'); }, ), onTap: () async { if (!perAppProxy) { await ref.read(Preferences.perAppProxyMode.notifier).update(PerAppProxyMode.exclude); } if (context.mounted) context.goNamed('perAppProxy'); }, ), ChoicePreferenceWidget( selected: ref.watch(ConfigOptions.region), preferences: ref.watch(ConfigOptions.region.notifier), choices: Region.values, title: t.pages.settings.routing.region, showFlag: true, icon: Icons.place_rounded, presentChoice: (value) => value.present(t), onChanged: (val) async { await ref.read(ConfigOptions.directDnsAddress.notifier).reset(); final autoRegion = ref.read(Preferences.autoAppsSelectionRegion); final mode = ref.read(Preferences.perAppProxyMode).toAppProxy(); if (autoRegion != val && autoRegion != null && val != Region.other && mode != null && PlatformUtils.isAndroid) { await ref .read(dialogNotifierProvider.notifier) .showOk( t.pages.settings.routing.perAppProxy.autoSelection.dialog.title, t.pages.settings.routing.perAppProxy.autoSelection.dialog.msg(region: val.name), ); await ref.read(PerAppProxyProvider(mode).notifier).clearAutoSelected(); } }, ), ChoicePreferenceWidget( title: t.pages.settings.routing.balancerStrategy.title, icon: Icons.balance_rounded, selected: ref.watch(ConfigOptions.balancerStrategy), preferences: ref.watch(ConfigOptions.balancerStrategy.notifier), choices: BalancerStrategy.values, presentChoice: (value) => value.present(t), ), SwitchListTile.adaptive( title: Text(t.pages.settings.routing.blockAds), secondary: const Icon(Icons.block_rounded), value: ref.watch(ConfigOptions.blockAds), onChanged: ref.read(ConfigOptions.blockAds.notifier).update, ), SwitchListTile.adaptive( title: Text(t.pages.settings.routing.bypassLan), secondary: const Icon(Icons.call_split_rounded), value: ref.watch(ConfigOptions.bypassLan), onChanged: ref.read(ConfigOptions.bypassLan.notifier).update, ), SwitchListTile.adaptive( title: Text(t.pages.settings.routing.resolveDestination), secondary: const Icon(Icons.security_rounded), value: ref.watch(ConfigOptions.resolveDestination), onChanged: ref.read(ConfigOptions.resolveDestination.notifier).update, ), ChoicePreferenceWidget( selected: ref.watch(ConfigOptions.ipv6Mode), preferences: ref.watch(ConfigOptions.ipv6Mode.notifier), choices: IPv6Mode.values, title: t.pages.settings.routing.ipv6Route, icon: Icons.looks_6_rounded, presentChoice: (value) => value.present(t), ), ], ), ); } } ================================================ FILE: lib/features/settings/overview/sections/tls_tricks_page.dart ================================================ import 'package:flutter/material.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/model/optional_range.dart'; import 'package:hiddify/features/settings/data/config_option_repository.dart'; import 'package:hiddify/features/settings/widget/preference_tile.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class TlsTricksPage extends HookConsumerWidget { const TlsTricksPage({super.key}); String _presentFragmentPackets(TranslationsEn t, String value) => switch (value) { "tlshello" => t.pages.settings.tlsTricks.packetsTlsHello, "1-1" => t.pages.settings.tlsTricks.packets1_1, "1-2" => t.pages.settings.tlsTricks.packets1_2, "1-3" => t.pages.settings.tlsTricks.packets1_3, "1-4" => t.pages.settings.tlsTricks.packets1_4, "1-5" => t.pages.settings.tlsTricks.packets1_5, _ => value, }; @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; final canChangeOption = ref.watch(ConfigOptions.enableTlsFragment); return Scaffold( appBar: AppBar(title: Text(t.pages.settings.tlsTricks.title)), body: ListView( children: [ SwitchListTile.adaptive( title: Text(t.pages.settings.tlsTricks.enable), value: ref.watch(ConfigOptions.enableTlsFragment), secondary: const Icon(Icons.content_cut_rounded), onChanged: ref.read(ConfigOptions.enableTlsFragment.notifier).update, ), ChoicePreferenceWidget( selected: ref.watch(ConfigOptions.fragmentPackets), preferences: ref.watch(ConfigOptions.fragmentPackets.notifier), choices: ["tlshello", "1-1", "1-2", "1-3", "1-4", "1-5"], title: t.pages.settings.tlsTricks.packets, icon: Icons.layers_rounded, presentChoice: (value) => _presentFragmentPackets(t, value), enabled: canChangeOption, ), ValuePreferenceWidget( value: ref.watch(ConfigOptions.tlsFragmentSize), preferences: ref.watch(ConfigOptions.tlsFragmentSize.notifier), title: t.pages.settings.tlsTricks.size, icon: Icons.straighten_rounded, inputToValue: OptionalRange.tryParse, presentValue: (value) => value.present(t), formatInputValue: (value) => value.format(), enabled: canChangeOption, ), ValuePreferenceWidget( value: ref.watch(ConfigOptions.tlsFragmentSleep), preferences: ref.watch(ConfigOptions.tlsFragmentSleep.notifier), title: t.pages.settings.tlsTricks.sleep, icon: Icons.snooze_rounded, inputToValue: OptionalRange.tryParse, presentValue: (value) => value.present(t), formatInputValue: (value) => value.format(), enabled: canChangeOption, ), SwitchListTile.adaptive( title: Text(t.pages.settings.tlsTricks.mixedSniCase.enable), value: ref.watch(ConfigOptions.enableTlsMixedSniCase), secondary: const Icon(Icons.text_fields_rounded), onChanged: canChangeOption ? ref.read(ConfigOptions.enableTlsMixedSniCase.notifier).update : null, ), SwitchListTile.adaptive( title: Text(t.pages.settings.tlsTricks.padding.enable), value: ref.watch(ConfigOptions.enableTlsPadding), secondary: const Icon(Icons.expand_rounded), onChanged: canChangeOption ? ref.read(ConfigOptions.enableTlsPadding.notifier).update : null, ), ValuePreferenceWidget( value: ref.watch(ConfigOptions.tlsPaddingSize), preferences: ref.watch(ConfigOptions.tlsPaddingSize.notifier), title: t.pages.settings.tlsTricks.padding.size, icon: Icons.straighten_rounded, inputToValue: OptionalRange.tryParse, presentValue: (value) => value.format(), formatInputValue: (value) => value.format(), enabled: canChangeOption, ), ], ), ); } } ================================================ FILE: lib/features/settings/overview/sections/warp_options_page.dart ================================================ import 'package:flutter/material.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/model/optional_range.dart'; import 'package:hiddify/features/settings/data/config_option_repository.dart'; import 'package:hiddify/features/settings/notifier/warp_option/warp_option_notifier.dart'; import 'package:hiddify/features/settings/widget/preference_tile.dart'; import 'package:hiddify/singbox/model/singbox_config_enum.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class WarpOptionsPage extends HookConsumerWidget { const WarpOptionsPage({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; final theme = Theme.of(context); final warpOptions = ref.watch(warpOptionNotifierProvider); final isWarpEnabled = ref.watch(ConfigOptions.enableWarp); return Scaffold( appBar: AppBar(title: Text(t.pages.settings.warp.title)), body: ListView( children: [ SwitchListTile.adaptive( title: Text(t.pages.settings.warp.enable), value: isWarpEnabled, secondary: const Icon(Icons.cloud_rounded), onChanged: (value) async { await ref.read(ConfigOptions.enableWarp.notifier).update(value); if (value) await ref.read(warpOptionNotifierProvider.notifier).genWarps(); }, ), ListTile( title: Text(t.pages.settings.warp.generateConfig), subtitle: !isWarpEnabled ? null : warpOptions.when( loading: () => null, data: (_) => null, error: (_, _) => Text(t.pages.settings.warp.missingConfig, style: TextStyle(color: theme.colorScheme.error)), ), trailing: warpOptions.isLoading ? const SizedBox(width: 24, height: 24, child: CircularProgressIndicator()) : null, leading: const Icon(Icons.build_rounded), enabled: isWarpEnabled && !warpOptions.isLoading, onTap: warpOptions.isLoading ? null : () async { await ref.read(warpOptionNotifierProvider.notifier).genWarps(); }, ), ChoicePreferenceWidget( selected: ref.watch(ConfigOptions.warpDetourMode), preferences: ref.watch(ConfigOptions.warpDetourMode.notifier), enabled: isWarpEnabled, choices: WarpDetourMode.values, title: t.pages.settings.warp.detourMode, icon: Icons.alt_route_rounded, presentChoice: (value) => value.present(t), ), ValuePreferenceWidget( value: ref.watch(ConfigOptions.warpLicenseKey), preferences: ref.watch(ConfigOptions.warpLicenseKey.notifier), enabled: isWarpEnabled, title: t.pages.settings.warp.licenseKey, icon: Icons.key_rounded, presentValue: (value) => value.isEmpty ? t.common.notSet : value, ), ValuePreferenceWidget( value: ref.watch(ConfigOptions.warpCleanIp), preferences: ref.watch(ConfigOptions.warpCleanIp.notifier), enabled: isWarpEnabled, title: t.pages.settings.warp.cleanIp, icon: Icons.auto_awesome_rounded, ), ValuePreferenceWidget( value: ref.watch(ConfigOptions.warpPort), preferences: ref.watch(ConfigOptions.warpPort.notifier), enabled: isWarpEnabled, title: t.pages.settings.warp.port, icon: Icons.device_hub_rounded, inputToValue: int.tryParse, validateInput: isPort, digitsOnly: true, ), ValuePreferenceWidget( value: ref.watch(ConfigOptions.warpNoise), preferences: ref.watch(ConfigOptions.warpNoise.notifier), enabled: isWarpEnabled, title: t.pages.settings.warp.noise.count, icon: Icons.web_stories_rounded, inputToValue: (input) => OptionalRange.tryParse(input, allowEmpty: true), presentValue: (value) => value.present(t), formatInputValue: (value) => value.format(), ), ValuePreferenceWidget( value: ref.watch(ConfigOptions.warpNoiseMode), preferences: ref.watch(ConfigOptions.warpNoiseMode.notifier), enabled: isWarpEnabled, title: t.pages.settings.warp.noise.mode, icon: Icons.mode_standby_rounded, ), ValuePreferenceWidget( value: ref.watch(ConfigOptions.warpNoiseSize), preferences: ref.watch(ConfigOptions.warpNoiseSize.notifier), enabled: isWarpEnabled, title: t.pages.settings.warp.noise.size, icon: Icons.settings_ethernet_rounded, inputToValue: (input) => OptionalRange.tryParse(input, allowEmpty: true), presentValue: (value) => value.present(t), formatInputValue: (value) => value.format(), ), ValuePreferenceWidget( value: ref.watch(ConfigOptions.warpNoiseDelay), preferences: ref.watch(ConfigOptions.warpNoiseDelay.notifier), enabled: isWarpEnabled, title: t.pages.settings.warp.noise.delay, icon: Icons.schedule_rounded, inputToValue: (input) => OptionalRange.tryParse(input, allowEmpty: true), presentValue: (value) => value.present(t), formatInputValue: (value) => value.format(), ), ], ), ); } } ================================================ FILE: lib/features/settings/overview/settings_page.dart ================================================ import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; import 'package:go_router/go_router.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/router/dialog/dialog_notifier.dart'; import 'package:hiddify/core/router/go_router/helper/active_breakpoint_notifier.dart'; import 'package:hiddify/features/settings/notifier/config_option/config_option_notifier.dart'; import 'package:hiddify/features/settings/notifier/reset_tunnel/reset_tunnel_notifier.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; enum ConfigOptionSection { warp, fragment; static final _warpKey = GlobalKey(debugLabel: "warp-section-key"); static final _fragmentKey = GlobalKey(debugLabel: "fragment-section-key"); GlobalKey get key => switch (this) { ConfigOptionSection.warp => _warpKey, ConfigOptionSection.fragment => _fragmentKey, }; } class SettingsPage extends HookConsumerWidget { SettingsPage({super.key, String? section}) : section = section != null ? ConfigOptionSection.values.byName(section) : null; final ConfigOptionSection? section; @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; // final scrollController = useScrollController(); // useMemoized( // () { // if (section != null) { // WidgetsBinding.instance.addPostFrameCallback( // (_) { // final box = section!.key.currentContext?.findRenderObject() as RenderBox?; // final offset = box?.localToGlobal(Offset.zero); // if (offset == null) return; // final height = scrollController.offset + offset.dy - MediaQueryData.fromView(View.of(context)).padding.top - kToolbarHeight; // scrollController.animateTo( // height, // duration: const Duration(milliseconds: 500), // curve: Curves.decelerate, // ); // }, // ); // } // }, // ); return Scaffold( appBar: AppBar( title: Text(t.pages.settings.title), actions: [ MenuAnchor( menuChildren: [ SubmenuButton( menuChildren: [ MenuItemButton( onPressed: () async => await ref .read(dialogNotifierProvider.notifier) .showConfirmation( title: t.common.msg.import.confirm, message: t.dialogs.confirmation.settings.import.msg, ) .then((shouldImport) async { if (shouldImport) { await ref.read(configOptionNotifierProvider.notifier).importFromClipboard(); } }), child: Text(t.pages.settings.options.import.clipboard), ), MenuItemButton( onPressed: () async => await ref .read(dialogNotifierProvider.notifier) .showConfirmation( title: t.common.msg.import.confirm, message: t.dialogs.confirmation.settings.import.msg, ) .then((shouldImport) async { if (shouldImport) { await ref.read(configOptionNotifierProvider.notifier).importFromJsonFile(); } }), child: Text(t.pages.settings.options.import.file), ), ], child: Text(t.common.import), ), SubmenuButton( menuChildren: [ MenuItemButton( onPressed: () async => await ref.read(configOptionNotifierProvider.notifier).exportJsonClipboard(), child: Text(t.pages.settings.options.export.anonymousToClipboard), ), MenuItemButton( onPressed: () async => await ref.read(configOptionNotifierProvider.notifier).exportJsonFile(), child: Text(t.pages.settings.options.export.anonymousToFile), ), const PopupMenuDivider(), MenuItemButton( onPressed: () async => await ref .read(configOptionNotifierProvider.notifier) .exportJsonClipboard(excludePrivate: false), child: Text(t.pages.settings.options.export.allToClipboard), ), MenuItemButton( onPressed: () async => await ref.read(configOptionNotifierProvider.notifier).exportJsonFile(excludePrivate: false), child: Text(t.pages.settings.options.export.allToFile), ), ], child: Text(t.common.export), ), const PopupMenuDivider(), MenuItemButton( child: Text(t.pages.settings.options.reset), onPressed: () async => await ref.read(configOptionNotifierProvider.notifier).resetOption(), ), ], builder: (context, controller, child) => IconButton( onPressed: () { if (controller.isOpen) { controller.close(); } else { controller.open(); } }, icon: const Icon(Icons.more_vert_rounded), ), ), const Gap(8), ], ), body: ListView( children: [ // TipCard(message: t.settings.experimentalMsg), SettingsSection( title: t.pages.settings.general.title, icon: Icons.layers_rounded, namedLocation: context.namedLocation('general'), ), SettingsSection( title: t.pages.settings.routing.title, icon: Icons.route_rounded, namedLocation: context.namedLocation('routeOptions'), ), SettingsSection( title: t.pages.settings.dns.title, icon: Icons.dns_rounded, namedLocation: context.namedLocation('dnsOptions'), ), SettingsSection( title: t.pages.settings.inbound.title, icon: Icons.input_rounded, namedLocation: context.namedLocation('inboundOptions'), ), SettingsSection( title: t.pages.settings.tlsTricks.title, icon: Icons.content_cut_rounded, namedLocation: context.namedLocation('tlsTricks'), ), SettingsSection( title: t.pages.settings.warp.title, icon: Icons.cloud_rounded, namedLocation: context.namedLocation('warpOptions'), ), if (PlatformUtils.isIOS) Material( child: ListTile( title: Text(t.pages.settings.resetTunnel), leading: const Icon(Icons.autorenew_rounded), onTap: () async { await ref.read(resetTunnelNotifierProvider.notifier).run(); }, ), ), if (Breakpoint(context).isMobile()) ...[ SettingsSection( title: t.pages.logs.title, icon: Icons.description_rounded, namedLocation: context.namedLocation('logs'), ), SettingsSection( title: t.pages.about.title, icon: Icons.info_rounded, namedLocation: context.namedLocation('about'), ), ], ], ), ); } } class SettingsSection extends HookConsumerWidget { const SettingsSection({super.key, required this.title, required this.icon, required this.namedLocation}); final String title; final IconData icon; final String namedLocation; @override Widget build(BuildContext context, WidgetRef ref) { return ListTile( leading: Icon(icon), title: Text(title), trailing: const Icon(Icons.chevron_right_rounded), onTap: () => context.go(namedLocation), ); } } ================================================ FILE: lib/features/settings/widget/autocomplete_field.dart ================================================ import 'package:flutter/material.dart'; class AutocompleteField extends StatelessWidget { const AutocompleteField({super.key, required this.initialValue, required this.options}); final List options; final String initialValue; @override Widget build(BuildContext context) { return Autocomplete( initialValue: TextEditingValue( text: initialValue, selection: TextSelection(baseOffset: 0, extentOffset: initialValue.length), // Selects the entire text ), optionsBuilder: (TextEditingValue textEditingValue) { // if (textEditingValue.text == '') { // return const Iterable.empty(); // } return options.where((String option) { return option.contains(textEditingValue.text.toLowerCase()); }); }, onSelected: (String selection) { //debugPrint('You just selected $selection'); }, ); } } ================================================ FILE: lib/features/settings/widget/preference_tile.dart ================================================ import 'package:flutter/material.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/router/dialog/dialog_notifier.dart'; import 'package:hiddify/core/utils/preferences_utils.dart'; import 'package:hiddify/features/settings/notifier/battery_optimization/battery_optimizations_notifier.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class ValuePreferenceWidget extends HookConsumerWidget { const ValuePreferenceWidget({ super.key, required this.value, required this.preferences, this.enabled = true, required this.title, this.presentValue, this.formatInputValue, this.validateInput, this.inputToValue, this.digitsOnly = false, this.icon, }); final T value; final PreferencesNotifier preferences; final bool enabled; final String title; final String Function(T value)? presentValue; final String Function(T value)? formatInputValue; final bool Function(String value)? validateInput; final T? Function(String input)? inputToValue; final bool digitsOnly; final IconData? icon; @override Widget build(BuildContext context, WidgetRef ref) { return ListTile( title: Text(title), subtitle: Text(presentValue?.call(value) ?? value.toString()), leading: icon != null ? Icon(icon) : null, // material: (context, platform) => MaterialListTileData( enabled: enabled, // ), onTap: () async { final inputValue = await ref .read(dialogNotifierProvider.notifier) .showSettingInput( title: title, initialValue: value, validator: validateInput, valueFormatter: formatInputValue, onReset: preferences.reset, digitsOnly: digitsOnly, mapTo: inputToValue, possibleValues: preferences.possibleValues, ); if (inputValue == null) { return; } await preferences.update(inputValue); }, ); } } class ChoicePreferenceWidget extends HookConsumerWidget { const ChoicePreferenceWidget({ super.key, required this.selected, required this.preferences, this.enabled = true, required this.choices, required this.title, this.showFlag = false, this.icon, required this.presentChoice, this.validateInput, this.onChanged, }); final T selected; final PreferencesNotifier preferences; final bool enabled; final List choices; final String title; final bool showFlag; final IconData? icon; final String Function(T value) presentChoice; final bool Function(String value)? validateInput; final ValueChanged? onChanged; @override Widget build(BuildContext context, WidgetRef ref) { return ListTile( title: Text(title), subtitle: Text(presentChoice(selected)), leading: icon != null ? Icon(icon) : null, enabled: enabled, onTap: () async { final selection = await ref .read(dialogNotifierProvider.notifier) .showSettingPicker( title: title, showFlag: showFlag, selected: selected, options: choices, getTitle: (e) => presentChoice(e), onReset: preferences.reset, ); if (selection == null) return; final out = await preferences.update(selection); onChanged?.call(selection); return out; }, ); } } class BatteryOptimizationWidget extends HookConsumerWidget { const BatteryOptimizationWidget({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; final isIgnoringBatteryOptimizations = ref.watch(batteryOptimizationNotifierProvider); return isIgnoringBatteryOptimizations.when( data: (isIgnored) => isIgnored ? const SizedBox() : ListTile( title: Text(t.pages.settings.general.ignoreBatteryOptimizations), subtitle: Text(t.pages.settings.general.ignoreBatteryOptimizationsMsg), leading: const Icon(Icons.battery_saver_rounded), onTap: () async { await ref.read(batteryOptimizationNotifierProvider.notifier).requestToIgnore(); }, ), error: (_, _) => const SizedBox(), loading: () => const SizedBox( height: 48, child: Center( child: Padding(padding: EdgeInsets.symmetric(horizontal: 12), child: LinearProgressIndicator()), ), ), ); } } ================================================ FILE: lib/features/shortcut/shortcut_wrapper.dart ================================================ import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:hiddify/core/router/bottom_sheets/bottom_sheets_notifier.dart'; import 'package:hiddify/core/router/go_router/go_router_notifier.dart'; import 'package:hiddify/features/window/notifier/window_notifier.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class ShortcutWrapper extends HookConsumerWidget { const ShortcutWrapper(this.child, {super.key}); final Widget child; @override Widget build(BuildContext context, WidgetRef ref) { return Shortcuts( shortcuts: { // Android TV D-pad select support LogicalKeySet(LogicalKeyboardKey.select): const ActivateIntent(), if (!kIsWeb) ...{ if (Platform.isLinux) ...{ // quit app using Control+Q on Linux const SingleActivator(LogicalKeyboardKey.keyQ, control: true): QuitAppIntent(), }, if (Platform.isMacOS) ...{ // close window using Command+W on macOS const SingleActivator(LogicalKeyboardKey.keyW, meta: true): CloseWindowIntent(), // open settings using Command+, on macOS const SingleActivator(LogicalKeyboardKey.comma, meta: true): OpenSettingsIntent(), }, }, // try adding profile using Command+V and Control+V const SingleActivator(LogicalKeyboardKey.keyV, meta: true): PasteIntent(), const SingleActivator(LogicalKeyboardKey.keyV, control: true): PasteIntent(), }, child: Actions( actions: { CloseWindowIntent: CallbackAction( onInvoke: (_) async { await ref.read(windowNotifierProvider.notifier).hide(); return null; }, ), QuitAppIntent: CallbackAction( onInvoke: (_) async { await ref.read(windowNotifierProvider.notifier).exit(); return null; }, ), OpenSettingsIntent: CallbackAction( onInvoke: (_) { if (rootNavKey.currentContext != null) { // const SettingsRoute().go(rootNavigatorKey.currentContext!); } return null; }, ), PasteIntent: CallbackAction( onInvoke: (_) async { if (rootNavKey.currentContext != null) { final captureResult = await Clipboard.getData(Clipboard.kTextPlain).then((value) => value?.text ?? ''); ref.read(bottomSheetsNotifierProvider.notifier).showAddProfile(url: captureResult); } return null; }, ), }, child: child, ), ); } } class CloseWindowIntent extends Intent {} class QuitAppIntent extends Intent {} class OpenSettingsIntent extends Intent {} class PasteIntent extends Intent {} ================================================ FILE: lib/features/stats/data/stats_data_providers.dart ================================================ import 'package:hiddify/features/stats/data/stats_repository.dart'; import 'package:hiddify/hiddifycore/hiddify_core_service_provider.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'stats_data_providers.g.dart'; @Riverpod(keepAlive: true) StatsRepository statsRepository(StatsRepositoryRef ref) { return StatsRepositoryImpl(singbox: ref.watch(hiddifyCoreServiceProvider)); } ================================================ FILE: lib/features/stats/data/stats_repository.dart ================================================ import 'package:fpdart/fpdart.dart'; import 'package:hiddify/core/utils/exception_handler.dart'; import 'package:hiddify/features/stats/model/stats_failure.dart'; import 'package:hiddify/hiddifycore/generated/v2/hcore/hcore.pb.dart'; import 'package:hiddify/hiddifycore/hiddify_core_service.dart'; import 'package:hiddify/utils/custom_loggers.dart'; abstract interface class StatsRepository { Stream> watchStats(); } class StatsRepositoryImpl with ExceptionHandler, InfraLogger implements StatsRepository { StatsRepositoryImpl({required this.singbox}); final HiddifyCoreService singbox; @override Stream> watchStats() { return singbox.watchStats().handleExceptions(StatsUnexpectedFailure.new); } } ================================================ FILE: lib/features/stats/model/stats_entity.dart ================================================ import 'package:freezed_annotation/freezed_annotation.dart'; part 'stats_entity.freezed.dart'; @freezed class StatsEntity with _$StatsEntity { const StatsEntity._(); const factory StatsEntity({ required int uplink, required int downlink, required int uplinkTotal, required int downlinkTotal, }) = _StatsEntity; factory StatsEntity.empty() => const StatsEntity(uplink: 0, downlink: 0, uplinkTotal: 0, downlinkTotal: 0); } ================================================ FILE: lib/features/stats/model/stats_failure.dart ================================================ import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/model/failures.dart'; part 'stats_failure.freezed.dart'; @freezed sealed class StatsFailure with _$StatsFailure, Failure { const StatsFailure._(); @With() const factory StatsFailure.unexpected([Object? error, StackTrace? stackTrace]) = StatsUnexpectedFailure; @override ({String type, String? message}) present(TranslationsEn t) { return switch (this) { StatsUnexpectedFailure() => (type: t.errors.unexpected, message: null), }; } } ================================================ FILE: lib/features/stats/notifier/stats_notifier.dart ================================================ import 'package:hiddify/features/connection/notifier/connection_notifier.dart'; import 'package:hiddify/features/stats/data/stats_data_providers.dart'; import 'package:hiddify/hiddifycore/generated/v2/hcore/hcore.pb.dart'; import 'package:hiddify/utils/custom_loggers.dart'; import 'package:hiddify/utils/riverpod_utils.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'stats_notifier.g.dart'; @riverpod class StatsNotifier extends _$StatsNotifier with AppLogger { @override Stream build() async* { ref.disposeDelay(const Duration(seconds: 10)); final serviceRunning = await ref.watch(serviceRunningProvider.future); if (serviceRunning) { yield* ref .watch(statsRepositoryProvider) .watchStats() .map((event) => event.getOrElse((_) => SystemInfo.create())); } else { yield* Stream.value(SystemInfo.create()); } } } ================================================ FILE: lib/features/stats/widget/connection_stats_card.dart ================================================ import 'package:fluentui_system_icons/fluentui_system_icons.dart'; import 'package:flutter/material.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/widget/shimmer_skeleton.dart'; import 'package:hiddify/features/proxy/active/active_proxy_notifier.dart'; import 'package:hiddify/features/proxy/active/ip_widget.dart'; import 'package:hiddify/features/stats/widget/stats_card.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class ConnectionStatsCard extends HookConsumerWidget { const ConnectionStatsCard({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; final activeProxy = ref.watch(activeProxyNotifierProvider); // final ipInfo = ref.watch(ipInfoNotifierProvider); return StatsCard( title: t.components.stats.connection, stats: [ switch (activeProxy) { AsyncData(value: final proxy) => ( label: const Icon(FluentIcons.arrow_routing_20_regular), data: Text(proxy.tagDisplay), semanticLabel: null, ), _ => (label: const Icon(FluentIcons.arrow_routing_20_regular), data: const Text("..."), semanticLabel: null), }, switch (activeProxy) { AsyncData(value: final proxy) when proxy.ipinfo.ip.isNotEmpty => ( label: Row( children: [ IPCountryFlag(countryCode: proxy.ipinfo.countryCode, size: 16), // const Gap(4), // OrganisationFlag(organization: proxy.ipinfo.org, size: 16), ], ), data: IPText( ip: proxy.ipinfo.ip, onLongPress: () async { ref.read(ipInfoNotifierProvider.notifier).refresh(); }, constrained: true, ), semanticLabel: null, ), _ => ( label: const Icon(FluentIcons.question_circle_20_regular), data: const ShimmerSkeleton(widthFactor: .85, height: 14), semanticLabel: null, ), }, // switch (ipInfo) { // AsyncData(value: final info) => ( // label: Row( // children: [ // IPCountryFlag( // countryCode: info.countryCode, // size: 16, // ), // const Gap(4), // OrganisationFlag(organization: info.org ?? "", size: 16), // ], // ), // data: IPText( // ip: info.ip, // onLongPress: () async { // ref.read(ipInfoNotifierProvider.notifier).refresh(); // }, // constrained: true, // ), // semanticLabel: null, // ), // AsyncLoading() => ( // label: const Icon(FluentIcons.question_circle_20_regular), // data: const ShimmerSkeleton(widthFactor: .85, height: 14), // semanticLabel: null, // ), // AsyncError(error: final UnknownIp _) => ( // label: const Icon(FluentIcons.arrow_sync_20_regular), // data: UnknownIPText( // text: t.proxies.checkIp, // onTap: () async { // ref.read(ipInfoNotifierProvider.notifier).refresh(); // }, // constrained: true, // ), // semanticLabel: null, // ), // _ => ( // label: const Icon(FluentIcons.error_circle_20_regular), // data: UnknownIPText( // text: t.proxies.unknownIp, // onTap: () async { // ref.read(ipInfoNotifierProvider.notifier).refresh(); // }, // constrained: true, // ), // semanticLabel: null, // ), // }, ], ); } } ================================================ FILE: lib/features/stats/widget/side_bar_stats_overview.dart ================================================ import 'package:fluentui_system_icons/fluentui_system_icons.dart'; import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/model/constants.dart'; import 'package:hiddify/core/utils/preferences_utils.dart'; import 'package:hiddify/core/widget/animated_text.dart'; import 'package:hiddify/features/stats/notifier/stats_notifier.dart'; import 'package:hiddify/features/stats/widget/stats_card.dart'; import 'package:hiddify/hiddifycore/generated/v2/hcore/hcore.pb.dart'; import 'package:hiddify/utils/number_formatters.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; final showAllSidebarStatsProvider = PreferencesNotifier.createAutoDispose("show_all_sidebar_stats", false); class SideBarStatsOverview extends HookConsumerWidget { const SideBarStatsOverview({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider).requireValue; final stats = ref.watch(statsNotifierProvider).asData?.value ?? SystemInfo.create(); final showAll = ref.watch(showAllSidebarStatsProvider); return Padding( padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 16), child: Column( mainAxisSize: MainAxisSize.min, children: [ Padding( padding: const EdgeInsets.all(2.0), child: TextButton.icon( style: TextButton.styleFrom( padding: const EdgeInsets.symmetric(horizontal: 8), textStyle: Theme.of(context).textTheme.labelSmall, ), onPressed: () { ref.read(showAllSidebarStatsProvider.notifier).update(!showAll); }, icon: AnimatedRotation( turns: showAll ? 1 : 0.5, duration: kAnimationDuration, child: const Icon(FluentIcons.chevron_down_16_regular, size: 16), ), label: AnimatedText(showAll ? t.common.showLess : t.common.showMore), ), ), // const ConnectionStatsCard(), const Gap(8), AnimatedCrossFade( crossFadeState: showAll ? CrossFadeState.showSecond : CrossFadeState.showFirst, duration: kAnimationDuration, firstChild: StatsCard( title: t.components.stats.traffic, stats: [ ( label: const Icon(FluentIcons.arrow_download_16_regular), data: Text(stats.downlink.toInt().speed()), semanticLabel: t.components.stats.speed, ), ( label: const Icon(FluentIcons.arrow_bidirectional_up_down_16_regular), data: Text(stats.downlinkTotal.toInt().size()), semanticLabel: t.components.stats.totalTransferred, ), ], ), secondChild: Column( mainAxisSize: MainAxisSize.min, children: [ StatsCard( title: t.components.stats.trafficLive, stats: [ ( label: const Text("↑", style: TextStyle(color: Colors.green)), data: Text(stats.uplink.toInt().speed()), semanticLabel: t.components.stats.uplink, ), ( label: Text("↓", style: TextStyle(color: Theme.of(context).colorScheme.error)), data: Text(stats.downlink.toInt().speed()), semanticLabel: t.components.stats.downlink, ), ], ), const Gap(8), StatsCard( title: t.components.stats.trafficTotal, stats: [ ( label: const Text("↑", style: TextStyle(color: Colors.green)), data: Text(stats.uplinkTotal.toInt().size()), semanticLabel: t.components.stats.uplink, ), ( label: Text("↓", style: TextStyle(color: Theme.of(context).colorScheme.error)), data: Text(stats.downlinkTotal.toInt().size()), semanticLabel: t.components.stats.downlink, ), ], ), ], ), ), ], ), ); } } ================================================ FILE: lib/features/stats/widget/stats_card.dart ================================================ import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; import 'package:hiddify/core/widget/spaced_list_widget.dart'; typedef PresentableStat = ({Widget label, Widget data, String? semanticLabel}); class StatsCard extends StatelessWidget { const StatsCard({ super.key, this.title, this.titleStyle, this.padding = const EdgeInsets.symmetric(vertical: 4, horizontal: 8), this.labelStyle, this.dataStyle, required this.stats, }); final String? title; final TextStyle? titleStyle; final EdgeInsets padding; final TextStyle? labelStyle; final TextStyle? dataStyle; final List stats; @override Widget build(BuildContext context) { final effectiveTitleStyle = titleStyle ?? Theme.of(context).textTheme.bodySmall; final effectiveLabelStyle = labelStyle ?? Theme.of(context).textTheme.bodySmall?.copyWith(fontWeight: FontWeight.w300); final effectiveDataStyle = dataStyle ?? Theme.of(context).textTheme.bodySmall?.copyWith(fontWeight: FontWeight.w300); return Card( margin: EdgeInsets.zero, shadowColor: Colors.transparent, child: Padding( padding: padding, child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ if (title != null) ...[Text(title!, style: effectiveTitleStyle), const Gap(4)], ...stats .map((stat) { Widget label = IconTheme.merge( data: const IconThemeData(size: 14), child: DefaultTextStyle( style: effectiveLabelStyle!, overflow: TextOverflow.ellipsis, child: stat.label, ), ); if (stat.semanticLabel != null) { label = Tooltip(message: stat.semanticLabel, verticalOffset: 8, child: label); } return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ label, const Gap(2), DefaultTextStyle(style: effectiveDataStyle!, overflow: TextOverflow.ellipsis, child: stat.data), ], ); }) .toList() .spaceBy(height: 2), ], ), ), ); } } ================================================ FILE: lib/features/system_tray/notifier/system_tray_notifier.dart ================================================ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/model/constants.dart'; import 'package:hiddify/features/connection/model/connection_status.dart'; import 'package:hiddify/features/connection/notifier/connection_notifier.dart'; import 'package:hiddify/features/proxy/active/active_proxy_notifier.dart'; import 'package:hiddify/features/settings/data/config_option_repository.dart'; import 'package:hiddify/features/window/notifier/window_notifier.dart'; import 'package:hiddify/gen/assets.gen.dart'; import 'package:hiddify/hiddifycore/generated/v2/hcore/hcore.pb.dart'; import 'package:hiddify/singbox/model/singbox_config_enum.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:tray_manager/tray_manager.dart'; import 'package:window_manager/window_manager.dart'; part 'system_tray_notifier.g.dart'; @Riverpod(keepAlive: true) class SystemTrayNotifier extends _$SystemTrayNotifier with TrayListener, AppLogger { bool listenerAdded = false; @override Future build() async { assert(PlatformUtils.isDesktop); if (!listenerAdded) { trayManager.addListener(this); listenerAdded = true; } await _initializeTray(); } Future _initializeTray() async { final t = await ref.watch(translationsProvider.future); final urlTestDelay = await ref .watch(activeProxyNotifierProvider.future) .catchError((e) { loggy.warning("error getting active proxy", e); return OutboundInfo(urlTestDelay: 0); }) .then((connection) => connection.urlTestDelay); final connection = await ref .watch(connectionNotifierProvider.future) .catchError((e) { loggy.warning("error getting connection status", e); return const ConnectionStatus.disconnected(); }) .then((connection) => _modifyConnectionStatus(connection, urlTestDelay)); final serviceMode = ref.watch(ConfigOptions.serviceMode); await trayManager.setIcon(_trayIconPath(connection), isTemplate: PlatformUtils.isMacOS); if (!PlatformUtils.isLinux) await trayManager.setToolTip(_trayTooltip(connection, urlTestDelay, t)); await trayManager.setContextMenu(_trayMenu(connection, serviceMode, t)); } Menu _trayMenu(ConnectionStatus connection, ServiceMode serviceMode, Translations t) => Menu( items: [ if (PlatformUtils.isLinux) ...[MenuItem(key: 'dashboard', label: t.common.dashboard), MenuItem.separator()], MenuItem( key: 'connection', label: switch (connection) { Disconnected() => t.connection.connect, Connecting() => t.connection.connecting, Connected() => t.connection.disconnect, Disconnecting() => t.connection.disconnecting, }, disabled: connection.isSwitching, ), MenuItem.submenu( label: t.pages.settings.inbound.serviceMode, icon: Assets.images.trayIconIco, submenu: Menu( items: [ ...ServiceMode.values.map( (e) => MenuItem.checkbox(checked: e == serviceMode, key: e.name, label: e.present(t)), ), ], ), ), MenuItem.separator(), MenuItem(key: 'quit', label: t.common.quit), ], ); String _trayIconPath(ConnectionStatus status) { final isDarkMode = WidgetsBinding.instance.platformDispatcher.platformBrightness == Brightness.dark; const images = Assets.images; final isWindows = PlatformUtils.isWindows; switch (status) { case Connected(): return isWindows ? images.trayIconConnectedIco : images.trayIconConnectedPng.path; case Connecting(): case Disconnecting(): return isWindows ? images.trayIconDisconnectedIco : images.trayIconDisconnectedPng.path; case Disconnected(): return isWindows ? isDarkMode ? images.trayIconIco : images.trayIconDarkIco : isDarkMode ? images.trayIconDarkPng.path : images.trayIconPng.path; } } String _trayTooltip(ConnectionStatus connection, int urlTestDelay, Translations t) { final r = "${Constants.appName} - ${connection.present(t)}"; if (connection is Connected) { if (Platform.isMacOS) windowManager.setBadgeLabel("${urlTestDelay}ms"); return '$r : ${urlTestDelay}ms"'; } else { if (Platform.isMacOS) windowManager.setBadgeLabel("-ms"); return r; } } ConnectionStatus _modifyConnectionStatus(ConnectionStatus connection, int urlTestDelay) { if (connection is Connected) { return urlTestDelay > 0 && urlTestDelay < 65000 ? const Connected() : const Connecting(); } else { return connection; } } @override Future onTrayMenuItemClick(MenuItem menuItem) async { // if (menuItem.key == 'dashboard') { // await ref.read(windowNotifierProvider.notifier).open(); // } if (menuItem.key == 'dashboard') { await ref.read(windowNotifierProvider.notifier).show(); } else if (menuItem.key == 'connection') { await ref.read(connectionNotifierProvider.notifier).toggleConnection(); } else if (menuItem.key == 'quit') { await ref.read(windowNotifierProvider.notifier).exit(); } else { final newMode = ServiceMode.values.byName(menuItem.key!); loggy.debug("switching service mode: [$newMode]"); await ref.read(ConfigOptions.serviceMode.notifier).update(newMode); } } @override Future onTrayIconMouseDown() async { // if (Platform.isMacOS) { // await trayManager.popUpContextMenu(); // } else { // await ref.read(windowNotifierProvider.notifier).hideOrShow(); // } await ref.read(windowNotifierProvider.notifier).showOrHide(); } @override Future onTrayIconRightMouseDown() async { await trayManager.popUpContextMenu(); } } // @Riverpod(keepAlive: true) // class SystemTrayNotifier extends _$SystemTrayNotifier with AppLogger { // @override // Future build() async { // if (!PlatformUtils.isDesktop) return; // final activeProxy = await ref.watch(activeProxyNotifierProvider.future); // final delay = activeProxy.urlTestDelay; // final newConnectionStatus = delay > 0 && delay < 65000; // ConnectionStatus connection; // try { // connection = await ref.watch(connectionNotifierProvider.future); // } catch (e) { // loggy.warning("error getting connection status", e); // connection = const ConnectionStatus.disconnected(); // } // final t = await ref.watch(translationsProvider.future); // var tooltip = Constants.appName; // final serviceMode = ref.watch(ConfigOptions.serviceMode); // if (connection is Disconnected) { // setIcon(connection); // } else if (newConnectionStatus) { // setIcon(const Connected()); // tooltip = "$tooltip - ${connection.present(t)}"; // if (newConnectionStatus) { // tooltip = "$tooltip : ${delay}ms"; // } else { // tooltip = "$tooltip : -"; // } // // else if (delay>1000) // // SystemTrayNotifier.setIcon(timeout ? Disconnecting() : Connecting()); // } else { // setIcon(const Disconnecting()); // tooltip = "$tooltip - ${connection.present(t)}"; // } // if (Platform.isMacOS) { // windowManager.setBadgeLabel("${delay}ms"); // } // if (!Platform.isLinux) await trayManager.setToolTip(tooltip); // // final destinations = <(String label, String location)>[ // // (t.home.pageTitle, const HomeRoute().location), // // (t.proxies.pageTitle, const ProfilesOverviewRoute().location), // // (t.logs.title, const LogsOverviewRoute().location), // // // (t.settings.pageTitle, const SettingsRoute().location), // // (t.about.pageTitle, const AboutRoute().location), // // ]; // // loggy.debug('updating system tray'); // final menu = Menu( // items: [ // MenuItem( // label: t.tray.dashboard, // onClick: (_) async { // await ref.read(windowNotifierProvider.notifier).open(); // }, // ), // MenuItem.separator(), // MenuItem.checkbox( // label: switch (connection) { // Disconnected() => t.tray.status.connect, // Connecting() => t.tray.status.connecting, // Connected() => t.tray.status.disconnect, // Disconnecting() => t.tray.status.disconnecting, // }, // // checked: connection.isConnected, // checked: false, // disabled: connection.isSwitching, // onClick: (_) async { // await ref.read(connectionNotifierProvider.notifier).toggleConnection(); // }, // ), // MenuItem.separator(), // MenuItem( // label: t.config.serviceMode, // icon: Assets.images.trayIconIco, // disabled: true, // ), // ...ServiceMode.values.map( // (e) => MenuItem.checkbox( // checked: e == serviceMode, // key: e.name, // label: e.present(t), // onClick: (menuItem) async { // final newMode = ServiceMode.values.byName(menuItem.key!); // loggy.debug("switching service mode: [$newMode]"); // await ref.read(ConfigOptions.serviceMode.notifier).update(newMode); // }, // ), // ), // // MenuItem.submenu( // // label: t.tray.open, // // submenu: Menu( // // items: [ // // ...destinations.map( // // (e) => MenuItem( // // label: e.$1, // // onClick: (_) async { // // await ref.read(windowNotifierProvider.notifier).open(); // // ref.read(routerProvider).go(e.$2); // // }, // // ), // // ), // // ], // // ), // // ), // MenuItem.separator(), // MenuItem( // label: t.tray.quit, // onClick: (_) async { // return ref.read(windowNotifierProvider.notifier).quit(); // }, // ), // ], // ); // await trayManager.setContextMenu(menu); // } // static void setIcon(ConnectionStatus status) { // if (!PlatformUtils.isDesktop) return; // trayManager // .setIcon( // _trayIconPath(status), // isTemplate: Platform.isMacOS, // ) // .asStream(); // } // static String _trayIconPath(ConnectionStatus status) { // if (Platform.isWindows) { // final Brightness brightness = WidgetsBinding.instance.platformDispatcher.platformBrightness; // final isDarkMode = brightness == Brightness.dark; // switch (status) { // case Connected(): // return Assets.images.trayIconConnectedIco; // case Connecting(): // return Assets.images.trayIconDisconnectedIco; // case Disconnecting(): // return Assets.images.trayIconDisconnectedIco; // case Disconnected(): // if (isDarkMode) { // return Assets.images.trayIconIco; // } else { // return Assets.images.trayIconDarkIco; // } // } // } // // const isDarkMode = false; // switch (status) { // case Connected(): // return Assets.images.trayIconConnectedPng.path; // case Connecting(): // return Assets.images.trayIconDisconnectedPng.path; // case Disconnecting(): // return Assets.images.trayIconDisconnectedPng.path; // case Disconnected(): // // if (isDarkMode) { // // return Assets.images.trayIconDarkPng.path; // // } else { // // return Assets.images.trayIconPng.path; // // } // return Assets.images.trayIconPng.path; // } // // return Assets.images.trayIconPng.path; // } // } ================================================ FILE: lib/features/system_tray/widget/system_tray_wrapper.dart ================================================ // class TrayWrapper extends StatefulHookConsumerWidget { // const TrayWrapper(this.child, {super.key}); // final Widget child; // @override // ConsumerState createState() => _TrayWrapperState(); // } // class _TrayWrapperState extends ConsumerState with TrayListener, AppLogger { // @override // Widget build(BuildContext context) { // ref.listen(systemTrayNotifierProvider, (_, __) {}); // return widget.child; // } // @override // void initState() { // super.initState(); // trayManager.addListener(this); // } // @override // void dispose() { // trayManager.removeListener(this); // super.dispose(); // } // @override // Future onTrayIconMouseDown() async { // if (Platform.isMacOS) { // await trayManager.popUpContextMenu(); // } else { // await ref.read(windowNotifierProvider.notifier).open(); // } // } // @override // Future onTrayIconRightMouseDown() async { // await trayManager.popUpContextMenu(); // } // } ================================================ FILE: lib/features/window/notifier/window_notifier.dart ================================================ import 'dart:io'; import 'dart:ui'; import 'package:flutter/material.dart'; import 'package:hiddify/core/preferences/general_preferences.dart'; import 'package:hiddify/features/connection/notifier/connection_notifier.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:screen_retriever/screen_retriever.dart'; import 'package:tray_manager/tray_manager.dart'; import 'package:window_manager/window_manager.dart'; part 'window_notifier.g.dart'; const minimumWindowSize = Size(368, 568); const defaultWindowSize = Size(868, 668); @Riverpod(keepAlive: true) class WindowNotifier extends _$WindowNotifier with AppLogger { @override Future build() async { if (!PlatformUtils.isDesktop) return; // if (Platform.isWindows) { // loggy.debug("ensuring single instance"); // await WindowsSingleInstance.ensureSingleInstance([], "Hiddify"); // } await windowManager.ensureInitialized(); await initWindowState(); } Future saveWindowState() async { if (await windowManager.isMaximized()) { await ref.read(Preferences.windowMaximized.notifier).update(true); } else { final size = await windowManager.getSize(); final position = await windowManager.getPosition(); await ref.read(Preferences.windowMaximized.notifier).update(false); await ref.read(Preferences.windowSize.notifier).update(size); await ref.read(Preferences.windowPosition.notifier).update(position); } } Future initWindowState() async { final isMaximized = ref.read(Preferences.windowMaximized); loggy.debug("window state. maximized: $isMaximized"); final size = ref.read(Preferences.windowSize); loggy.debug("window state. size: $size"); final position = ref.read(Preferences.windowPosition); final isWindowVisible = position != null && await checkWindowVisivility(position, size); loggy.debug("window state. position: ${isWindowVisible ? position : "centered"}"); final silentStart = ref.read(Preferences.silentStart); loggy.debug("window state. silent start: ${silentStart ? "Enabled" : "Disabled"}"); await windowManager.waitUntilReadyToShow( WindowOptions(size: size, center: !isWindowVisible, minimumSize: minimumWindowSize), ); if (isWindowVisible) { await windowManager.setPosition(position); loggy.debug("restoring window to position: $position"); } else { loggy.debug("no previous position found, centering window"); } if (isMaximized) { await windowManager.maximize(); loggy.debug("restoring window to maximized state"); } if (!silentStart) { await ref.read(windowNotifierProvider.notifier).show(focus: false); loggy.debug("showing app window on start"); } else { loggy.debug("silent start, remain hidden accessible via tray"); } } Future checkWindowVisivility(Offset windowPos, Size windowSize, {double tolerance = 10.0}) async { final Rect windowRect = windowPos & windowSize; final displays = await screenRetriever.getAllDisplays(); for (final display in displays) { if (display.visiblePosition == null || display.visibleSize == null) { continue; } final Rect monitorRect = display.visiblePosition! & display.visibleSize!; if (windowRect.left >= (monitorRect.left - tolerance) && windowRect.top >= (monitorRect.top - tolerance) && windowRect.right <= (monitorRect.right + tolerance) && windowRect.bottom <= (monitorRect.bottom + tolerance)) { return true; } } return false; } Future show({bool focus = true}) async { await windowManager.show(); if (focus) await windowManager.focus(); if (Platform.isMacOS) { await windowManager.setSkipTaskbar(false); } } Future hide() async { await windowManager.hide(); if (Platform.isMacOS) { await windowManager.setSkipTaskbar(true); } } Future showOrHide() async { if (await windowManager.isVisible()) { await hide(); } else { await show(); } } Future exit() async { await ref .read(connectionNotifierProvider.notifier) .abortConnection() .timeout(const Duration(seconds: 2)) .catchError((e) { loggy.warning("error aborting connection on quit", e); }); await trayManager.destroy(); await windowManager.destroy(); } } ================================================ FILE: lib/features/window/widget/window_wrapper.dart ================================================ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:hiddify/core/preferences/actions_at_closing.dart'; import 'package:hiddify/core/preferences/general_preferences.dart'; import 'package:hiddify/core/router/dialog/dialog_notifier.dart'; import 'package:hiddify/core/router/go_router/go_router_notifier.dart'; import 'package:hiddify/features/window/notifier/window_notifier.dart'; import 'package:hiddify/utils/custom_loggers.dart'; import 'package:hiddify/utils/platform_utils.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:window_manager/window_manager.dart'; class WindowWrapper extends StatefulHookConsumerWidget { const WindowWrapper(this.child, {super.key}); final Widget child; @override ConsumerState createState() => _WindowWrapperState(); } class _WindowWrapperState extends ConsumerState with WindowListener, AppLogger { late AlertDialog closeDialog; bool isWindowClosingDialogOpened = false; @override Widget build(BuildContext context) { ref.watch(windowNotifierProvider); return widget.child; } @override void initState() { super.initState(); windowManager.addListener(this); if (PlatformUtils.isDesktop) { WidgetsBinding.instance.addPostFrameCallback((_) async { await windowManager.setPreventClose(true); }); } } @override void dispose() { windowManager.removeListener(this); super.dispose(); } @override Future onWindowClose() async { if (rootNavKey.currentContext == null) { await ref.read(windowNotifierProvider.notifier).hide(); return; } switch (ref.read(Preferences.actionAtClose)) { case ActionsAtClosing.ask: if (isWindowClosingDialogOpened) return; isWindowClosingDialogOpened = true; await ref.read(dialogNotifierProvider.notifier).showWindowClosing(); isWindowClosingDialogOpened = false; case ActionsAtClosing.hide: await ref.read(windowNotifierProvider.notifier).hide(); case ActionsAtClosing.exit: await ref.read(windowNotifierProvider.notifier).exit(); } } @override Future onWindowResized() async { await ref.read(windowNotifierProvider.notifier).saveWindowState(); } @override Future onWindowMoved() async { await ref.read(windowNotifierProvider.notifier).saveWindowState(); } @override Future onWindowMaximize() async { await ref.read(windowNotifierProvider.notifier).saveWindowState(); } @override Future onWindowUnmaximize() async { await ref.read(windowNotifierProvider.notifier).saveWindowState(); } @override void onWindowFocus() { setState(() {}); } } ================================================ FILE: lib/gen/hiddify_core_generated_bindings.dart ================================================ // AUTO GENERATED FILE, DO NOT EDIT. // // Generated by `package:ffigen`. // ignore_for_file: type=lint import 'dart:ffi' as ffi; /// Bindings to Hiddify Core Library class HiddifyCoreNativeLibrary { /// Holds the symbol lookup function. final ffi.Pointer Function(String symbolName) _lookup; /// The symbols are looked up in [dynamicLibrary]. HiddifyCoreNativeLibrary(ffi.DynamicLibrary dynamicLibrary) : _lookup = dynamicLibrary.lookup; /// The symbols are looked up with [lookup]. HiddifyCoreNativeLibrary.fromLookup(ffi.Pointer Function(String symbolName) lookup) : _lookup = lookup; ffi.Pointer> signal( int arg0, ffi.Pointer> arg1, ) { return _signal(arg0, arg1); } late final _signalPtr = _lookup< ffi.NativeFunction< ffi.Pointer> Function( ffi.Int, ffi.Pointer>, ) > >('signal'); late final _signal = _signalPtr .asFunction< ffi.Pointer> Function( int, ffi.Pointer>, ) >(); int getpriority(int arg0, int arg1) { return _getpriority(arg0, arg1); } late final _getpriorityPtr = _lookup>('getpriority'); late final _getpriority = _getpriorityPtr.asFunction(); int getiopolicy_np(int arg0, int arg1) { return _getiopolicy_np(arg0, arg1); } late final _getiopolicy_npPtr = _lookup>('getiopolicy_np'); late final _getiopolicy_np = _getiopolicy_npPtr.asFunction(); int getrlimit(int arg0, ffi.Pointer arg1) { return _getrlimit(arg0, arg1); } late final _getrlimitPtr = _lookup)>>('getrlimit'); late final _getrlimit = _getrlimitPtr.asFunction)>(); int getrusage(int arg0, ffi.Pointer arg1) { return _getrusage(arg0, arg1); } late final _getrusagePtr = _lookup)>>('getrusage'); late final _getrusage = _getrusagePtr.asFunction)>(); int setpriority(int arg0, int arg1, int arg2) { return _setpriority(arg0, arg1, arg2); } late final _setpriorityPtr = _lookup>('setpriority'); late final _setpriority = _setpriorityPtr.asFunction(); int setiopolicy_np(int arg0, int arg1, int arg2) { return _setiopolicy_np(arg0, arg1, arg2); } late final _setiopolicy_npPtr = _lookup>( 'setiopolicy_np', ); late final _setiopolicy_np = _setiopolicy_npPtr.asFunction(); int setrlimit(int arg0, ffi.Pointer arg1) { return _setrlimit(arg0, arg1); } late final _setrlimitPtr = _lookup)>>('setrlimit'); late final _setrlimit = _setrlimitPtr.asFunction)>(); int wait1(ffi.Pointer arg0) { return _wait1(arg0); } late final _wait1Ptr = _lookup)>>('wait'); late final _wait1 = _wait1Ptr.asFunction)>(); int waitpid(int arg0, ffi.Pointer arg1, int arg2) { return _waitpid(arg0, arg1, arg2); } late final _waitpidPtr = _lookup, ffi.Int)>>('waitpid'); late final _waitpid = _waitpidPtr.asFunction, int)>(); int waitid(idtype_t arg0, Dart__uint32_t arg1, ffi.Pointer arg2, int arg3) { return _waitid(arg0.value, arg1, arg2, arg3); } late final _waitidPtr = _lookup, ffi.Int)>>('waitid'); late final _waitid = _waitidPtr.asFunction, int)>(); int wait3(ffi.Pointer arg0, int arg1, ffi.Pointer arg2) { return _wait3(arg0, arg1, arg2); } late final _wait3Ptr = _lookup, ffi.Int, ffi.Pointer)>>('wait3'); late final _wait3 = _wait3Ptr.asFunction, int, ffi.Pointer)>(); int wait4(int arg0, ffi.Pointer arg1, int arg2, ffi.Pointer arg3) { return _wait4(arg0, arg1, arg2, arg3); } late final _wait4Ptr = _lookup, ffi.Int, ffi.Pointer)>>('wait4'); late final _wait4 = _wait4Ptr.asFunction, int, ffi.Pointer)>(); ffi.Pointer alloca(int arg0) { return _alloca(arg0); } late final _allocaPtr = _lookup Function(ffi.Size)>>('alloca'); late final _alloca = _allocaPtr.asFunction Function(int)>(); late final ffi.Pointer ___mb_cur_max = _lookup('__mb_cur_max'); int get __mb_cur_max => ___mb_cur_max.value; set __mb_cur_max(int value) => ___mb_cur_max.value = value; ffi.Pointer malloc_type_malloc(int size, int type_id) { return _malloc_type_malloc(size, type_id); } late final _malloc_type_mallocPtr = _lookup Function(ffi.Size, malloc_type_id_t)>>('malloc_type_malloc'); late final _malloc_type_malloc = _malloc_type_mallocPtr.asFunction Function(int, int)>(); ffi.Pointer malloc_type_calloc(int count, int size, int type_id) { return _malloc_type_calloc(count, size, type_id); } late final _malloc_type_callocPtr = _lookup Function(ffi.Size, ffi.Size, malloc_type_id_t)>>( 'malloc_type_calloc', ); late final _malloc_type_calloc = _malloc_type_callocPtr.asFunction Function(int, int, int)>(); void malloc_type_free(ffi.Pointer ptr, int type_id) { return _malloc_type_free(ptr, type_id); } late final _malloc_type_freePtr = _lookup, malloc_type_id_t)>>('malloc_type_free'); late final _malloc_type_free = _malloc_type_freePtr.asFunction, int)>(); ffi.Pointer malloc_type_realloc(ffi.Pointer ptr, int size, int type_id) { return _malloc_type_realloc(ptr, size, type_id); } late final _malloc_type_reallocPtr = _lookup Function(ffi.Pointer, ffi.Size, malloc_type_id_t)>>( 'malloc_type_realloc', ); late final _malloc_type_realloc = _malloc_type_reallocPtr .asFunction Function(ffi.Pointer, int, int)>(); ffi.Pointer malloc_type_valloc(int size, int type_id) { return _malloc_type_valloc(size, type_id); } late final _malloc_type_vallocPtr = _lookup Function(ffi.Size, malloc_type_id_t)>>('malloc_type_valloc'); late final _malloc_type_valloc = _malloc_type_vallocPtr.asFunction Function(int, int)>(); ffi.Pointer malloc_type_aligned_alloc(int alignment, int size, int type_id) { return _malloc_type_aligned_alloc(alignment, size, type_id); } late final _malloc_type_aligned_allocPtr = _lookup Function(ffi.Size, ffi.Size, malloc_type_id_t)>>( 'malloc_type_aligned_alloc', ); late final _malloc_type_aligned_alloc = _malloc_type_aligned_allocPtr .asFunction Function(int, int, int)>(); int malloc_type_posix_memalign(ffi.Pointer> memptr, int alignment, int size, int type_id) { return _malloc_type_posix_memalign(memptr, alignment, size, type_id); } late final _malloc_type_posix_memalignPtr = _lookup< ffi.NativeFunction>, ffi.Size, ffi.Size, malloc_type_id_t)> >('malloc_type_posix_memalign'); late final _malloc_type_posix_memalign = _malloc_type_posix_memalignPtr .asFunction>, int, int, int)>(); ffi.Pointer malloc_type_zone_malloc(ffi.Pointer zone, int size, int type_id) { return _malloc_type_zone_malloc(zone, size, type_id); } late final _malloc_type_zone_mallocPtr = _lookup< ffi.NativeFunction Function(ffi.Pointer, ffi.Size, malloc_type_id_t)> >('malloc_type_zone_malloc'); late final _malloc_type_zone_malloc = _malloc_type_zone_mallocPtr .asFunction Function(ffi.Pointer, int, int)>(); ffi.Pointer malloc_type_zone_calloc(ffi.Pointer zone, int count, int size, int type_id) { return _malloc_type_zone_calloc(zone, count, size, type_id); } late final _malloc_type_zone_callocPtr = _lookup< ffi.NativeFunction< ffi.Pointer Function(ffi.Pointer, ffi.Size, ffi.Size, malloc_type_id_t) > >('malloc_type_zone_calloc'); late final _malloc_type_zone_calloc = _malloc_type_zone_callocPtr .asFunction Function(ffi.Pointer, int, int, int)>(); void malloc_type_zone_free(ffi.Pointer zone, ffi.Pointer ptr, int type_id) { return _malloc_type_zone_free(zone, ptr, type_id); } late final _malloc_type_zone_freePtr = _lookup< ffi.NativeFunction, ffi.Pointer, malloc_type_id_t)> >('malloc_type_zone_free'); late final _malloc_type_zone_free = _malloc_type_zone_freePtr .asFunction, ffi.Pointer, int)>(); ffi.Pointer malloc_type_zone_realloc( ffi.Pointer zone, ffi.Pointer ptr, int size, int type_id, ) { return _malloc_type_zone_realloc(zone, ptr, size, type_id); } late final _malloc_type_zone_reallocPtr = _lookup< ffi.NativeFunction< ffi.Pointer Function(ffi.Pointer, ffi.Pointer, ffi.Size, malloc_type_id_t) > >('malloc_type_zone_realloc'); late final _malloc_type_zone_realloc = _malloc_type_zone_reallocPtr .asFunction Function(ffi.Pointer, ffi.Pointer, int, int)>(); ffi.Pointer malloc_type_zone_valloc(ffi.Pointer zone, int size, int type_id) { return _malloc_type_zone_valloc(zone, size, type_id); } late final _malloc_type_zone_vallocPtr = _lookup< ffi.NativeFunction Function(ffi.Pointer, ffi.Size, malloc_type_id_t)> >('malloc_type_zone_valloc'); late final _malloc_type_zone_valloc = _malloc_type_zone_vallocPtr .asFunction Function(ffi.Pointer, int, int)>(); ffi.Pointer malloc_type_zone_memalign( ffi.Pointer zone, int alignment, int size, int type_id, ) { return _malloc_type_zone_memalign(zone, alignment, size, type_id); } late final _malloc_type_zone_memalignPtr = _lookup< ffi.NativeFunction< ffi.Pointer Function(ffi.Pointer, ffi.Size, ffi.Size, malloc_type_id_t) > >('malloc_type_zone_memalign'); late final _malloc_type_zone_memalign = _malloc_type_zone_memalignPtr .asFunction Function(ffi.Pointer, int, int, int)>(); ffi.Pointer malloc(int __size) { return _malloc(__size); } late final _mallocPtr = _lookup Function(ffi.Size)>>('malloc'); late final _malloc = _mallocPtr.asFunction Function(int)>(); ffi.Pointer calloc(int __count, int __size) { return _calloc(__count, __size); } late final _callocPtr = _lookup Function(ffi.Size, ffi.Size)>>('calloc'); late final _calloc = _callocPtr.asFunction Function(int, int)>(); void free(ffi.Pointer arg0) { return _free(arg0); } late final _freePtr = _lookup)>>('free'); late final _free = _freePtr.asFunction)>(); ffi.Pointer realloc(ffi.Pointer __ptr, int __size) { return _realloc(__ptr, __size); } late final _reallocPtr = _lookup Function(ffi.Pointer, ffi.Size)>>( 'realloc', ); late final _realloc = _reallocPtr.asFunction Function(ffi.Pointer, int)>(); ffi.Pointer reallocf(ffi.Pointer __ptr, int __size) { return _reallocf(__ptr, __size); } late final _reallocfPtr = _lookup Function(ffi.Pointer, ffi.Size)>>('reallocf'); late final _reallocf = _reallocfPtr.asFunction Function(ffi.Pointer, int)>(); ffi.Pointer valloc(int arg0) { return _valloc(arg0); } late final _vallocPtr = _lookup Function(ffi.Size)>>('valloc'); late final _valloc = _vallocPtr.asFunction Function(int)>(); ffi.Pointer aligned_alloc(int __alignment, int __size) { return _aligned_alloc(__alignment, __size); } late final _aligned_allocPtr = _lookup Function(ffi.Size, ffi.Size)>>( 'aligned_alloc', ); late final _aligned_alloc = _aligned_allocPtr.asFunction Function(int, int)>(); int posix_memalign(ffi.Pointer> __memptr, int __alignment, int __size) { return _posix_memalign(__memptr, __alignment, __size); } late final _posix_memalignPtr = _lookup>, ffi.Size, ffi.Size)>>( 'posix_memalign', ); late final _posix_memalign = _posix_memalignPtr .asFunction>, int, int)>(); void abort() { return _abort(); } late final _abortPtr = _lookup>('abort'); late final _abort = _abortPtr.asFunction(); int abs(int arg0) { return _abs(arg0); } late final _absPtr = _lookup>('abs'); late final _abs = _absPtr.asFunction(); int atexit(ffi.Pointer> arg0) { return _atexit(arg0); } late final _atexitPtr = _lookup>)>>('atexit'); late final _atexit = _atexitPtr.asFunction>)>(); double atof(ffi.Pointer arg0) { return _atof(arg0); } late final _atofPtr = _lookup)>>('atof'); late final _atof = _atofPtr.asFunction)>(); int atoi(ffi.Pointer arg0) { return _atoi(arg0); } late final _atoiPtr = _lookup)>>('atoi'); late final _atoi = _atoiPtr.asFunction)>(); int atol(ffi.Pointer arg0) { return _atol(arg0); } late final _atolPtr = _lookup)>>('atol'); late final _atol = _atolPtr.asFunction)>(); int atoll(ffi.Pointer arg0) { return _atoll(arg0); } late final _atollPtr = _lookup)>>('atoll'); late final _atoll = _atollPtr.asFunction)>(); ffi.Pointer bsearch( ffi.Pointer __key, ffi.Pointer __base, int __nel, int __width, ffi.Pointer, ffi.Pointer)>> __compar, ) { return _bsearch(__key, __base, __nel, __width, __compar); } late final _bsearchPtr = _lookup< ffi.NativeFunction< ffi.Pointer Function( ffi.Pointer, ffi.Pointer, ffi.Size, ffi.Size, ffi.Pointer, ffi.Pointer)>>, ) > >('bsearch'); late final _bsearch = _bsearchPtr .asFunction< ffi.Pointer Function( ffi.Pointer, ffi.Pointer, int, int, ffi.Pointer, ffi.Pointer)>>, ) >(); div_t div(int arg0, int arg1) { return _div(arg0, arg1); } late final _divPtr = _lookup>('div'); late final _div = _divPtr.asFunction(); void exit(int arg0) { return _exit(arg0); } late final _exitPtr = _lookup>('exit'); late final _exit = _exitPtr.asFunction(); ffi.Pointer getenv(ffi.Pointer arg0) { return _getenv(arg0); } late final _getenvPtr = _lookup Function(ffi.Pointer)>>('getenv'); late final _getenv = _getenvPtr.asFunction Function(ffi.Pointer)>(); int labs(int arg0) { return _labs(arg0); } late final _labsPtr = _lookup>('labs'); late final _labs = _labsPtr.asFunction(); ldiv_t ldiv(int arg0, int arg1) { return _ldiv(arg0, arg1); } late final _ldivPtr = _lookup>('ldiv'); late final _ldiv = _ldivPtr.asFunction(); int llabs(int arg0) { return _llabs(arg0); } late final _llabsPtr = _lookup>('llabs'); late final _llabs = _llabsPtr.asFunction(); lldiv_t lldiv(int arg0, int arg1) { return _lldiv(arg0, arg1); } late final _lldivPtr = _lookup>('lldiv'); late final _lldiv = _lldivPtr.asFunction(); int mblen(ffi.Pointer __s, int __n) { return _mblen(__s, __n); } late final _mblenPtr = _lookup, ffi.Size)>>('mblen'); late final _mblen = _mblenPtr.asFunction, int)>(); int mbstowcs(ffi.Pointer arg0, ffi.Pointer arg1, int arg2) { return _mbstowcs(arg0, arg1, arg2); } late final _mbstowcsPtr = _lookup, ffi.Pointer, ffi.Size)>>( 'mbstowcs', ); late final _mbstowcs = _mbstowcsPtr.asFunction, ffi.Pointer, int)>(); int mbtowc(ffi.Pointer arg0, ffi.Pointer arg1, int arg2) { return _mbtowc(arg0, arg1, arg2); } late final _mbtowcPtr = _lookup, ffi.Pointer, ffi.Size)>>('mbtowc'); late final _mbtowc = _mbtowcPtr.asFunction, ffi.Pointer, int)>(); void qsort( ffi.Pointer __base, int __nel, int __width, ffi.Pointer, ffi.Pointer)>> __compar, ) { return _qsort(__base, __nel, __width, __compar); } late final _qsortPtr = _lookup< ffi.NativeFunction< ffi.Void Function( ffi.Pointer, ffi.Size, ffi.Size, ffi.Pointer, ffi.Pointer)>>, ) > >('qsort'); late final _qsort = _qsortPtr .asFunction< void Function( ffi.Pointer, int, int, ffi.Pointer, ffi.Pointer)>>, ) >(); int rand() { return _rand(); } late final _randPtr = _lookup>('rand'); late final _rand = _randPtr.asFunction(); void srand(int arg0) { return _srand(arg0); } late final _srandPtr = _lookup>('srand'); late final _srand = _srandPtr.asFunction(); double strtod(ffi.Pointer arg0, ffi.Pointer> arg1) { return _strtod(arg0, arg1); } late final _strtodPtr = _lookup, ffi.Pointer>)>>( 'strtod', ); late final _strtod = _strtodPtr .asFunction, ffi.Pointer>)>(); double strtof(ffi.Pointer arg0, ffi.Pointer> arg1) { return _strtof(arg0, arg1); } late final _strtofPtr = _lookup, ffi.Pointer>)>>( 'strtof', ); late final _strtof = _strtofPtr .asFunction, ffi.Pointer>)>(); int strtol(ffi.Pointer __str, ffi.Pointer> __endptr, int __base) { return _strtol(__str, __endptr, __base); } late final _strtolPtr = _lookup< ffi.NativeFunction, ffi.Pointer>, ffi.Int)> >('strtol'); late final _strtol = _strtolPtr .asFunction, ffi.Pointer>, int)>(); int strtoll(ffi.Pointer __str, ffi.Pointer> __endptr, int __base) { return _strtoll(__str, __endptr, __base); } late final _strtollPtr = _lookup< ffi.NativeFunction, ffi.Pointer>, ffi.Int)> >('strtoll'); late final _strtoll = _strtollPtr .asFunction, ffi.Pointer>, int)>(); int strtoul(ffi.Pointer __str, ffi.Pointer> __endptr, int __base) { return _strtoul(__str, __endptr, __base); } late final _strtoulPtr = _lookup< ffi.NativeFunction< ffi.UnsignedLong Function(ffi.Pointer, ffi.Pointer>, ffi.Int) > >('strtoul'); late final _strtoul = _strtoulPtr .asFunction, ffi.Pointer>, int)>(); int strtoull(ffi.Pointer __str, ffi.Pointer> __endptr, int __base) { return _strtoull(__str, __endptr, __base); } late final _strtoullPtr = _lookup< ffi.NativeFunction< ffi.UnsignedLongLong Function(ffi.Pointer, ffi.Pointer>, ffi.Int) > >('strtoull'); late final _strtoull = _strtoullPtr .asFunction, ffi.Pointer>, int)>(); int system(ffi.Pointer arg0) { return _system(arg0); } late final _systemPtr = _lookup)>>('system'); late final _system = _systemPtr.asFunction)>(); int wcstombs(ffi.Pointer arg0, ffi.Pointer arg1, int arg2) { return _wcstombs(arg0, arg1, arg2); } late final _wcstombsPtr = _lookup, ffi.Pointer, ffi.Size)>>( 'wcstombs', ); late final _wcstombs = _wcstombsPtr.asFunction, ffi.Pointer, int)>(); int wctomb(ffi.Pointer arg0, int arg1) { return _wctomb(arg0, arg1); } late final _wctombPtr = _lookup, ffi.WChar)>>('wctomb'); late final _wctomb = _wctombPtr.asFunction, int)>(); void _Exit(int arg0) { return __Exit(arg0); } late final __ExitPtr = _lookup>('_Exit'); late final __Exit = __ExitPtr.asFunction(); int a64l(ffi.Pointer arg0) { return _a64l(arg0); } late final _a64lPtr = _lookup)>>('a64l'); late final _a64l = _a64lPtr.asFunction)>(); double drand48() { return _drand48(); } late final _drand48Ptr = _lookup>('drand48'); late final _drand48 = _drand48Ptr.asFunction(); ffi.Pointer ecvt(double arg0, int arg1, ffi.Pointer arg2, ffi.Pointer arg3) { return _ecvt(arg0, arg1, arg2, arg3); } late final _ecvtPtr = _lookup< ffi.NativeFunction< ffi.Pointer Function(ffi.Double, ffi.Int, ffi.Pointer, ffi.Pointer) > >('ecvt'); late final _ecvt = _ecvtPtr .asFunction Function(double, int, ffi.Pointer, ffi.Pointer)>(); double erand48(ffi.Pointer arg0) { return _erand48(arg0); } late final _erand48Ptr = _lookup)>>('erand48'); late final _erand48 = _erand48Ptr.asFunction)>(); ffi.Pointer fcvt(double arg0, int arg1, ffi.Pointer arg2, ffi.Pointer arg3) { return _fcvt(arg0, arg1, arg2, arg3); } late final _fcvtPtr = _lookup< ffi.NativeFunction< ffi.Pointer Function(ffi.Double, ffi.Int, ffi.Pointer, ffi.Pointer) > >('fcvt'); late final _fcvt = _fcvtPtr .asFunction Function(double, int, ffi.Pointer, ffi.Pointer)>(); ffi.Pointer gcvt(double arg0, int arg1, ffi.Pointer arg2) { return _gcvt(arg0, arg1, arg2); } late final _gcvtPtr = _lookup Function(ffi.Double, ffi.Int, ffi.Pointer)>>('gcvt'); late final _gcvt = _gcvtPtr.asFunction Function(double, int, ffi.Pointer)>(); int getsubopt( ffi.Pointer> arg0, ffi.Pointer> arg1, ffi.Pointer> arg2, ) { return _getsubopt(arg0, arg1, arg2); } late final _getsuboptPtr = _lookup< ffi.NativeFunction< ffi.Int Function( ffi.Pointer>, ffi.Pointer>, ffi.Pointer>, ) > >('getsubopt'); late final _getsubopt = _getsuboptPtr .asFunction< int Function( ffi.Pointer>, ffi.Pointer>, ffi.Pointer>, ) >(); int grantpt(int arg0) { return _grantpt(arg0); } late final _grantptPtr = _lookup>('grantpt'); late final _grantpt = _grantptPtr.asFunction(); ffi.Pointer initstate(int arg0, ffi.Pointer arg1, int arg2) { return _initstate(arg0, arg1, arg2); } late final _initstatePtr = _lookup Function(ffi.UnsignedInt, ffi.Pointer, ffi.Size)>>( 'initstate', ); late final _initstate = _initstatePtr.asFunction Function(int, ffi.Pointer, int)>(); int jrand48(ffi.Pointer arg0) { return _jrand48(arg0); } late final _jrand48Ptr = _lookup)>>('jrand48'); late final _jrand48 = _jrand48Ptr.asFunction)>(); ffi.Pointer l64a(int arg0) { return _l64a(arg0); } late final _l64aPtr = _lookup Function(ffi.Long)>>('l64a'); late final _l64a = _l64aPtr.asFunction Function(int)>(); void lcong48(ffi.Pointer arg0) { return _lcong48(arg0); } late final _lcong48Ptr = _lookup)>>('lcong48'); late final _lcong48 = _lcong48Ptr.asFunction)>(); int lrand48() { return _lrand48(); } late final _lrand48Ptr = _lookup>('lrand48'); late final _lrand48 = _lrand48Ptr.asFunction(); ffi.Pointer mktemp(ffi.Pointer arg0) { return _mktemp(arg0); } late final _mktempPtr = _lookup Function(ffi.Pointer)>>('mktemp'); late final _mktemp = _mktempPtr.asFunction Function(ffi.Pointer)>(); int mkstemp(ffi.Pointer arg0) { return _mkstemp(arg0); } late final _mkstempPtr = _lookup)>>('mkstemp'); late final _mkstemp = _mkstempPtr.asFunction)>(); int mrand48() { return _mrand48(); } late final _mrand48Ptr = _lookup>('mrand48'); late final _mrand48 = _mrand48Ptr.asFunction(); int nrand48(ffi.Pointer arg0) { return _nrand48(arg0); } late final _nrand48Ptr = _lookup)>>('nrand48'); late final _nrand48 = _nrand48Ptr.asFunction)>(); int posix_openpt(int arg0) { return _posix_openpt(arg0); } late final _posix_openptPtr = _lookup>('posix_openpt'); late final _posix_openpt = _posix_openptPtr.asFunction(); ffi.Pointer ptsname(int arg0) { return _ptsname(arg0); } late final _ptsnamePtr = _lookup Function(ffi.Int)>>('ptsname'); late final _ptsname = _ptsnamePtr.asFunction Function(int)>(); int ptsname_r(int fildes, ffi.Pointer buffer, int buflen) { return _ptsname_r(fildes, buffer, buflen); } late final _ptsname_rPtr = _lookup, ffi.Size)>>( 'ptsname_r', ); late final _ptsname_r = _ptsname_rPtr.asFunction, int)>(); int putenv(ffi.Pointer arg0) { return _putenv(arg0); } late final _putenvPtr = _lookup)>>('putenv'); late final _putenv = _putenvPtr.asFunction)>(); int random() { return _random(); } late final _randomPtr = _lookup>('random'); late final _random = _randomPtr.asFunction(); int rand_r(ffi.Pointer arg0) { return _rand_r(arg0); } late final _rand_rPtr = _lookup)>>('rand_r'); late final _rand_r = _rand_rPtr.asFunction)>(); ffi.Pointer realpath(ffi.Pointer arg0, ffi.Pointer arg1) { return _realpath(arg0, arg1); } late final _realpathPtr = _lookup Function(ffi.Pointer, ffi.Pointer)>>( 'realpath', ); late final _realpath = _realpathPtr .asFunction Function(ffi.Pointer, ffi.Pointer)>(); ffi.Pointer seed48(ffi.Pointer arg0) { return _seed48(arg0); } late final _seed48Ptr = _lookup Function(ffi.Pointer)>>('seed48'); late final _seed48 = _seed48Ptr.asFunction Function(ffi.Pointer)>(); int setenv(ffi.Pointer __name, ffi.Pointer __value, int __overwrite) { return _setenv(__name, __value, __overwrite); } late final _setenvPtr = _lookup, ffi.Pointer, ffi.Int)>>('setenv'); late final _setenv = _setenvPtr.asFunction, ffi.Pointer, int)>(); void setkey(ffi.Pointer arg0) { return _setkey(arg0); } late final _setkeyPtr = _lookup)>>('setkey'); late final _setkey = _setkeyPtr.asFunction)>(); ffi.Pointer setstate(ffi.Pointer arg0) { return _setstate(arg0); } late final _setstatePtr = _lookup Function(ffi.Pointer)>>( 'setstate', ); late final _setstate = _setstatePtr.asFunction Function(ffi.Pointer)>(); void srand48(int arg0) { return _srand48(arg0); } late final _srand48Ptr = _lookup>('srand48'); late final _srand48 = _srand48Ptr.asFunction(); void srandom(int arg0) { return _srandom(arg0); } late final _srandomPtr = _lookup>('srandom'); late final _srandom = _srandomPtr.asFunction(); int unlockpt(int arg0) { return _unlockpt(arg0); } late final _unlockptPtr = _lookup>('unlockpt'); late final _unlockpt = _unlockptPtr.asFunction(); int unsetenv(ffi.Pointer arg0) { return _unsetenv(arg0); } late final _unsetenvPtr = _lookup)>>('unsetenv'); late final _unsetenv = _unsetenvPtr.asFunction)>(); int arc4random() { return _arc4random(); } late final _arc4randomPtr = _lookup>('arc4random'); late final _arc4random = _arc4randomPtr.asFunction(); void arc4random_addrandom(ffi.Pointer arg0, int arg1) { return _arc4random_addrandom(arg0, arg1); } late final _arc4random_addrandomPtr = _lookup, ffi.Int)>>('arc4random_addrandom'); late final _arc4random_addrandom = _arc4random_addrandomPtr .asFunction, int)>(); void arc4random_buf(ffi.Pointer __buf, int __nbytes) { return _arc4random_buf(__buf, __nbytes); } late final _arc4random_bufPtr = _lookup, ffi.Size)>>( 'arc4random_buf', ); late final _arc4random_buf = _arc4random_bufPtr.asFunction, int)>(); void arc4random_stir() { return _arc4random_stir(); } late final _arc4random_stirPtr = _lookup>('arc4random_stir'); late final _arc4random_stir = _arc4random_stirPtr.asFunction(); int arc4random_uniform(int __upper_bound) { return _arc4random_uniform(__upper_bound); } late final _arc4random_uniformPtr = _lookup>( 'arc4random_uniform', ); late final _arc4random_uniform = _arc4random_uniformPtr.asFunction(); ffi.Pointer cgetcap(ffi.Pointer arg0, ffi.Pointer arg1, int arg2) { return _cgetcap(arg0, arg1, arg2); } late final _cgetcapPtr = _lookup< ffi.NativeFunction Function(ffi.Pointer, ffi.Pointer, ffi.Int)> >('cgetcap'); late final _cgetcap = _cgetcapPtr .asFunction Function(ffi.Pointer, ffi.Pointer, int)>(); int cgetclose() { return _cgetclose(); } late final _cgetclosePtr = _lookup>('cgetclose'); late final _cgetclose = _cgetclosePtr.asFunction(); int cgetent( ffi.Pointer> arg0, ffi.Pointer> arg1, ffi.Pointer arg2, ) { return _cgetent(arg0, arg1, arg2); } late final _cgetentPtr = _lookup< ffi.NativeFunction< ffi.Int Function( ffi.Pointer>, ffi.Pointer>, ffi.Pointer, ) > >('cgetent'); late final _cgetent = _cgetentPtr .asFunction< int Function(ffi.Pointer>, ffi.Pointer>, ffi.Pointer) >(); int cgetfirst(ffi.Pointer> arg0, ffi.Pointer> arg1) { return _cgetfirst(arg0, arg1); } late final _cgetfirstPtr = _lookup< ffi.NativeFunction>, ffi.Pointer>)> >('cgetfirst'); late final _cgetfirst = _cgetfirstPtr .asFunction>, ffi.Pointer>)>(); int cgetmatch(ffi.Pointer arg0, ffi.Pointer arg1) { return _cgetmatch(arg0, arg1); } late final _cgetmatchPtr = _lookup, ffi.Pointer)>>('cgetmatch'); late final _cgetmatch = _cgetmatchPtr.asFunction, ffi.Pointer)>(); int cgetnext(ffi.Pointer> arg0, ffi.Pointer> arg1) { return _cgetnext(arg0, arg1); } late final _cgetnextPtr = _lookup< ffi.NativeFunction>, ffi.Pointer>)> >('cgetnext'); late final _cgetnext = _cgetnextPtr .asFunction>, ffi.Pointer>)>(); int cgetnum(ffi.Pointer arg0, ffi.Pointer arg1, ffi.Pointer arg2) { return _cgetnum(arg0, arg1, arg2); } late final _cgetnumPtr = _lookup< ffi.NativeFunction, ffi.Pointer, ffi.Pointer)> >('cgetnum'); late final _cgetnum = _cgetnumPtr .asFunction, ffi.Pointer, ffi.Pointer)>(); int cgetset(ffi.Pointer arg0) { return _cgetset(arg0); } late final _cgetsetPtr = _lookup)>>('cgetset'); late final _cgetset = _cgetsetPtr.asFunction)>(); int cgetstr(ffi.Pointer arg0, ffi.Pointer arg1, ffi.Pointer> arg2) { return _cgetstr(arg0, arg1, arg2); } late final _cgetstrPtr = _lookup< ffi.NativeFunction< ffi.Int Function(ffi.Pointer, ffi.Pointer, ffi.Pointer>) > >('cgetstr'); late final _cgetstr = _cgetstrPtr .asFunction, ffi.Pointer, ffi.Pointer>)>(); int cgetustr(ffi.Pointer arg0, ffi.Pointer arg1, ffi.Pointer> arg2) { return _cgetustr(arg0, arg1, arg2); } late final _cgetustrPtr = _lookup< ffi.NativeFunction< ffi.Int Function(ffi.Pointer, ffi.Pointer, ffi.Pointer>) > >('cgetustr'); late final _cgetustr = _cgetustrPtr .asFunction, ffi.Pointer, ffi.Pointer>)>(); int daemon(int arg0, int arg1) { return _daemon(arg0, arg1); } late final _daemonPtr = _lookup>('daemon'); late final _daemon = _daemonPtr.asFunction(); ffi.Pointer devname(int arg0, int arg1) { return _devname(arg0, arg1); } late final _devnamePtr = _lookup Function(dev_t, mode_t)>>('devname'); late final _devname = _devnamePtr.asFunction Function(int, int)>(); ffi.Pointer devname_r(int arg0, int arg1, ffi.Pointer buf, int len) { return _devname_r(arg0, arg1, buf, len); } late final _devname_rPtr = _lookup Function(dev_t, mode_t, ffi.Pointer, ffi.Int)>>( 'devname_r', ); late final _devname_r = _devname_rPtr .asFunction Function(int, int, ffi.Pointer, int)>(); ffi.Pointer getbsize(ffi.Pointer arg0, ffi.Pointer arg1) { return _getbsize(arg0, arg1); } late final _getbsizePtr = _lookup Function(ffi.Pointer, ffi.Pointer)>>( 'getbsize', ); late final _getbsize = _getbsizePtr .asFunction Function(ffi.Pointer, ffi.Pointer)>(); int getloadavg(ffi.Pointer arg0, int arg1) { return _getloadavg(arg0, arg1); } late final _getloadavgPtr = _lookup, ffi.Int)>>( 'getloadavg', ); late final _getloadavg = _getloadavgPtr.asFunction, int)>(); ffi.Pointer getprogname() { return _getprogname(); } late final _getprognamePtr = _lookup Function()>>('getprogname'); late final _getprogname = _getprognamePtr.asFunction Function()>(); void setprogname(ffi.Pointer arg0) { return _setprogname(arg0); } late final _setprognamePtr = _lookup)>>('setprogname'); late final _setprogname = _setprognamePtr.asFunction)>(); int heapsort( ffi.Pointer __base, int __nel, int __width, ffi.Pointer, ffi.Pointer)>> __compar, ) { return _heapsort(__base, __nel, __width, __compar); } late final _heapsortPtr = _lookup< ffi.NativeFunction< ffi.Int Function( ffi.Pointer, ffi.Size, ffi.Size, ffi.Pointer, ffi.Pointer)>>, ) > >('heapsort'); late final _heapsort = _heapsortPtr .asFunction< int Function( ffi.Pointer, int, int, ffi.Pointer, ffi.Pointer)>>, ) >(); int mergesort( ffi.Pointer __base, int __nel, int __width, ffi.Pointer, ffi.Pointer)>> __compar, ) { return _mergesort(__base, __nel, __width, __compar); } late final _mergesortPtr = _lookup< ffi.NativeFunction< ffi.Int Function( ffi.Pointer, ffi.Size, ffi.Size, ffi.Pointer, ffi.Pointer)>>, ) > >('mergesort'); late final _mergesort = _mergesortPtr .asFunction< int Function( ffi.Pointer, int, int, ffi.Pointer, ffi.Pointer)>>, ) >(); void psort( ffi.Pointer __base, int __nel, int __width, ffi.Pointer, ffi.Pointer)>> __compar, ) { return _psort(__base, __nel, __width, __compar); } late final _psortPtr = _lookup< ffi.NativeFunction< ffi.Void Function( ffi.Pointer, ffi.Size, ffi.Size, ffi.Pointer, ffi.Pointer)>>, ) > >('psort'); late final _psort = _psortPtr .asFunction< void Function( ffi.Pointer, int, int, ffi.Pointer, ffi.Pointer)>>, ) >(); void psort_r( ffi.Pointer __base, int __nel, int __width, ffi.Pointer arg3, ffi.Pointer< ffi.NativeFunction, ffi.Pointer, ffi.Pointer)> > __compar, ) { return _psort_r(__base, __nel, __width, arg3, __compar); } late final _psort_rPtr = _lookup< ffi.NativeFunction< ffi.Void Function( ffi.Pointer, ffi.Size, ffi.Size, ffi.Pointer, ffi.Pointer< ffi.NativeFunction, ffi.Pointer, ffi.Pointer)> >, ) > >('psort_r'); late final _psort_r = _psort_rPtr .asFunction< void Function( ffi.Pointer, int, int, ffi.Pointer, ffi.Pointer< ffi.NativeFunction, ffi.Pointer, ffi.Pointer)> >, ) >(); void qsort_r( ffi.Pointer __base, int __nel, int __width, ffi.Pointer arg3, ffi.Pointer< ffi.NativeFunction, ffi.Pointer, ffi.Pointer)> > __compar, ) { return _qsort_r(__base, __nel, __width, arg3, __compar); } late final _qsort_rPtr = _lookup< ffi.NativeFunction< ffi.Void Function( ffi.Pointer, ffi.Size, ffi.Size, ffi.Pointer, ffi.Pointer< ffi.NativeFunction, ffi.Pointer, ffi.Pointer)> >, ) > >('qsort_r'); late final _qsort_r = _qsort_rPtr .asFunction< void Function( ffi.Pointer, int, int, ffi.Pointer, ffi.Pointer< ffi.NativeFunction, ffi.Pointer, ffi.Pointer)> >, ) >(); int radixsort( ffi.Pointer> __base, int __nel, ffi.Pointer __table, int __endbyte, ) { return _radixsort(__base, __nel, __table, __endbyte); } late final _radixsortPtr = _lookup< ffi.NativeFunction< ffi.Int Function( ffi.Pointer>, ffi.Int, ffi.Pointer, ffi.UnsignedInt, ) > >('radixsort'); late final _radixsort = _radixsortPtr .asFunction>, int, ffi.Pointer, int)>(); int rpmatch(ffi.Pointer arg0) { return _rpmatch(arg0); } late final _rpmatchPtr = _lookup)>>('rpmatch'); late final _rpmatch = _rpmatchPtr.asFunction)>(); int sradixsort( ffi.Pointer> __base, int __nel, ffi.Pointer __table, int __endbyte, ) { return _sradixsort(__base, __nel, __table, __endbyte); } late final _sradixsortPtr = _lookup< ffi.NativeFunction< ffi.Int Function( ffi.Pointer>, ffi.Int, ffi.Pointer, ffi.UnsignedInt, ) > >('sradixsort'); late final _sradixsort = _sradixsortPtr .asFunction>, int, ffi.Pointer, int)>(); void sranddev() { return _sranddev(); } late final _sranddevPtr = _lookup>('sranddev'); late final _sranddev = _sranddevPtr.asFunction(); void srandomdev() { return _srandomdev(); } late final _srandomdevPtr = _lookup>('srandomdev'); late final _srandomdev = _srandomdevPtr.asFunction(); int strtonum( ffi.Pointer __numstr, int __minval, int __maxval, ffi.Pointer> __errstrp, ) { return _strtonum(__numstr, __minval, __maxval, __errstrp); } late final _strtonumPtr = _lookup< ffi.NativeFunction< ffi.LongLong Function(ffi.Pointer, ffi.LongLong, ffi.LongLong, ffi.Pointer>) > >('strtonum'); late final _strtonum = _strtonumPtr .asFunction, int, int, ffi.Pointer>)>(); int strtoq(ffi.Pointer __str, ffi.Pointer> __endptr, int __base) { return _strtoq(__str, __endptr, __base); } late final _strtoqPtr = _lookup< ffi.NativeFunction, ffi.Pointer>, ffi.Int)> >('strtoq'); late final _strtoq = _strtoqPtr .asFunction, ffi.Pointer>, int)>(); int strtouq(ffi.Pointer __str, ffi.Pointer> __endptr, int __base) { return _strtouq(__str, __endptr, __base); } late final _strtouqPtr = _lookup< ffi.NativeFunction< ffi.UnsignedLongLong Function(ffi.Pointer, ffi.Pointer>, ffi.Int) > >('strtouq'); late final _strtouq = _strtouqPtr .asFunction, ffi.Pointer>, int)>(); late final ffi.Pointer> _suboptarg = _lookup>('suboptarg'); ffi.Pointer get suboptarg => _suboptarg.value; set suboptarg(ffi.Pointer value) => _suboptarg.value = value; late final ffi.Pointer>> _sys_signame = _lookup>>( 'sys_signame', ); ffi.Pointer> get sys_signame => _sys_signame.value; set sys_signame(ffi.Pointer> value) => _sys_signame.value = value; late final ffi.Pointer>> _sys_siglist = _lookup>>( 'sys_siglist', ); ffi.Pointer> get sys_siglist => _sys_siglist.value; set sys_siglist(ffi.Pointer> value) => _sys_siglist.value = value; int raise(int arg0) { return _raise(arg0); } late final _raisePtr = _lookup>('raise'); late final _raise = _raisePtr.asFunction(); ffi.Pointer> bsd_signal( int arg0, ffi.Pointer> arg1, ) { return _bsd_signal(arg0, arg1); } late final _bsd_signalPtr = _lookup< ffi.NativeFunction< ffi.Pointer> Function( ffi.Int, ffi.Pointer>, ) > >('bsd_signal'); late final _bsd_signal = _bsd_signalPtr .asFunction< ffi.Pointer> Function( int, ffi.Pointer>, ) >(); int kill(int arg0, int arg1) { return _kill(arg0, arg1); } late final _killPtr = _lookup>('kill'); late final _kill = _killPtr.asFunction(); int killpg(int arg0, int arg1) { return _killpg(arg0, arg1); } late final _killpgPtr = _lookup>('killpg'); late final _killpg = _killpgPtr.asFunction(); int pthread_kill(pthread_t arg0, int arg1) { return _pthread_kill(arg0, arg1); } late final _pthread_killPtr = _lookup>('pthread_kill'); late final _pthread_kill = _pthread_killPtr.asFunction(); int pthread_sigmask(int arg0, ffi.Pointer arg1, ffi.Pointer arg2) { return _pthread_sigmask(arg0, arg1, arg2); } late final _pthread_sigmaskPtr = _lookup, ffi.Pointer)>>( 'pthread_sigmask', ); late final _pthread_sigmask = _pthread_sigmaskPtr .asFunction, ffi.Pointer)>(); int sigaction1(int arg0, ffi.Pointer arg1, ffi.Pointer arg2) { return _sigaction1(arg0, arg1, arg2); } late final _sigaction1Ptr = _lookup, ffi.Pointer)>>( 'sigaction', ); late final _sigaction1 = _sigaction1Ptr .asFunction, ffi.Pointer)>(); int sigaddset(ffi.Pointer arg0, int arg1) { return _sigaddset(arg0, arg1); } late final _sigaddsetPtr = _lookup, ffi.Int)>>('sigaddset'); late final _sigaddset = _sigaddsetPtr.asFunction, int)>(); int sigaltstack(ffi.Pointer arg0, ffi.Pointer arg1) { return _sigaltstack(arg0, arg1); } late final _sigaltstackPtr = _lookup, ffi.Pointer)>>('sigaltstack'); late final _sigaltstack = _sigaltstackPtr.asFunction, ffi.Pointer)>(); int sigdelset(ffi.Pointer arg0, int arg1) { return _sigdelset(arg0, arg1); } late final _sigdelsetPtr = _lookup, ffi.Int)>>('sigdelset'); late final _sigdelset = _sigdelsetPtr.asFunction, int)>(); int sigemptyset(ffi.Pointer arg0) { return _sigemptyset(arg0); } late final _sigemptysetPtr = _lookup)>>('sigemptyset'); late final _sigemptyset = _sigemptysetPtr.asFunction)>(); int sigfillset(ffi.Pointer arg0) { return _sigfillset(arg0); } late final _sigfillsetPtr = _lookup)>>('sigfillset'); late final _sigfillset = _sigfillsetPtr.asFunction)>(); int sighold(int arg0) { return _sighold(arg0); } late final _sigholdPtr = _lookup>('sighold'); late final _sighold = _sigholdPtr.asFunction(); int sigignore(int arg0) { return _sigignore(arg0); } late final _sigignorePtr = _lookup>('sigignore'); late final _sigignore = _sigignorePtr.asFunction(); int siginterrupt(int arg0, int arg1) { return _siginterrupt(arg0, arg1); } late final _siginterruptPtr = _lookup>('siginterrupt'); late final _siginterrupt = _siginterruptPtr.asFunction(); int sigismember(ffi.Pointer arg0, int arg1) { return _sigismember(arg0, arg1); } late final _sigismemberPtr = _lookup, ffi.Int)>>( 'sigismember', ); late final _sigismember = _sigismemberPtr.asFunction, int)>(); int sigpause(int arg0) { return _sigpause(arg0); } late final _sigpausePtr = _lookup>('sigpause'); late final _sigpause = _sigpausePtr.asFunction(); int sigpending(ffi.Pointer arg0) { return _sigpending(arg0); } late final _sigpendingPtr = _lookup)>>('sigpending'); late final _sigpending = _sigpendingPtr.asFunction)>(); int sigprocmask(int arg0, ffi.Pointer arg1, ffi.Pointer arg2) { return _sigprocmask(arg0, arg1, arg2); } late final _sigprocmaskPtr = _lookup, ffi.Pointer)>>( 'sigprocmask', ); late final _sigprocmask = _sigprocmaskPtr .asFunction, ffi.Pointer)>(); int sigrelse(int arg0) { return _sigrelse(arg0); } late final _sigrelsePtr = _lookup>('sigrelse'); late final _sigrelse = _sigrelsePtr.asFunction(); ffi.Pointer> sigset( int arg0, ffi.Pointer> arg1, ) { return _sigset(arg0, arg1); } late final _sigsetPtr = _lookup< ffi.NativeFunction< ffi.Pointer> Function( ffi.Int, ffi.Pointer>, ) > >('sigset'); late final _sigset = _sigsetPtr .asFunction< ffi.Pointer> Function( int, ffi.Pointer>, ) >(); int sigsuspend(ffi.Pointer arg0) { return _sigsuspend(arg0); } late final _sigsuspendPtr = _lookup)>>('sigsuspend'); late final _sigsuspend = _sigsuspendPtr.asFunction)>(); int sigwait(ffi.Pointer arg0, ffi.Pointer arg1) { return _sigwait(arg0, arg1); } late final _sigwaitPtr = _lookup, ffi.Pointer)>>( 'sigwait', ); late final _sigwait = _sigwaitPtr.asFunction, ffi.Pointer)>(); void psignal(int arg0, ffi.Pointer arg1) { return _psignal(arg0, arg1); } late final _psignalPtr = _lookup)>>('psignal'); late final _psignal = _psignalPtr.asFunction)>(); int sigblock(int arg0) { return _sigblock(arg0); } late final _sigblockPtr = _lookup>('sigblock'); late final _sigblock = _sigblockPtr.asFunction(); int sigsetmask(int arg0) { return _sigsetmask(arg0); } late final _sigsetmaskPtr = _lookup>('sigsetmask'); late final _sigsetmask = _sigsetmaskPtr.asFunction(); int sigvec1(int arg0, ffi.Pointer arg1, ffi.Pointer arg2) { return _sigvec1(arg0, arg1, arg2); } late final _sigvec1Ptr = _lookup, ffi.Pointer)>>('sigvec'); late final _sigvec1 = _sigvec1Ptr.asFunction, ffi.Pointer)>(); void init_signals() { return _init_signals(); } late final _init_signalsPtr = _lookup>('init_signals'); late final _init_signals = _init_signalsPtr.asFunction(); void cleanup_signals() { return _cleanup_signals(); } late final _cleanup_signalsPtr = _lookup>('cleanup_signals'); late final _cleanup_signals = _cleanup_signalsPtr.asFunction(); ffi.Pointer parseCli(int argc, ffi.Pointer> argv) { return _parseCli(argc, argv); } late final _parseCliPtr = _lookup Function(ffi.Int, ffi.Pointer>)>>( 'parseCli', ); late final _parseCli = _parseCliPtr .asFunction Function(int, ffi.Pointer>)>(); void cleanup() { return _cleanup(); } late final _cleanupPtr = _lookup>('cleanup'); late final _cleanup = _cleanupPtr.asFunction(); ffi.Pointer setup( ffi.Pointer baseDir, ffi.Pointer workingDir, ffi.Pointer tempDir, int mode, ffi.Pointer listen, ffi.Pointer secret, int statusPort, int debug, ) { return _setup(baseDir, workingDir, tempDir, mode, listen, secret, statusPort, debug); } late final _setupPtr = _lookup< ffi.NativeFunction< ffi.Pointer Function( ffi.Pointer, ffi.Pointer, ffi.Pointer, ffi.Int, ffi.Pointer, ffi.Pointer, ffi.LongLong, GoUint8, ) > >('setup'); late final _setup = _setupPtr .asFunction< ffi.Pointer Function( ffi.Pointer, ffi.Pointer, ffi.Pointer, int, ffi.Pointer, ffi.Pointer, int, int, ) >(); void freeString(ffi.Pointer str) { return _freeString(str); } late final _freeStringPtr = _lookup)>>('freeString'); late final _freeString = _freeStringPtr.asFunction)>(); ffi.Pointer start(ffi.Pointer configPath, int disableMemoryLimit) { return _start(configPath, disableMemoryLimit); } late final _startPtr = _lookup Function(ffi.Pointer, GoUint8)>>( 'start', ); late final _start = _startPtr.asFunction Function(ffi.Pointer, int)>(); ffi.Pointer stop() { return _stop(); } late final _stopPtr = _lookup Function()>>('stop'); late final _stop = _stopPtr.asFunction Function()>(); ffi.Pointer restart(ffi.Pointer configPath, int disableMemoryLimit) { return _restart(configPath, disableMemoryLimit); } late final _restartPtr = _lookup Function(ffi.Pointer, GoUint8)>>( 'restart', ); late final _restart = _restartPtr.asFunction Function(ffi.Pointer, int)>(); ffi.Pointer GetServerPublicKey() { return _GetServerPublicKey(); } late final _GetServerPublicKeyPtr = _lookup Function()>>( 'GetServerPublicKey', ); late final _GetServerPublicKey = _GetServerPublicKeyPtr.asFunction Function()>(); ffi.Pointer AddGrpcClientPublicKey(ffi.Pointer clientPublicKey) { return _AddGrpcClientPublicKey(clientPublicKey); } late final _AddGrpcClientPublicKeyPtr = _lookup Function(ffi.Pointer)>>('AddGrpcClientPublicKey'); late final _AddGrpcClientPublicKey = _AddGrpcClientPublicKeyPtr.asFunction Function(ffi.Pointer)>(); void closeGrpc(int mode) { return _closeGrpc(mode); } late final _closeGrpcPtr = _lookup>('closeGrpc'); late final _closeGrpc = _closeGrpcPtr.asFunction(); ffi.Pointer StartCoreGrpcServer(ffi.Pointer listenAddress) { return _StartCoreGrpcServer(listenAddress); } late final _StartCoreGrpcServerPtr = _lookup Function(ffi.Pointer)>>('StartCoreGrpcServer'); late final _StartCoreGrpcServer = _StartCoreGrpcServerPtr.asFunction Function(ffi.Pointer)>(); } final class __mbstate_t extends ffi.Union { @ffi.Array.multi([128]) external ffi.Array __mbstate8; @ffi.LongLong() external int _mbstateL; } final class __darwin_pthread_handler_rec extends ffi.Struct { external ffi.Pointer)>> __routine; external ffi.Pointer __arg; external ffi.Pointer<__darwin_pthread_handler_rec> __next; } final class _opaque_pthread_attr_t extends ffi.Struct { @ffi.Long() external int __sig; @ffi.Array.multi([56]) external ffi.Array __opaque; } final class _opaque_pthread_cond_t extends ffi.Struct { @ffi.Long() external int __sig; @ffi.Array.multi([40]) external ffi.Array __opaque; } final class _opaque_pthread_condattr_t extends ffi.Struct { @ffi.Long() external int __sig; @ffi.Array.multi([8]) external ffi.Array __opaque; } final class _opaque_pthread_mutex_t extends ffi.Struct { @ffi.Long() external int __sig; @ffi.Array.multi([56]) external ffi.Array __opaque; } final class _opaque_pthread_mutexattr_t extends ffi.Struct { @ffi.Long() external int __sig; @ffi.Array.multi([8]) external ffi.Array __opaque; } final class _opaque_pthread_once_t extends ffi.Struct { @ffi.Long() external int __sig; @ffi.Array.multi([8]) external ffi.Array __opaque; } final class _opaque_pthread_rwlock_t extends ffi.Struct { @ffi.Long() external int __sig; @ffi.Array.multi([192]) external ffi.Array __opaque; } final class _opaque_pthread_rwlockattr_t extends ffi.Struct { @ffi.Long() external int __sig; @ffi.Array.multi([16]) external ffi.Array __opaque; } final class _opaque_pthread_t extends ffi.Struct { @ffi.Long() external int __sig; external ffi.Pointer<__darwin_pthread_handler_rec> __cleanup_stack; @ffi.Array.multi([8176]) external ffi.Array __opaque; } final class _GoString_ extends ffi.Struct { external ffi.Pointer p; @ptrdiff_t() external int n; } typedef ptrdiff_t = __darwin_ptrdiff_t; typedef __darwin_ptrdiff_t = ffi.Long; typedef Dart__darwin_ptrdiff_t = int; enum idtype_t { P_ALL(0), P_PID(1), P_PGID(2); final int value; const idtype_t(this.value); static idtype_t fromValue(int value) => switch (value) { 0 => P_ALL, 1 => P_PID, 2 => P_PGID, _ => throw ArgumentError("Unknown value for idtype_t: $value"), }; } final class __darwin_arm_exception_state extends ffi.Struct { @__uint32_t() external int __exception; @__uint32_t() external int __fsr; @__uint32_t() external int __far; } typedef __uint32_t = ffi.UnsignedInt; typedef Dart__uint32_t = int; final class __darwin_arm_exception_state64 extends ffi.Struct { @__uint64_t() external int __far; @__uint32_t() external int __esr; @__uint32_t() external int __exception; } typedef __uint64_t = ffi.UnsignedLongLong; typedef Dart__uint64_t = int; final class __darwin_arm_thread_state extends ffi.Struct { @ffi.Array.multi([13]) external ffi.Array<__uint32_t> __r; @__uint32_t() external int __sp; @__uint32_t() external int __lr; @__uint32_t() external int __pc; @__uint32_t() external int __cpsr; } final class __darwin_arm_thread_state64 extends ffi.Struct { @ffi.Array.multi([29]) external ffi.Array<__uint64_t> __x; @__uint64_t() external int __fp; @__uint64_t() external int __lr; @__uint64_t() external int __sp; @__uint64_t() external int __pc; @__uint32_t() external int __cpsr; @__uint32_t() external int __pad; } final class __darwin_arm_vfp_state extends ffi.Struct { @ffi.Array.multi([64]) external ffi.Array<__uint32_t> __r; @__uint32_t() external int __fpscr; } final class __darwin_arm_neon_state64 extends ffi.Opaque {} final class __darwin_arm_neon_state extends ffi.Opaque {} final class __arm_pagein_state extends ffi.Struct { @ffi.Int() external int __pagein_error; } final class __arm_legacy_debug_state extends ffi.Struct { @ffi.Array.multi([16]) external ffi.Array<__uint32_t> __bvr; @ffi.Array.multi([16]) external ffi.Array<__uint32_t> __bcr; @ffi.Array.multi([16]) external ffi.Array<__uint32_t> __wvr; @ffi.Array.multi([16]) external ffi.Array<__uint32_t> __wcr; } final class __darwin_arm_debug_state32 extends ffi.Struct { @ffi.Array.multi([16]) external ffi.Array<__uint32_t> __bvr; @ffi.Array.multi([16]) external ffi.Array<__uint32_t> __bcr; @ffi.Array.multi([16]) external ffi.Array<__uint32_t> __wvr; @ffi.Array.multi([16]) external ffi.Array<__uint32_t> __wcr; @__uint64_t() external int __mdscr_el1; } final class __darwin_arm_debug_state64 extends ffi.Struct { @ffi.Array.multi([16]) external ffi.Array<__uint64_t> __bvr; @ffi.Array.multi([16]) external ffi.Array<__uint64_t> __bcr; @ffi.Array.multi([16]) external ffi.Array<__uint64_t> __wvr; @ffi.Array.multi([16]) external ffi.Array<__uint64_t> __wcr; @__uint64_t() external int __mdscr_el1; } final class __darwin_arm_cpmu_state64 extends ffi.Struct { @ffi.Array.multi([16]) external ffi.Array<__uint64_t> __ctrs; } final class __darwin_mcontext32 extends ffi.Struct { external __darwin_arm_exception_state __es; external __darwin_arm_thread_state __ss; external __darwin_arm_vfp_state __fs; } final class __darwin_mcontext64 extends ffi.Opaque {} final class __darwin_sigaltstack extends ffi.Struct { external ffi.Pointer ss_sp; @__darwin_size_t() external int ss_size; @ffi.Int() external int ss_flags; } typedef __darwin_size_t = ffi.UnsignedLong; typedef Dart__darwin_size_t = int; final class __darwin_ucontext extends ffi.Struct { @ffi.Int() external int uc_onstack; @__darwin_sigset_t() external int uc_sigmask; external __darwin_sigaltstack uc_stack; external ffi.Pointer<__darwin_ucontext> uc_link; @__darwin_size_t() external int uc_mcsize; external ffi.Pointer<__darwin_mcontext64> uc_mcontext; } typedef __darwin_sigset_t = __uint32_t; final class sigval extends ffi.Union { @ffi.Int() external int sival_int; external ffi.Pointer sival_ptr; } final class sigevent extends ffi.Struct { @ffi.Int() external int sigev_notify; @ffi.Int() external int sigev_signo; external sigval sigev_value; external ffi.Pointer> sigev_notify_function; external ffi.Pointer sigev_notify_attributes; } typedef pthread_attr_t = __darwin_pthread_attr_t; typedef __darwin_pthread_attr_t = _opaque_pthread_attr_t; final class __siginfo extends ffi.Struct { @ffi.Int() external int si_signo; @ffi.Int() external int si_errno; @ffi.Int() external int si_code; @pid_t() external int si_pid; @uid_t() external int si_uid; @ffi.Int() external int si_status; external ffi.Pointer si_addr; external sigval si_value; @ffi.Long() external int si_band; @ffi.Array.multi([7]) external ffi.Array __pad; } typedef pid_t = __darwin_pid_t; typedef __darwin_pid_t = __int32_t; typedef __int32_t = ffi.Int; typedef Dart__int32_t = int; typedef uid_t = __darwin_uid_t; typedef __darwin_uid_t = __uint32_t; final class __sigaction_u extends ffi.Union { external ffi.Pointer> __sa_handler; external ffi.Pointer, ffi.Pointer)>> __sa_sigaction; } final class __sigaction extends ffi.Struct { external __sigaction_u __sigaction_u1; external ffi.Pointer< ffi.NativeFunction< ffi.Void Function(ffi.Pointer, ffi.Int, ffi.Int, ffi.Pointer, ffi.Pointer) > > sa_tramp; @sigset_t() external int sa_mask; @ffi.Int() external int sa_flags; } typedef siginfo_t = __siginfo; typedef sigset_t = __darwin_sigset_t; final class sigaction extends ffi.Struct { external __sigaction_u __sigaction_u1; @sigset_t() external int sa_mask; @ffi.Int() external int sa_flags; } final class sigvec extends ffi.Struct { external ffi.Pointer> sv_handler; @ffi.Int() external int sv_mask; @ffi.Int() external int sv_flags; } final class sigstack extends ffi.Struct { external ffi.Pointer ss_sp; @ffi.Int() external int ss_onstack; } final class timeval extends ffi.Struct { @__darwin_time_t() external int tv_sec; @__darwin_suseconds_t() external int tv_usec; } typedef __darwin_time_t = ffi.Long; typedef Dart__darwin_time_t = int; typedef __darwin_suseconds_t = __int32_t; final class rusage extends ffi.Struct { external timeval ru_utime; external timeval ru_stime; @ffi.Long() external int ru_maxrss; @ffi.Long() external int ru_ixrss; @ffi.Long() external int ru_idrss; @ffi.Long() external int ru_isrss; @ffi.Long() external int ru_minflt; @ffi.Long() external int ru_majflt; @ffi.Long() external int ru_nswap; @ffi.Long() external int ru_inblock; @ffi.Long() external int ru_oublock; @ffi.Long() external int ru_msgsnd; @ffi.Long() external int ru_msgrcv; @ffi.Long() external int ru_nsignals; @ffi.Long() external int ru_nvcsw; @ffi.Long() external int ru_nivcsw; } final class rusage_info_v0 extends ffi.Struct { @ffi.Array.multi([16]) external ffi.Array ri_uuid; @ffi.Uint64() external int ri_user_time; @ffi.Uint64() external int ri_system_time; @ffi.Uint64() external int ri_pkg_idle_wkups; @ffi.Uint64() external int ri_interrupt_wkups; @ffi.Uint64() external int ri_pageins; @ffi.Uint64() external int ri_wired_size; @ffi.Uint64() external int ri_resident_size; @ffi.Uint64() external int ri_phys_footprint; @ffi.Uint64() external int ri_proc_start_abstime; @ffi.Uint64() external int ri_proc_exit_abstime; } final class rusage_info_v1 extends ffi.Struct { @ffi.Array.multi([16]) external ffi.Array ri_uuid; @ffi.Uint64() external int ri_user_time; @ffi.Uint64() external int ri_system_time; @ffi.Uint64() external int ri_pkg_idle_wkups; @ffi.Uint64() external int ri_interrupt_wkups; @ffi.Uint64() external int ri_pageins; @ffi.Uint64() external int ri_wired_size; @ffi.Uint64() external int ri_resident_size; @ffi.Uint64() external int ri_phys_footprint; @ffi.Uint64() external int ri_proc_start_abstime; @ffi.Uint64() external int ri_proc_exit_abstime; @ffi.Uint64() external int ri_child_user_time; @ffi.Uint64() external int ri_child_system_time; @ffi.Uint64() external int ri_child_pkg_idle_wkups; @ffi.Uint64() external int ri_child_interrupt_wkups; @ffi.Uint64() external int ri_child_pageins; @ffi.Uint64() external int ri_child_elapsed_abstime; } final class rusage_info_v2 extends ffi.Struct { @ffi.Array.multi([16]) external ffi.Array ri_uuid; @ffi.Uint64() external int ri_user_time; @ffi.Uint64() external int ri_system_time; @ffi.Uint64() external int ri_pkg_idle_wkups; @ffi.Uint64() external int ri_interrupt_wkups; @ffi.Uint64() external int ri_pageins; @ffi.Uint64() external int ri_wired_size; @ffi.Uint64() external int ri_resident_size; @ffi.Uint64() external int ri_phys_footprint; @ffi.Uint64() external int ri_proc_start_abstime; @ffi.Uint64() external int ri_proc_exit_abstime; @ffi.Uint64() external int ri_child_user_time; @ffi.Uint64() external int ri_child_system_time; @ffi.Uint64() external int ri_child_pkg_idle_wkups; @ffi.Uint64() external int ri_child_interrupt_wkups; @ffi.Uint64() external int ri_child_pageins; @ffi.Uint64() external int ri_child_elapsed_abstime; @ffi.Uint64() external int ri_diskio_bytesread; @ffi.Uint64() external int ri_diskio_byteswritten; } final class rusage_info_v3 extends ffi.Struct { @ffi.Array.multi([16]) external ffi.Array ri_uuid; @ffi.Uint64() external int ri_user_time; @ffi.Uint64() external int ri_system_time; @ffi.Uint64() external int ri_pkg_idle_wkups; @ffi.Uint64() external int ri_interrupt_wkups; @ffi.Uint64() external int ri_pageins; @ffi.Uint64() external int ri_wired_size; @ffi.Uint64() external int ri_resident_size; @ffi.Uint64() external int ri_phys_footprint; @ffi.Uint64() external int ri_proc_start_abstime; @ffi.Uint64() external int ri_proc_exit_abstime; @ffi.Uint64() external int ri_child_user_time; @ffi.Uint64() external int ri_child_system_time; @ffi.Uint64() external int ri_child_pkg_idle_wkups; @ffi.Uint64() external int ri_child_interrupt_wkups; @ffi.Uint64() external int ri_child_pageins; @ffi.Uint64() external int ri_child_elapsed_abstime; @ffi.Uint64() external int ri_diskio_bytesread; @ffi.Uint64() external int ri_diskio_byteswritten; @ffi.Uint64() external int ri_cpu_time_qos_default; @ffi.Uint64() external int ri_cpu_time_qos_maintenance; @ffi.Uint64() external int ri_cpu_time_qos_background; @ffi.Uint64() external int ri_cpu_time_qos_utility; @ffi.Uint64() external int ri_cpu_time_qos_legacy; @ffi.Uint64() external int ri_cpu_time_qos_user_initiated; @ffi.Uint64() external int ri_cpu_time_qos_user_interactive; @ffi.Uint64() external int ri_billed_system_time; @ffi.Uint64() external int ri_serviced_system_time; } final class rusage_info_v4 extends ffi.Struct { @ffi.Array.multi([16]) external ffi.Array ri_uuid; @ffi.Uint64() external int ri_user_time; @ffi.Uint64() external int ri_system_time; @ffi.Uint64() external int ri_pkg_idle_wkups; @ffi.Uint64() external int ri_interrupt_wkups; @ffi.Uint64() external int ri_pageins; @ffi.Uint64() external int ri_wired_size; @ffi.Uint64() external int ri_resident_size; @ffi.Uint64() external int ri_phys_footprint; @ffi.Uint64() external int ri_proc_start_abstime; @ffi.Uint64() external int ri_proc_exit_abstime; @ffi.Uint64() external int ri_child_user_time; @ffi.Uint64() external int ri_child_system_time; @ffi.Uint64() external int ri_child_pkg_idle_wkups; @ffi.Uint64() external int ri_child_interrupt_wkups; @ffi.Uint64() external int ri_child_pageins; @ffi.Uint64() external int ri_child_elapsed_abstime; @ffi.Uint64() external int ri_diskio_bytesread; @ffi.Uint64() external int ri_diskio_byteswritten; @ffi.Uint64() external int ri_cpu_time_qos_default; @ffi.Uint64() external int ri_cpu_time_qos_maintenance; @ffi.Uint64() external int ri_cpu_time_qos_background; @ffi.Uint64() external int ri_cpu_time_qos_utility; @ffi.Uint64() external int ri_cpu_time_qos_legacy; @ffi.Uint64() external int ri_cpu_time_qos_user_initiated; @ffi.Uint64() external int ri_cpu_time_qos_user_interactive; @ffi.Uint64() external int ri_billed_system_time; @ffi.Uint64() external int ri_serviced_system_time; @ffi.Uint64() external int ri_logical_writes; @ffi.Uint64() external int ri_lifetime_max_phys_footprint; @ffi.Uint64() external int ri_instructions; @ffi.Uint64() external int ri_cycles; @ffi.Uint64() external int ri_billed_energy; @ffi.Uint64() external int ri_serviced_energy; @ffi.Uint64() external int ri_interval_max_phys_footprint; @ffi.Uint64() external int ri_runnable_time; } final class rusage_info_v5 extends ffi.Struct { @ffi.Array.multi([16]) external ffi.Array ri_uuid; @ffi.Uint64() external int ri_user_time; @ffi.Uint64() external int ri_system_time; @ffi.Uint64() external int ri_pkg_idle_wkups; @ffi.Uint64() external int ri_interrupt_wkups; @ffi.Uint64() external int ri_pageins; @ffi.Uint64() external int ri_wired_size; @ffi.Uint64() external int ri_resident_size; @ffi.Uint64() external int ri_phys_footprint; @ffi.Uint64() external int ri_proc_start_abstime; @ffi.Uint64() external int ri_proc_exit_abstime; @ffi.Uint64() external int ri_child_user_time; @ffi.Uint64() external int ri_child_system_time; @ffi.Uint64() external int ri_child_pkg_idle_wkups; @ffi.Uint64() external int ri_child_interrupt_wkups; @ffi.Uint64() external int ri_child_pageins; @ffi.Uint64() external int ri_child_elapsed_abstime; @ffi.Uint64() external int ri_diskio_bytesread; @ffi.Uint64() external int ri_diskio_byteswritten; @ffi.Uint64() external int ri_cpu_time_qos_default; @ffi.Uint64() external int ri_cpu_time_qos_maintenance; @ffi.Uint64() external int ri_cpu_time_qos_background; @ffi.Uint64() external int ri_cpu_time_qos_utility; @ffi.Uint64() external int ri_cpu_time_qos_legacy; @ffi.Uint64() external int ri_cpu_time_qos_user_initiated; @ffi.Uint64() external int ri_cpu_time_qos_user_interactive; @ffi.Uint64() external int ri_billed_system_time; @ffi.Uint64() external int ri_serviced_system_time; @ffi.Uint64() external int ri_logical_writes; @ffi.Uint64() external int ri_lifetime_max_phys_footprint; @ffi.Uint64() external int ri_instructions; @ffi.Uint64() external int ri_cycles; @ffi.Uint64() external int ri_billed_energy; @ffi.Uint64() external int ri_serviced_energy; @ffi.Uint64() external int ri_interval_max_phys_footprint; @ffi.Uint64() external int ri_runnable_time; @ffi.Uint64() external int ri_flags; } final class rusage_info_v6 extends ffi.Struct { @ffi.Array.multi([16]) external ffi.Array ri_uuid; @ffi.Uint64() external int ri_user_time; @ffi.Uint64() external int ri_system_time; @ffi.Uint64() external int ri_pkg_idle_wkups; @ffi.Uint64() external int ri_interrupt_wkups; @ffi.Uint64() external int ri_pageins; @ffi.Uint64() external int ri_wired_size; @ffi.Uint64() external int ri_resident_size; @ffi.Uint64() external int ri_phys_footprint; @ffi.Uint64() external int ri_proc_start_abstime; @ffi.Uint64() external int ri_proc_exit_abstime; @ffi.Uint64() external int ri_child_user_time; @ffi.Uint64() external int ri_child_system_time; @ffi.Uint64() external int ri_child_pkg_idle_wkups; @ffi.Uint64() external int ri_child_interrupt_wkups; @ffi.Uint64() external int ri_child_pageins; @ffi.Uint64() external int ri_child_elapsed_abstime; @ffi.Uint64() external int ri_diskio_bytesread; @ffi.Uint64() external int ri_diskio_byteswritten; @ffi.Uint64() external int ri_cpu_time_qos_default; @ffi.Uint64() external int ri_cpu_time_qos_maintenance; @ffi.Uint64() external int ri_cpu_time_qos_background; @ffi.Uint64() external int ri_cpu_time_qos_utility; @ffi.Uint64() external int ri_cpu_time_qos_legacy; @ffi.Uint64() external int ri_cpu_time_qos_user_initiated; @ffi.Uint64() external int ri_cpu_time_qos_user_interactive; @ffi.Uint64() external int ri_billed_system_time; @ffi.Uint64() external int ri_serviced_system_time; @ffi.Uint64() external int ri_logical_writes; @ffi.Uint64() external int ri_lifetime_max_phys_footprint; @ffi.Uint64() external int ri_instructions; @ffi.Uint64() external int ri_cycles; @ffi.Uint64() external int ri_billed_energy; @ffi.Uint64() external int ri_serviced_energy; @ffi.Uint64() external int ri_interval_max_phys_footprint; @ffi.Uint64() external int ri_runnable_time; @ffi.Uint64() external int ri_flags; @ffi.Uint64() external int ri_user_ptime; @ffi.Uint64() external int ri_system_ptime; @ffi.Uint64() external int ri_pinstructions; @ffi.Uint64() external int ri_pcycles; @ffi.Uint64() external int ri_energy_nj; @ffi.Uint64() external int ri_penergy_nj; @ffi.Uint64() external int ri_secure_time_in_system; @ffi.Uint64() external int ri_secure_ptime_in_system; @ffi.Array.multi([12]) external ffi.Array ri_reserved; } final class rlimit extends ffi.Struct { @rlim_t() external int rlim_cur; @rlim_t() external int rlim_max; } typedef rlim_t = __uint64_t; final class proc_rlimit_control_wakeupmon extends ffi.Struct { @ffi.Uint32() external int wm_flags; @ffi.Int32() external int wm_rate; } typedef id_t = __darwin_id_t; typedef __darwin_id_t = __uint32_t; @ffi.Packed(1) final class _OSUnalignedU16 extends ffi.Struct { @ffi.Uint16() external int __val; } @ffi.Packed(1) final class _OSUnalignedU32 extends ffi.Struct { @ffi.Uint32() external int __val; } @ffi.Packed(1) final class _OSUnalignedU64 extends ffi.Struct { @ffi.Uint64() external int __val; } final class wait extends ffi.Opaque {} final class div_t extends ffi.Struct { @ffi.Int() external int quot; @ffi.Int() external int rem; } final class ldiv_t extends ffi.Struct { @ffi.Long() external int quot; @ffi.Long() external int rem; } final class lldiv_t extends ffi.Struct { @ffi.LongLong() external int quot; @ffi.LongLong() external int rem; } typedef malloc_type_id_t = ffi.UnsignedLongLong; typedef Dartmalloc_type_id_t = int; final class _malloc_zone_t extends ffi.Opaque {} typedef malloc_zone_t = _malloc_zone_t; typedef dev_t = __darwin_dev_t; typedef __darwin_dev_t = __int32_t; typedef mode_t = __darwin_mode_t; typedef __darwin_mode_t = __uint16_t; typedef __uint16_t = ffi.UnsignedShort; typedef Dart__uint16_t = int; typedef pthread_t = __darwin_pthread_t; typedef __darwin_pthread_t = ffi.Pointer<_opaque_pthread_t>; typedef stack_t = __darwin_sigaltstack; final class GoInterface extends ffi.Struct { external ffi.Pointer t; external ffi.Pointer v; } final class GoSlice extends ffi.Struct { external ffi.Pointer data; @GoInt() external int len; @GoInt() external int cap; } typedef GoInt = GoInt64; typedef GoInt64 = ffi.LongLong; typedef DartGoInt64 = int; typedef GoUint8 = ffi.UnsignedChar; typedef DartGoUint8 = int; const int __has_safe_buffers = 1; const int __DARWIN_ONLY_64_BIT_INO_T = 1; const int __DARWIN_ONLY_UNIX_CONFORMANCE = 1; const int __DARWIN_ONLY_VERS_1050 = 1; const int __DARWIN_UNIX03 = 1; const int __DARWIN_64_BIT_INO_T = 1; const int __DARWIN_VERS_1050 = 1; const int __DARWIN_NON_CANCELABLE = 0; const String __DARWIN_SUF_EXTSN = '\$DARWIN_EXTSN'; const int __DARWIN_C_ANSI = 4096; const int __DARWIN_C_FULL = 900000; const int __DARWIN_C_LEVEL = 900000; const int __STDC_WANT_LIB_EXT1__ = 1; const int __DARWIN_NO_LONG_LONG = 0; const int _DARWIN_FEATURE_64_BIT_INODE = 1; const int _DARWIN_FEATURE_ONLY_64_BIT_INODE = 1; const int _DARWIN_FEATURE_ONLY_VERS_1050 = 1; const int _DARWIN_FEATURE_ONLY_UNIX_CONFORMANCE = 1; const int _DARWIN_FEATURE_UNIX_CONFORMANCE = 3; const int __has_ptrcheck = 0; const int __DARWIN_NULL = 0; const int __PTHREAD_SIZE__ = 8176; const int __PTHREAD_ATTR_SIZE__ = 56; const int __PTHREAD_MUTEXATTR_SIZE__ = 8; const int __PTHREAD_MUTEX_SIZE__ = 56; const int __PTHREAD_CONDATTR_SIZE__ = 8; const int __PTHREAD_COND_SIZE__ = 40; const int __PTHREAD_ONCE_SIZE__ = 8; const int __PTHREAD_RWLOCK_SIZE__ = 192; const int __PTHREAD_RWLOCKATTR_SIZE__ = 16; const int __DARWIN_WCHAR_MAX = 2147483647; const int __DARWIN_WCHAR_MIN = -2147483648; const int __DARWIN_WEOF = -1; const int _FORTIFY_SOURCE = 2; const int NULL = 0; const int USER_ADDR_NULL = 0; const int __API_TO_BE_DEPRECATED = 100000; const int __API_TO_BE_DEPRECATED_MACOS = 100000; const int __API_TO_BE_DEPRECATED_IOS = 100000; const int __API_TO_BE_DEPRECATED_MACCATALYST = 100000; const int __API_TO_BE_DEPRECATED_WATCHOS = 100000; const int __API_TO_BE_DEPRECATED_TVOS = 100000; const int __API_TO_BE_DEPRECATED_DRIVERKIT = 100000; const int __API_TO_BE_DEPRECATED_VISIONOS = 100000; const int __MAC_10_0 = 1000; const int __MAC_10_1 = 1010; const int __MAC_10_2 = 1020; const int __MAC_10_3 = 1030; const int __MAC_10_4 = 1040; const int __MAC_10_5 = 1050; const int __MAC_10_6 = 1060; const int __MAC_10_7 = 1070; const int __MAC_10_8 = 1080; const int __MAC_10_9 = 1090; const int __MAC_10_10 = 101000; const int __MAC_10_10_2 = 101002; const int __MAC_10_10_3 = 101003; const int __MAC_10_11 = 101100; const int __MAC_10_11_2 = 101102; const int __MAC_10_11_3 = 101103; const int __MAC_10_11_4 = 101104; const int __MAC_10_12 = 101200; const int __MAC_10_12_1 = 101201; const int __MAC_10_12_2 = 101202; const int __MAC_10_12_4 = 101204; const int __MAC_10_13 = 101300; const int __MAC_10_13_1 = 101301; const int __MAC_10_13_2 = 101302; const int __MAC_10_13_4 = 101304; const int __MAC_10_14 = 101400; const int __MAC_10_14_1 = 101401; const int __MAC_10_14_4 = 101404; const int __MAC_10_14_5 = 101405; const int __MAC_10_14_6 = 101406; const int __MAC_10_15 = 101500; const int __MAC_10_15_1 = 101501; const int __MAC_10_15_4 = 101504; const int __MAC_10_16 = 101600; const int __MAC_11_0 = 110000; const int __MAC_11_1 = 110100; const int __MAC_11_3 = 110300; const int __MAC_11_4 = 110400; const int __MAC_11_5 = 110500; const int __MAC_11_6 = 110600; const int __MAC_12_0 = 120000; const int __MAC_12_1 = 120100; const int __MAC_12_2 = 120200; const int __MAC_12_3 = 120300; const int __MAC_12_4 = 120400; const int __MAC_12_5 = 120500; const int __MAC_12_6 = 120600; const int __MAC_12_7 = 120700; const int __MAC_13_0 = 130000; const int __MAC_13_1 = 130100; const int __MAC_13_2 = 130200; const int __MAC_13_3 = 130300; const int __MAC_13_4 = 130400; const int __MAC_13_5 = 130500; const int __MAC_13_6 = 130600; const int __MAC_14_0 = 140000; const int __MAC_14_1 = 140100; const int __MAC_14_2 = 140200; const int __MAC_14_3 = 140300; const int __MAC_14_4 = 140400; const int __MAC_14_5 = 140500; const int __IPHONE_2_0 = 20000; const int __IPHONE_2_1 = 20100; const int __IPHONE_2_2 = 20200; const int __IPHONE_3_0 = 30000; const int __IPHONE_3_1 = 30100; const int __IPHONE_3_2 = 30200; const int __IPHONE_4_0 = 40000; const int __IPHONE_4_1 = 40100; const int __IPHONE_4_2 = 40200; const int __IPHONE_4_3 = 40300; const int __IPHONE_5_0 = 50000; const int __IPHONE_5_1 = 50100; const int __IPHONE_6_0 = 60000; const int __IPHONE_6_1 = 60100; const int __IPHONE_7_0 = 70000; const int __IPHONE_7_1 = 70100; const int __IPHONE_8_0 = 80000; const int __IPHONE_8_1 = 80100; const int __IPHONE_8_2 = 80200; const int __IPHONE_8_3 = 80300; const int __IPHONE_8_4 = 80400; const int __IPHONE_9_0 = 90000; const int __IPHONE_9_1 = 90100; const int __IPHONE_9_2 = 90200; const int __IPHONE_9_3 = 90300; const int __IPHONE_10_0 = 100000; const int __IPHONE_10_1 = 100100; const int __IPHONE_10_2 = 100200; const int __IPHONE_10_3 = 100300; const int __IPHONE_11_0 = 110000; const int __IPHONE_11_1 = 110100; const int __IPHONE_11_2 = 110200; const int __IPHONE_11_3 = 110300; const int __IPHONE_11_4 = 110400; const int __IPHONE_12_0 = 120000; const int __IPHONE_12_1 = 120100; const int __IPHONE_12_2 = 120200; const int __IPHONE_12_3 = 120300; const int __IPHONE_12_4 = 120400; const int __IPHONE_13_0 = 130000; const int __IPHONE_13_1 = 130100; const int __IPHONE_13_2 = 130200; const int __IPHONE_13_3 = 130300; const int __IPHONE_13_4 = 130400; const int __IPHONE_13_5 = 130500; const int __IPHONE_13_6 = 130600; const int __IPHONE_13_7 = 130700; const int __IPHONE_14_0 = 140000; const int __IPHONE_14_1 = 140100; const int __IPHONE_14_2 = 140200; const int __IPHONE_14_3 = 140300; const int __IPHONE_14_5 = 140500; const int __IPHONE_14_4 = 140400; const int __IPHONE_14_6 = 140600; const int __IPHONE_14_7 = 140700; const int __IPHONE_14_8 = 140800; const int __IPHONE_15_0 = 150000; const int __IPHONE_15_1 = 150100; const int __IPHONE_15_2 = 150200; const int __IPHONE_15_3 = 150300; const int __IPHONE_15_4 = 150400; const int __IPHONE_15_5 = 150500; const int __IPHONE_15_6 = 150600; const int __IPHONE_15_7 = 150700; const int __IPHONE_15_8 = 150800; const int __IPHONE_16_0 = 160000; const int __IPHONE_16_1 = 160100; const int __IPHONE_16_2 = 160200; const int __IPHONE_16_3 = 160300; const int __IPHONE_16_4 = 160400; const int __IPHONE_16_5 = 160500; const int __IPHONE_16_6 = 160600; const int __IPHONE_16_7 = 160700; const int __IPHONE_17_0 = 170000; const int __IPHONE_17_1 = 170100; const int __IPHONE_17_2 = 170200; const int __IPHONE_17_3 = 170300; const int __IPHONE_17_4 = 170400; const int __IPHONE_17_5 = 170500; const int __WATCHOS_1_0 = 10000; const int __WATCHOS_2_0 = 20000; const int __WATCHOS_2_1 = 20100; const int __WATCHOS_2_2 = 20200; const int __WATCHOS_3_0 = 30000; const int __WATCHOS_3_1 = 30100; const int __WATCHOS_3_1_1 = 30101; const int __WATCHOS_3_2 = 30200; const int __WATCHOS_4_0 = 40000; const int __WATCHOS_4_1 = 40100; const int __WATCHOS_4_2 = 40200; const int __WATCHOS_4_3 = 40300; const int __WATCHOS_5_0 = 50000; const int __WATCHOS_5_1 = 50100; const int __WATCHOS_5_2 = 50200; const int __WATCHOS_5_3 = 50300; const int __WATCHOS_6_0 = 60000; const int __WATCHOS_6_1 = 60100; const int __WATCHOS_6_2 = 60200; const int __WATCHOS_7_0 = 70000; const int __WATCHOS_7_1 = 70100; const int __WATCHOS_7_2 = 70200; const int __WATCHOS_7_3 = 70300; const int __WATCHOS_7_4 = 70400; const int __WATCHOS_7_5 = 70500; const int __WATCHOS_7_6 = 70600; const int __WATCHOS_8_0 = 80000; const int __WATCHOS_8_1 = 80100; const int __WATCHOS_8_3 = 80300; const int __WATCHOS_8_4 = 80400; const int __WATCHOS_8_5 = 80500; const int __WATCHOS_8_6 = 80600; const int __WATCHOS_8_7 = 80700; const int __WATCHOS_8_8 = 80800; const int __WATCHOS_9_0 = 90000; const int __WATCHOS_9_1 = 90100; const int __WATCHOS_9_2 = 90200; const int __WATCHOS_9_3 = 90300; const int __WATCHOS_9_4 = 90400; const int __WATCHOS_9_5 = 90500; const int __WATCHOS_9_6 = 90600; const int __WATCHOS_10_0 = 100000; const int __WATCHOS_10_1 = 100100; const int __WATCHOS_10_2 = 100200; const int __WATCHOS_10_3 = 100300; const int __WATCHOS_10_4 = 100400; const int __WATCHOS_10_5 = 100500; const int __TVOS_9_0 = 90000; const int __TVOS_9_1 = 90100; const int __TVOS_9_2 = 90200; const int __TVOS_10_0 = 100000; const int __TVOS_10_0_1 = 100001; const int __TVOS_10_1 = 100100; const int __TVOS_10_2 = 100200; const int __TVOS_11_0 = 110000; const int __TVOS_11_1 = 110100; const int __TVOS_11_2 = 110200; const int __TVOS_11_3 = 110300; const int __TVOS_11_4 = 110400; const int __TVOS_12_0 = 120000; const int __TVOS_12_1 = 120100; const int __TVOS_12_2 = 120200; const int __TVOS_12_3 = 120300; const int __TVOS_12_4 = 120400; const int __TVOS_13_0 = 130000; const int __TVOS_13_2 = 130200; const int __TVOS_13_3 = 130300; const int __TVOS_13_4 = 130400; const int __TVOS_14_0 = 140000; const int __TVOS_14_1 = 140100; const int __TVOS_14_2 = 140200; const int __TVOS_14_3 = 140300; const int __TVOS_14_5 = 140500; const int __TVOS_14_6 = 140600; const int __TVOS_14_7 = 140700; const int __TVOS_15_0 = 150000; const int __TVOS_15_1 = 150100; const int __TVOS_15_2 = 150200; const int __TVOS_15_3 = 150300; const int __TVOS_15_4 = 150400; const int __TVOS_15_5 = 150500; const int __TVOS_15_6 = 150600; const int __TVOS_16_0 = 160000; const int __TVOS_16_1 = 160100; const int __TVOS_16_2 = 160200; const int __TVOS_16_3 = 160300; const int __TVOS_16_4 = 160400; const int __TVOS_16_5 = 160500; const int __TVOS_16_6 = 160600; const int __TVOS_17_0 = 170000; const int __TVOS_17_1 = 170100; const int __TVOS_17_2 = 170200; const int __TVOS_17_3 = 170300; const int __TVOS_17_4 = 170400; const int __TVOS_17_5 = 170500; const int __BRIDGEOS_2_0 = 20000; const int __BRIDGEOS_3_0 = 30000; const int __BRIDGEOS_3_1 = 30100; const int __BRIDGEOS_3_4 = 30400; const int __BRIDGEOS_4_0 = 40000; const int __BRIDGEOS_4_1 = 40100; const int __BRIDGEOS_5_0 = 50000; const int __BRIDGEOS_5_1 = 50100; const int __BRIDGEOS_5_3 = 50300; const int __BRIDGEOS_6_0 = 60000; const int __BRIDGEOS_6_2 = 60200; const int __BRIDGEOS_6_4 = 60400; const int __BRIDGEOS_6_5 = 60500; const int __BRIDGEOS_6_6 = 60600; const int __BRIDGEOS_7_0 = 70000; const int __BRIDGEOS_7_1 = 70100; const int __BRIDGEOS_7_2 = 70200; const int __BRIDGEOS_7_3 = 70300; const int __BRIDGEOS_7_4 = 70400; const int __BRIDGEOS_7_6 = 70600; const int __BRIDGEOS_8_0 = 80000; const int __BRIDGEOS_8_1 = 80100; const int __BRIDGEOS_8_2 = 80200; const int __BRIDGEOS_8_3 = 80300; const int __BRIDGEOS_8_4 = 80400; const int __BRIDGEOS_8_5 = 80500; const int __DRIVERKIT_19_0 = 190000; const int __DRIVERKIT_20_0 = 200000; const int __DRIVERKIT_21_0 = 210000; const int __DRIVERKIT_22_0 = 220000; const int __DRIVERKIT_22_4 = 220400; const int __DRIVERKIT_22_5 = 220500; const int __DRIVERKIT_22_6 = 220600; const int __DRIVERKIT_23_0 = 230000; const int __DRIVERKIT_23_1 = 230100; const int __DRIVERKIT_23_2 = 230200; const int __DRIVERKIT_23_3 = 230300; const int __DRIVERKIT_23_4 = 230400; const int __DRIVERKIT_23_5 = 230500; const int __VISIONOS_1_0 = 10000; const int __VISIONOS_1_1 = 10100; const int __VISIONOS_1_2 = 10200; const int MAC_OS_X_VERSION_10_0 = 1000; const int MAC_OS_X_VERSION_10_1 = 1010; const int MAC_OS_X_VERSION_10_2 = 1020; const int MAC_OS_X_VERSION_10_3 = 1030; const int MAC_OS_X_VERSION_10_4 = 1040; const int MAC_OS_X_VERSION_10_5 = 1050; const int MAC_OS_X_VERSION_10_6 = 1060; const int MAC_OS_X_VERSION_10_7 = 1070; const int MAC_OS_X_VERSION_10_8 = 1080; const int MAC_OS_X_VERSION_10_9 = 1090; const int MAC_OS_X_VERSION_10_10 = 101000; const int MAC_OS_X_VERSION_10_10_2 = 101002; const int MAC_OS_X_VERSION_10_10_3 = 101003; const int MAC_OS_X_VERSION_10_11 = 101100; const int MAC_OS_X_VERSION_10_11_2 = 101102; const int MAC_OS_X_VERSION_10_11_3 = 101103; const int MAC_OS_X_VERSION_10_11_4 = 101104; const int MAC_OS_X_VERSION_10_12 = 101200; const int MAC_OS_X_VERSION_10_12_1 = 101201; const int MAC_OS_X_VERSION_10_12_2 = 101202; const int MAC_OS_X_VERSION_10_12_4 = 101204; const int MAC_OS_X_VERSION_10_13 = 101300; const int MAC_OS_X_VERSION_10_13_1 = 101301; const int MAC_OS_X_VERSION_10_13_2 = 101302; const int MAC_OS_X_VERSION_10_13_4 = 101304; const int MAC_OS_X_VERSION_10_14 = 101400; const int MAC_OS_X_VERSION_10_14_1 = 101401; const int MAC_OS_X_VERSION_10_14_4 = 101404; const int MAC_OS_X_VERSION_10_14_5 = 101405; const int MAC_OS_X_VERSION_10_14_6 = 101406; const int MAC_OS_X_VERSION_10_15 = 101500; const int MAC_OS_X_VERSION_10_15_1 = 101501; const int MAC_OS_X_VERSION_10_15_4 = 101504; const int MAC_OS_X_VERSION_10_16 = 101600; const int MAC_OS_VERSION_11_0 = 110000; const int MAC_OS_VERSION_11_1 = 110100; const int MAC_OS_VERSION_11_3 = 110300; const int MAC_OS_VERSION_11_4 = 110400; const int MAC_OS_VERSION_11_5 = 110500; const int MAC_OS_VERSION_11_6 = 110600; const int MAC_OS_VERSION_12_0 = 120000; const int MAC_OS_VERSION_12_1 = 120100; const int MAC_OS_VERSION_12_2 = 120200; const int MAC_OS_VERSION_12_3 = 120300; const int MAC_OS_VERSION_12_4 = 120400; const int MAC_OS_VERSION_12_5 = 120500; const int MAC_OS_VERSION_12_6 = 120600; const int MAC_OS_VERSION_12_7 = 120700; const int MAC_OS_VERSION_13_0 = 130000; const int MAC_OS_VERSION_13_1 = 130100; const int MAC_OS_VERSION_13_2 = 130200; const int MAC_OS_VERSION_13_3 = 130300; const int MAC_OS_VERSION_13_4 = 130400; const int MAC_OS_VERSION_13_5 = 130500; const int MAC_OS_VERSION_13_6 = 130600; const int MAC_OS_VERSION_14_0 = 140000; const int MAC_OS_VERSION_14_1 = 140100; const int MAC_OS_VERSION_14_2 = 140200; const int MAC_OS_VERSION_14_3 = 140300; const int MAC_OS_VERSION_14_4 = 140400; const int MAC_OS_VERSION_14_5 = 140500; const int __MAC_OS_X_VERSION_MIN_REQUIRED = 140000; const int __MAC_OS_X_VERSION_MAX_ALLOWED = 140500; const int __ENABLE_LEGACY_MAC_AVAILABILITY = 1; const int __DARWIN_NSIG = 32; const int NSIG = 32; const int _ARM_SIGNAL_ = 1; const int SIGHUP = 1; const int SIGINT = 2; const int SIGQUIT = 3; const int SIGILL = 4; const int SIGTRAP = 5; const int SIGABRT = 6; const int SIGIOT = 6; const int SIGEMT = 7; const int SIGFPE = 8; const int SIGKILL = 9; const int SIGBUS = 10; const int SIGSEGV = 11; const int SIGSYS = 12; const int SIGPIPE = 13; const int SIGALRM = 14; const int SIGTERM = 15; const int SIGURG = 16; const int SIGSTOP = 17; const int SIGTSTP = 18; const int SIGCONT = 19; const int SIGCHLD = 20; const int SIGTTIN = 21; const int SIGTTOU = 22; const int SIGIO = 23; const int SIGXCPU = 24; const int SIGXFSZ = 25; const int SIGVTALRM = 26; const int SIGPROF = 27; const int SIGWINCH = 28; const int SIGINFO = 29; const int SIGUSR1 = 30; const int SIGUSR2 = 31; const int __DARWIN_OPAQUE_ARM_THREAD_STATE64 = 0; const int SIGEV_NONE = 0; const int SIGEV_SIGNAL = 1; const int SIGEV_THREAD = 3; const int ILL_NOOP = 0; const int ILL_ILLOPC = 1; const int ILL_ILLTRP = 2; const int ILL_PRVOPC = 3; const int ILL_ILLOPN = 4; const int ILL_ILLADR = 5; const int ILL_PRVREG = 6; const int ILL_COPROC = 7; const int ILL_BADSTK = 8; const int FPE_NOOP = 0; const int FPE_FLTDIV = 1; const int FPE_FLTOVF = 2; const int FPE_FLTUND = 3; const int FPE_FLTRES = 4; const int FPE_FLTINV = 5; const int FPE_FLTSUB = 6; const int FPE_INTDIV = 7; const int FPE_INTOVF = 8; const int SEGV_NOOP = 0; const int SEGV_MAPERR = 1; const int SEGV_ACCERR = 2; const int BUS_NOOP = 0; const int BUS_ADRALN = 1; const int BUS_ADRERR = 2; const int BUS_OBJERR = 3; const int TRAP_BRKPT = 1; const int TRAP_TRACE = 2; const int CLD_NOOP = 0; const int CLD_EXITED = 1; const int CLD_KILLED = 2; const int CLD_DUMPED = 3; const int CLD_TRAPPED = 4; const int CLD_STOPPED = 5; const int CLD_CONTINUED = 6; const int POLL_IN = 1; const int POLL_OUT = 2; const int POLL_MSG = 3; const int POLL_ERR = 4; const int POLL_PRI = 5; const int POLL_HUP = 6; const int SA_ONSTACK = 1; const int SA_RESTART = 2; const int SA_RESETHAND = 4; const int SA_NOCLDSTOP = 8; const int SA_NODEFER = 16; const int SA_NOCLDWAIT = 32; const int SA_SIGINFO = 64; const int SA_USERTRAMP = 256; const int SA_64REGSET = 512; const int SA_USERSPACE_MASK = 127; const int SIG_BLOCK = 1; const int SIG_UNBLOCK = 2; const int SIG_SETMASK = 3; const int SI_USER = 65537; const int SI_QUEUE = 65538; const int SI_TIMER = 65539; const int SI_ASYNCIO = 65540; const int SI_MESGQ = 65541; const int SS_ONSTACK = 1; const int SS_DISABLE = 4; const int MINSIGSTKSZ = 32768; const int SIGSTKSZ = 131072; const int SV_ONSTACK = 1; const int SV_INTERRUPT = 2; const int SV_RESETHAND = 4; const int SV_NODEFER = 16; const int SV_NOCLDSTOP = 8; const int SV_SIGINFO = 64; const int __WORDSIZE = 64; const int INT8_MAX = 127; const int INT16_MAX = 32767; const int INT32_MAX = 2147483647; const int INT64_MAX = 9223372036854775807; const int INT8_MIN = -128; const int INT16_MIN = -32768; const int INT32_MIN = -2147483648; const int INT64_MIN = -9223372036854775808; const int UINT8_MAX = 255; const int UINT16_MAX = 65535; const int UINT32_MAX = 4294967295; const int UINT64_MAX = -1; const int INT_LEAST8_MIN = -128; const int INT_LEAST16_MIN = -32768; const int INT_LEAST32_MIN = -2147483648; const int INT_LEAST64_MIN = -9223372036854775808; const int INT_LEAST8_MAX = 127; const int INT_LEAST16_MAX = 32767; const int INT_LEAST32_MAX = 2147483647; const int INT_LEAST64_MAX = 9223372036854775807; const int UINT_LEAST8_MAX = 255; const int UINT_LEAST16_MAX = 65535; const int UINT_LEAST32_MAX = 4294967295; const int UINT_LEAST64_MAX = -1; const int INT_FAST8_MIN = -128; const int INT_FAST16_MIN = -32768; const int INT_FAST32_MIN = -2147483648; const int INT_FAST64_MIN = -9223372036854775808; const int INT_FAST8_MAX = 127; const int INT_FAST16_MAX = 32767; const int INT_FAST32_MAX = 2147483647; const int INT_FAST64_MAX = 9223372036854775807; const int UINT_FAST8_MAX = 255; const int UINT_FAST16_MAX = 65535; const int UINT_FAST32_MAX = 4294967295; const int UINT_FAST64_MAX = -1; const int INTPTR_MAX = 9223372036854775807; const int INTPTR_MIN = -9223372036854775808; const int UINTPTR_MAX = -1; const int INTMAX_MAX = 9223372036854775807; const int UINTMAX_MAX = -1; const int INTMAX_MIN = -9223372036854775808; const int PTRDIFF_MIN = -9223372036854775808; const int PTRDIFF_MAX = 9223372036854775807; const int SIZE_MAX = -1; const int RSIZE_MAX = 9223372036854775807; const int WCHAR_MAX = 2147483647; const int WCHAR_MIN = -2147483648; const int WINT_MIN = -2147483648; const int WINT_MAX = 2147483647; const int SIG_ATOMIC_MIN = -2147483648; const int SIG_ATOMIC_MAX = 2147483647; const int PRIO_PROCESS = 0; const int PRIO_PGRP = 1; const int PRIO_USER = 2; const int PRIO_DARWIN_THREAD = 3; const int PRIO_DARWIN_PROCESS = 4; const int PRIO_MIN = -20; const int PRIO_MAX = 20; const int PRIO_DARWIN_BG = 4096; const int PRIO_DARWIN_NONUI = 4097; const int RUSAGE_SELF = 0; const int RUSAGE_CHILDREN = -1; const int RUSAGE_INFO_V0 = 0; const int RUSAGE_INFO_V1 = 1; const int RUSAGE_INFO_V2 = 2; const int RUSAGE_INFO_V3 = 3; const int RUSAGE_INFO_V4 = 4; const int RUSAGE_INFO_V5 = 5; const int RUSAGE_INFO_V6 = 6; const int RUSAGE_INFO_CURRENT = 6; const int RU_PROC_RUNS_RESLIDE = 1; const int RLIM_INFINITY = 9223372036854775807; const int RLIM_SAVED_MAX = 9223372036854775807; const int RLIM_SAVED_CUR = 9223372036854775807; const int RLIMIT_CPU = 0; const int RLIMIT_FSIZE = 1; const int RLIMIT_DATA = 2; const int RLIMIT_STACK = 3; const int RLIMIT_CORE = 4; const int RLIMIT_AS = 5; const int RLIMIT_RSS = 5; const int RLIMIT_MEMLOCK = 6; const int RLIMIT_NPROC = 7; const int RLIMIT_NOFILE = 8; const int RLIM_NLIMITS = 9; const int _RLIMIT_POSIX_FLAG = 4096; const int RLIMIT_WAKEUPS_MONITOR = 1; const int RLIMIT_CPU_USAGE_MONITOR = 2; const int RLIMIT_THREAD_CPULIMITS = 3; const int RLIMIT_FOOTPRINT_INTERVAL = 4; const int WAKEMON_ENABLE = 1; const int WAKEMON_DISABLE = 2; const int WAKEMON_GET_PARAMS = 4; const int WAKEMON_SET_DEFAULTS = 8; const int WAKEMON_MAKE_FATAL = 16; const int CPUMON_MAKE_FATAL = 4096; const int FOOTPRINT_INTERVAL_RESET = 1; const int IOPOL_TYPE_DISK = 0; const int IOPOL_TYPE_VFS_ATIME_UPDATES = 2; const int IOPOL_TYPE_VFS_MATERIALIZE_DATALESS_FILES = 3; const int IOPOL_TYPE_VFS_STATFS_NO_DATA_VOLUME = 4; const int IOPOL_TYPE_VFS_TRIGGER_RESOLVE = 5; const int IOPOL_TYPE_VFS_IGNORE_CONTENT_PROTECTION = 6; const int IOPOL_TYPE_VFS_IGNORE_PERMISSIONS = 7; const int IOPOL_TYPE_VFS_SKIP_MTIME_UPDATE = 8; const int IOPOL_TYPE_VFS_ALLOW_LOW_SPACE_WRITES = 9; const int IOPOL_TYPE_VFS_DISALLOW_RW_FOR_O_EVTONLY = 10; const int IOPOL_SCOPE_PROCESS = 0; const int IOPOL_SCOPE_THREAD = 1; const int IOPOL_SCOPE_DARWIN_BG = 2; const int IOPOL_DEFAULT = 0; const int IOPOL_IMPORTANT = 1; const int IOPOL_PASSIVE = 2; const int IOPOL_THROTTLE = 3; const int IOPOL_UTILITY = 4; const int IOPOL_STANDARD = 5; const int IOPOL_APPLICATION = 5; const int IOPOL_NORMAL = 1; const int IOPOL_ATIME_UPDATES_DEFAULT = 0; const int IOPOL_ATIME_UPDATES_OFF = 1; const int IOPOL_MATERIALIZE_DATALESS_FILES_DEFAULT = 0; const int IOPOL_MATERIALIZE_DATALESS_FILES_OFF = 1; const int IOPOL_MATERIALIZE_DATALESS_FILES_ON = 2; const int IOPOL_VFS_STATFS_NO_DATA_VOLUME_DEFAULT = 0; const int IOPOL_VFS_STATFS_FORCE_NO_DATA_VOLUME = 1; const int IOPOL_VFS_TRIGGER_RESOLVE_DEFAULT = 0; const int IOPOL_VFS_TRIGGER_RESOLVE_OFF = 1; const int IOPOL_VFS_CONTENT_PROTECTION_DEFAULT = 0; const int IOPOL_VFS_CONTENT_PROTECTION_IGNORE = 1; const int IOPOL_VFS_IGNORE_PERMISSIONS_OFF = 0; const int IOPOL_VFS_IGNORE_PERMISSIONS_ON = 1; const int IOPOL_VFS_SKIP_MTIME_UPDATE_OFF = 0; const int IOPOL_VFS_SKIP_MTIME_UPDATE_ON = 1; const int IOPOL_VFS_ALLOW_LOW_SPACE_WRITES_OFF = 0; const int IOPOL_VFS_ALLOW_LOW_SPACE_WRITES_ON = 1; const int IOPOL_VFS_DISALLOW_RW_FOR_O_EVTONLY_DEFAULT = 0; const int IOPOL_VFS_DISALLOW_RW_FOR_O_EVTONLY_ON = 1; const int IOPOL_VFS_NOCACHE_WRITE_FS_BLKSIZE_DEFAULT = 0; const int IOPOL_VFS_NOCACHE_WRITE_FS_BLKSIZE_ON = 1; const int WNOHANG = 1; const int WUNTRACED = 2; const int WCOREFLAG = 128; const int _WSTOPPED = 127; const int WEXITED = 4; const int WSTOPPED = 8; const int WCONTINUED = 16; const int WNOWAIT = 32; const int WAIT_ANY = -1; const int WAIT_MYPGRP = 0; const int _QUAD_HIGHWORD = 1; const int _QUAD_LOWWORD = 0; const int __DARWIN_LITTLE_ENDIAN = 1234; const int __DARWIN_BIG_ENDIAN = 4321; const int __DARWIN_PDP_ENDIAN = 3412; const int __DARWIN_BYTE_ORDER = 1234; const int LITTLE_ENDIAN = 1234; const int BIG_ENDIAN = 4321; const int PDP_ENDIAN = 3412; const int BYTE_ORDER = 1234; const int EXIT_FAILURE = 1; const int EXIT_SUCCESS = 0; const int RAND_MAX = 2147483647; ================================================ FILE: lib/hiddifycore/core_interface/core_interface.dart ================================================ import 'package:hiddify/core/model/directories.dart'; import 'package:hiddify/hiddifycore/generated/v2/hcore/hcore_service.pbgrpc.dart'; import 'package:hiddify/singbox/model/core_status.dart'; class CoreInterface { late CoreClient fgClient; late CoreClient bgClient; Future setup(Directories directories, bool debug, int mode) async { return ""; } Future setupBackground(String path, String name) async { return const CoreStarted(); } Future restart(String path, String name) async { return false; } Future stop() async { return false; } Future isBgClientAvailable() async { return true; } bool isSingleChannel() { // return true; return fgClient == bgClient; } Future resetTunnel() async { return false; } Future isActiveFg() async { return true; } Future isActiveBg() async { return true; } bool isInitialized() { try { bgClient; // touch it return true; } catch (_) { return false; } } } ================================================ FILE: lib/hiddifycore/core_interface/core_interface_desktop.dart ================================================ import 'dart:ffi'; import 'dart:io'; import 'dart:math'; import 'package:ffi/ffi.dart'; import 'package:grpc/grpc.dart'; import 'package:hiddify/core/model/directories.dart'; import 'package:hiddify/gen/hiddify_core_generated_bindings.dart'; import 'package:hiddify/hiddifycore/core_interface/core_interface.dart'; import 'package:hiddify/hiddifycore/core_interface/mtls_channel_cred.dart'; import 'package:hiddify/hiddifycore/generated/v2/hcore/hcore.pb.dart'; import 'package:hiddify/hiddifycore/generated/v2/hcore/hcore_service.pbgrpc.dart'; import 'package:hiddify/hiddifycore/generated/v2/hello/hello.pb.dart'; import 'package:hiddify/hiddifycore/generated/v2/hello/hello_service.pbgrpc.dart'; import 'package:hiddify/utils/custom_loggers.dart'; import 'package:loggy/loggy.dart'; import 'package:path/path.dart' as p; final _logger = Loggy('HiddifyCoreFFI'); typedef StopFunc = Pointer Function(); typedef StopFuncDart = Pointer Function(); class CoreInterfaceDesktop extends CoreInterface with InfraLogger { static final HiddifyCoreNativeLibrary _box = _gen(); static HiddifyCoreNativeLibrary _gen() { String fullPath = ""; if (Platform.environment.containsKey('FLUTTER_TEST')) { fullPath = "hiddify-core"; } if (Platform.isWindows) { fullPath = p.join(fullPath, "hiddify-core.dll"); } else if (Platform.isMacOS) { fullPath = p.join(fullPath, "hiddify-core.dylib"); } else { fullPath = p.join(fullPath, "hiddify-core.so"); } _logger.debug('hiddify-core native libs path: "$fullPath"'); final lib = DynamicLibrary.open(fullPath); // final stopFunc = lib.lookup>('stop').asFunction(); // final errPtr2 = stopFunc(); // final err = errPtr2.cast().toDartString(); return HiddifyCoreNativeLibrary(lib); } Future isMusl() async { try { final result = await Process.run('ldd', ['--version']); return result.stdout.toString().toLowerCase().contains('musl'); } catch (_) { return false; } } final port = 17078; static String generateRandomPassword(int length) { const characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; final random = Random(); return List.generate(length, (_) => characters[random.nextInt(characters.length)]).join(); } static final String secret = generateRandomPassword(100); @override Future setup(Directories directories, bool debug, int mode) async { // Generate a random password for the grpc service // final errPtr2 = _box.stop(); // final err = errPtr2.cast().toDartString(); // throw Exception('stop: $err'); const channelOption = ChannelCredentials.insecure(); final helloClient = HelloClient( ClientChannel( '127.0.0.1', port: port, options: const ChannelOptions(credentials: channelOption), ), ); try { await helloClient.sayHello(HelloRequest(name: "test")); loggy.info("core is already started!"); } catch (e) { //core is not started yet final errPtr = _box.setup( directories.baseDir.path.toNativeUtf8().cast(), directories.workingDir.path.toNativeUtf8().cast(), directories.tempDir.path.toNativeUtf8().cast(), SetupMode.GRPC_NORMAL_INSECURE.value, "127.0.0.1:$port".toNativeUtf8().cast(), secret.toNativeUtf8().cast(), 0, debug ? 1 : 0, ); final err = errPtr.cast().toDartString(); if (err.isNotEmpty) { return err; } final res = await helloClient.sayHello(HelloRequest(name: "test")); loggy.info(res.toString()); } bgClient = fgClient = CoreClient( ClientChannel( 'localhost', port: port, options: const ChannelOptions( credentials: ChannelCredentials.insecure(), // credentials: ChannelCredentials.secure( // password: secret, // onBadCertificate: (certificate, host) => true, // ), ), ), ); return ""; } @override Future restart(String path, String name) async { return false; } @override Future stop() async { return false; } } ================================================ FILE: lib/hiddifycore/core_interface/core_interface_mobile.dart ================================================ import 'dart:async'; import 'dart:io'; import 'package:basic_utils/basic_utils.dart'; import 'package:flutter/services.dart'; import 'package:grpc/grpc.dart'; import 'package:hiddify/core/model/directories.dart'; import 'package:hiddify/core/utils/laststeam.dart'; import 'package:hiddify/hiddifycore/core_interface/core_interface.dart'; import 'package:hiddify/hiddifycore/core_interface/mtls_channel_cred.dart'; import 'package:hiddify/hiddifycore/generated/v2/hcore/hcore_service.pbgrpc.dart'; import 'package:hiddify/hiddifycore/generated/v2/hello/hello.pb.dart'; import 'package:hiddify/hiddifycore/generated/v2/hello/hello_service.pbgrpc.dart'; import 'package:hiddify/singbox/model/core_status.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:loggy/loggy.dart'; import 'package:rxdart/rxdart.dart'; final _logger = Loggy('FFIHiddifyCoreService'); class CoreInterfaceMobile extends CoreInterface with InfraLogger { static const channelPrefix = "com.hiddify.app"; static const methodChannel = MethodChannel("$channelPrefix/method"); static const statusChannel = EventChannel("$channelPrefix/service.status", JSONMethodCodec()); static const alertsChannel = EventChannel("$channelPrefix/service.alerts", JSONMethodCodec()); late Uint8List serverPublicKey; static final cert = CryptoUtils.generateEcKeyPair(); static const portBack = 17079; static const portFront = 17078; bool _isBgClientAvailable = false; bool _debug = false; late LastStream _status; @override Future setup(Directories directories, bool debug, int mode) async { final channelOption = [1, 2].contains(mode) ? MTLSChannelCredentials(serverPublicKey: serverPublicKey, clientKey: cert) : const ChannelCredentials.insecure(); _debug = debug; final helloClient = HelloClient( ClientChannel( '127.0.0.1', port: portFront, options: ChannelOptions(credentials: channelOption), ), ); final status = statusChannel.receiveBroadcastStream().map(CoreStatus.fromEvent); final alerts = alertsChannel.receiveBroadcastStream().map(CoreStatus.fromEvent); _status = LastStream(ValueConnectableStream(Rx.merge([status, alerts])).autoConnect()); try { await helloClient.sayHello(HelloRequest(name: "test")); loggy.info("core is already started!"); } catch (e) { //core is not started yet await methodChannel.invokeMethod("setup", { "baseDir": directories.baseDir.path, "workingDir": directories.workingDir.path, "tempDir": directories.tempDir.path, "grpcPort": portFront, "mode": mode, "debug": debug, }); final res = await helloClient.sayHello(HelloRequest(name: "test")); loggy.info(res.toString()); } // serverPublicKey = await methodChannel.invokeMethod("get_grpc_server_public_key") ?? Uint8List.fromList([]); // await methodChannel.invokeMethod( // "add_grpc_client_public_key", // { // "clientPublicKey": ascii.encode(CryptoUtils.encodeEcPublicKeyToPem(cert.publicKey as ECPublicKey)), // }, // ); // serverPublicKey = X509Utils.x509CertificateFromPem(String.fromCharCodes(serverPublicKey)); // var chanelOption = ChannelOptions( // credentials: MTLSChannelCredentials(serverPublicKey: serverPublicKey, clientPrivateKey: cert.privateKey as ECPrivateKey), // ); fgClient = CoreClient( ClientChannel( '127.0.0.1', port: portFront, options: ChannelOptions(credentials: channelOption), ), ); bgClient = CoreClient( ClientChannel( '127.0.0.1', port: portBack, options: ChannelOptions(credentials: channelOption), ), ); // await start("/sdcard/Android/data/app.hiddify.com/files/configs/cdc633e9-8cfc-4a67-948d-009f779a5c91.json", "hiddify"); return ""; } @override Future setupBackground(String path, String name) async { // if (!await waitUntilPort(portBack, false, stop)) return const CoreStatus.stopped(alert: CoreAlert.createService); if (!await stop()) return const CoreStatus.stopped(alert: CoreAlert.createService); _status.clean(); await methodChannel.invokeMethod("start", { "path": path, "name": name, "grpcPort": portBack, "startBg": true, "debug": _debug, }); _isBgClientAvailable = true; loggy.info("Waiting for starting core"); for (var i = 0; i < 20; i++) { try { final res = await _status.get(timeout: const Duration(seconds: 1)); switch (res) { case CoreStarted(): break; case CoreStopped(): if (res.alert != null) { return res; } case CoreStopping(): // return res; case CoreStarting(): } await Future.delayed(const Duration(milliseconds: 200)); } on TimeoutException { // just retry } } loggy.info("Waiting for starting core finished"); if (!await waitUntilPort(portBack, true, null, maxTry: 10)) { await stopMethodChannel(); return const CoreStatus.stopped(alert: CoreAlert.startService, message: "starting background core..."); } return const CoreStarted(); } @override Future stop() async { await stopMethodChannel(); if (!await waitUntilPort(portBack, false, null, maxTry: 10)) { return false; } _isBgClientAvailable = false; return true; } Future stopMethodChannel() async { await methodChannel.invokeMethod("stop"); } @override Future isBgClientAvailable() async { return _isBgClientAvailable; } @override Future resetTunnel() async { await methodChannel.invokeMethod("reset"); return true; } @override Future isActiveFg() async { return await isPortOpen("127.0.0.1", portFront); } @override Future isActiveBg() async { return await isPortOpen("127.0.0.1", portBack); } } Future waitUntilPort( int portNumber, bool isOpen, Future Function()? callFunctionAfterEachFail, { int maxTry = 10, }) async { for (var i = 0; i < maxTry; i++) { if (await isPortOpen("127.0.0.1", portNumber) == isOpen) { return true; } if (callFunctionAfterEachFail != null) { await callFunctionAfterEachFail(); } await Future.delayed(const Duration(milliseconds: 200)); } return false; } Future isPortOpen(String host, int port, {Duration timeout = const Duration(milliseconds: 300)}) async { try { final socket = await Socket.connect(host, port, timeout: timeout); await socket.close(); return true; } on SocketException catch (_) { return false; } catch (_) { return false; } } ================================================ FILE: lib/hiddifycore/core_interface/core_interface_wrapper.dart ================================================ import 'dart:io'; import 'package:hiddify/hiddifycore/core_interface/core_interface.dart'; import 'package:hiddify/hiddifycore/core_interface/core_interface_desktop.dart'; import 'package:hiddify/hiddifycore/core_interface/core_interface_mobile.dart'; CoreInterface getCoreInterface() { if (Platform.isAndroid || Platform.isIOS) { return CoreInterfaceMobile(); } return CoreInterfaceDesktop(); } ================================================ FILE: lib/hiddifycore/core_interface/core_interface_wrapper_stub.dart ================================================ import 'package:hiddify/hiddifycore/core_interface/core_interface.dart'; CoreInterface getCoreInterface() { return CoreInterface(); } ================================================ FILE: lib/hiddifycore/core_interface/mtls_channel_cred.dart ================================================ import 'dart:io'; import 'dart:typed_data'; import 'package:basic_utils/basic_utils.dart'; import 'package:grpc/grpc.dart'; class MTLSChannelCredentials extends ChannelCredentials { final SecurityContext ctx = SecurityContext(withTrustedRoots: false); MTLSChannelCredentials({ required Uint8List serverPublicKey, required AsymmetricKeyPair clientKey, }) : super.secure() { // Ensure server public key is in PEM format final serverPublicKeyPem = String.fromCharCodes(serverPublicKey); if (!serverPublicKeyPem.contains('-----BEGIN CERTIFICATE-----')) { throw ArgumentError('Server public key must be in PEM format.'); } // Set the server's public key as trusted ctx.setTrustedCertificatesBytes(serverPublicKey); // Convert the client's private key to PEM format String privateKeyPem; if (clientKey.privateKey is ECPrivateKey) { privateKeyPem = CryptoUtils.encodeEcPrivateKeyToPem(clientKey.privateKey as ECPrivateKey); } else if (clientKey.privateKey is RSAPrivateKey) { privateKeyPem = CryptoUtils.encodeRSAPrivateKeyToPem(clientKey.privateKey as RSAPrivateKey); } else { throw ArgumentError('Unsupported private key type.'); } ctx.usePrivateKeyBytes(Uint8List.fromList(privateKeyPem.codeUnits)); final cert = X509Utils.generateSelfSignedCertificate(clientKey.privateKey, 'CN=Client', 365); ctx.useCertificateChainBytes(Uint8List.fromList(cert.codeUnits)); } @override SecurityContext get securityContext => ctx; @override bool get isSecure => true; } ================================================ FILE: lib/hiddifycore/generated/extension/extension.pb.dart ================================================ /// // Generated code. Do not modify. // source: extension/extension.proto // // @dart = 2.12 // ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name import 'dart:core' as $core; import 'package:protobuf/protobuf.dart' as $pb; import '../v2/hcommon/common.pbenum.dart' as $1; import 'extension.pbenum.dart'; export 'extension.pbenum.dart'; class ExtensionActionResult extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'ExtensionActionResult', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'extension'), createEmptyInstance: create) ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'extensionId') ..e<$1.ResponseCode>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'code', $pb.PbFieldType.OE, defaultOrMaker: $1.ResponseCode.OK, valueOf: $1.ResponseCode.valueOf, enumValues: $1.ResponseCode.values) ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'message') ..hasRequiredFields = false ; ExtensionActionResult._() : super(); factory ExtensionActionResult({ $core.String? extensionId, $1.ResponseCode? code, $core.String? message, }) { final _result = create(); if (extensionId != null) { _result.extensionId = extensionId; } if (code != null) { _result.code = code; } if (message != null) { _result.message = message; } return _result; } factory ExtensionActionResult.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory ExtensionActionResult.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') ExtensionActionResult clone() => ExtensionActionResult()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') ExtensionActionResult copyWith(void Function(ExtensionActionResult) updates) => super.copyWith((message) => updates(message as ExtensionActionResult)) as ExtensionActionResult; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static ExtensionActionResult create() => ExtensionActionResult._(); ExtensionActionResult createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static ExtensionActionResult getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static ExtensionActionResult? _defaultInstance; @$pb.TagNumber(1) $core.String get extensionId => $_getSZ(0); @$pb.TagNumber(1) set extensionId($core.String v) { $_setString(0, v); } @$pb.TagNumber(1) $core.bool hasExtensionId() => $_has(0); @$pb.TagNumber(1) void clearExtensionId() => clearField(1); @$pb.TagNumber(2) $1.ResponseCode get code => $_getN(1); @$pb.TagNumber(2) set code($1.ResponseCode v) { setField(2, v); } @$pb.TagNumber(2) $core.bool hasCode() => $_has(1); @$pb.TagNumber(2) void clearCode() => clearField(2); @$pb.TagNumber(3) $core.String get message => $_getSZ(2); @$pb.TagNumber(3) set message($core.String v) { $_setString(2, v); } @$pb.TagNumber(3) $core.bool hasMessage() => $_has(2); @$pb.TagNumber(3) void clearMessage() => clearField(3); } class ExtensionList extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'ExtensionList', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'extension'), createEmptyInstance: create) ..pc(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'extensions', $pb.PbFieldType.PM, subBuilder: ExtensionMsg.create) ..hasRequiredFields = false ; ExtensionList._() : super(); factory ExtensionList({ $core.Iterable? extensions, }) { final _result = create(); if (extensions != null) { _result.extensions.addAll(extensions); } return _result; } factory ExtensionList.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory ExtensionList.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') ExtensionList clone() => ExtensionList()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') ExtensionList copyWith(void Function(ExtensionList) updates) => super.copyWith((message) => updates(message as ExtensionList)) as ExtensionList; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static ExtensionList create() => ExtensionList._(); ExtensionList createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static ExtensionList getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static ExtensionList? _defaultInstance; @$pb.TagNumber(1) $core.List get extensions => $_getList(0); } class EditExtensionRequest extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'EditExtensionRequest', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'extension'), createEmptyInstance: create) ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'extensionId') ..aOB(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'enable') ..hasRequiredFields = false ; EditExtensionRequest._() : super(); factory EditExtensionRequest({ $core.String? extensionId, $core.bool? enable, }) { final _result = create(); if (extensionId != null) { _result.extensionId = extensionId; } if (enable != null) { _result.enable = enable; } return _result; } factory EditExtensionRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory EditExtensionRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') EditExtensionRequest clone() => EditExtensionRequest()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') EditExtensionRequest copyWith(void Function(EditExtensionRequest) updates) => super.copyWith((message) => updates(message as EditExtensionRequest)) as EditExtensionRequest; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static EditExtensionRequest create() => EditExtensionRequest._(); EditExtensionRequest createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static EditExtensionRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static EditExtensionRequest? _defaultInstance; @$pb.TagNumber(1) $core.String get extensionId => $_getSZ(0); @$pb.TagNumber(1) set extensionId($core.String v) { $_setString(0, v); } @$pb.TagNumber(1) $core.bool hasExtensionId() => $_has(0); @$pb.TagNumber(1) void clearExtensionId() => clearField(1); @$pb.TagNumber(2) $core.bool get enable => $_getBF(1); @$pb.TagNumber(2) set enable($core.bool v) { $_setBool(1, v); } @$pb.TagNumber(2) $core.bool hasEnable() => $_has(1); @$pb.TagNumber(2) void clearEnable() => clearField(2); } class ExtensionMsg extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'ExtensionMsg', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'extension'), createEmptyInstance: create) ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'id') ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'title') ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'description') ..aOB(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'enable') ..hasRequiredFields = false ; ExtensionMsg._() : super(); factory ExtensionMsg({ $core.String? id, $core.String? title, $core.String? description, $core.bool? enable, }) { final _result = create(); if (id != null) { _result.id = id; } if (title != null) { _result.title = title; } if (description != null) { _result.description = description; } if (enable != null) { _result.enable = enable; } return _result; } factory ExtensionMsg.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory ExtensionMsg.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') ExtensionMsg clone() => ExtensionMsg()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') ExtensionMsg copyWith(void Function(ExtensionMsg) updates) => super.copyWith((message) => updates(message as ExtensionMsg)) as ExtensionMsg; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static ExtensionMsg create() => ExtensionMsg._(); ExtensionMsg createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static ExtensionMsg getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static ExtensionMsg? _defaultInstance; @$pb.TagNumber(1) $core.String get id => $_getSZ(0); @$pb.TagNumber(1) set id($core.String v) { $_setString(0, v); } @$pb.TagNumber(1) $core.bool hasId() => $_has(0); @$pb.TagNumber(1) void clearId() => clearField(1); @$pb.TagNumber(2) $core.String get title => $_getSZ(1); @$pb.TagNumber(2) set title($core.String v) { $_setString(1, v); } @$pb.TagNumber(2) $core.bool hasTitle() => $_has(1); @$pb.TagNumber(2) void clearTitle() => clearField(2); @$pb.TagNumber(3) $core.String get description => $_getSZ(2); @$pb.TagNumber(3) set description($core.String v) { $_setString(2, v); } @$pb.TagNumber(3) $core.bool hasDescription() => $_has(2); @$pb.TagNumber(3) void clearDescription() => clearField(3); @$pb.TagNumber(4) $core.bool get enable => $_getBF(3); @$pb.TagNumber(4) set enable($core.bool v) { $_setBool(3, v); } @$pb.TagNumber(4) $core.bool hasEnable() => $_has(3); @$pb.TagNumber(4) void clearEnable() => clearField(4); } class ExtensionRequest extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'ExtensionRequest', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'extension'), createEmptyInstance: create) ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'extensionId') ..m<$core.String, $core.String>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'data', entryClassName: 'ExtensionRequest.DataEntry', keyFieldType: $pb.PbFieldType.OS, valueFieldType: $pb.PbFieldType.OS, packageName: const $pb.PackageName('extension')) ..hasRequiredFields = false ; ExtensionRequest._() : super(); factory ExtensionRequest({ $core.String? extensionId, $core.Map<$core.String, $core.String>? data, }) { final _result = create(); if (extensionId != null) { _result.extensionId = extensionId; } if (data != null) { _result.data.addAll(data); } return _result; } factory ExtensionRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory ExtensionRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') ExtensionRequest clone() => ExtensionRequest()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') ExtensionRequest copyWith(void Function(ExtensionRequest) updates) => super.copyWith((message) => updates(message as ExtensionRequest)) as ExtensionRequest; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static ExtensionRequest create() => ExtensionRequest._(); ExtensionRequest createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static ExtensionRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static ExtensionRequest? _defaultInstance; @$pb.TagNumber(1) $core.String get extensionId => $_getSZ(0); @$pb.TagNumber(1) set extensionId($core.String v) { $_setString(0, v); } @$pb.TagNumber(1) $core.bool hasExtensionId() => $_has(0); @$pb.TagNumber(1) void clearExtensionId() => clearField(1); @$pb.TagNumber(2) $core.Map<$core.String, $core.String> get data => $_getMap(1); } class SendExtensionDataRequest extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'SendExtensionDataRequest', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'extension'), createEmptyInstance: create) ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'extensionId') ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'button') ..m<$core.String, $core.String>(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'data', entryClassName: 'SendExtensionDataRequest.DataEntry', keyFieldType: $pb.PbFieldType.OS, valueFieldType: $pb.PbFieldType.OS, packageName: const $pb.PackageName('extension')) ..hasRequiredFields = false ; SendExtensionDataRequest._() : super(); factory SendExtensionDataRequest({ $core.String? extensionId, $core.String? button, $core.Map<$core.String, $core.String>? data, }) { final _result = create(); if (extensionId != null) { _result.extensionId = extensionId; } if (button != null) { _result.button = button; } if (data != null) { _result.data.addAll(data); } return _result; } factory SendExtensionDataRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory SendExtensionDataRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') SendExtensionDataRequest clone() => SendExtensionDataRequest()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') SendExtensionDataRequest copyWith(void Function(SendExtensionDataRequest) updates) => super.copyWith((message) => updates(message as SendExtensionDataRequest)) as SendExtensionDataRequest; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static SendExtensionDataRequest create() => SendExtensionDataRequest._(); SendExtensionDataRequest createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static SendExtensionDataRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static SendExtensionDataRequest? _defaultInstance; @$pb.TagNumber(1) $core.String get extensionId => $_getSZ(0); @$pb.TagNumber(1) set extensionId($core.String v) { $_setString(0, v); } @$pb.TagNumber(1) $core.bool hasExtensionId() => $_has(0); @$pb.TagNumber(1) void clearExtensionId() => clearField(1); @$pb.TagNumber(2) $core.String get button => $_getSZ(1); @$pb.TagNumber(2) set button($core.String v) { $_setString(1, v); } @$pb.TagNumber(2) $core.bool hasButton() => $_has(1); @$pb.TagNumber(2) void clearButton() => clearField(2); @$pb.TagNumber(3) $core.Map<$core.String, $core.String> get data => $_getMap(2); } class ExtensionResponse extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'ExtensionResponse', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'extension'), createEmptyInstance: create) ..e(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'type', $pb.PbFieldType.OE, defaultOrMaker: ExtensionResponseType.NOTHING, valueOf: ExtensionResponseType.valueOf, enumValues: ExtensionResponseType.values) ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'extensionId') ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'jsonUi') ..hasRequiredFields = false ; ExtensionResponse._() : super(); factory ExtensionResponse({ ExtensionResponseType? type, $core.String? extensionId, $core.String? jsonUi, }) { final _result = create(); if (type != null) { _result.type = type; } if (extensionId != null) { _result.extensionId = extensionId; } if (jsonUi != null) { _result.jsonUi = jsonUi; } return _result; } factory ExtensionResponse.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory ExtensionResponse.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') ExtensionResponse clone() => ExtensionResponse()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') ExtensionResponse copyWith(void Function(ExtensionResponse) updates) => super.copyWith((message) => updates(message as ExtensionResponse)) as ExtensionResponse; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static ExtensionResponse create() => ExtensionResponse._(); ExtensionResponse createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static ExtensionResponse getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static ExtensionResponse? _defaultInstance; @$pb.TagNumber(1) ExtensionResponseType get type => $_getN(0); @$pb.TagNumber(1) set type(ExtensionResponseType v) { setField(1, v); } @$pb.TagNumber(1) $core.bool hasType() => $_has(0); @$pb.TagNumber(1) void clearType() => clearField(1); @$pb.TagNumber(2) $core.String get extensionId => $_getSZ(1); @$pb.TagNumber(2) set extensionId($core.String v) { $_setString(1, v); } @$pb.TagNumber(2) $core.bool hasExtensionId() => $_has(1); @$pb.TagNumber(2) void clearExtensionId() => clearField(2); @$pb.TagNumber(3) $core.String get jsonUi => $_getSZ(2); @$pb.TagNumber(3) set jsonUi($core.String v) { $_setString(2, v); } @$pb.TagNumber(3) $core.bool hasJsonUi() => $_has(2); @$pb.TagNumber(3) void clearJsonUi() => clearField(3); } ================================================ FILE: lib/hiddifycore/generated/extension/extension.pbenum.dart ================================================ /// // Generated code. Do not modify. // source: extension/extension.proto // // @dart = 2.12 // ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name // ignore_for_file: UNDEFINED_SHOWN_NAME import 'dart:core' as $core; import 'package:protobuf/protobuf.dart' as $pb; class ExtensionResponseType extends $pb.ProtobufEnum { static const ExtensionResponseType NOTHING = ExtensionResponseType._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'NOTHING'); static const ExtensionResponseType UPDATE_UI = ExtensionResponseType._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UPDATE_UI'); static const ExtensionResponseType SHOW_DIALOG = ExtensionResponseType._(2, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'SHOW_DIALOG'); static const ExtensionResponseType END = ExtensionResponseType._(3, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'END'); static const $core.List values = [ NOTHING, UPDATE_UI, SHOW_DIALOG, END, ]; static final $core.Map<$core.int, ExtensionResponseType> _byValue = $pb.ProtobufEnum.initByValue(values); static ExtensionResponseType? valueOf($core.int value) => _byValue[value]; const ExtensionResponseType._($core.int v, $core.String n) : super(v, n); } ================================================ FILE: lib/hiddifycore/generated/extension/extension.pbjson.dart ================================================ /// // Generated code. Do not modify. // source: extension/extension.proto // // @dart = 2.12 // ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,deprecated_member_use_from_same_package,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name import 'dart:core' as $core; import 'dart:convert' as $convert; import 'dart:typed_data' as $typed_data; @$core.Deprecated('Use extensionResponseTypeDescriptor instead') const ExtensionResponseType$json = const { '1': 'ExtensionResponseType', '2': const [ const {'1': 'NOTHING', '2': 0}, const {'1': 'UPDATE_UI', '2': 1}, const {'1': 'SHOW_DIALOG', '2': 2}, const {'1': 'END', '2': 3}, ], }; /// Descriptor for `ExtensionResponseType`. Decode as a `google.protobuf.EnumDescriptorProto`. final $typed_data.Uint8List extensionResponseTypeDescriptor = $convert.base64Decode('ChVFeHRlbnNpb25SZXNwb25zZVR5cGUSCwoHTk9USElORxAAEg0KCVVQREFURV9VSRABEg8KC1NIT1dfRElBTE9HEAISBwoDRU5EEAM='); @$core.Deprecated('Use extensionActionResultDescriptor instead') const ExtensionActionResult$json = const { '1': 'ExtensionActionResult', '2': const [ const {'1': 'extension_id', '3': 1, '4': 1, '5': 9, '10': 'extensionId'}, const {'1': 'code', '3': 2, '4': 1, '5': 14, '6': '.hcommon.ResponseCode', '10': 'code'}, const {'1': 'message', '3': 3, '4': 1, '5': 9, '10': 'message'}, ], }; /// Descriptor for `ExtensionActionResult`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List extensionActionResultDescriptor = $convert.base64Decode('ChVFeHRlbnNpb25BY3Rpb25SZXN1bHQSIQoMZXh0ZW5zaW9uX2lkGAEgASgJUgtleHRlbnNpb25JZBIpCgRjb2RlGAIgASgOMhUuaGNvbW1vbi5SZXNwb25zZUNvZGVSBGNvZGUSGAoHbWVzc2FnZRgDIAEoCVIHbWVzc2FnZQ=='); @$core.Deprecated('Use extensionListDescriptor instead') const ExtensionList$json = const { '1': 'ExtensionList', '2': const [ const {'1': 'extensions', '3': 1, '4': 3, '5': 11, '6': '.extension.ExtensionMsg', '10': 'extensions'}, ], }; /// Descriptor for `ExtensionList`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List extensionListDescriptor = $convert.base64Decode('Cg1FeHRlbnNpb25MaXN0EjcKCmV4dGVuc2lvbnMYASADKAsyFy5leHRlbnNpb24uRXh0ZW5zaW9uTXNnUgpleHRlbnNpb25z'); @$core.Deprecated('Use editExtensionRequestDescriptor instead') const EditExtensionRequest$json = const { '1': 'EditExtensionRequest', '2': const [ const {'1': 'extension_id', '3': 1, '4': 1, '5': 9, '10': 'extensionId'}, const {'1': 'enable', '3': 2, '4': 1, '5': 8, '10': 'enable'}, ], }; /// Descriptor for `EditExtensionRequest`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List editExtensionRequestDescriptor = $convert.base64Decode('ChRFZGl0RXh0ZW5zaW9uUmVxdWVzdBIhCgxleHRlbnNpb25faWQYASABKAlSC2V4dGVuc2lvbklkEhYKBmVuYWJsZRgCIAEoCFIGZW5hYmxl'); @$core.Deprecated('Use extensionMsgDescriptor instead') const ExtensionMsg$json = const { '1': 'ExtensionMsg', '2': const [ const {'1': 'id', '3': 1, '4': 1, '5': 9, '10': 'id'}, const {'1': 'title', '3': 2, '4': 1, '5': 9, '10': 'title'}, const {'1': 'description', '3': 3, '4': 1, '5': 9, '10': 'description'}, const {'1': 'enable', '3': 4, '4': 1, '5': 8, '10': 'enable'}, ], }; /// Descriptor for `ExtensionMsg`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List extensionMsgDescriptor = $convert.base64Decode('CgxFeHRlbnNpb25Nc2cSDgoCaWQYASABKAlSAmlkEhQKBXRpdGxlGAIgASgJUgV0aXRsZRIgCgtkZXNjcmlwdGlvbhgDIAEoCVILZGVzY3JpcHRpb24SFgoGZW5hYmxlGAQgASgIUgZlbmFibGU='); @$core.Deprecated('Use extensionRequestDescriptor instead') const ExtensionRequest$json = const { '1': 'ExtensionRequest', '2': const [ const {'1': 'extension_id', '3': 1, '4': 1, '5': 9, '10': 'extensionId'}, const {'1': 'data', '3': 2, '4': 3, '5': 11, '6': '.extension.ExtensionRequest.DataEntry', '10': 'data'}, ], '3': const [ExtensionRequest_DataEntry$json], }; @$core.Deprecated('Use extensionRequestDescriptor instead') const ExtensionRequest_DataEntry$json = const { '1': 'DataEntry', '2': const [ const {'1': 'key', '3': 1, '4': 1, '5': 9, '10': 'key'}, const {'1': 'value', '3': 2, '4': 1, '5': 9, '10': 'value'}, ], '7': const {'7': true}, }; /// Descriptor for `ExtensionRequest`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List extensionRequestDescriptor = $convert.base64Decode('ChBFeHRlbnNpb25SZXF1ZXN0EiEKDGV4dGVuc2lvbl9pZBgBIAEoCVILZXh0ZW5zaW9uSWQSOQoEZGF0YRgCIAMoCzIlLmV4dGVuc2lvbi5FeHRlbnNpb25SZXF1ZXN0LkRhdGFFbnRyeVIEZGF0YRo3CglEYXRhRW50cnkSEAoDa2V5GAEgASgJUgNrZXkSFAoFdmFsdWUYAiABKAlSBXZhbHVlOgI4AQ=='); @$core.Deprecated('Use sendExtensionDataRequestDescriptor instead') const SendExtensionDataRequest$json = const { '1': 'SendExtensionDataRequest', '2': const [ const {'1': 'extension_id', '3': 1, '4': 1, '5': 9, '10': 'extensionId'}, const {'1': 'button', '3': 2, '4': 1, '5': 9, '10': 'button'}, const {'1': 'data', '3': 3, '4': 3, '5': 11, '6': '.extension.SendExtensionDataRequest.DataEntry', '10': 'data'}, ], '3': const [SendExtensionDataRequest_DataEntry$json], }; @$core.Deprecated('Use sendExtensionDataRequestDescriptor instead') const SendExtensionDataRequest_DataEntry$json = const { '1': 'DataEntry', '2': const [ const {'1': 'key', '3': 1, '4': 1, '5': 9, '10': 'key'}, const {'1': 'value', '3': 2, '4': 1, '5': 9, '10': 'value'}, ], '7': const {'7': true}, }; /// Descriptor for `SendExtensionDataRequest`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List sendExtensionDataRequestDescriptor = $convert.base64Decode('ChhTZW5kRXh0ZW5zaW9uRGF0YVJlcXVlc3QSIQoMZXh0ZW5zaW9uX2lkGAEgASgJUgtleHRlbnNpb25JZBIWCgZidXR0b24YAiABKAlSBmJ1dHRvbhJBCgRkYXRhGAMgAygLMi0uZXh0ZW5zaW9uLlNlbmRFeHRlbnNpb25EYXRhUmVxdWVzdC5EYXRhRW50cnlSBGRhdGEaNwoJRGF0YUVudHJ5EhAKA2tleRgBIAEoCVIDa2V5EhQKBXZhbHVlGAIgASgJUgV2YWx1ZToCOAE='); @$core.Deprecated('Use extensionResponseDescriptor instead') const ExtensionResponse$json = const { '1': 'ExtensionResponse', '2': const [ const {'1': 'type', '3': 1, '4': 1, '5': 14, '6': '.extension.ExtensionResponseType', '10': 'type'}, const {'1': 'extension_id', '3': 2, '4': 1, '5': 9, '10': 'extensionId'}, const {'1': 'json_ui', '3': 3, '4': 1, '5': 9, '10': 'jsonUi'}, ], }; /// Descriptor for `ExtensionResponse`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List extensionResponseDescriptor = $convert.base64Decode('ChFFeHRlbnNpb25SZXNwb25zZRI0CgR0eXBlGAEgASgOMiAuZXh0ZW5zaW9uLkV4dGVuc2lvblJlc3BvbnNlVHlwZVIEdHlwZRIhCgxleHRlbnNpb25faWQYAiABKAlSC2V4dGVuc2lvbklkEhcKB2pzb25fdWkYAyABKAlSBmpzb25VaQ=='); ================================================ FILE: lib/hiddifycore/generated/extension/extension_service.pb.dart ================================================ /// // Generated code. Do not modify. // source: extension/extension_service.proto // // @dart = 2.12 // ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name import 'dart:core' as $core; ================================================ FILE: lib/hiddifycore/generated/extension/extension_service.pbenum.dart ================================================ /// // Generated code. Do not modify. // source: extension/extension_service.proto // // @dart = 2.12 // ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name ================================================ FILE: lib/hiddifycore/generated/extension/extension_service.pbgrpc.dart ================================================ /// // Generated code. Do not modify. // source: extension/extension_service.proto // // @dart = 2.12 // ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name import 'dart:async' as $async; import 'dart:core' as $core; import 'package:grpc/service_api.dart' as $grpc; import '../v2/hcommon/common.pb.dart' as $1; import 'extension.pb.dart' as $6; export 'extension_service.pb.dart'; class ExtensionHostServiceClient extends $grpc.Client { static final _$listExtensions = $grpc.ClientMethod<$1.Empty, $6.ExtensionList>( '/extension.ExtensionHostService/ListExtensions', ($1.Empty value) => value.writeToBuffer(), ($core.List<$core.int> value) => $6.ExtensionList.fromBuffer(value)); static final _$connect = $grpc.ClientMethod<$6.ExtensionRequest, $6.ExtensionResponse>( '/extension.ExtensionHostService/Connect', ($6.ExtensionRequest value) => value.writeToBuffer(), ($core.List<$core.int> value) => $6.ExtensionResponse.fromBuffer(value)); static final _$editExtension = $grpc.ClientMethod<$6.EditExtensionRequest, $6.ExtensionActionResult>( '/extension.ExtensionHostService/EditExtension', ($6.EditExtensionRequest value) => value.writeToBuffer(), ($core.List<$core.int> value) => $6.ExtensionActionResult.fromBuffer(value)); static final _$submitForm = $grpc.ClientMethod<$6.SendExtensionDataRequest, $6.ExtensionActionResult>( '/extension.ExtensionHostService/SubmitForm', ($6.SendExtensionDataRequest value) => value.writeToBuffer(), ($core.List<$core.int> value) => $6.ExtensionActionResult.fromBuffer(value)); static final _$close = $grpc.ClientMethod<$6.ExtensionRequest, $6.ExtensionActionResult>( '/extension.ExtensionHostService/Close', ($6.ExtensionRequest value) => value.writeToBuffer(), ($core.List<$core.int> value) => $6.ExtensionActionResult.fromBuffer(value)); static final _$getUI = $grpc.ClientMethod<$6.ExtensionRequest, $6.ExtensionActionResult>( '/extension.ExtensionHostService/GetUI', ($6.ExtensionRequest value) => value.writeToBuffer(), ($core.List<$core.int> value) => $6.ExtensionActionResult.fromBuffer(value)); ExtensionHostServiceClient($grpc.ClientChannel channel, {$grpc.CallOptions? options, $core.Iterable<$grpc.ClientInterceptor>? interceptors}) : super(channel, options: options, interceptors: interceptors); $grpc.ResponseFuture<$6.ExtensionList> listExtensions($1.Empty request, {$grpc.CallOptions? options}) { return $createUnaryCall(_$listExtensions, request, options: options); } $grpc.ResponseStream<$6.ExtensionResponse> connect( $6.ExtensionRequest request, {$grpc.CallOptions? options}) { return $createStreamingCall( _$connect, $async.Stream.fromIterable([request]), options: options); } $grpc.ResponseFuture<$6.ExtensionActionResult> editExtension( $6.EditExtensionRequest request, {$grpc.CallOptions? options}) { return $createUnaryCall(_$editExtension, request, options: options); } $grpc.ResponseFuture<$6.ExtensionActionResult> submitForm( $6.SendExtensionDataRequest request, {$grpc.CallOptions? options}) { return $createUnaryCall(_$submitForm, request, options: options); } $grpc.ResponseFuture<$6.ExtensionActionResult> close( $6.ExtensionRequest request, {$grpc.CallOptions? options}) { return $createUnaryCall(_$close, request, options: options); } $grpc.ResponseFuture<$6.ExtensionActionResult> getUI( $6.ExtensionRequest request, {$grpc.CallOptions? options}) { return $createUnaryCall(_$getUI, request, options: options); } } abstract class ExtensionHostServiceBase extends $grpc.Service { $core.String get $name => 'extension.ExtensionHostService'; ExtensionHostServiceBase() { $addMethod($grpc.ServiceMethod<$1.Empty, $6.ExtensionList>( 'ListExtensions', listExtensions_Pre, false, false, ($core.List<$core.int> value) => $1.Empty.fromBuffer(value), ($6.ExtensionList value) => value.writeToBuffer())); $addMethod($grpc.ServiceMethod<$6.ExtensionRequest, $6.ExtensionResponse>( 'Connect', connect_Pre, false, true, ($core.List<$core.int> value) => $6.ExtensionRequest.fromBuffer(value), ($6.ExtensionResponse value) => value.writeToBuffer())); $addMethod( $grpc.ServiceMethod<$6.EditExtensionRequest, $6.ExtensionActionResult>( 'EditExtension', editExtension_Pre, false, false, ($core.List<$core.int> value) => $6.EditExtensionRequest.fromBuffer(value), ($6.ExtensionActionResult value) => value.writeToBuffer())); $addMethod($grpc.ServiceMethod<$6.SendExtensionDataRequest, $6.ExtensionActionResult>( 'SubmitForm', submitForm_Pre, false, false, ($core.List<$core.int> value) => $6.SendExtensionDataRequest.fromBuffer(value), ($6.ExtensionActionResult value) => value.writeToBuffer())); $addMethod( $grpc.ServiceMethod<$6.ExtensionRequest, $6.ExtensionActionResult>( 'Close', close_Pre, false, false, ($core.List<$core.int> value) => $6.ExtensionRequest.fromBuffer(value), ($6.ExtensionActionResult value) => value.writeToBuffer())); $addMethod( $grpc.ServiceMethod<$6.ExtensionRequest, $6.ExtensionActionResult>( 'GetUI', getUI_Pre, false, false, ($core.List<$core.int> value) => $6.ExtensionRequest.fromBuffer(value), ($6.ExtensionActionResult value) => value.writeToBuffer())); } $async.Future<$6.ExtensionList> listExtensions_Pre( $grpc.ServiceCall call, $async.Future<$1.Empty> request) async { return listExtensions(call, await request); } $async.Stream<$6.ExtensionResponse> connect_Pre($grpc.ServiceCall call, $async.Future<$6.ExtensionRequest> request) async* { yield* connect(call, await request); } $async.Future<$6.ExtensionActionResult> editExtension_Pre( $grpc.ServiceCall call, $async.Future<$6.EditExtensionRequest> request) async { return editExtension(call, await request); } $async.Future<$6.ExtensionActionResult> submitForm_Pre($grpc.ServiceCall call, $async.Future<$6.SendExtensionDataRequest> request) async { return submitForm(call, await request); } $async.Future<$6.ExtensionActionResult> close_Pre($grpc.ServiceCall call, $async.Future<$6.ExtensionRequest> request) async { return close(call, await request); } $async.Future<$6.ExtensionActionResult> getUI_Pre($grpc.ServiceCall call, $async.Future<$6.ExtensionRequest> request) async { return getUI(call, await request); } $async.Future<$6.ExtensionList> listExtensions( $grpc.ServiceCall call, $1.Empty request); $async.Stream<$6.ExtensionResponse> connect( $grpc.ServiceCall call, $6.ExtensionRequest request); $async.Future<$6.ExtensionActionResult> editExtension( $grpc.ServiceCall call, $6.EditExtensionRequest request); $async.Future<$6.ExtensionActionResult> submitForm( $grpc.ServiceCall call, $6.SendExtensionDataRequest request); $async.Future<$6.ExtensionActionResult> close( $grpc.ServiceCall call, $6.ExtensionRequest request); $async.Future<$6.ExtensionActionResult> getUI( $grpc.ServiceCall call, $6.ExtensionRequest request); } ================================================ FILE: lib/hiddifycore/generated/extension/extension_service.pbjson.dart ================================================ /// // Generated code. Do not modify. // source: extension/extension_service.proto // // @dart = 2.12 // ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,deprecated_member_use_from_same_package,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name import 'dart:core' as $core; import 'dart:convert' as $convert; import 'dart:typed_data' as $typed_data; ================================================ FILE: lib/hiddifycore/generated/google/protobuf/timestamp.pb.dart ================================================ /// // Generated code. Do not modify. // source: google/protobuf/timestamp.proto // // @dart = 2.12 // ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name import 'dart:core' as $core; import 'package:fixnum/fixnum.dart' as $fixnum; import 'package:protobuf/protobuf.dart' as $pb; import 'package:protobuf/src/protobuf/mixins/well_known.dart' as $mixin; class Timestamp extends $pb.GeneratedMessage with $mixin.TimestampMixin { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'Timestamp', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'google.protobuf'), createEmptyInstance: create, toProto3Json: $mixin.TimestampMixin.toProto3JsonHelper, fromProto3Json: $mixin.TimestampMixin.fromProto3JsonHelper) ..aInt64(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'seconds') ..a<$core.int>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'nanos', $pb.PbFieldType.O3) ..hasRequiredFields = false ; Timestamp._() : super(); factory Timestamp({ $fixnum.Int64? seconds, $core.int? nanos, }) { final _result = create(); if (seconds != null) { _result.seconds = seconds; } if (nanos != null) { _result.nanos = nanos; } return _result; } factory Timestamp.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory Timestamp.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') Timestamp clone() => Timestamp()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') Timestamp copyWith(void Function(Timestamp) updates) => super.copyWith((message) => updates(message as Timestamp)) as Timestamp; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static Timestamp create() => Timestamp._(); Timestamp createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static Timestamp getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static Timestamp? _defaultInstance; @$pb.TagNumber(1) $fixnum.Int64 get seconds => $_getI64(0); @$pb.TagNumber(1) set seconds($fixnum.Int64 v) { $_setInt64(0, v); } @$pb.TagNumber(1) $core.bool hasSeconds() => $_has(0); @$pb.TagNumber(1) void clearSeconds() => clearField(1); @$pb.TagNumber(2) $core.int get nanos => $_getIZ(1); @$pb.TagNumber(2) set nanos($core.int v) { $_setSignedInt32(1, v); } @$pb.TagNumber(2) $core.bool hasNanos() => $_has(1); @$pb.TagNumber(2) void clearNanos() => clearField(2); /// Creates a new instance from [dateTime]. /// /// Time zone information will not be preserved. static Timestamp fromDateTime($core.DateTime dateTime) { final result = create(); $mixin.TimestampMixin.setFromDateTime(result, dateTime); return result; } } ================================================ FILE: lib/hiddifycore/generated/google/protobuf/timestamp.pbenum.dart ================================================ /// // Generated code. Do not modify. // source: google/protobuf/timestamp.proto // // @dart = 2.12 // ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name ================================================ FILE: lib/hiddifycore/generated/google/protobuf/timestamp.pbjson.dart ================================================ /// // Generated code. Do not modify. // source: google/protobuf/timestamp.proto // // @dart = 2.12 // ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,deprecated_member_use_from_same_package,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name import 'dart:core' as $core; import 'dart:convert' as $convert; import 'dart:typed_data' as $typed_data; @$core.Deprecated('Use timestampDescriptor instead') const Timestamp$json = const { '1': 'Timestamp', '2': const [ const {'1': 'seconds', '3': 1, '4': 1, '5': 3, '10': 'seconds'}, const {'1': 'nanos', '3': 2, '4': 1, '5': 5, '10': 'nanos'}, ], }; /// Descriptor for `Timestamp`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List timestampDescriptor = $convert.base64Decode('CglUaW1lc3RhbXASGAoHc2Vjb25kcxgBIAEoA1IHc2Vjb25kcxIUCgVuYW5vcxgCIAEoBVIFbmFub3M='); ================================================ FILE: lib/hiddifycore/generated/v2/common/common.pb.dart ================================================ // // Generated code. Do not modify. // source: v2/common/common.proto // // @dart = 2.12 // ignore_for_file: annotate_overrides, camel_case_types, comment_references // ignore_for_file: constant_identifier_names, library_prefixes // ignore_for_file: non_constant_identifier_names, prefer_final_fields // ignore_for_file: unnecessary_import, unnecessary_this, unused_import import 'dart:core' as $core; import 'package:protobuf/protobuf.dart' as $pb; import 'common.pbenum.dart'; export 'common.pbenum.dart'; class Empty extends $pb.GeneratedMessage { factory Empty() => create(); Empty._() : super(); factory Empty.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory Empty.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'Empty', package: const $pb.PackageName(_omitMessageNames ? '' : 'common'), createEmptyInstance: create) ..hasRequiredFields = false; @$core.Deprecated('Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') Empty clone() => Empty()..mergeFromMessage(this); @$core.Deprecated('Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') Empty copyWith(void Function(Empty) updates) => super.copyWith((message) => updates(message as Empty)) as Empty; $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static Empty create() => Empty._(); Empty createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static Empty getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static Empty? _defaultInstance; } class Response extends $pb.GeneratedMessage { factory Response({ ResponseCode? code, $core.String? message, }) { final $result = create(); if (code != null) { $result.code = code; } if (message != null) { $result.message = message; } return $result; } Response._() : super(); factory Response.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory Response.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'Response', package: const $pb.PackageName(_omitMessageNames ? '' : 'common'), createEmptyInstance: create) ..e(1, _omitFieldNames ? '' : 'code', $pb.PbFieldType.OE, defaultOrMaker: ResponseCode.OK, valueOf: ResponseCode.valueOf, enumValues: ResponseCode.values) ..aOS(2, _omitFieldNames ? '' : 'message') ..hasRequiredFields = false; @$core.Deprecated('Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') Response clone() => Response()..mergeFromMessage(this); @$core.Deprecated('Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') Response copyWith(void Function(Response) updates) => super.copyWith((message) => updates(message as Response)) as Response; $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static Response create() => Response._(); Response createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static Response getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static Response? _defaultInstance; @$pb.TagNumber(1) ResponseCode get code => $_getN(0); @$pb.TagNumber(1) set code(ResponseCode v) { setField(1, v); } @$pb.TagNumber(1) $core.bool hasCode() => $_has(0); @$pb.TagNumber(1) void clearCode() => clearField(1); @$pb.TagNumber(2) $core.String get message => $_getSZ(1); @$pb.TagNumber(2) set message($core.String v) { $_setString(1, v); } @$pb.TagNumber(2) $core.bool hasMessage() => $_has(1); @$pb.TagNumber(2) void clearMessage() => clearField(2); } const _omitFieldNames = $core.bool.fromEnvironment('protobuf.omit_field_names'); const _omitMessageNames = $core.bool.fromEnvironment('protobuf.omit_message_names'); ================================================ FILE: lib/hiddifycore/generated/v2/common/common.pbenum.dart ================================================ // // Generated code. Do not modify. // source: v2/common/common.proto // // @dart = 2.12 // ignore_for_file: annotate_overrides, camel_case_types, comment_references // ignore_for_file: constant_identifier_names, library_prefixes // ignore_for_file: non_constant_identifier_names, prefer_final_fields // ignore_for_file: unnecessary_import, unnecessary_this, unused_import import 'dart:core' as $core; import 'package:protobuf/protobuf.dart' as $pb; class ResponseCode extends $pb.ProtobufEnum { static const ResponseCode OK = ResponseCode._(0, _omitEnumNames ? '' : 'OK'); static const ResponseCode FAILED = ResponseCode._(1, _omitEnumNames ? '' : 'FAILED'); static const ResponseCode AUTH_NEED = ResponseCode._(2, _omitEnumNames ? '' : 'AUTH_NEED'); static const $core.List values = [ OK, FAILED, AUTH_NEED, ]; static final $core.Map<$core.int, ResponseCode> _byValue = $pb.ProtobufEnum.initByValue(values); static ResponseCode? valueOf($core.int value) => _byValue[value]; const ResponseCode._($core.int v, $core.String n) : super(v, n); } const _omitEnumNames = $core.bool.fromEnvironment('protobuf.omit_enum_names'); ================================================ FILE: lib/hiddifycore/generated/v2/common/common.pbjson.dart ================================================ // // Generated code. Do not modify. // source: v2/common/common.proto // // @dart = 2.12 // ignore_for_file: annotate_overrides, camel_case_types, comment_references // ignore_for_file: constant_identifier_names, library_prefixes // ignore_for_file: non_constant_identifier_names, prefer_final_fields // ignore_for_file: unnecessary_import, unnecessary_this, unused_import import 'dart:convert' as $convert; import 'dart:core' as $core; import 'dart:typed_data' as $typed_data; @$core.Deprecated('Use responseCodeDescriptor instead') const ResponseCode$json = { '1': 'ResponseCode', '2': [ {'1': 'OK', '2': 0}, {'1': 'FAILED', '2': 1}, {'1': 'AUTH_NEED', '2': 2}, ], }; /// Descriptor for `ResponseCode`. Decode as a `google.protobuf.EnumDescriptorProto`. final $typed_data.Uint8List responseCodeDescriptor = $convert.base64Decode('CgxSZXNwb25zZUNvZGUSBgoCT0sQABIKCgZGQUlMRUQQARINCglBVVRIX05FRUQQAg=='); @$core.Deprecated('Use emptyDescriptor instead') const Empty$json = { '1': 'Empty', }; /// Descriptor for `Empty`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List emptyDescriptor = $convert.base64Decode('CgVFbXB0eQ=='); @$core.Deprecated('Use responseDescriptor instead') const Response$json = { '1': 'Response', '2': [ {'1': 'code', '3': 1, '4': 1, '5': 14, '6': '.common.ResponseCode', '10': 'code'}, {'1': 'message', '3': 2, '4': 1, '5': 9, '10': 'message'}, ], }; /// Descriptor for `Response`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List responseDescriptor = $convert.base64Decode('CghSZXNwb25zZRIoCgRjb2RlGAEgASgOMhQuY29tbW9uLlJlc3BvbnNlQ29kZVIEY29kZRIYCg' 'dtZXNzYWdlGAIgASgJUgdtZXNzYWdl'); ================================================ FILE: lib/hiddifycore/generated/v2/config/route_rule.pb.dart ================================================ /// // Generated code. Do not modify. // source: v2/config/route_rule.proto // // @dart = 2.12 // ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name import 'dart:core' as $core; import 'package:protobuf/protobuf.dart' as $pb; import 'route_rule.pbenum.dart'; export 'route_rule.pbenum.dart'; class RouteRule extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'RouteRule', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'config'), createEmptyInstance: create) ..pc(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'rules', $pb.PbFieldType.PM, subBuilder: Rule.create) ..hasRequiredFields = false ; RouteRule._() : super(); factory RouteRule({ $core.Iterable? rules, }) { final _result = create(); if (rules != null) { _result.rules.addAll(rules); } return _result; } factory RouteRule.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory RouteRule.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') RouteRule clone() => RouteRule()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') RouteRule copyWith(void Function(RouteRule) updates) => super.copyWith((message) => updates(message as RouteRule)) as RouteRule; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static RouteRule create() => RouteRule._(); RouteRule createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static RouteRule getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static RouteRule? _defaultInstance; @$pb.TagNumber(1) $core.List get rules => $_getList(0); } class Rule extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'Rule', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'config'), createEmptyInstance: create) ..a<$core.int>(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'list_order', $pb.PbFieldType.OU3) ..aOB(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'enabled') ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'name') ..e(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'outbound', $pb.PbFieldType.OE, defaultOrMaker: Outbound.proxy, valueOf: Outbound.valueOf, enumValues: Outbound.values) ..pPS(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'rule_set', protoName: 'rule_sets') ..pPS(6, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'package_name', protoName: 'package_names') ..pPS(7, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'process_name', protoName: 'process_names') ..pPS(8, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'process_path', protoName: 'process_paths') ..e(9, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'network', $pb.PbFieldType.OE, defaultOrMaker: Network.all, valueOf: Network.valueOf, enumValues: Network.values) ..pPS(10, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'port_range', protoName: 'port_ranges') ..pPS(11, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'source_port_range', protoName: 'source_port_ranges') ..pc(12, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'protocol', $pb.PbFieldType.KE, protoName: 'protocols', valueOf: Protocol.valueOf, enumValues: Protocol.values, defaultEnumValue: Protocol.tls) ..pPS(13, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'ip_cidr', protoName: 'ip_cidrs') ..pPS(14, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'source_ip_cidr', protoName: 'source_ip_cidrs') ..pPS(15, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'domain', protoName: 'domains') ..pPS(16, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'domain_suffix', protoName: 'domain_suffixes') ..pPS(17, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'domain_keyword', protoName: 'domain_keywords') ..pPS(18, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'domain_regex', protoName: 'domain_regexes') ..hasRequiredFields = false ; Rule._() : super(); factory Rule({ $core.int? listOrder, $core.bool? enabled, $core.String? name, Outbound? outbound, $core.Iterable<$core.String>? ruleSets, $core.Iterable<$core.String>? packageNames, $core.Iterable<$core.String>? processNames, $core.Iterable<$core.String>? processPaths, Network? network, $core.Iterable<$core.String>? portRanges, $core.Iterable<$core.String>? sourcePortRanges, $core.Iterable? protocols, $core.Iterable<$core.String>? ipCidrs, $core.Iterable<$core.String>? sourceIpCidrs, $core.Iterable<$core.String>? domains, $core.Iterable<$core.String>? domainSuffixes, $core.Iterable<$core.String>? domainKeywords, $core.Iterable<$core.String>? domainRegexes, }) { final _result = create(); if (listOrder != null) { _result.listOrder = listOrder; } if (enabled != null) { _result.enabled = enabled; } if (name != null) { _result.name = name; } if (outbound != null) { _result.outbound = outbound; } if (ruleSets != null) { _result.ruleSets.addAll(ruleSets); } if (packageNames != null) { _result.packageNames.addAll(packageNames); } if (processNames != null) { _result.processNames.addAll(processNames); } if (processPaths != null) { _result.processPaths.addAll(processPaths); } if (network != null) { _result.network = network; } if (portRanges != null) { _result.portRanges.addAll(portRanges); } if (sourcePortRanges != null) { _result.sourcePortRanges.addAll(sourcePortRanges); } if (protocols != null) { _result.protocols.addAll(protocols); } if (ipCidrs != null) { _result.ipCidrs.addAll(ipCidrs); } if (sourceIpCidrs != null) { _result.sourceIpCidrs.addAll(sourceIpCidrs); } if (domains != null) { _result.domains.addAll(domains); } if (domainSuffixes != null) { _result.domainSuffixes.addAll(domainSuffixes); } if (domainKeywords != null) { _result.domainKeywords.addAll(domainKeywords); } if (domainRegexes != null) { _result.domainRegexes.addAll(domainRegexes); } return _result; } factory Rule.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory Rule.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') Rule clone() => Rule()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') Rule copyWith(void Function(Rule) updates) => super.copyWith((message) => updates(message as Rule)) as Rule; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static Rule create() => Rule._(); Rule createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static Rule getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static Rule? _defaultInstance; @$pb.TagNumber(1) $core.int get listOrder => $_getIZ(0); @$pb.TagNumber(1) set listOrder($core.int v) { $_setUnsignedInt32(0, v); } @$pb.TagNumber(1) $core.bool hasListOrder() => $_has(0); @$pb.TagNumber(1) void clearListOrder() => clearField(1); @$pb.TagNumber(2) $core.bool get enabled => $_getBF(1); @$pb.TagNumber(2) set enabled($core.bool v) { $_setBool(1, v); } @$pb.TagNumber(2) $core.bool hasEnabled() => $_has(1); @$pb.TagNumber(2) void clearEnabled() => clearField(2); @$pb.TagNumber(3) $core.String get name => $_getSZ(2); @$pb.TagNumber(3) set name($core.String v) { $_setString(2, v); } @$pb.TagNumber(3) $core.bool hasName() => $_has(2); @$pb.TagNumber(3) void clearName() => clearField(3); @$pb.TagNumber(4) Outbound get outbound => $_getN(3); @$pb.TagNumber(4) set outbound(Outbound v) { setField(4, v); } @$pb.TagNumber(4) $core.bool hasOutbound() => $_has(3); @$pb.TagNumber(4) void clearOutbound() => clearField(4); @$pb.TagNumber(5) $core.List<$core.String> get ruleSets => $_getList(4); @$pb.TagNumber(6) $core.List<$core.String> get packageNames => $_getList(5); @$pb.TagNumber(7) $core.List<$core.String> get processNames => $_getList(6); @$pb.TagNumber(8) $core.List<$core.String> get processPaths => $_getList(7); @$pb.TagNumber(9) Network get network => $_getN(8); @$pb.TagNumber(9) set network(Network v) { setField(9, v); } @$pb.TagNumber(9) $core.bool hasNetwork() => $_has(8); @$pb.TagNumber(9) void clearNetwork() => clearField(9); @$pb.TagNumber(10) $core.List<$core.String> get portRanges => $_getList(9); @$pb.TagNumber(11) $core.List<$core.String> get sourcePortRanges => $_getList(10); @$pb.TagNumber(12) $core.List get protocols => $_getList(11); @$pb.TagNumber(13) $core.List<$core.String> get ipCidrs => $_getList(12); @$pb.TagNumber(14) $core.List<$core.String> get sourceIpCidrs => $_getList(13); @$pb.TagNumber(15) $core.List<$core.String> get domains => $_getList(14); @$pb.TagNumber(16) $core.List<$core.String> get domainSuffixes => $_getList(15); @$pb.TagNumber(17) $core.List<$core.String> get domainKeywords => $_getList(16); @$pb.TagNumber(18) $core.List<$core.String> get domainRegexes => $_getList(17); } ================================================ FILE: lib/hiddifycore/generated/v2/config/route_rule.pbenum.dart ================================================ /// // Generated code. Do not modify. // source: v2/config/route_rule.proto // // @dart = 2.12 // ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name // ignore_for_file: UNDEFINED_SHOWN_NAME import 'dart:core' as $core; import 'package:protobuf/protobuf.dart' as $pb; class Outbound extends $pb.ProtobufEnum { static const Outbound proxy = Outbound._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'proxy'); static const Outbound direct = Outbound._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'direct'); static const Outbound direct_with_fragment = Outbound._(2, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'direct_with_fragment'); static const Outbound block = Outbound._(3, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'block'); static const $core.List values = [ proxy, direct, direct_with_fragment, block, ]; static final $core.Map<$core.int, Outbound> _byValue = $pb.ProtobufEnum.initByValue(values); static Outbound? valueOf($core.int value) => _byValue[value]; const Outbound._($core.int v, $core.String n) : super(v, n); } class Network extends $pb.ProtobufEnum { static const Network all = Network._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'all'); static const Network tcp = Network._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'tcp'); static const Network udp = Network._(2, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'udp'); static const $core.List values = [ all, tcp, udp, ]; static final $core.Map<$core.int, Network> _byValue = $pb.ProtobufEnum.initByValue(values); static Network? valueOf($core.int value) => _byValue[value]; const Network._($core.int v, $core.String n) : super(v, n); } class Protocol extends $pb.ProtobufEnum { static const Protocol tls = Protocol._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'tls'); static const Protocol http = Protocol._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'http'); static const Protocol quic = Protocol._(2, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'quic'); static const Protocol stun = Protocol._(3, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'stun'); static const Protocol dns = Protocol._(4, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'dns'); static const Protocol bittorrent = Protocol._(5, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'bittorrent'); static const $core.List values = [ tls, http, quic, stun, dns, bittorrent, ]; static final $core.Map<$core.int, Protocol> _byValue = $pb.ProtobufEnum.initByValue(values); static Protocol? valueOf($core.int value) => _byValue[value]; const Protocol._($core.int v, $core.String n) : super(v, n); } ================================================ FILE: lib/hiddifycore/generated/v2/config/route_rule.pbjson.dart ================================================ /// // Generated code. Do not modify. // source: v2/config/route_rule.proto // // @dart = 2.12 // ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,deprecated_member_use_from_same_package,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name import 'dart:core' as $core; import 'dart:convert' as $convert; import 'dart:typed_data' as $typed_data; @$core.Deprecated('Use outboundDescriptor instead') const Outbound$json = const { '1': 'Outbound', '2': const [ const {'1': 'proxy', '2': 0}, const {'1': 'direct', '2': 1}, const {'1': 'direct_with_fragment', '2': 2}, const {'1': 'block', '2': 3}, ], }; /// Descriptor for `Outbound`. Decode as a `google.protobuf.EnumDescriptorProto`. final $typed_data.Uint8List outboundDescriptor = $convert.base64Decode('CghPdXRib3VuZBIJCgVwcm94eRAAEgoKBmRpcmVjdBABEhgKFGRpcmVjdF93aXRoX2ZyYWdtZW50EAISCQoFYmxvY2sQAw=='); @$core.Deprecated('Use networkDescriptor instead') const Network$json = const { '1': 'Network', '2': const [ const {'1': 'all', '2': 0}, const {'1': 'tcp', '2': 1}, const {'1': 'udp', '2': 2}, ], }; /// Descriptor for `Network`. Decode as a `google.protobuf.EnumDescriptorProto`. final $typed_data.Uint8List networkDescriptor = $convert.base64Decode('CgdOZXR3b3JrEgcKA2FsbBAAEgcKA3RjcBABEgcKA3VkcBAC'); @$core.Deprecated('Use protocolDescriptor instead') const Protocol$json = const { '1': 'Protocol', '2': const [ const {'1': 'tls', '2': 0}, const {'1': 'http', '2': 1}, const {'1': 'quic', '2': 2}, const {'1': 'stun', '2': 3}, const {'1': 'dns', '2': 4}, const {'1': 'bittorrent', '2': 5}, ], }; /// Descriptor for `Protocol`. Decode as a `google.protobuf.EnumDescriptorProto`. final $typed_data.Uint8List protocolDescriptor = $convert.base64Decode('CghQcm90b2NvbBIHCgN0bHMQABIICgRodHRwEAESCAoEcXVpYxACEggKBHN0dW4QAxIHCgNkbnMQBBIOCgpiaXR0b3JyZW50EAU='); @$core.Deprecated('Use routeRuleDescriptor instead') const RouteRule$json = const { '1': 'RouteRule', '2': const [ const {'1': 'rules', '3': 1, '4': 3, '5': 11, '6': '.config.Rule', '10': 'rules'}, ], }; /// Descriptor for `RouteRule`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List routeRuleDescriptor = $convert.base64Decode('CglSb3V0ZVJ1bGUSIgoFcnVsZXMYASADKAsyDC5jb25maWcuUnVsZVIFcnVsZXM='); @$core.Deprecated('Use ruleDescriptor instead') const Rule$json = const { '1': 'Rule', '2': const [ const {'1': 'list_order', '3': 1, '4': 1, '5': 13, '10': 'list_order'}, const {'1': 'enabled', '3': 2, '4': 1, '5': 8, '10': 'enabled'}, const {'1': 'name', '3': 3, '4': 1, '5': 9, '10': 'name'}, const {'1': 'outbound', '3': 4, '4': 1, '5': 14, '6': '.config.Outbound', '10': 'outbound'}, const {'1': 'rule_sets', '3': 5, '4': 3, '5': 9, '10': 'rule_set'}, const {'1': 'package_names', '3': 6, '4': 3, '5': 9, '10': 'package_name'}, const {'1': 'process_names', '3': 7, '4': 3, '5': 9, '10': 'process_name'}, const {'1': 'process_paths', '3': 8, '4': 3, '5': 9, '10': 'process_path'}, const {'1': 'network', '3': 9, '4': 1, '5': 14, '6': '.config.Network', '10': 'network'}, const {'1': 'port_ranges', '3': 10, '4': 3, '5': 9, '10': 'port_range'}, const {'1': 'source_port_ranges', '3': 11, '4': 3, '5': 9, '10': 'source_port_range'}, const {'1': 'protocols', '3': 12, '4': 3, '5': 14, '6': '.config.Protocol', '10': 'protocol'}, const {'1': 'ip_cidrs', '3': 13, '4': 3, '5': 9, '10': 'ip_cidr'}, const {'1': 'source_ip_cidrs', '3': 14, '4': 3, '5': 9, '10': 'source_ip_cidr'}, const {'1': 'domains', '3': 15, '4': 3, '5': 9, '10': 'domain'}, const {'1': 'domain_suffixes', '3': 16, '4': 3, '5': 9, '10': 'domain_suffix'}, const {'1': 'domain_keywords', '3': 17, '4': 3, '5': 9, '10': 'domain_keyword'}, const {'1': 'domain_regexes', '3': 18, '4': 3, '5': 9, '10': 'domain_regex'}, ], }; /// Descriptor for `Rule`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List ruleDescriptor = $convert.base64Decode('CgRSdWxlEh4KCmxpc3Rfb3JkZXIYASABKA1SCmxpc3Rfb3JkZXISGAoHZW5hYmxlZBgCIAEoCFIHZW5hYmxlZBISCgRuYW1lGAMgASgJUgRuYW1lEiwKCG91dGJvdW5kGAQgASgOMhAuY29uZmlnLk91dGJvdW5kUghvdXRib3VuZBIbCglydWxlX3NldHMYBSADKAlSCHJ1bGVfc2V0EiMKDXBhY2thZ2VfbmFtZXMYBiADKAlSDHBhY2thZ2VfbmFtZRIjCg1wcm9jZXNzX25hbWVzGAcgAygJUgxwcm9jZXNzX25hbWUSIwoNcHJvY2Vzc19wYXRocxgIIAMoCVIMcHJvY2Vzc19wYXRoEikKB25ldHdvcmsYCSABKA4yDy5jb25maWcuTmV0d29ya1IHbmV0d29yaxIfCgtwb3J0X3JhbmdlcxgKIAMoCVIKcG9ydF9yYW5nZRItChJzb3VyY2VfcG9ydF9yYW5nZXMYCyADKAlSEXNvdXJjZV9wb3J0X3JhbmdlEi0KCXByb3RvY29scxgMIAMoDjIQLmNvbmZpZy5Qcm90b2NvbFIIcHJvdG9jb2wSGQoIaXBfY2lkcnMYDSADKAlSB2lwX2NpZHISJwoPc291cmNlX2lwX2NpZHJzGA4gAygJUg5zb3VyY2VfaXBfY2lkchIXCgdkb21haW5zGA8gAygJUgZkb21haW4SJgoPZG9tYWluX3N1ZmZpeGVzGBAgAygJUg1kb21haW5fc3VmZml4EicKD2RvbWFpbl9rZXl3b3JkcxgRIAMoCVIOZG9tYWluX2tleXdvcmQSJAoOZG9tYWluX3JlZ2V4ZXMYEiADKAlSDGRvbWFpbl9yZWdleA=='); ================================================ FILE: lib/hiddifycore/generated/v2/hcommon/common.pb.dart ================================================ /// // Generated code. Do not modify. // source: v2/hcommon/common.proto // // @dart = 2.12 // ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name import 'dart:core' as $core; import 'package:protobuf/protobuf.dart' as $pb; import 'common.pbenum.dart'; export 'common.pbenum.dart'; class Empty extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'Empty', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'hcommon'), createEmptyInstance: create) ..hasRequiredFields = false ; Empty._() : super(); factory Empty() => create(); factory Empty.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory Empty.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') Empty clone() => Empty()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') Empty copyWith(void Function(Empty) updates) => super.copyWith((message) => updates(message as Empty)) as Empty; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static Empty create() => Empty._(); Empty createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static Empty getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static Empty? _defaultInstance; } class Response extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'Response', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'hcommon'), createEmptyInstance: create) ..e(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'code', $pb.PbFieldType.OE, defaultOrMaker: ResponseCode.OK, valueOf: ResponseCode.valueOf, enumValues: ResponseCode.values) ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'message') ..hasRequiredFields = false ; Response._() : super(); factory Response({ ResponseCode? code, $core.String? message, }) { final _result = create(); if (code != null) { _result.code = code; } if (message != null) { _result.message = message; } return _result; } factory Response.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory Response.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') Response clone() => Response()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') Response copyWith(void Function(Response) updates) => super.copyWith((message) => updates(message as Response)) as Response; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static Response create() => Response._(); Response createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static Response getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static Response? _defaultInstance; @$pb.TagNumber(1) ResponseCode get code => $_getN(0); @$pb.TagNumber(1) set code(ResponseCode v) { setField(1, v); } @$pb.TagNumber(1) $core.bool hasCode() => $_has(0); @$pb.TagNumber(1) void clearCode() => clearField(1); @$pb.TagNumber(2) $core.String get message => $_getSZ(1); @$pb.TagNumber(2) set message($core.String v) { $_setString(1, v); } @$pb.TagNumber(2) $core.bool hasMessage() => $_has(1); @$pb.TagNumber(2) void clearMessage() => clearField(2); } ================================================ FILE: lib/hiddifycore/generated/v2/hcommon/common.pbenum.dart ================================================ /// // Generated code. Do not modify. // source: v2/hcommon/common.proto // // @dart = 2.12 // ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name // ignore_for_file: UNDEFINED_SHOWN_NAME import 'dart:core' as $core; import 'package:protobuf/protobuf.dart' as $pb; class ResponseCode extends $pb.ProtobufEnum { static const ResponseCode OK = ResponseCode._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'OK'); static const ResponseCode FAILED = ResponseCode._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'FAILED'); static const ResponseCode AUTH_NEED = ResponseCode._(2, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'AUTH_NEED'); static const $core.List values = [ OK, FAILED, AUTH_NEED, ]; static final $core.Map<$core.int, ResponseCode> _byValue = $pb.ProtobufEnum.initByValue(values); static ResponseCode? valueOf($core.int value) => _byValue[value]; const ResponseCode._($core.int v, $core.String n) : super(v, n); } ================================================ FILE: lib/hiddifycore/generated/v2/hcommon/common.pbjson.dart ================================================ /// // Generated code. Do not modify. // source: v2/hcommon/common.proto // // @dart = 2.12 // ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,deprecated_member_use_from_same_package,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name import 'dart:core' as $core; import 'dart:convert' as $convert; import 'dart:typed_data' as $typed_data; @$core.Deprecated('Use responseCodeDescriptor instead') const ResponseCode$json = const { '1': 'ResponseCode', '2': const [ const {'1': 'OK', '2': 0}, const {'1': 'FAILED', '2': 1}, const {'1': 'AUTH_NEED', '2': 2}, ], }; /// Descriptor for `ResponseCode`. Decode as a `google.protobuf.EnumDescriptorProto`. final $typed_data.Uint8List responseCodeDescriptor = $convert.base64Decode('CgxSZXNwb25zZUNvZGUSBgoCT0sQABIKCgZGQUlMRUQQARINCglBVVRIX05FRUQQAg=='); @$core.Deprecated('Use emptyDescriptor instead') const Empty$json = const { '1': 'Empty', }; /// Descriptor for `Empty`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List emptyDescriptor = $convert.base64Decode('CgVFbXB0eQ=='); @$core.Deprecated('Use responseDescriptor instead') const Response$json = const { '1': 'Response', '2': const [ const {'1': 'code', '3': 1, '4': 1, '5': 14, '6': '.hcommon.ResponseCode', '10': 'code'}, const {'1': 'message', '3': 2, '4': 1, '5': 9, '10': 'message'}, ], }; /// Descriptor for `Response`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List responseDescriptor = $convert.base64Decode('CghSZXNwb25zZRIpCgRjb2RlGAEgASgOMhUuaGNvbW1vbi5SZXNwb25zZUNvZGVSBGNvZGUSGAoHbWVzc2FnZRgCIAEoCVIHbWVzc2FnZQ=='); ================================================ FILE: lib/hiddifycore/generated/v2/hcore/hcore.pb.dart ================================================ /// // Generated code. Do not modify. // source: v2/hcore/hcore.proto // // @dart = 2.12 // ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name import 'dart:core' as $core; import 'package:fixnum/fixnum.dart' as $fixnum; import 'package:protobuf/protobuf.dart' as $pb; import '../../google/protobuf/timestamp.pb.dart' as $7; import 'hcore.pbenum.dart'; import '../hcommon/common.pbenum.dart' as $1; export 'hcore.pbenum.dart'; class CoreInfoResponse extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'CoreInfoResponse', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'hcore'), createEmptyInstance: create) ..e(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'coreState', $pb.PbFieldType.OE, defaultOrMaker: CoreStates.STOPPED, valueOf: CoreStates.valueOf, enumValues: CoreStates.values) ..e(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'messageType', $pb.PbFieldType.OE, defaultOrMaker: MessageType.EMPTY, valueOf: MessageType.valueOf, enumValues: MessageType.values) ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'message') ..hasRequiredFields = false ; CoreInfoResponse._() : super(); factory CoreInfoResponse({ CoreStates? coreState, MessageType? messageType, $core.String? message, }) { final _result = create(); if (coreState != null) { _result.coreState = coreState; } if (messageType != null) { _result.messageType = messageType; } if (message != null) { _result.message = message; } return _result; } factory CoreInfoResponse.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory CoreInfoResponse.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') CoreInfoResponse clone() => CoreInfoResponse()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') CoreInfoResponse copyWith(void Function(CoreInfoResponse) updates) => super.copyWith((message) => updates(message as CoreInfoResponse)) as CoreInfoResponse; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static CoreInfoResponse create() => CoreInfoResponse._(); CoreInfoResponse createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static CoreInfoResponse getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static CoreInfoResponse? _defaultInstance; @$pb.TagNumber(1) CoreStates get coreState => $_getN(0); @$pb.TagNumber(1) set coreState(CoreStates v) { setField(1, v); } @$pb.TagNumber(1) $core.bool hasCoreState() => $_has(0); @$pb.TagNumber(1) void clearCoreState() => clearField(1); @$pb.TagNumber(2) MessageType get messageType => $_getN(1); @$pb.TagNumber(2) set messageType(MessageType v) { setField(2, v); } @$pb.TagNumber(2) $core.bool hasMessageType() => $_has(1); @$pb.TagNumber(2) void clearMessageType() => clearField(2); @$pb.TagNumber(3) $core.String get message => $_getSZ(2); @$pb.TagNumber(3) set message($core.String v) { $_setString(2, v); } @$pb.TagNumber(3) $core.bool hasMessage() => $_has(2); @$pb.TagNumber(3) void clearMessage() => clearField(3); } class StartRequest extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'StartRequest', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'hcore'), createEmptyInstance: create) ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'configPath') ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'configContent') ..aOB(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'disableMemoryLimit') ..aOB(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'delayStart') ..aOB(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'enableOldCommandServer') ..aOB(6, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'enableRawConfig') ..aOS(7, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'configName') ..hasRequiredFields = false ; StartRequest._() : super(); factory StartRequest({ $core.String? configPath, $core.String? configContent, $core.bool? disableMemoryLimit, $core.bool? delayStart, $core.bool? enableOldCommandServer, $core.bool? enableRawConfig, $core.String? configName, }) { final _result = create(); if (configPath != null) { _result.configPath = configPath; } if (configContent != null) { _result.configContent = configContent; } if (disableMemoryLimit != null) { _result.disableMemoryLimit = disableMemoryLimit; } if (delayStart != null) { _result.delayStart = delayStart; } if (enableOldCommandServer != null) { _result.enableOldCommandServer = enableOldCommandServer; } if (enableRawConfig != null) { _result.enableRawConfig = enableRawConfig; } if (configName != null) { _result.configName = configName; } return _result; } factory StartRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory StartRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') StartRequest clone() => StartRequest()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') StartRequest copyWith(void Function(StartRequest) updates) => super.copyWith((message) => updates(message as StartRequest)) as StartRequest; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static StartRequest create() => StartRequest._(); StartRequest createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static StartRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static StartRequest? _defaultInstance; @$pb.TagNumber(1) $core.String get configPath => $_getSZ(0); @$pb.TagNumber(1) set configPath($core.String v) { $_setString(0, v); } @$pb.TagNumber(1) $core.bool hasConfigPath() => $_has(0); @$pb.TagNumber(1) void clearConfigPath() => clearField(1); @$pb.TagNumber(2) $core.String get configContent => $_getSZ(1); @$pb.TagNumber(2) set configContent($core.String v) { $_setString(1, v); } @$pb.TagNumber(2) $core.bool hasConfigContent() => $_has(1); @$pb.TagNumber(2) void clearConfigContent() => clearField(2); @$pb.TagNumber(3) $core.bool get disableMemoryLimit => $_getBF(2); @$pb.TagNumber(3) set disableMemoryLimit($core.bool v) { $_setBool(2, v); } @$pb.TagNumber(3) $core.bool hasDisableMemoryLimit() => $_has(2); @$pb.TagNumber(3) void clearDisableMemoryLimit() => clearField(3); @$pb.TagNumber(4) $core.bool get delayStart => $_getBF(3); @$pb.TagNumber(4) set delayStart($core.bool v) { $_setBool(3, v); } @$pb.TagNumber(4) $core.bool hasDelayStart() => $_has(3); @$pb.TagNumber(4) void clearDelayStart() => clearField(4); @$pb.TagNumber(5) $core.bool get enableOldCommandServer => $_getBF(4); @$pb.TagNumber(5) set enableOldCommandServer($core.bool v) { $_setBool(4, v); } @$pb.TagNumber(5) $core.bool hasEnableOldCommandServer() => $_has(4); @$pb.TagNumber(5) void clearEnableOldCommandServer() => clearField(5); @$pb.TagNumber(6) $core.bool get enableRawConfig => $_getBF(5); @$pb.TagNumber(6) set enableRawConfig($core.bool v) { $_setBool(5, v); } @$pb.TagNumber(6) $core.bool hasEnableRawConfig() => $_has(5); @$pb.TagNumber(6) void clearEnableRawConfig() => clearField(6); @$pb.TagNumber(7) $core.String get configName => $_getSZ(6); @$pb.TagNumber(7) set configName($core.String v) { $_setString(6, v); } @$pb.TagNumber(7) $core.bool hasConfigName() => $_has(6); @$pb.TagNumber(7) void clearConfigName() => clearField(7); } class CloseRequest extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'CloseRequest', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'hcore'), createEmptyInstance: create) ..e(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'mode', $pb.PbFieldType.OE, defaultOrMaker: SetupMode.OLD, valueOf: SetupMode.valueOf, enumValues: SetupMode.values) ..hasRequiredFields = false ; CloseRequest._() : super(); factory CloseRequest({ SetupMode? mode, }) { final _result = create(); if (mode != null) { _result.mode = mode; } return _result; } factory CloseRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory CloseRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') CloseRequest clone() => CloseRequest()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') CloseRequest copyWith(void Function(CloseRequest) updates) => super.copyWith((message) => updates(message as CloseRequest)) as CloseRequest; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static CloseRequest create() => CloseRequest._(); CloseRequest createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static CloseRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static CloseRequest? _defaultInstance; @$pb.TagNumber(1) SetupMode get mode => $_getN(0); @$pb.TagNumber(1) set mode(SetupMode v) { setField(1, v); } @$pb.TagNumber(1) $core.bool hasMode() => $_has(0); @$pb.TagNumber(1) void clearMode() => clearField(1); } class SetupRequest extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'SetupRequest', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'hcore'), createEmptyInstance: create) ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'basePath') ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'workingDir') ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'tempDir') ..aInt64(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'flutterStatusPort') ..aOS(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'listen') ..aOS(6, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'secret') ..aOB(7, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'debug') ..e(8, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'mode', $pb.PbFieldType.OE, defaultOrMaker: SetupMode.OLD, valueOf: SetupMode.valueOf, enumValues: SetupMode.values) ..aOB(9, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'fixAndroidStack') ..hasRequiredFields = false ; SetupRequest._() : super(); factory SetupRequest({ $core.String? basePath, $core.String? workingDir, $core.String? tempDir, $fixnum.Int64? flutterStatusPort, $core.String? listen, $core.String? secret, $core.bool? debug, SetupMode? mode, $core.bool? fixAndroidStack, }) { final _result = create(); if (basePath != null) { _result.basePath = basePath; } if (workingDir != null) { _result.workingDir = workingDir; } if (tempDir != null) { _result.tempDir = tempDir; } if (flutterStatusPort != null) { _result.flutterStatusPort = flutterStatusPort; } if (listen != null) { _result.listen = listen; } if (secret != null) { _result.secret = secret; } if (debug != null) { _result.debug = debug; } if (mode != null) { _result.mode = mode; } if (fixAndroidStack != null) { _result.fixAndroidStack = fixAndroidStack; } return _result; } factory SetupRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory SetupRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') SetupRequest clone() => SetupRequest()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') SetupRequest copyWith(void Function(SetupRequest) updates) => super.copyWith((message) => updates(message as SetupRequest)) as SetupRequest; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static SetupRequest create() => SetupRequest._(); SetupRequest createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static SetupRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static SetupRequest? _defaultInstance; @$pb.TagNumber(1) $core.String get basePath => $_getSZ(0); @$pb.TagNumber(1) set basePath($core.String v) { $_setString(0, v); } @$pb.TagNumber(1) $core.bool hasBasePath() => $_has(0); @$pb.TagNumber(1) void clearBasePath() => clearField(1); @$pb.TagNumber(2) $core.String get workingDir => $_getSZ(1); @$pb.TagNumber(2) set workingDir($core.String v) { $_setString(1, v); } @$pb.TagNumber(2) $core.bool hasWorkingDir() => $_has(1); @$pb.TagNumber(2) void clearWorkingDir() => clearField(2); @$pb.TagNumber(3) $core.String get tempDir => $_getSZ(2); @$pb.TagNumber(3) set tempDir($core.String v) { $_setString(2, v); } @$pb.TagNumber(3) $core.bool hasTempDir() => $_has(2); @$pb.TagNumber(3) void clearTempDir() => clearField(3); @$pb.TagNumber(4) $fixnum.Int64 get flutterStatusPort => $_getI64(3); @$pb.TagNumber(4) set flutterStatusPort($fixnum.Int64 v) { $_setInt64(3, v); } @$pb.TagNumber(4) $core.bool hasFlutterStatusPort() => $_has(3); @$pb.TagNumber(4) void clearFlutterStatusPort() => clearField(4); @$pb.TagNumber(5) $core.String get listen => $_getSZ(4); @$pb.TagNumber(5) set listen($core.String v) { $_setString(4, v); } @$pb.TagNumber(5) $core.bool hasListen() => $_has(4); @$pb.TagNumber(5) void clearListen() => clearField(5); @$pb.TagNumber(6) $core.String get secret => $_getSZ(5); @$pb.TagNumber(6) set secret($core.String v) { $_setString(5, v); } @$pb.TagNumber(6) $core.bool hasSecret() => $_has(5); @$pb.TagNumber(6) void clearSecret() => clearField(6); @$pb.TagNumber(7) $core.bool get debug => $_getBF(6); @$pb.TagNumber(7) set debug($core.bool v) { $_setBool(6, v); } @$pb.TagNumber(7) $core.bool hasDebug() => $_has(6); @$pb.TagNumber(7) void clearDebug() => clearField(7); @$pb.TagNumber(8) SetupMode get mode => $_getN(7); @$pb.TagNumber(8) set mode(SetupMode v) { setField(8, v); } @$pb.TagNumber(8) $core.bool hasMode() => $_has(7); @$pb.TagNumber(8) void clearMode() => clearField(8); @$pb.TagNumber(9) $core.bool get fixAndroidStack => $_getBF(8); @$pb.TagNumber(9) set fixAndroidStack($core.bool v) { $_setBool(8, v); } @$pb.TagNumber(9) $core.bool hasFixAndroidStack() => $_has(8); @$pb.TagNumber(9) void clearFixAndroidStack() => clearField(9); } class SystemInfo extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'SystemInfo', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'hcore'), createEmptyInstance: create) ..aInt64(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'memory') ..a<$core.int>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'goroutines', $pb.PbFieldType.O3) ..a<$core.int>(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'connectionsIn', $pb.PbFieldType.O3) ..a<$core.int>(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'connectionsOut', $pb.PbFieldType.O3) ..aOB(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'trafficAvailable') ..aInt64(6, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'uplink') ..aInt64(7, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'downlink') ..aInt64(8, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'uplinkTotal') ..aInt64(9, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'downlinkTotal') ..aOS(10, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'currentOutbound') ..aOS(11, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'currentProfile') ..hasRequiredFields = false ; SystemInfo._() : super(); factory SystemInfo({ $fixnum.Int64? memory, $core.int? goroutines, $core.int? connectionsIn, $core.int? connectionsOut, $core.bool? trafficAvailable, $fixnum.Int64? uplink, $fixnum.Int64? downlink, $fixnum.Int64? uplinkTotal, $fixnum.Int64? downlinkTotal, $core.String? currentOutbound, $core.String? currentProfile, }) { final _result = create(); if (memory != null) { _result.memory = memory; } if (goroutines != null) { _result.goroutines = goroutines; } if (connectionsIn != null) { _result.connectionsIn = connectionsIn; } if (connectionsOut != null) { _result.connectionsOut = connectionsOut; } if (trafficAvailable != null) { _result.trafficAvailable = trafficAvailable; } if (uplink != null) { _result.uplink = uplink; } if (downlink != null) { _result.downlink = downlink; } if (uplinkTotal != null) { _result.uplinkTotal = uplinkTotal; } if (downlinkTotal != null) { _result.downlinkTotal = downlinkTotal; } if (currentOutbound != null) { _result.currentOutbound = currentOutbound; } if (currentProfile != null) { _result.currentProfile = currentProfile; } return _result; } factory SystemInfo.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory SystemInfo.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') SystemInfo clone() => SystemInfo()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') SystemInfo copyWith(void Function(SystemInfo) updates) => super.copyWith((message) => updates(message as SystemInfo)) as SystemInfo; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static SystemInfo create() => SystemInfo._(); SystemInfo createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static SystemInfo getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static SystemInfo? _defaultInstance; @$pb.TagNumber(1) $fixnum.Int64 get memory => $_getI64(0); @$pb.TagNumber(1) set memory($fixnum.Int64 v) { $_setInt64(0, v); } @$pb.TagNumber(1) $core.bool hasMemory() => $_has(0); @$pb.TagNumber(1) void clearMemory() => clearField(1); @$pb.TagNumber(2) $core.int get goroutines => $_getIZ(1); @$pb.TagNumber(2) set goroutines($core.int v) { $_setSignedInt32(1, v); } @$pb.TagNumber(2) $core.bool hasGoroutines() => $_has(1); @$pb.TagNumber(2) void clearGoroutines() => clearField(2); @$pb.TagNumber(3) $core.int get connectionsIn => $_getIZ(2); @$pb.TagNumber(3) set connectionsIn($core.int v) { $_setSignedInt32(2, v); } @$pb.TagNumber(3) $core.bool hasConnectionsIn() => $_has(2); @$pb.TagNumber(3) void clearConnectionsIn() => clearField(3); @$pb.TagNumber(4) $core.int get connectionsOut => $_getIZ(3); @$pb.TagNumber(4) set connectionsOut($core.int v) { $_setSignedInt32(3, v); } @$pb.TagNumber(4) $core.bool hasConnectionsOut() => $_has(3); @$pb.TagNumber(4) void clearConnectionsOut() => clearField(4); @$pb.TagNumber(5) $core.bool get trafficAvailable => $_getBF(4); @$pb.TagNumber(5) set trafficAvailable($core.bool v) { $_setBool(4, v); } @$pb.TagNumber(5) $core.bool hasTrafficAvailable() => $_has(4); @$pb.TagNumber(5) void clearTrafficAvailable() => clearField(5); @$pb.TagNumber(6) $fixnum.Int64 get uplink => $_getI64(5); @$pb.TagNumber(6) set uplink($fixnum.Int64 v) { $_setInt64(5, v); } @$pb.TagNumber(6) $core.bool hasUplink() => $_has(5); @$pb.TagNumber(6) void clearUplink() => clearField(6); @$pb.TagNumber(7) $fixnum.Int64 get downlink => $_getI64(6); @$pb.TagNumber(7) set downlink($fixnum.Int64 v) { $_setInt64(6, v); } @$pb.TagNumber(7) $core.bool hasDownlink() => $_has(6); @$pb.TagNumber(7) void clearDownlink() => clearField(7); @$pb.TagNumber(8) $fixnum.Int64 get uplinkTotal => $_getI64(7); @$pb.TagNumber(8) set uplinkTotal($fixnum.Int64 v) { $_setInt64(7, v); } @$pb.TagNumber(8) $core.bool hasUplinkTotal() => $_has(7); @$pb.TagNumber(8) void clearUplinkTotal() => clearField(8); @$pb.TagNumber(9) $fixnum.Int64 get downlinkTotal => $_getI64(8); @$pb.TagNumber(9) set downlinkTotal($fixnum.Int64 v) { $_setInt64(8, v); } @$pb.TagNumber(9) $core.bool hasDownlinkTotal() => $_has(8); @$pb.TagNumber(9) void clearDownlinkTotal() => clearField(9); @$pb.TagNumber(10) $core.String get currentOutbound => $_getSZ(9); @$pb.TagNumber(10) set currentOutbound($core.String v) { $_setString(9, v); } @$pb.TagNumber(10) $core.bool hasCurrentOutbound() => $_has(9); @$pb.TagNumber(10) void clearCurrentOutbound() => clearField(10); @$pb.TagNumber(11) $core.String get currentProfile => $_getSZ(10); @$pb.TagNumber(11) set currentProfile($core.String v) { $_setString(10, v); } @$pb.TagNumber(11) $core.bool hasCurrentProfile() => $_has(10); @$pb.TagNumber(11) void clearCurrentProfile() => clearField(11); } class OutboundInfo extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'OutboundInfo', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'hcore'), createEmptyInstance: create) ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'tag') ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'type') ..aOM<$7.Timestamp>(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'urlTestTime', subBuilder: $7.Timestamp.create) ..a<$core.int>(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'urlTestDelay', $pb.PbFieldType.O3) ..aOM(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'ipinfo', subBuilder: IpInfo.create) ..aOB(6, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'isSelected') ..aOB(7, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'isGroup') ..aOB(8, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'isSecure') ..aOB(9, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'isVisible') ..a<$core.int>(10, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'port', $pb.PbFieldType.OU3) ..aOS(11, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'host') ..aOS(12, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'tagDisplay') ..aOS(13, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'groupSelectedTag') ..aOS(14, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'groupSelectedTagDisplay') ..aInt64(15, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'upload') ..aInt64(16, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'download') ..hasRequiredFields = false ; OutboundInfo._() : super(); factory OutboundInfo({ $core.String? tag, $core.String? type, $7.Timestamp? urlTestTime, $core.int? urlTestDelay, IpInfo? ipinfo, $core.bool? isSelected, $core.bool? isGroup, $core.bool? isSecure, $core.bool? isVisible, $core.int? port, $core.String? host, $core.String? tagDisplay, $core.String? groupSelectedTag, $core.String? groupSelectedTagDisplay, $fixnum.Int64? upload, $fixnum.Int64? download, }) { final _result = create(); if (tag != null) { _result.tag = tag; } if (type != null) { _result.type = type; } if (urlTestTime != null) { _result.urlTestTime = urlTestTime; } if (urlTestDelay != null) { _result.urlTestDelay = urlTestDelay; } if (ipinfo != null) { _result.ipinfo = ipinfo; } if (isSelected != null) { _result.isSelected = isSelected; } if (isGroup != null) { _result.isGroup = isGroup; } if (isSecure != null) { _result.isSecure = isSecure; } if (isVisible != null) { _result.isVisible = isVisible; } if (port != null) { _result.port = port; } if (host != null) { _result.host = host; } if (tagDisplay != null) { _result.tagDisplay = tagDisplay; } if (groupSelectedTag != null) { _result.groupSelectedTag = groupSelectedTag; } if (groupSelectedTagDisplay != null) { _result.groupSelectedTagDisplay = groupSelectedTagDisplay; } if (upload != null) { _result.upload = upload; } if (download != null) { _result.download = download; } return _result; } factory OutboundInfo.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory OutboundInfo.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') OutboundInfo clone() => OutboundInfo()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') OutboundInfo copyWith(void Function(OutboundInfo) updates) => super.copyWith((message) => updates(message as OutboundInfo)) as OutboundInfo; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static OutboundInfo create() => OutboundInfo._(); OutboundInfo createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static OutboundInfo getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static OutboundInfo? _defaultInstance; @$pb.TagNumber(1) $core.String get tag => $_getSZ(0); @$pb.TagNumber(1) set tag($core.String v) { $_setString(0, v); } @$pb.TagNumber(1) $core.bool hasTag() => $_has(0); @$pb.TagNumber(1) void clearTag() => clearField(1); @$pb.TagNumber(2) $core.String get type => $_getSZ(1); @$pb.TagNumber(2) set type($core.String v) { $_setString(1, v); } @$pb.TagNumber(2) $core.bool hasType() => $_has(1); @$pb.TagNumber(2) void clearType() => clearField(2); @$pb.TagNumber(3) $7.Timestamp get urlTestTime => $_getN(2); @$pb.TagNumber(3) set urlTestTime($7.Timestamp v) { setField(3, v); } @$pb.TagNumber(3) $core.bool hasUrlTestTime() => $_has(2); @$pb.TagNumber(3) void clearUrlTestTime() => clearField(3); @$pb.TagNumber(3) $7.Timestamp ensureUrlTestTime() => $_ensure(2); @$pb.TagNumber(4) $core.int get urlTestDelay => $_getIZ(3); @$pb.TagNumber(4) set urlTestDelay($core.int v) { $_setSignedInt32(3, v); } @$pb.TagNumber(4) $core.bool hasUrlTestDelay() => $_has(3); @$pb.TagNumber(4) void clearUrlTestDelay() => clearField(4); @$pb.TagNumber(5) IpInfo get ipinfo => $_getN(4); @$pb.TagNumber(5) set ipinfo(IpInfo v) { setField(5, v); } @$pb.TagNumber(5) $core.bool hasIpinfo() => $_has(4); @$pb.TagNumber(5) void clearIpinfo() => clearField(5); @$pb.TagNumber(5) IpInfo ensureIpinfo() => $_ensure(4); @$pb.TagNumber(6) $core.bool get isSelected => $_getBF(5); @$pb.TagNumber(6) set isSelected($core.bool v) { $_setBool(5, v); } @$pb.TagNumber(6) $core.bool hasIsSelected() => $_has(5); @$pb.TagNumber(6) void clearIsSelected() => clearField(6); @$pb.TagNumber(7) $core.bool get isGroup => $_getBF(6); @$pb.TagNumber(7) set isGroup($core.bool v) { $_setBool(6, v); } @$pb.TagNumber(7) $core.bool hasIsGroup() => $_has(6); @$pb.TagNumber(7) void clearIsGroup() => clearField(7); @$pb.TagNumber(8) $core.bool get isSecure => $_getBF(7); @$pb.TagNumber(8) set isSecure($core.bool v) { $_setBool(7, v); } @$pb.TagNumber(8) $core.bool hasIsSecure() => $_has(7); @$pb.TagNumber(8) void clearIsSecure() => clearField(8); @$pb.TagNumber(9) $core.bool get isVisible => $_getBF(8); @$pb.TagNumber(9) set isVisible($core.bool v) { $_setBool(8, v); } @$pb.TagNumber(9) $core.bool hasIsVisible() => $_has(8); @$pb.TagNumber(9) void clearIsVisible() => clearField(9); @$pb.TagNumber(10) $core.int get port => $_getIZ(9); @$pb.TagNumber(10) set port($core.int v) { $_setUnsignedInt32(9, v); } @$pb.TagNumber(10) $core.bool hasPort() => $_has(9); @$pb.TagNumber(10) void clearPort() => clearField(10); @$pb.TagNumber(11) $core.String get host => $_getSZ(10); @$pb.TagNumber(11) set host($core.String v) { $_setString(10, v); } @$pb.TagNumber(11) $core.bool hasHost() => $_has(10); @$pb.TagNumber(11) void clearHost() => clearField(11); @$pb.TagNumber(12) $core.String get tagDisplay => $_getSZ(11); @$pb.TagNumber(12) set tagDisplay($core.String v) { $_setString(11, v); } @$pb.TagNumber(12) $core.bool hasTagDisplay() => $_has(11); @$pb.TagNumber(12) void clearTagDisplay() => clearField(12); @$pb.TagNumber(13) $core.String get groupSelectedTag => $_getSZ(12); @$pb.TagNumber(13) set groupSelectedTag($core.String v) { $_setString(12, v); } @$pb.TagNumber(13) $core.bool hasGroupSelectedTag() => $_has(12); @$pb.TagNumber(13) void clearGroupSelectedTag() => clearField(13); @$pb.TagNumber(14) $core.String get groupSelectedTagDisplay => $_getSZ(13); @$pb.TagNumber(14) set groupSelectedTagDisplay($core.String v) { $_setString(13, v); } @$pb.TagNumber(14) $core.bool hasGroupSelectedTagDisplay() => $_has(13); @$pb.TagNumber(14) void clearGroupSelectedTagDisplay() => clearField(14); @$pb.TagNumber(15) $fixnum.Int64 get upload => $_getI64(14); @$pb.TagNumber(15) set upload($fixnum.Int64 v) { $_setInt64(14, v); } @$pb.TagNumber(15) $core.bool hasUpload() => $_has(14); @$pb.TagNumber(15) void clearUpload() => clearField(15); @$pb.TagNumber(16) $fixnum.Int64 get download => $_getI64(15); @$pb.TagNumber(16) set download($fixnum.Int64 v) { $_setInt64(15, v); } @$pb.TagNumber(16) $core.bool hasDownload() => $_has(15); @$pb.TagNumber(16) void clearDownload() => clearField(16); } class IpInfo extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'IpInfo', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'hcore'), createEmptyInstance: create) ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'ip') ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'country_code') ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'region') ..aOS(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'city') ..a<$core.int>(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'asn', $pb.PbFieldType.O3) ..aOS(6, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'org') ..a<$core.double>(7, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'latitude', $pb.PbFieldType.OD) ..a<$core.double>(8, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'longitude', $pb.PbFieldType.OD) ..aOS(9, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'postal_code') ..hasRequiredFields = false ; IpInfo._() : super(); factory IpInfo({ $core.String? ip, $core.String? countryCode, $core.String? region, $core.String? city, $core.int? asn, $core.String? org, $core.double? latitude, $core.double? longitude, $core.String? postalCode, }) { final _result = create(); if (ip != null) { _result.ip = ip; } if (countryCode != null) { _result.countryCode = countryCode; } if (region != null) { _result.region = region; } if (city != null) { _result.city = city; } if (asn != null) { _result.asn = asn; } if (org != null) { _result.org = org; } if (latitude != null) { _result.latitude = latitude; } if (longitude != null) { _result.longitude = longitude; } if (postalCode != null) { _result.postalCode = postalCode; } return _result; } factory IpInfo.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory IpInfo.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') IpInfo clone() => IpInfo()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') IpInfo copyWith(void Function(IpInfo) updates) => super.copyWith((message) => updates(message as IpInfo)) as IpInfo; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static IpInfo create() => IpInfo._(); IpInfo createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static IpInfo getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static IpInfo? _defaultInstance; @$pb.TagNumber(1) $core.String get ip => $_getSZ(0); @$pb.TagNumber(1) set ip($core.String v) { $_setString(0, v); } @$pb.TagNumber(1) $core.bool hasIp() => $_has(0); @$pb.TagNumber(1) void clearIp() => clearField(1); @$pb.TagNumber(2) $core.String get countryCode => $_getSZ(1); @$pb.TagNumber(2) set countryCode($core.String v) { $_setString(1, v); } @$pb.TagNumber(2) $core.bool hasCountryCode() => $_has(1); @$pb.TagNumber(2) void clearCountryCode() => clearField(2); @$pb.TagNumber(3) $core.String get region => $_getSZ(2); @$pb.TagNumber(3) set region($core.String v) { $_setString(2, v); } @$pb.TagNumber(3) $core.bool hasRegion() => $_has(2); @$pb.TagNumber(3) void clearRegion() => clearField(3); @$pb.TagNumber(4) $core.String get city => $_getSZ(3); @$pb.TagNumber(4) set city($core.String v) { $_setString(3, v); } @$pb.TagNumber(4) $core.bool hasCity() => $_has(3); @$pb.TagNumber(4) void clearCity() => clearField(4); @$pb.TagNumber(5) $core.int get asn => $_getIZ(4); @$pb.TagNumber(5) set asn($core.int v) { $_setSignedInt32(4, v); } @$pb.TagNumber(5) $core.bool hasAsn() => $_has(4); @$pb.TagNumber(5) void clearAsn() => clearField(5); @$pb.TagNumber(6) $core.String get org => $_getSZ(5); @$pb.TagNumber(6) set org($core.String v) { $_setString(5, v); } @$pb.TagNumber(6) $core.bool hasOrg() => $_has(5); @$pb.TagNumber(6) void clearOrg() => clearField(6); @$pb.TagNumber(7) $core.double get latitude => $_getN(6); @$pb.TagNumber(7) set latitude($core.double v) { $_setDouble(6, v); } @$pb.TagNumber(7) $core.bool hasLatitude() => $_has(6); @$pb.TagNumber(7) void clearLatitude() => clearField(7); @$pb.TagNumber(8) $core.double get longitude => $_getN(7); @$pb.TagNumber(8) set longitude($core.double v) { $_setDouble(7, v); } @$pb.TagNumber(8) $core.bool hasLongitude() => $_has(7); @$pb.TagNumber(8) void clearLongitude() => clearField(8); @$pb.TagNumber(9) $core.String get postalCode => $_getSZ(8); @$pb.TagNumber(9) set postalCode($core.String v) { $_setString(8, v); } @$pb.TagNumber(9) $core.bool hasPostalCode() => $_has(8); @$pb.TagNumber(9) void clearPostalCode() => clearField(9); } class OutboundGroup extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'OutboundGroup', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'hcore'), createEmptyInstance: create) ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'tag') ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'type') ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'selected') ..aOB(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'selectable') ..aOB(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'IsExpand', protoName: 'Is_expand') ..pc(6, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'items', $pb.PbFieldType.PM, subBuilder: OutboundInfo.create) ..hasRequiredFields = false ; OutboundGroup._() : super(); factory OutboundGroup({ $core.String? tag, $core.String? type, $core.String? selected, $core.bool? selectable, $core.bool? isExpand, $core.Iterable? items, }) { final _result = create(); if (tag != null) { _result.tag = tag; } if (type != null) { _result.type = type; } if (selected != null) { _result.selected = selected; } if (selectable != null) { _result.selectable = selectable; } if (isExpand != null) { _result.isExpand = isExpand; } if (items != null) { _result.items.addAll(items); } return _result; } factory OutboundGroup.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory OutboundGroup.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') OutboundGroup clone() => OutboundGroup()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') OutboundGroup copyWith(void Function(OutboundGroup) updates) => super.copyWith((message) => updates(message as OutboundGroup)) as OutboundGroup; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static OutboundGroup create() => OutboundGroup._(); OutboundGroup createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static OutboundGroup getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static OutboundGroup? _defaultInstance; @$pb.TagNumber(1) $core.String get tag => $_getSZ(0); @$pb.TagNumber(1) set tag($core.String v) { $_setString(0, v); } @$pb.TagNumber(1) $core.bool hasTag() => $_has(0); @$pb.TagNumber(1) void clearTag() => clearField(1); @$pb.TagNumber(2) $core.String get type => $_getSZ(1); @$pb.TagNumber(2) set type($core.String v) { $_setString(1, v); } @$pb.TagNumber(2) $core.bool hasType() => $_has(1); @$pb.TagNumber(2) void clearType() => clearField(2); @$pb.TagNumber(3) $core.String get selected => $_getSZ(2); @$pb.TagNumber(3) set selected($core.String v) { $_setString(2, v); } @$pb.TagNumber(3) $core.bool hasSelected() => $_has(2); @$pb.TagNumber(3) void clearSelected() => clearField(3); @$pb.TagNumber(4) $core.bool get selectable => $_getBF(3); @$pb.TagNumber(4) set selectable($core.bool v) { $_setBool(3, v); } @$pb.TagNumber(4) $core.bool hasSelectable() => $_has(3); @$pb.TagNumber(4) void clearSelectable() => clearField(4); @$pb.TagNumber(5) $core.bool get isExpand => $_getBF(4); @$pb.TagNumber(5) set isExpand($core.bool v) { $_setBool(4, v); } @$pb.TagNumber(5) $core.bool hasIsExpand() => $_has(4); @$pb.TagNumber(5) void clearIsExpand() => clearField(5); @$pb.TagNumber(6) $core.List get items => $_getList(5); } class OutboundGroupList extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'OutboundGroupList', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'hcore'), createEmptyInstance: create) ..pc(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'items', $pb.PbFieldType.PM, subBuilder: OutboundGroup.create) ..hasRequiredFields = false ; OutboundGroupList._() : super(); factory OutboundGroupList({ $core.Iterable? items, }) { final _result = create(); if (items != null) { _result.items.addAll(items); } return _result; } factory OutboundGroupList.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory OutboundGroupList.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') OutboundGroupList clone() => OutboundGroupList()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') OutboundGroupList copyWith(void Function(OutboundGroupList) updates) => super.copyWith((message) => updates(message as OutboundGroupList)) as OutboundGroupList; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static OutboundGroupList create() => OutboundGroupList._(); OutboundGroupList createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static OutboundGroupList getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static OutboundGroupList? _defaultInstance; @$pb.TagNumber(1) $core.List get items => $_getList(0); } class WarpAccount extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'WarpAccount', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'hcore'), createEmptyInstance: create) ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'accountId') ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'accessToken') ..hasRequiredFields = false ; WarpAccount._() : super(); factory WarpAccount({ $core.String? accountId, $core.String? accessToken, }) { final _result = create(); if (accountId != null) { _result.accountId = accountId; } if (accessToken != null) { _result.accessToken = accessToken; } return _result; } factory WarpAccount.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory WarpAccount.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') WarpAccount clone() => WarpAccount()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') WarpAccount copyWith(void Function(WarpAccount) updates) => super.copyWith((message) => updates(message as WarpAccount)) as WarpAccount; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static WarpAccount create() => WarpAccount._(); WarpAccount createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static WarpAccount getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static WarpAccount? _defaultInstance; @$pb.TagNumber(1) $core.String get accountId => $_getSZ(0); @$pb.TagNumber(1) set accountId($core.String v) { $_setString(0, v); } @$pb.TagNumber(1) $core.bool hasAccountId() => $_has(0); @$pb.TagNumber(1) void clearAccountId() => clearField(1); @$pb.TagNumber(2) $core.String get accessToken => $_getSZ(1); @$pb.TagNumber(2) set accessToken($core.String v) { $_setString(1, v); } @$pb.TagNumber(2) $core.bool hasAccessToken() => $_has(1); @$pb.TagNumber(2) void clearAccessToken() => clearField(2); } class WarpWireguardConfig extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'WarpWireguardConfig', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'hcore'), createEmptyInstance: create) ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'private-key', protoName: 'private_key') ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'local-address-ipv4', protoName: 'local_address_ipv4') ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'local-address-ipv6', protoName: 'local_address_ipv6') ..aOS(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'peer-public-key', protoName: 'peer_public_key') ..aOS(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'client-id', protoName: 'client_id') ..hasRequiredFields = false ; WarpWireguardConfig._() : super(); factory WarpWireguardConfig({ $core.String? privateKey, $core.String? localAddressIpv4, $core.String? localAddressIpv6, $core.String? peerPublicKey, $core.String? clientId, }) { final _result = create(); if (privateKey != null) { _result.privateKey = privateKey; } if (localAddressIpv4 != null) { _result.localAddressIpv4 = localAddressIpv4; } if (localAddressIpv6 != null) { _result.localAddressIpv6 = localAddressIpv6; } if (peerPublicKey != null) { _result.peerPublicKey = peerPublicKey; } if (clientId != null) { _result.clientId = clientId; } return _result; } factory WarpWireguardConfig.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory WarpWireguardConfig.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') WarpWireguardConfig clone() => WarpWireguardConfig()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') WarpWireguardConfig copyWith(void Function(WarpWireguardConfig) updates) => super.copyWith((message) => updates(message as WarpWireguardConfig)) as WarpWireguardConfig; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static WarpWireguardConfig create() => WarpWireguardConfig._(); WarpWireguardConfig createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static WarpWireguardConfig getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static WarpWireguardConfig? _defaultInstance; @$pb.TagNumber(1) $core.String get privateKey => $_getSZ(0); @$pb.TagNumber(1) set privateKey($core.String v) { $_setString(0, v); } @$pb.TagNumber(1) $core.bool hasPrivateKey() => $_has(0); @$pb.TagNumber(1) void clearPrivateKey() => clearField(1); @$pb.TagNumber(2) $core.String get localAddressIpv4 => $_getSZ(1); @$pb.TagNumber(2) set localAddressIpv4($core.String v) { $_setString(1, v); } @$pb.TagNumber(2) $core.bool hasLocalAddressIpv4() => $_has(1); @$pb.TagNumber(2) void clearLocalAddressIpv4() => clearField(2); @$pb.TagNumber(3) $core.String get localAddressIpv6 => $_getSZ(2); @$pb.TagNumber(3) set localAddressIpv6($core.String v) { $_setString(2, v); } @$pb.TagNumber(3) $core.bool hasLocalAddressIpv6() => $_has(2); @$pb.TagNumber(3) void clearLocalAddressIpv6() => clearField(3); @$pb.TagNumber(4) $core.String get peerPublicKey => $_getSZ(3); @$pb.TagNumber(4) set peerPublicKey($core.String v) { $_setString(3, v); } @$pb.TagNumber(4) $core.bool hasPeerPublicKey() => $_has(3); @$pb.TagNumber(4) void clearPeerPublicKey() => clearField(4); @$pb.TagNumber(5) $core.String get clientId => $_getSZ(4); @$pb.TagNumber(5) set clientId($core.String v) { $_setString(4, v); } @$pb.TagNumber(5) $core.bool hasClientId() => $_has(4); @$pb.TagNumber(5) void clearClientId() => clearField(5); } class WarpGenerationResponse extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'WarpGenerationResponse', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'hcore'), createEmptyInstance: create) ..aOM(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'account', subBuilder: WarpAccount.create) ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'log') ..aOM(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'config', subBuilder: WarpWireguardConfig.create) ..hasRequiredFields = false ; WarpGenerationResponse._() : super(); factory WarpGenerationResponse({ WarpAccount? account, $core.String? log, WarpWireguardConfig? config, }) { final _result = create(); if (account != null) { _result.account = account; } if (log != null) { _result.log = log; } if (config != null) { _result.config = config; } return _result; } factory WarpGenerationResponse.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory WarpGenerationResponse.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') WarpGenerationResponse clone() => WarpGenerationResponse()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') WarpGenerationResponse copyWith(void Function(WarpGenerationResponse) updates) => super.copyWith((message) => updates(message as WarpGenerationResponse)) as WarpGenerationResponse; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static WarpGenerationResponse create() => WarpGenerationResponse._(); WarpGenerationResponse createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static WarpGenerationResponse getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static WarpGenerationResponse? _defaultInstance; @$pb.TagNumber(1) WarpAccount get account => $_getN(0); @$pb.TagNumber(1) set account(WarpAccount v) { setField(1, v); } @$pb.TagNumber(1) $core.bool hasAccount() => $_has(0); @$pb.TagNumber(1) void clearAccount() => clearField(1); @$pb.TagNumber(1) WarpAccount ensureAccount() => $_ensure(0); @$pb.TagNumber(2) $core.String get log => $_getSZ(1); @$pb.TagNumber(2) set log($core.String v) { $_setString(1, v); } @$pb.TagNumber(2) $core.bool hasLog() => $_has(1); @$pb.TagNumber(2) void clearLog() => clearField(2); @$pb.TagNumber(3) WarpWireguardConfig get config => $_getN(2); @$pb.TagNumber(3) set config(WarpWireguardConfig v) { setField(3, v); } @$pb.TagNumber(3) $core.bool hasConfig() => $_has(2); @$pb.TagNumber(3) void clearConfig() => clearField(3); @$pb.TagNumber(3) WarpWireguardConfig ensureConfig() => $_ensure(2); } class SystemProxyStatus extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'SystemProxyStatus', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'hcore'), createEmptyInstance: create) ..aOB(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'available') ..aOB(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'enabled') ..hasRequiredFields = false ; SystemProxyStatus._() : super(); factory SystemProxyStatus({ $core.bool? available, $core.bool? enabled, }) { final _result = create(); if (available != null) { _result.available = available; } if (enabled != null) { _result.enabled = enabled; } return _result; } factory SystemProxyStatus.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory SystemProxyStatus.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') SystemProxyStatus clone() => SystemProxyStatus()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') SystemProxyStatus copyWith(void Function(SystemProxyStatus) updates) => super.copyWith((message) => updates(message as SystemProxyStatus)) as SystemProxyStatus; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static SystemProxyStatus create() => SystemProxyStatus._(); SystemProxyStatus createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static SystemProxyStatus getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static SystemProxyStatus? _defaultInstance; @$pb.TagNumber(1) $core.bool get available => $_getBF(0); @$pb.TagNumber(1) set available($core.bool v) { $_setBool(0, v); } @$pb.TagNumber(1) $core.bool hasAvailable() => $_has(0); @$pb.TagNumber(1) void clearAvailable() => clearField(1); @$pb.TagNumber(2) $core.bool get enabled => $_getBF(1); @$pb.TagNumber(2) set enabled($core.bool v) { $_setBool(1, v); } @$pb.TagNumber(2) $core.bool hasEnabled() => $_has(1); @$pb.TagNumber(2) void clearEnabled() => clearField(2); } class ParseRequest extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'ParseRequest', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'hcore'), createEmptyInstance: create) ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'content') ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'configPath') ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'tempPath') ..aOB(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'debug') ..hasRequiredFields = false ; ParseRequest._() : super(); factory ParseRequest({ $core.String? content, $core.String? configPath, $core.String? tempPath, $core.bool? debug, }) { final _result = create(); if (content != null) { _result.content = content; } if (configPath != null) { _result.configPath = configPath; } if (tempPath != null) { _result.tempPath = tempPath; } if (debug != null) { _result.debug = debug; } return _result; } factory ParseRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory ParseRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') ParseRequest clone() => ParseRequest()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') ParseRequest copyWith(void Function(ParseRequest) updates) => super.copyWith((message) => updates(message as ParseRequest)) as ParseRequest; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static ParseRequest create() => ParseRequest._(); ParseRequest createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static ParseRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static ParseRequest? _defaultInstance; @$pb.TagNumber(1) $core.String get content => $_getSZ(0); @$pb.TagNumber(1) set content($core.String v) { $_setString(0, v); } @$pb.TagNumber(1) $core.bool hasContent() => $_has(0); @$pb.TagNumber(1) void clearContent() => clearField(1); @$pb.TagNumber(2) $core.String get configPath => $_getSZ(1); @$pb.TagNumber(2) set configPath($core.String v) { $_setString(1, v); } @$pb.TagNumber(2) $core.bool hasConfigPath() => $_has(1); @$pb.TagNumber(2) void clearConfigPath() => clearField(2); @$pb.TagNumber(3) $core.String get tempPath => $_getSZ(2); @$pb.TagNumber(3) set tempPath($core.String v) { $_setString(2, v); } @$pb.TagNumber(3) $core.bool hasTempPath() => $_has(2); @$pb.TagNumber(3) void clearTempPath() => clearField(3); @$pb.TagNumber(4) $core.bool get debug => $_getBF(3); @$pb.TagNumber(4) set debug($core.bool v) { $_setBool(3, v); } @$pb.TagNumber(4) $core.bool hasDebug() => $_has(3); @$pb.TagNumber(4) void clearDebug() => clearField(4); } class ParseResponse extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'ParseResponse', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'hcore'), createEmptyInstance: create) ..e<$1.ResponseCode>(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'responseCode', $pb.PbFieldType.OE, defaultOrMaker: $1.ResponseCode.OK, valueOf: $1.ResponseCode.valueOf, enumValues: $1.ResponseCode.values) ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'content') ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'message') ..hasRequiredFields = false ; ParseResponse._() : super(); factory ParseResponse({ $1.ResponseCode? responseCode, $core.String? content, $core.String? message, }) { final _result = create(); if (responseCode != null) { _result.responseCode = responseCode; } if (content != null) { _result.content = content; } if (message != null) { _result.message = message; } return _result; } factory ParseResponse.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory ParseResponse.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') ParseResponse clone() => ParseResponse()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') ParseResponse copyWith(void Function(ParseResponse) updates) => super.copyWith((message) => updates(message as ParseResponse)) as ParseResponse; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static ParseResponse create() => ParseResponse._(); ParseResponse createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static ParseResponse getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static ParseResponse? _defaultInstance; @$pb.TagNumber(1) $1.ResponseCode get responseCode => $_getN(0); @$pb.TagNumber(1) set responseCode($1.ResponseCode v) { setField(1, v); } @$pb.TagNumber(1) $core.bool hasResponseCode() => $_has(0); @$pb.TagNumber(1) void clearResponseCode() => clearField(1); @$pb.TagNumber(2) $core.String get content => $_getSZ(1); @$pb.TagNumber(2) set content($core.String v) { $_setString(1, v); } @$pb.TagNumber(2) $core.bool hasContent() => $_has(1); @$pb.TagNumber(2) void clearContent() => clearField(2); @$pb.TagNumber(3) $core.String get message => $_getSZ(2); @$pb.TagNumber(3) set message($core.String v) { $_setString(2, v); } @$pb.TagNumber(3) $core.bool hasMessage() => $_has(2); @$pb.TagNumber(3) void clearMessage() => clearField(3); } class ChangeHiddifySettingsRequest extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'ChangeHiddifySettingsRequest', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'hcore'), createEmptyInstance: create) ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'hiddifySettingsJson') ..hasRequiredFields = false ; ChangeHiddifySettingsRequest._() : super(); factory ChangeHiddifySettingsRequest({ $core.String? hiddifySettingsJson, }) { final _result = create(); if (hiddifySettingsJson != null) { _result.hiddifySettingsJson = hiddifySettingsJson; } return _result; } factory ChangeHiddifySettingsRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory ChangeHiddifySettingsRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') ChangeHiddifySettingsRequest clone() => ChangeHiddifySettingsRequest()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') ChangeHiddifySettingsRequest copyWith(void Function(ChangeHiddifySettingsRequest) updates) => super.copyWith((message) => updates(message as ChangeHiddifySettingsRequest)) as ChangeHiddifySettingsRequest; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static ChangeHiddifySettingsRequest create() => ChangeHiddifySettingsRequest._(); ChangeHiddifySettingsRequest createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static ChangeHiddifySettingsRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static ChangeHiddifySettingsRequest? _defaultInstance; @$pb.TagNumber(1) $core.String get hiddifySettingsJson => $_getSZ(0); @$pb.TagNumber(1) set hiddifySettingsJson($core.String v) { $_setString(0, v); } @$pb.TagNumber(1) $core.bool hasHiddifySettingsJson() => $_has(0); @$pb.TagNumber(1) void clearHiddifySettingsJson() => clearField(1); } class GenerateConfigRequest extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'GenerateConfigRequest', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'hcore'), createEmptyInstance: create) ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'path') ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'tempPath') ..aOB(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'debug') ..hasRequiredFields = false ; GenerateConfigRequest._() : super(); factory GenerateConfigRequest({ $core.String? path, $core.String? tempPath, $core.bool? debug, }) { final _result = create(); if (path != null) { _result.path = path; } if (tempPath != null) { _result.tempPath = tempPath; } if (debug != null) { _result.debug = debug; } return _result; } factory GenerateConfigRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory GenerateConfigRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') GenerateConfigRequest clone() => GenerateConfigRequest()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') GenerateConfigRequest copyWith(void Function(GenerateConfigRequest) updates) => super.copyWith((message) => updates(message as GenerateConfigRequest)) as GenerateConfigRequest; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static GenerateConfigRequest create() => GenerateConfigRequest._(); GenerateConfigRequest createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static GenerateConfigRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static GenerateConfigRequest? _defaultInstance; @$pb.TagNumber(1) $core.String get path => $_getSZ(0); @$pb.TagNumber(1) set path($core.String v) { $_setString(0, v); } @$pb.TagNumber(1) $core.bool hasPath() => $_has(0); @$pb.TagNumber(1) void clearPath() => clearField(1); @$pb.TagNumber(2) $core.String get tempPath => $_getSZ(1); @$pb.TagNumber(2) set tempPath($core.String v) { $_setString(1, v); } @$pb.TagNumber(2) $core.bool hasTempPath() => $_has(1); @$pb.TagNumber(2) void clearTempPath() => clearField(2); @$pb.TagNumber(3) $core.bool get debug => $_getBF(2); @$pb.TagNumber(3) set debug($core.bool v) { $_setBool(2, v); } @$pb.TagNumber(3) $core.bool hasDebug() => $_has(2); @$pb.TagNumber(3) void clearDebug() => clearField(3); } class GenerateConfigResponse extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'GenerateConfigResponse', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'hcore'), createEmptyInstance: create) ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'configContent') ..hasRequiredFields = false ; GenerateConfigResponse._() : super(); factory GenerateConfigResponse({ $core.String? configContent, }) { final _result = create(); if (configContent != null) { _result.configContent = configContent; } return _result; } factory GenerateConfigResponse.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory GenerateConfigResponse.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') GenerateConfigResponse clone() => GenerateConfigResponse()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') GenerateConfigResponse copyWith(void Function(GenerateConfigResponse) updates) => super.copyWith((message) => updates(message as GenerateConfigResponse)) as GenerateConfigResponse; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static GenerateConfigResponse create() => GenerateConfigResponse._(); GenerateConfigResponse createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static GenerateConfigResponse getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static GenerateConfigResponse? _defaultInstance; @$pb.TagNumber(1) $core.String get configContent => $_getSZ(0); @$pb.TagNumber(1) set configContent($core.String v) { $_setString(0, v); } @$pb.TagNumber(1) $core.bool hasConfigContent() => $_has(0); @$pb.TagNumber(1) void clearConfigContent() => clearField(1); } class SelectOutboundRequest extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'SelectOutboundRequest', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'hcore'), createEmptyInstance: create) ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'groupTag') ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'outboundTag') ..hasRequiredFields = false ; SelectOutboundRequest._() : super(); factory SelectOutboundRequest({ $core.String? groupTag, $core.String? outboundTag, }) { final _result = create(); if (groupTag != null) { _result.groupTag = groupTag; } if (outboundTag != null) { _result.outboundTag = outboundTag; } return _result; } factory SelectOutboundRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory SelectOutboundRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') SelectOutboundRequest clone() => SelectOutboundRequest()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') SelectOutboundRequest copyWith(void Function(SelectOutboundRequest) updates) => super.copyWith((message) => updates(message as SelectOutboundRequest)) as SelectOutboundRequest; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static SelectOutboundRequest create() => SelectOutboundRequest._(); SelectOutboundRequest createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static SelectOutboundRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static SelectOutboundRequest? _defaultInstance; @$pb.TagNumber(1) $core.String get groupTag => $_getSZ(0); @$pb.TagNumber(1) set groupTag($core.String v) { $_setString(0, v); } @$pb.TagNumber(1) $core.bool hasGroupTag() => $_has(0); @$pb.TagNumber(1) void clearGroupTag() => clearField(1); @$pb.TagNumber(2) $core.String get outboundTag => $_getSZ(1); @$pb.TagNumber(2) set outboundTag($core.String v) { $_setString(1, v); } @$pb.TagNumber(2) $core.bool hasOutboundTag() => $_has(1); @$pb.TagNumber(2) void clearOutboundTag() => clearField(2); } class UrlTestRequest extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'UrlTestRequest', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'hcore'), createEmptyInstance: create) ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'tag') ..hasRequiredFields = false ; UrlTestRequest._() : super(); factory UrlTestRequest({ $core.String? tag, }) { final _result = create(); if (tag != null) { _result.tag = tag; } return _result; } factory UrlTestRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory UrlTestRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') UrlTestRequest clone() => UrlTestRequest()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') UrlTestRequest copyWith(void Function(UrlTestRequest) updates) => super.copyWith((message) => updates(message as UrlTestRequest)) as UrlTestRequest; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static UrlTestRequest create() => UrlTestRequest._(); UrlTestRequest createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static UrlTestRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static UrlTestRequest? _defaultInstance; @$pb.TagNumber(1) $core.String get tag => $_getSZ(0); @$pb.TagNumber(1) set tag($core.String v) { $_setString(0, v); } @$pb.TagNumber(1) $core.bool hasTag() => $_has(0); @$pb.TagNumber(1) void clearTag() => clearField(1); } class GenerateWarpConfigRequest extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'GenerateWarpConfigRequest', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'hcore'), createEmptyInstance: create) ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'licenseKey') ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'accountId') ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'accessToken') ..hasRequiredFields = false ; GenerateWarpConfigRequest._() : super(); factory GenerateWarpConfigRequest({ $core.String? licenseKey, $core.String? accountId, $core.String? accessToken, }) { final _result = create(); if (licenseKey != null) { _result.licenseKey = licenseKey; } if (accountId != null) { _result.accountId = accountId; } if (accessToken != null) { _result.accessToken = accessToken; } return _result; } factory GenerateWarpConfigRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory GenerateWarpConfigRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') GenerateWarpConfigRequest clone() => GenerateWarpConfigRequest()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') GenerateWarpConfigRequest copyWith(void Function(GenerateWarpConfigRequest) updates) => super.copyWith((message) => updates(message as GenerateWarpConfigRequest)) as GenerateWarpConfigRequest; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static GenerateWarpConfigRequest create() => GenerateWarpConfigRequest._(); GenerateWarpConfigRequest createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static GenerateWarpConfigRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static GenerateWarpConfigRequest? _defaultInstance; @$pb.TagNumber(1) $core.String get licenseKey => $_getSZ(0); @$pb.TagNumber(1) set licenseKey($core.String v) { $_setString(0, v); } @$pb.TagNumber(1) $core.bool hasLicenseKey() => $_has(0); @$pb.TagNumber(1) void clearLicenseKey() => clearField(1); @$pb.TagNumber(2) $core.String get accountId => $_getSZ(1); @$pb.TagNumber(2) set accountId($core.String v) { $_setString(1, v); } @$pb.TagNumber(2) $core.bool hasAccountId() => $_has(1); @$pb.TagNumber(2) void clearAccountId() => clearField(2); @$pb.TagNumber(3) $core.String get accessToken => $_getSZ(2); @$pb.TagNumber(3) set accessToken($core.String v) { $_setString(2, v); } @$pb.TagNumber(3) $core.bool hasAccessToken() => $_has(2); @$pb.TagNumber(3) void clearAccessToken() => clearField(3); } class SetSystemProxyEnabledRequest extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'SetSystemProxyEnabledRequest', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'hcore'), createEmptyInstance: create) ..aOB(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'isEnabled') ..hasRequiredFields = false ; SetSystemProxyEnabledRequest._() : super(); factory SetSystemProxyEnabledRequest({ $core.bool? isEnabled, }) { final _result = create(); if (isEnabled != null) { _result.isEnabled = isEnabled; } return _result; } factory SetSystemProxyEnabledRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory SetSystemProxyEnabledRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') SetSystemProxyEnabledRequest clone() => SetSystemProxyEnabledRequest()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') SetSystemProxyEnabledRequest copyWith(void Function(SetSystemProxyEnabledRequest) updates) => super.copyWith((message) => updates(message as SetSystemProxyEnabledRequest)) as SetSystemProxyEnabledRequest; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static SetSystemProxyEnabledRequest create() => SetSystemProxyEnabledRequest._(); SetSystemProxyEnabledRequest createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static SetSystemProxyEnabledRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static SetSystemProxyEnabledRequest? _defaultInstance; @$pb.TagNumber(1) $core.bool get isEnabled => $_getBF(0); @$pb.TagNumber(1) set isEnabled($core.bool v) { $_setBool(0, v); } @$pb.TagNumber(1) $core.bool hasIsEnabled() => $_has(0); @$pb.TagNumber(1) void clearIsEnabled() => clearField(1); } class LogMessage extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'LogMessage', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'hcore'), createEmptyInstance: create) ..e(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'level', $pb.PbFieldType.OE, defaultOrMaker: LogLevel.TRACE, valueOf: LogLevel.valueOf, enumValues: LogLevel.values) ..e(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'type', $pb.PbFieldType.OE, defaultOrMaker: LogType.CORE, valueOf: LogType.valueOf, enumValues: LogType.values) ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'message') ..aOM<$7.Timestamp>(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'time', subBuilder: $7.Timestamp.create) ..hasRequiredFields = false ; LogMessage._() : super(); factory LogMessage({ LogLevel? level, LogType? type, $core.String? message, $7.Timestamp? time, }) { final _result = create(); if (level != null) { _result.level = level; } if (type != null) { _result.type = type; } if (message != null) { _result.message = message; } if (time != null) { _result.time = time; } return _result; } factory LogMessage.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory LogMessage.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') LogMessage clone() => LogMessage()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') LogMessage copyWith(void Function(LogMessage) updates) => super.copyWith((message) => updates(message as LogMessage)) as LogMessage; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static LogMessage create() => LogMessage._(); LogMessage createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static LogMessage getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static LogMessage? _defaultInstance; @$pb.TagNumber(1) LogLevel get level => $_getN(0); @$pb.TagNumber(1) set level(LogLevel v) { setField(1, v); } @$pb.TagNumber(1) $core.bool hasLevel() => $_has(0); @$pb.TagNumber(1) void clearLevel() => clearField(1); @$pb.TagNumber(2) LogType get type => $_getN(1); @$pb.TagNumber(2) set type(LogType v) { setField(2, v); } @$pb.TagNumber(2) $core.bool hasType() => $_has(1); @$pb.TagNumber(2) void clearType() => clearField(2); @$pb.TagNumber(3) $core.String get message => $_getSZ(2); @$pb.TagNumber(3) set message($core.String v) { $_setString(2, v); } @$pb.TagNumber(3) $core.bool hasMessage() => $_has(2); @$pb.TagNumber(3) void clearMessage() => clearField(3); @$pb.TagNumber(4) $7.Timestamp get time => $_getN(3); @$pb.TagNumber(4) set time($7.Timestamp v) { setField(4, v); } @$pb.TagNumber(4) $core.bool hasTime() => $_has(3); @$pb.TagNumber(4) void clearTime() => clearField(4); @$pb.TagNumber(4) $7.Timestamp ensureTime() => $_ensure(3); } class LogRequest extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'LogRequest', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'hcore'), createEmptyInstance: create) ..e(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'level', $pb.PbFieldType.OE, defaultOrMaker: LogLevel.TRACE, valueOf: LogLevel.valueOf, enumValues: LogLevel.values) ..hasRequiredFields = false ; LogRequest._() : super(); factory LogRequest({ LogLevel? level, }) { final _result = create(); if (level != null) { _result.level = level; } return _result; } factory LogRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory LogRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') LogRequest clone() => LogRequest()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') LogRequest copyWith(void Function(LogRequest) updates) => super.copyWith((message) => updates(message as LogRequest)) as LogRequest; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static LogRequest create() => LogRequest._(); LogRequest createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static LogRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static LogRequest? _defaultInstance; @$pb.TagNumber(1) LogLevel get level => $_getN(0); @$pb.TagNumber(1) set level(LogLevel v) { setField(1, v); } @$pb.TagNumber(1) $core.bool hasLevel() => $_has(0); @$pb.TagNumber(1) void clearLevel() => clearField(1); } class StopRequest extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'StopRequest', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'hcore'), createEmptyInstance: create) ..hasRequiredFields = false ; StopRequest._() : super(); factory StopRequest() => create(); factory StopRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory StopRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') StopRequest clone() => StopRequest()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') StopRequest copyWith(void Function(StopRequest) updates) => super.copyWith((message) => updates(message as StopRequest)) as StopRequest; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static StopRequest create() => StopRequest._(); StopRequest createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static StopRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static StopRequest? _defaultInstance; } ================================================ FILE: lib/hiddifycore/generated/v2/hcore/hcore.pbenum.dart ================================================ /// // Generated code. Do not modify. // source: v2/hcore/hcore.proto // // @dart = 2.12 // ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name // ignore_for_file: UNDEFINED_SHOWN_NAME import 'dart:core' as $core; import 'package:protobuf/protobuf.dart' as $pb; class CoreStates extends $pb.ProtobufEnum { static const CoreStates STOPPED = CoreStates._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'STOPPED'); static const CoreStates STARTING = CoreStates._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'STARTING'); static const CoreStates STARTED = CoreStates._(2, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'STARTED'); static const CoreStates STOPPING = CoreStates._(3, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'STOPPING'); static const $core.List values = [ STOPPED, STARTING, STARTED, STOPPING, ]; static final $core.Map<$core.int, CoreStates> _byValue = $pb.ProtobufEnum.initByValue(values); static CoreStates? valueOf($core.int value) => _byValue[value]; const CoreStates._($core.int v, $core.String n) : super(v, n); } class MessageType extends $pb.ProtobufEnum { static const MessageType EMPTY = MessageType._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'EMPTY'); static const MessageType EMPTY_CONFIGURATION = MessageType._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'EMPTY_CONFIGURATION'); static const MessageType START_COMMAND_SERVER = MessageType._(2, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'START_COMMAND_SERVER'); static const MessageType CREATE_SERVICE = MessageType._(3, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'CREATE_SERVICE'); static const MessageType START_SERVICE = MessageType._(4, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'START_SERVICE'); static const MessageType UNEXPECTED_ERROR = MessageType._(5, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UNEXPECTED_ERROR'); static const MessageType ALREADY_STARTED = MessageType._(6, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ALREADY_STARTED'); static const MessageType ALREADY_STOPPED = MessageType._(7, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ALREADY_STOPPED'); static const MessageType INSTANCE_NOT_FOUND = MessageType._(8, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'INSTANCE_NOT_FOUND'); static const MessageType INSTANCE_NOT_STOPPED = MessageType._(9, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'INSTANCE_NOT_STOPPED'); static const MessageType INSTANCE_NOT_STARTED = MessageType._(10, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'INSTANCE_NOT_STARTED'); static const MessageType ERROR_BUILDING_CONFIG = MessageType._(11, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ERROR_BUILDING_CONFIG'); static const MessageType ERROR_PARSING_CONFIG = MessageType._(12, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ERROR_PARSING_CONFIG'); static const MessageType ERROR_READING_CONFIG = MessageType._(13, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ERROR_READING_CONFIG'); static const MessageType ERROR_EXTENSION = MessageType._(14, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ERROR_EXTENSION'); static const $core.List values = [ EMPTY, EMPTY_CONFIGURATION, START_COMMAND_SERVER, CREATE_SERVICE, START_SERVICE, UNEXPECTED_ERROR, ALREADY_STARTED, ALREADY_STOPPED, INSTANCE_NOT_FOUND, INSTANCE_NOT_STOPPED, INSTANCE_NOT_STARTED, ERROR_BUILDING_CONFIG, ERROR_PARSING_CONFIG, ERROR_READING_CONFIG, ERROR_EXTENSION, ]; static final $core.Map<$core.int, MessageType> _byValue = $pb.ProtobufEnum.initByValue(values); static MessageType? valueOf($core.int value) => _byValue[value]; const MessageType._($core.int v, $core.String n) : super(v, n); } class SetupMode extends $pb.ProtobufEnum { static const SetupMode OLD = SetupMode._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'OLD'); static const SetupMode GRPC_NORMAL = SetupMode._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GRPC_NORMAL'); static const SetupMode GRPC_BACKGROUND = SetupMode._(2, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GRPC_BACKGROUND'); static const SetupMode GRPC_NORMAL_INSECURE = SetupMode._(3, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GRPC_NORMAL_INSECURE'); static const SetupMode GRPC_BACKGROUND_INSECURE = SetupMode._(4, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GRPC_BACKGROUND_INSECURE'); static const $core.List values = [ OLD, GRPC_NORMAL, GRPC_BACKGROUND, GRPC_NORMAL_INSECURE, GRPC_BACKGROUND_INSECURE, ]; static final $core.Map<$core.int, SetupMode> _byValue = $pb.ProtobufEnum.initByValue(values); static SetupMode? valueOf($core.int value) => _byValue[value]; const SetupMode._($core.int v, $core.String n) : super(v, n); } class LogLevel extends $pb.ProtobufEnum { static const LogLevel TRACE = LogLevel._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'TRACE'); static const LogLevel DEBUG = LogLevel._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DEBUG'); static const LogLevel INFO = LogLevel._(2, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'INFO'); static const LogLevel WARNING = LogLevel._(3, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'WARNING'); static const LogLevel ERROR = LogLevel._(4, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ERROR'); static const LogLevel FATAL = LogLevel._(5, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'FATAL'); static const $core.List values = [ TRACE, DEBUG, INFO, WARNING, ERROR, FATAL, ]; static final $core.Map<$core.int, LogLevel> _byValue = $pb.ProtobufEnum.initByValue(values); static LogLevel? valueOf($core.int value) => _byValue[value]; const LogLevel._($core.int v, $core.String n) : super(v, n); } class LogType extends $pb.ProtobufEnum { static const LogType CORE = LogType._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'CORE'); static const LogType SERVICE = LogType._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'SERVICE'); static const LogType CONFIG = LogType._(2, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'CONFIG'); static const $core.List values = [ CORE, SERVICE, CONFIG, ]; static final $core.Map<$core.int, LogType> _byValue = $pb.ProtobufEnum.initByValue(values); static LogType? valueOf($core.int value) => _byValue[value]; const LogType._($core.int v, $core.String n) : super(v, n); } ================================================ FILE: lib/hiddifycore/generated/v2/hcore/hcore.pbjson.dart ================================================ /// // Generated code. Do not modify. // source: v2/hcore/hcore.proto // // @dart = 2.12 // ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,deprecated_member_use_from_same_package,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name import 'dart:core' as $core; import 'dart:convert' as $convert; import 'dart:typed_data' as $typed_data; @$core.Deprecated('Use coreStatesDescriptor instead') const CoreStates$json = const { '1': 'CoreStates', '2': const [ const {'1': 'STOPPED', '2': 0}, const {'1': 'STARTING', '2': 1}, const {'1': 'STARTED', '2': 2}, const {'1': 'STOPPING', '2': 3}, ], }; /// Descriptor for `CoreStates`. Decode as a `google.protobuf.EnumDescriptorProto`. final $typed_data.Uint8List coreStatesDescriptor = $convert.base64Decode('CgpDb3JlU3RhdGVzEgsKB1NUT1BQRUQQABIMCghTVEFSVElORxABEgsKB1NUQVJURUQQAhIMCghTVE9QUElORxAD'); @$core.Deprecated('Use messageTypeDescriptor instead') const MessageType$json = const { '1': 'MessageType', '2': const [ const {'1': 'EMPTY', '2': 0}, const {'1': 'EMPTY_CONFIGURATION', '2': 1}, const {'1': 'START_COMMAND_SERVER', '2': 2}, const {'1': 'CREATE_SERVICE', '2': 3}, const {'1': 'START_SERVICE', '2': 4}, const {'1': 'UNEXPECTED_ERROR', '2': 5}, const {'1': 'ALREADY_STARTED', '2': 6}, const {'1': 'ALREADY_STOPPED', '2': 7}, const {'1': 'INSTANCE_NOT_FOUND', '2': 8}, const {'1': 'INSTANCE_NOT_STOPPED', '2': 9}, const {'1': 'INSTANCE_NOT_STARTED', '2': 10}, const {'1': 'ERROR_BUILDING_CONFIG', '2': 11}, const {'1': 'ERROR_PARSING_CONFIG', '2': 12}, const {'1': 'ERROR_READING_CONFIG', '2': 13}, const {'1': 'ERROR_EXTENSION', '2': 14}, ], }; /// Descriptor for `MessageType`. Decode as a `google.protobuf.EnumDescriptorProto`. final $typed_data.Uint8List messageTypeDescriptor = $convert.base64Decode('CgtNZXNzYWdlVHlwZRIJCgVFTVBUWRAAEhcKE0VNUFRZX0NPTkZJR1VSQVRJT04QARIYChRTVEFSVF9DT01NQU5EX1NFUlZFUhACEhIKDkNSRUFURV9TRVJWSUNFEAMSEQoNU1RBUlRfU0VSVklDRRAEEhQKEFVORVhQRUNURURfRVJST1IQBRITCg9BTFJFQURZX1NUQVJURUQQBhITCg9BTFJFQURZX1NUT1BQRUQQBxIWChJJTlNUQU5DRV9OT1RfRk9VTkQQCBIYChRJTlNUQU5DRV9OT1RfU1RPUFBFRBAJEhgKFElOU1RBTkNFX05PVF9TVEFSVEVEEAoSGQoVRVJST1JfQlVJTERJTkdfQ09ORklHEAsSGAoURVJST1JfUEFSU0lOR19DT05GSUcQDBIYChRFUlJPUl9SRUFESU5HX0NPTkZJRxANEhMKD0VSUk9SX0VYVEVOU0lPThAO'); @$core.Deprecated('Use setupModeDescriptor instead') const SetupMode$json = const { '1': 'SetupMode', '2': const [ const {'1': 'OLD', '2': 0}, const {'1': 'GRPC_NORMAL', '2': 1}, const {'1': 'GRPC_BACKGROUND', '2': 2}, const {'1': 'GRPC_NORMAL_INSECURE', '2': 3}, const {'1': 'GRPC_BACKGROUND_INSECURE', '2': 4}, ], }; /// Descriptor for `SetupMode`. Decode as a `google.protobuf.EnumDescriptorProto`. final $typed_data.Uint8List setupModeDescriptor = $convert.base64Decode('CglTZXR1cE1vZGUSBwoDT0xEEAASDwoLR1JQQ19OT1JNQUwQARITCg9HUlBDX0JBQ0tHUk9VTkQQAhIYChRHUlBDX05PUk1BTF9JTlNFQ1VSRRADEhwKGEdSUENfQkFDS0dST1VORF9JTlNFQ1VSRRAE'); @$core.Deprecated('Use logLevelDescriptor instead') const LogLevel$json = const { '1': 'LogLevel', '2': const [ const {'1': 'TRACE', '2': 0}, const {'1': 'DEBUG', '2': 1}, const {'1': 'INFO', '2': 2}, const {'1': 'WARNING', '2': 3}, const {'1': 'ERROR', '2': 4}, const {'1': 'FATAL', '2': 5}, ], }; /// Descriptor for `LogLevel`. Decode as a `google.protobuf.EnumDescriptorProto`. final $typed_data.Uint8List logLevelDescriptor = $convert.base64Decode('CghMb2dMZXZlbBIJCgVUUkFDRRAAEgkKBURFQlVHEAESCAoESU5GTxACEgsKB1dBUk5JTkcQAxIJCgVFUlJPUhAEEgkKBUZBVEFMEAU='); @$core.Deprecated('Use logTypeDescriptor instead') const LogType$json = const { '1': 'LogType', '2': const [ const {'1': 'CORE', '2': 0}, const {'1': 'SERVICE', '2': 1}, const {'1': 'CONFIG', '2': 2}, ], }; /// Descriptor for `LogType`. Decode as a `google.protobuf.EnumDescriptorProto`. final $typed_data.Uint8List logTypeDescriptor = $convert.base64Decode('CgdMb2dUeXBlEggKBENPUkUQABILCgdTRVJWSUNFEAESCgoGQ09ORklHEAI='); @$core.Deprecated('Use coreInfoResponseDescriptor instead') const CoreInfoResponse$json = const { '1': 'CoreInfoResponse', '2': const [ const {'1': 'core_state', '3': 1, '4': 1, '5': 14, '6': '.hcore.CoreStates', '10': 'coreState'}, const {'1': 'message_type', '3': 2, '4': 1, '5': 14, '6': '.hcore.MessageType', '10': 'messageType'}, const {'1': 'message', '3': 3, '4': 1, '5': 9, '10': 'message'}, ], }; /// Descriptor for `CoreInfoResponse`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List coreInfoResponseDescriptor = $convert.base64Decode('ChBDb3JlSW5mb1Jlc3BvbnNlEjAKCmNvcmVfc3RhdGUYASABKA4yES5oY29yZS5Db3JlU3RhdGVzUgljb3JlU3RhdGUSNQoMbWVzc2FnZV90eXBlGAIgASgOMhIuaGNvcmUuTWVzc2FnZVR5cGVSC21lc3NhZ2VUeXBlEhgKB21lc3NhZ2UYAyABKAlSB21lc3NhZ2U='); @$core.Deprecated('Use startRequestDescriptor instead') const StartRequest$json = const { '1': 'StartRequest', '2': const [ const {'1': 'config_path', '3': 1, '4': 1, '5': 9, '10': 'configPath'}, const {'1': 'config_content', '3': 2, '4': 1, '5': 9, '10': 'configContent'}, const {'1': 'disable_memory_limit', '3': 3, '4': 1, '5': 8, '10': 'disableMemoryLimit'}, const {'1': 'delay_start', '3': 4, '4': 1, '5': 8, '10': 'delayStart'}, const {'1': 'enable_old_command_server', '3': 5, '4': 1, '5': 8, '10': 'enableOldCommandServer'}, const {'1': 'enable_raw_config', '3': 6, '4': 1, '5': 8, '10': 'enableRawConfig'}, const {'1': 'config_name', '3': 7, '4': 1, '5': 9, '10': 'configName'}, ], }; /// Descriptor for `StartRequest`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List startRequestDescriptor = $convert.base64Decode('CgxTdGFydFJlcXVlc3QSHwoLY29uZmlnX3BhdGgYASABKAlSCmNvbmZpZ1BhdGgSJQoOY29uZmlnX2NvbnRlbnQYAiABKAlSDWNvbmZpZ0NvbnRlbnQSMAoUZGlzYWJsZV9tZW1vcnlfbGltaXQYAyABKAhSEmRpc2FibGVNZW1vcnlMaW1pdBIfCgtkZWxheV9zdGFydBgEIAEoCFIKZGVsYXlTdGFydBI5ChllbmFibGVfb2xkX2NvbW1hbmRfc2VydmVyGAUgASgIUhZlbmFibGVPbGRDb21tYW5kU2VydmVyEioKEWVuYWJsZV9yYXdfY29uZmlnGAYgASgIUg9lbmFibGVSYXdDb25maWcSHwoLY29uZmlnX25hbWUYByABKAlSCmNvbmZpZ05hbWU='); @$core.Deprecated('Use closeRequestDescriptor instead') const CloseRequest$json = const { '1': 'CloseRequest', '2': const [ const {'1': 'mode', '3': 1, '4': 1, '5': 14, '6': '.hcore.SetupMode', '10': 'mode'}, ], }; /// Descriptor for `CloseRequest`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List closeRequestDescriptor = $convert.base64Decode('CgxDbG9zZVJlcXVlc3QSJAoEbW9kZRgBIAEoDjIQLmhjb3JlLlNldHVwTW9kZVIEbW9kZQ=='); @$core.Deprecated('Use setupRequestDescriptor instead') const SetupRequest$json = const { '1': 'SetupRequest', '2': const [ const {'1': 'base_path', '3': 1, '4': 1, '5': 9, '10': 'basePath'}, const {'1': 'working_dir', '3': 2, '4': 1, '5': 9, '10': 'workingDir'}, const {'1': 'temp_dir', '3': 3, '4': 1, '5': 9, '10': 'tempDir'}, const {'1': 'flutter_status_port', '3': 4, '4': 1, '5': 3, '10': 'flutterStatusPort'}, const {'1': 'listen', '3': 5, '4': 1, '5': 9, '10': 'listen'}, const {'1': 'secret', '3': 6, '4': 1, '5': 9, '10': 'secret'}, const {'1': 'debug', '3': 7, '4': 1, '5': 8, '10': 'debug'}, const {'1': 'mode', '3': 8, '4': 1, '5': 14, '6': '.hcore.SetupMode', '10': 'mode'}, const {'1': 'fix_android_stack', '3': 9, '4': 1, '5': 8, '10': 'fixAndroidStack'}, ], }; /// Descriptor for `SetupRequest`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List setupRequestDescriptor = $convert.base64Decode('CgxTZXR1cFJlcXVlc3QSGwoJYmFzZV9wYXRoGAEgASgJUghiYXNlUGF0aBIfCgt3b3JraW5nX2RpchgCIAEoCVIKd29ya2luZ0RpchIZCgh0ZW1wX2RpchgDIAEoCVIHdGVtcERpchIuChNmbHV0dGVyX3N0YXR1c19wb3J0GAQgASgDUhFmbHV0dGVyU3RhdHVzUG9ydBIWCgZsaXN0ZW4YBSABKAlSBmxpc3RlbhIWCgZzZWNyZXQYBiABKAlSBnNlY3JldBIUCgVkZWJ1ZxgHIAEoCFIFZGVidWcSJAoEbW9kZRgIIAEoDjIQLmhjb3JlLlNldHVwTW9kZVIEbW9kZRIqChFmaXhfYW5kcm9pZF9zdGFjaxgJIAEoCFIPZml4QW5kcm9pZFN0YWNr'); @$core.Deprecated('Use systemInfoDescriptor instead') const SystemInfo$json = const { '1': 'SystemInfo', '2': const [ const {'1': 'memory', '3': 1, '4': 1, '5': 3, '10': 'memory'}, const {'1': 'goroutines', '3': 2, '4': 1, '5': 5, '10': 'goroutines'}, const {'1': 'connections_in', '3': 3, '4': 1, '5': 5, '10': 'connectionsIn'}, const {'1': 'connections_out', '3': 4, '4': 1, '5': 5, '10': 'connectionsOut'}, const {'1': 'traffic_available', '3': 5, '4': 1, '5': 8, '10': 'trafficAvailable'}, const {'1': 'uplink', '3': 6, '4': 1, '5': 3, '10': 'uplink'}, const {'1': 'downlink', '3': 7, '4': 1, '5': 3, '10': 'downlink'}, const {'1': 'uplink_total', '3': 8, '4': 1, '5': 3, '10': 'uplinkTotal'}, const {'1': 'downlink_total', '3': 9, '4': 1, '5': 3, '10': 'downlinkTotal'}, const {'1': 'current_outbound', '3': 10, '4': 1, '5': 9, '10': 'currentOutbound'}, const {'1': 'current_profile', '3': 11, '4': 1, '5': 9, '10': 'currentProfile'}, ], }; /// Descriptor for `SystemInfo`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List systemInfoDescriptor = $convert.base64Decode('CgpTeXN0ZW1JbmZvEhYKBm1lbW9yeRgBIAEoA1IGbWVtb3J5Eh4KCmdvcm91dGluZXMYAiABKAVSCmdvcm91dGluZXMSJQoOY29ubmVjdGlvbnNfaW4YAyABKAVSDWNvbm5lY3Rpb25zSW4SJwoPY29ubmVjdGlvbnNfb3V0GAQgASgFUg5jb25uZWN0aW9uc091dBIrChF0cmFmZmljX2F2YWlsYWJsZRgFIAEoCFIQdHJhZmZpY0F2YWlsYWJsZRIWCgZ1cGxpbmsYBiABKANSBnVwbGluaxIaCghkb3dubGluaxgHIAEoA1IIZG93bmxpbmsSIQoMdXBsaW5rX3RvdGFsGAggASgDUgt1cGxpbmtUb3RhbBIlCg5kb3dubGlua190b3RhbBgJIAEoA1INZG93bmxpbmtUb3RhbBIpChBjdXJyZW50X291dGJvdW5kGAogASgJUg9jdXJyZW50T3V0Ym91bmQSJwoPY3VycmVudF9wcm9maWxlGAsgASgJUg5jdXJyZW50UHJvZmlsZQ=='); @$core.Deprecated('Use outboundInfoDescriptor instead') const OutboundInfo$json = const { '1': 'OutboundInfo', '2': const [ const {'1': 'tag', '3': 1, '4': 1, '5': 9, '10': 'tag'}, const {'1': 'type', '3': 2, '4': 1, '5': 9, '10': 'type'}, const {'1': 'url_test_time', '3': 3, '4': 1, '5': 11, '6': '.google.protobuf.Timestamp', '10': 'urlTestTime'}, const {'1': 'url_test_delay', '3': 4, '4': 1, '5': 5, '10': 'urlTestDelay'}, const {'1': 'ipinfo', '3': 5, '4': 1, '5': 11, '6': '.hcore.IpInfo', '9': 0, '10': 'ipinfo', '17': true}, const {'1': 'is_selected', '3': 6, '4': 1, '5': 8, '10': 'isSelected'}, const {'1': 'is_group', '3': 7, '4': 1, '5': 8, '10': 'isGroup'}, const {'1': 'group_selected_tag', '3': 13, '4': 1, '5': 9, '9': 1, '10': 'groupSelectedTag', '17': true}, const {'1': 'group_selected_tag_display', '3': 14, '4': 1, '5': 9, '9': 2, '10': 'groupSelectedTagDisplay', '17': true}, const {'1': 'is_secure', '3': 8, '4': 1, '5': 8, '10': 'isSecure'}, const {'1': 'is_visible', '3': 9, '4': 1, '5': 8, '10': 'isVisible'}, const {'1': 'port', '3': 10, '4': 1, '5': 13, '10': 'port'}, const {'1': 'host', '3': 11, '4': 1, '5': 9, '10': 'host'}, const {'1': 'tag_display', '3': 12, '4': 1, '5': 9, '10': 'tagDisplay'}, const {'1': 'upload', '3': 15, '4': 1, '5': 3, '10': 'upload'}, const {'1': 'download', '3': 16, '4': 1, '5': 3, '10': 'download'}, ], '8': const [ const {'1': '_ipinfo'}, const {'1': '_group_selected_tag'}, const {'1': '_group_selected_tag_display'}, ], }; /// Descriptor for `OutboundInfo`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List outboundInfoDescriptor = $convert.base64Decode('CgxPdXRib3VuZEluZm8SEAoDdGFnGAEgASgJUgN0YWcSEgoEdHlwZRgCIAEoCVIEdHlwZRI+Cg11cmxfdGVzdF90aW1lGAMgASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcFILdXJsVGVzdFRpbWUSJAoOdXJsX3Rlc3RfZGVsYXkYBCABKAVSDHVybFRlc3REZWxheRIqCgZpcGluZm8YBSABKAsyDS5oY29yZS5JcEluZm9IAFIGaXBpbmZviAEBEh8KC2lzX3NlbGVjdGVkGAYgASgIUgppc1NlbGVjdGVkEhkKCGlzX2dyb3VwGAcgASgIUgdpc0dyb3VwEjEKEmdyb3VwX3NlbGVjdGVkX3RhZxgNIAEoCUgBUhBncm91cFNlbGVjdGVkVGFniAEBEkAKGmdyb3VwX3NlbGVjdGVkX3RhZ19kaXNwbGF5GA4gASgJSAJSF2dyb3VwU2VsZWN0ZWRUYWdEaXNwbGF5iAEBEhsKCWlzX3NlY3VyZRgIIAEoCFIIaXNTZWN1cmUSHQoKaXNfdmlzaWJsZRgJIAEoCFIJaXNWaXNpYmxlEhIKBHBvcnQYCiABKA1SBHBvcnQSEgoEaG9zdBgLIAEoCVIEaG9zdBIfCgt0YWdfZGlzcGxheRgMIAEoCVIKdGFnRGlzcGxheRIWCgZ1cGxvYWQYDyABKANSBnVwbG9hZBIaCghkb3dubG9hZBgQIAEoA1IIZG93bmxvYWRCCQoHX2lwaW5mb0IVChNfZ3JvdXBfc2VsZWN0ZWRfdGFnQh0KG19ncm91cF9zZWxlY3RlZF90YWdfZGlzcGxheQ=='); @$core.Deprecated('Use ipInfoDescriptor instead') const IpInfo$json = const { '1': 'IpInfo', '2': const [ const {'1': 'ip', '3': 1, '4': 1, '5': 9, '10': 'ip'}, const {'1': 'country_code', '3': 2, '4': 1, '5': 9, '10': 'country_code'}, const {'1': 'region', '3': 3, '4': 1, '5': 9, '10': 'region'}, const {'1': 'city', '3': 4, '4': 1, '5': 9, '10': 'city'}, const {'1': 'asn', '3': 5, '4': 1, '5': 5, '10': 'asn'}, const {'1': 'org', '3': 6, '4': 1, '5': 9, '10': 'org'}, const {'1': 'latitude', '3': 7, '4': 1, '5': 1, '10': 'latitude'}, const {'1': 'longitude', '3': 8, '4': 1, '5': 1, '10': 'longitude'}, const {'1': 'postal_code', '3': 9, '4': 1, '5': 9, '10': 'postal_code'}, ], }; /// Descriptor for `IpInfo`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List ipInfoDescriptor = $convert.base64Decode('CgZJcEluZm8SDgoCaXAYASABKAlSAmlwEiIKDGNvdW50cnlfY29kZRgCIAEoCVIMY291bnRyeV9jb2RlEhYKBnJlZ2lvbhgDIAEoCVIGcmVnaW9uEhIKBGNpdHkYBCABKAlSBGNpdHkSEAoDYXNuGAUgASgFUgNhc24SEAoDb3JnGAYgASgJUgNvcmcSGgoIbGF0aXR1ZGUYByABKAFSCGxhdGl0dWRlEhwKCWxvbmdpdHVkZRgIIAEoAVIJbG9uZ2l0dWRlEiAKC3Bvc3RhbF9jb2RlGAkgASgJUgtwb3N0YWxfY29kZQ=='); @$core.Deprecated('Use outboundGroupDescriptor instead') const OutboundGroup$json = const { '1': 'OutboundGroup', '2': const [ const {'1': 'tag', '3': 1, '4': 1, '5': 9, '10': 'tag'}, const {'1': 'type', '3': 2, '4': 1, '5': 9, '10': 'type'}, const {'1': 'selected', '3': 3, '4': 1, '5': 9, '10': 'selected'}, const {'1': 'selectable', '3': 4, '4': 1, '5': 8, '10': 'selectable'}, const {'1': 'Is_expand', '3': 5, '4': 1, '5': 8, '10': 'IsExpand'}, const {'1': 'items', '3': 6, '4': 3, '5': 11, '6': '.hcore.OutboundInfo', '10': 'items'}, ], }; /// Descriptor for `OutboundGroup`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List outboundGroupDescriptor = $convert.base64Decode('Cg1PdXRib3VuZEdyb3VwEhAKA3RhZxgBIAEoCVIDdGFnEhIKBHR5cGUYAiABKAlSBHR5cGUSGgoIc2VsZWN0ZWQYAyABKAlSCHNlbGVjdGVkEh4KCnNlbGVjdGFibGUYBCABKAhSCnNlbGVjdGFibGUSGwoJSXNfZXhwYW5kGAUgASgIUghJc0V4cGFuZBIpCgVpdGVtcxgGIAMoCzITLmhjb3JlLk91dGJvdW5kSW5mb1IFaXRlbXM='); @$core.Deprecated('Use outboundGroupListDescriptor instead') const OutboundGroupList$json = const { '1': 'OutboundGroupList', '2': const [ const {'1': 'items', '3': 1, '4': 3, '5': 11, '6': '.hcore.OutboundGroup', '10': 'items'}, ], }; /// Descriptor for `OutboundGroupList`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List outboundGroupListDescriptor = $convert.base64Decode('ChFPdXRib3VuZEdyb3VwTGlzdBIqCgVpdGVtcxgBIAMoCzIULmhjb3JlLk91dGJvdW5kR3JvdXBSBWl0ZW1z'); @$core.Deprecated('Use warpAccountDescriptor instead') const WarpAccount$json = const { '1': 'WarpAccount', '2': const [ const {'1': 'account_id', '3': 1, '4': 1, '5': 9, '10': 'accountId'}, const {'1': 'access_token', '3': 2, '4': 1, '5': 9, '10': 'accessToken'}, ], }; /// Descriptor for `WarpAccount`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List warpAccountDescriptor = $convert.base64Decode('CgtXYXJwQWNjb3VudBIdCgphY2NvdW50X2lkGAEgASgJUglhY2NvdW50SWQSIQoMYWNjZXNzX3Rva2VuGAIgASgJUgthY2Nlc3NUb2tlbg=='); @$core.Deprecated('Use warpWireguardConfigDescriptor instead') const WarpWireguardConfig$json = const { '1': 'WarpWireguardConfig', '2': const [ const {'1': 'private_key', '3': 1, '4': 1, '5': 9, '10': 'private-key'}, const {'1': 'local_address_ipv4', '3': 2, '4': 1, '5': 9, '10': 'local-address-ipv4'}, const {'1': 'local_address_ipv6', '3': 3, '4': 1, '5': 9, '10': 'local-address-ipv6'}, const {'1': 'peer_public_key', '3': 4, '4': 1, '5': 9, '10': 'peer-public-key'}, const {'1': 'client_id', '3': 5, '4': 1, '5': 9, '10': 'client-id'}, ], }; /// Descriptor for `WarpWireguardConfig`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List warpWireguardConfigDescriptor = $convert.base64Decode('ChNXYXJwV2lyZWd1YXJkQ29uZmlnEiAKC3ByaXZhdGVfa2V5GAEgASgJUgtwcml2YXRlLWtleRIuChJsb2NhbF9hZGRyZXNzX2lwdjQYAiABKAlSEmxvY2FsLWFkZHJlc3MtaXB2NBIuChJsb2NhbF9hZGRyZXNzX2lwdjYYAyABKAlSEmxvY2FsLWFkZHJlc3MtaXB2NhIoCg9wZWVyX3B1YmxpY19rZXkYBCABKAlSD3BlZXItcHVibGljLWtleRIcCgljbGllbnRfaWQYBSABKAlSCWNsaWVudC1pZA=='); @$core.Deprecated('Use warpGenerationResponseDescriptor instead') const WarpGenerationResponse$json = const { '1': 'WarpGenerationResponse', '2': const [ const {'1': 'account', '3': 1, '4': 1, '5': 11, '6': '.hcore.WarpAccount', '10': 'account'}, const {'1': 'log', '3': 2, '4': 1, '5': 9, '10': 'log'}, const {'1': 'config', '3': 3, '4': 1, '5': 11, '6': '.hcore.WarpWireguardConfig', '10': 'config'}, ], }; /// Descriptor for `WarpGenerationResponse`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List warpGenerationResponseDescriptor = $convert.base64Decode('ChZXYXJwR2VuZXJhdGlvblJlc3BvbnNlEiwKB2FjY291bnQYASABKAsyEi5oY29yZS5XYXJwQWNjb3VudFIHYWNjb3VudBIQCgNsb2cYAiABKAlSA2xvZxIyCgZjb25maWcYAyABKAsyGi5oY29yZS5XYXJwV2lyZWd1YXJkQ29uZmlnUgZjb25maWc='); @$core.Deprecated('Use systemProxyStatusDescriptor instead') const SystemProxyStatus$json = const { '1': 'SystemProxyStatus', '2': const [ const {'1': 'available', '3': 1, '4': 1, '5': 8, '10': 'available'}, const {'1': 'enabled', '3': 2, '4': 1, '5': 8, '10': 'enabled'}, ], }; /// Descriptor for `SystemProxyStatus`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List systemProxyStatusDescriptor = $convert.base64Decode('ChFTeXN0ZW1Qcm94eVN0YXR1cxIcCglhdmFpbGFibGUYASABKAhSCWF2YWlsYWJsZRIYCgdlbmFibGVkGAIgASgIUgdlbmFibGVk'); @$core.Deprecated('Use parseRequestDescriptor instead') const ParseRequest$json = const { '1': 'ParseRequest', '2': const [ const {'1': 'content', '3': 1, '4': 1, '5': 9, '10': 'content'}, const {'1': 'config_path', '3': 2, '4': 1, '5': 9, '10': 'configPath'}, const {'1': 'temp_path', '3': 3, '4': 1, '5': 9, '10': 'tempPath'}, const {'1': 'debug', '3': 4, '4': 1, '5': 8, '10': 'debug'}, ], }; /// Descriptor for `ParseRequest`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List parseRequestDescriptor = $convert.base64Decode('CgxQYXJzZVJlcXVlc3QSGAoHY29udGVudBgBIAEoCVIHY29udGVudBIfCgtjb25maWdfcGF0aBgCIAEoCVIKY29uZmlnUGF0aBIbCgl0ZW1wX3BhdGgYAyABKAlSCHRlbXBQYXRoEhQKBWRlYnVnGAQgASgIUgVkZWJ1Zw=='); @$core.Deprecated('Use parseResponseDescriptor instead') const ParseResponse$json = const { '1': 'ParseResponse', '2': const [ const {'1': 'response_code', '3': 1, '4': 1, '5': 14, '6': '.hcommon.ResponseCode', '10': 'responseCode'}, const {'1': 'content', '3': 2, '4': 1, '5': 9, '10': 'content'}, const {'1': 'message', '3': 3, '4': 1, '5': 9, '10': 'message'}, ], }; /// Descriptor for `ParseResponse`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List parseResponseDescriptor = $convert.base64Decode('Cg1QYXJzZVJlc3BvbnNlEjoKDXJlc3BvbnNlX2NvZGUYASABKA4yFS5oY29tbW9uLlJlc3BvbnNlQ29kZVIMcmVzcG9uc2VDb2RlEhgKB2NvbnRlbnQYAiABKAlSB2NvbnRlbnQSGAoHbWVzc2FnZRgDIAEoCVIHbWVzc2FnZQ=='); @$core.Deprecated('Use changeHiddifySettingsRequestDescriptor instead') const ChangeHiddifySettingsRequest$json = const { '1': 'ChangeHiddifySettingsRequest', '2': const [ const {'1': 'hiddify_settings_json', '3': 1, '4': 1, '5': 9, '10': 'hiddifySettingsJson'}, ], }; /// Descriptor for `ChangeHiddifySettingsRequest`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List changeHiddifySettingsRequestDescriptor = $convert.base64Decode('ChxDaGFuZ2VIaWRkaWZ5U2V0dGluZ3NSZXF1ZXN0EjIKFWhpZGRpZnlfc2V0dGluZ3NfanNvbhgBIAEoCVITaGlkZGlmeVNldHRpbmdzSnNvbg=='); @$core.Deprecated('Use generateConfigRequestDescriptor instead') const GenerateConfigRequest$json = const { '1': 'GenerateConfigRequest', '2': const [ const {'1': 'path', '3': 1, '4': 1, '5': 9, '10': 'path'}, const {'1': 'temp_path', '3': 2, '4': 1, '5': 9, '10': 'tempPath'}, const {'1': 'debug', '3': 3, '4': 1, '5': 8, '10': 'debug'}, ], }; /// Descriptor for `GenerateConfigRequest`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List generateConfigRequestDescriptor = $convert.base64Decode('ChVHZW5lcmF0ZUNvbmZpZ1JlcXVlc3QSEgoEcGF0aBgBIAEoCVIEcGF0aBIbCgl0ZW1wX3BhdGgYAiABKAlSCHRlbXBQYXRoEhQKBWRlYnVnGAMgASgIUgVkZWJ1Zw=='); @$core.Deprecated('Use generateConfigResponseDescriptor instead') const GenerateConfigResponse$json = const { '1': 'GenerateConfigResponse', '2': const [ const {'1': 'config_content', '3': 1, '4': 1, '5': 9, '10': 'configContent'}, ], }; /// Descriptor for `GenerateConfigResponse`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List generateConfigResponseDescriptor = $convert.base64Decode('ChZHZW5lcmF0ZUNvbmZpZ1Jlc3BvbnNlEiUKDmNvbmZpZ19jb250ZW50GAEgASgJUg1jb25maWdDb250ZW50'); @$core.Deprecated('Use selectOutboundRequestDescriptor instead') const SelectOutboundRequest$json = const { '1': 'SelectOutboundRequest', '2': const [ const {'1': 'group_tag', '3': 1, '4': 1, '5': 9, '10': 'groupTag'}, const {'1': 'outbound_tag', '3': 2, '4': 1, '5': 9, '10': 'outboundTag'}, ], }; /// Descriptor for `SelectOutboundRequest`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List selectOutboundRequestDescriptor = $convert.base64Decode('ChVTZWxlY3RPdXRib3VuZFJlcXVlc3QSGwoJZ3JvdXBfdGFnGAEgASgJUghncm91cFRhZxIhCgxvdXRib3VuZF90YWcYAiABKAlSC291dGJvdW5kVGFn'); @$core.Deprecated('Use urlTestRequestDescriptor instead') const UrlTestRequest$json = const { '1': 'UrlTestRequest', '2': const [ const {'1': 'tag', '3': 1, '4': 1, '5': 9, '10': 'tag'}, ], }; /// Descriptor for `UrlTestRequest`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List urlTestRequestDescriptor = $convert.base64Decode('Cg5VcmxUZXN0UmVxdWVzdBIQCgN0YWcYASABKAlSA3RhZw=='); @$core.Deprecated('Use generateWarpConfigRequestDescriptor instead') const GenerateWarpConfigRequest$json = const { '1': 'GenerateWarpConfigRequest', '2': const [ const {'1': 'license_key', '3': 1, '4': 1, '5': 9, '10': 'licenseKey'}, const {'1': 'account_id', '3': 2, '4': 1, '5': 9, '10': 'accountId'}, const {'1': 'access_token', '3': 3, '4': 1, '5': 9, '10': 'accessToken'}, ], }; /// Descriptor for `GenerateWarpConfigRequest`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List generateWarpConfigRequestDescriptor = $convert.base64Decode('ChlHZW5lcmF0ZVdhcnBDb25maWdSZXF1ZXN0Eh8KC2xpY2Vuc2Vfa2V5GAEgASgJUgpsaWNlbnNlS2V5Eh0KCmFjY291bnRfaWQYAiABKAlSCWFjY291bnRJZBIhCgxhY2Nlc3NfdG9rZW4YAyABKAlSC2FjY2Vzc1Rva2Vu'); @$core.Deprecated('Use setSystemProxyEnabledRequestDescriptor instead') const SetSystemProxyEnabledRequest$json = const { '1': 'SetSystemProxyEnabledRequest', '2': const [ const {'1': 'is_enabled', '3': 1, '4': 1, '5': 8, '10': 'isEnabled'}, ], }; /// Descriptor for `SetSystemProxyEnabledRequest`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List setSystemProxyEnabledRequestDescriptor = $convert.base64Decode('ChxTZXRTeXN0ZW1Qcm94eUVuYWJsZWRSZXF1ZXN0Eh0KCmlzX2VuYWJsZWQYASABKAhSCWlzRW5hYmxlZA=='); @$core.Deprecated('Use logMessageDescriptor instead') const LogMessage$json = const { '1': 'LogMessage', '2': const [ const {'1': 'level', '3': 1, '4': 1, '5': 14, '6': '.hcore.LogLevel', '10': 'level'}, const {'1': 'type', '3': 2, '4': 1, '5': 14, '6': '.hcore.LogType', '10': 'type'}, const {'1': 'message', '3': 3, '4': 1, '5': 9, '10': 'message'}, const {'1': 'time', '3': 4, '4': 1, '5': 11, '6': '.google.protobuf.Timestamp', '10': 'time'}, ], }; /// Descriptor for `LogMessage`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List logMessageDescriptor = $convert.base64Decode('CgpMb2dNZXNzYWdlEiUKBWxldmVsGAEgASgOMg8uaGNvcmUuTG9nTGV2ZWxSBWxldmVsEiIKBHR5cGUYAiABKA4yDi5oY29yZS5Mb2dUeXBlUgR0eXBlEhgKB21lc3NhZ2UYAyABKAlSB21lc3NhZ2USLgoEdGltZRgEIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXBSBHRpbWU='); @$core.Deprecated('Use logRequestDescriptor instead') const LogRequest$json = const { '1': 'LogRequest', '2': const [ const {'1': 'level', '3': 1, '4': 1, '5': 14, '6': '.hcore.LogLevel', '10': 'level'}, ], }; /// Descriptor for `LogRequest`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List logRequestDescriptor = $convert.base64Decode('CgpMb2dSZXF1ZXN0EiUKBWxldmVsGAEgASgOMg8uaGNvcmUuTG9nTGV2ZWxSBWxldmVs'); @$core.Deprecated('Use stopRequestDescriptor instead') const StopRequest$json = const { '1': 'StopRequest', }; /// Descriptor for `StopRequest`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List stopRequestDescriptor = $convert.base64Decode('CgtTdG9wUmVxdWVzdA=='); ================================================ FILE: lib/hiddifycore/generated/v2/hcore/hcore_service.pb.dart ================================================ /// // Generated code. Do not modify. // source: v2/hcore/hcore_service.proto // // @dart = 2.12 // ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name import 'dart:core' as $core; ================================================ FILE: lib/hiddifycore/generated/v2/hcore/hcore_service.pbenum.dart ================================================ /// // Generated code. Do not modify. // source: v2/hcore/hcore_service.proto // // @dart = 2.12 // ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name ================================================ FILE: lib/hiddifycore/generated/v2/hcore/hcore_service.pbgrpc.dart ================================================ /// // Generated code. Do not modify. // source: v2/hcore/hcore_service.proto // // @dart = 2.12 // ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name import 'dart:async' as $async; import 'dart:core' as $core; import 'package:grpc/service_api.dart' as $grpc; import 'hcore.pb.dart' as $0; import '../hcommon/common.pb.dart' as $1; export 'hcore_service.pb.dart'; class CoreClient extends $grpc.Client { static final _$start = $grpc.ClientMethod<$0.StartRequest, $0.CoreInfoResponse>( '/hcore.Core/Start', ($0.StartRequest value) => value.writeToBuffer(), ($core.List<$core.int> value) => $0.CoreInfoResponse.fromBuffer(value)); static final _$coreInfoListener = $grpc.ClientMethod<$1.Empty, $0.CoreInfoResponse>( '/hcore.Core/CoreInfoListener', ($1.Empty value) => value.writeToBuffer(), ($core.List<$core.int> value) => $0.CoreInfoResponse.fromBuffer(value)); static final _$outboundsInfo = $grpc.ClientMethod<$1.Empty, $0.OutboundGroupList>( '/hcore.Core/OutboundsInfo', ($1.Empty value) => value.writeToBuffer(), ($core.List<$core.int> value) => $0.OutboundGroupList.fromBuffer(value)); static final _$mainOutboundsInfo = $grpc.ClientMethod<$1.Empty, $0.OutboundGroupList>( '/hcore.Core/MainOutboundsInfo', ($1.Empty value) => value.writeToBuffer(), ($core.List<$core.int> value) => $0.OutboundGroupList.fromBuffer(value)); static final _$getSystemInfo = $grpc.ClientMethod<$1.Empty, $0.SystemInfo>( '/hcore.Core/GetSystemInfo', ($1.Empty value) => value.writeToBuffer(), ($core.List<$core.int> value) => $0.SystemInfo.fromBuffer(value)); static final _$getSystemInfoStream = $grpc.ClientMethod<$1.Empty, $0.SystemInfo>( '/hcore.Core/GetSystemInfoStream', ($1.Empty value) => value.writeToBuffer(), ($core.List<$core.int> value) => $0.SystemInfo.fromBuffer(value)); static final _$setup = $grpc.ClientMethod<$0.SetupRequest, $1.Response>( '/hcore.Core/Setup', ($0.SetupRequest value) => value.writeToBuffer(), ($core.List<$core.int> value) => $1.Response.fromBuffer(value)); static final _$parse = $grpc.ClientMethod<$0.ParseRequest, $0.ParseResponse>( '/hcore.Core/Parse', ($0.ParseRequest value) => value.writeToBuffer(), ($core.List<$core.int> value) => $0.ParseResponse.fromBuffer(value)); static final _$changeHiddifySettings = $grpc.ClientMethod<$0.ChangeHiddifySettingsRequest, $0.CoreInfoResponse>( '/hcore.Core/ChangeHiddifySettings', ($0.ChangeHiddifySettingsRequest value) => value.writeToBuffer(), ($core.List<$core.int> value) => $0.CoreInfoResponse.fromBuffer(value)); static final _$startService = $grpc.ClientMethod<$0.StartRequest, $0.CoreInfoResponse>( '/hcore.Core/StartService', ($0.StartRequest value) => value.writeToBuffer(), ($core.List<$core.int> value) => $0.CoreInfoResponse.fromBuffer(value)); static final _$stop = $grpc.ClientMethod<$1.Empty, $0.CoreInfoResponse>( '/hcore.Core/Stop', ($1.Empty value) => value.writeToBuffer(), ($core.List<$core.int> value) => $0.CoreInfoResponse.fromBuffer(value)); static final _$restart = $grpc.ClientMethod<$0.StartRequest, $0.CoreInfoResponse>( '/hcore.Core/Restart', ($0.StartRequest value) => value.writeToBuffer(), ($core.List<$core.int> value) => $0.CoreInfoResponse.fromBuffer(value)); static final _$selectOutbound = $grpc.ClientMethod<$0.SelectOutboundRequest, $1.Response>( '/hcore.Core/SelectOutbound', ($0.SelectOutboundRequest value) => value.writeToBuffer(), ($core.List<$core.int> value) => $1.Response.fromBuffer(value)); static final _$urlTest = $grpc.ClientMethod<$0.UrlTestRequest, $1.Response>( '/hcore.Core/UrlTest', ($0.UrlTestRequest value) => value.writeToBuffer(), ($core.List<$core.int> value) => $1.Response.fromBuffer(value)); static final _$urlTestActive = $grpc.ClientMethod<$1.Empty, $1.Response>( '/hcore.Core/UrlTestActive', ($1.Empty value) => value.writeToBuffer(), ($core.List<$core.int> value) => $1.Response.fromBuffer(value)); static final _$generateWarpConfig = $grpc.ClientMethod< $0.GenerateWarpConfigRequest, $0.WarpGenerationResponse>( '/hcore.Core/GenerateWarpConfig', ($0.GenerateWarpConfigRequest value) => value.writeToBuffer(), ($core.List<$core.int> value) => $0.WarpGenerationResponse.fromBuffer(value)); static final _$getSystemProxyStatus = $grpc.ClientMethod<$1.Empty, $0.SystemProxyStatus>( '/hcore.Core/GetSystemProxyStatus', ($1.Empty value) => value.writeToBuffer(), ($core.List<$core.int> value) => $0.SystemProxyStatus.fromBuffer(value)); static final _$setSystemProxyEnabled = $grpc.ClientMethod<$0.SetSystemProxyEnabledRequest, $1.Response>( '/hcore.Core/SetSystemProxyEnabled', ($0.SetSystemProxyEnabledRequest value) => value.writeToBuffer(), ($core.List<$core.int> value) => $1.Response.fromBuffer(value)); static final _$logListener = $grpc.ClientMethod<$0.LogRequest, $0.LogMessage>( '/hcore.Core/LogListener', ($0.LogRequest value) => value.writeToBuffer(), ($core.List<$core.int> value) => $0.LogMessage.fromBuffer(value)); static final _$close = $grpc.ClientMethod<$0.CloseRequest, $1.Empty>( '/hcore.Core/Close', ($0.CloseRequest value) => value.writeToBuffer(), ($core.List<$core.int> value) => $1.Empty.fromBuffer(value)); CoreClient($grpc.ClientChannel channel, {$grpc.CallOptions? options, $core.Iterable<$grpc.ClientInterceptor>? interceptors}) : super(channel, options: options, interceptors: interceptors); $grpc.ResponseFuture<$0.CoreInfoResponse> start($0.StartRequest request, {$grpc.CallOptions? options}) { return $createUnaryCall(_$start, request, options: options); } $grpc.ResponseStream<$0.CoreInfoResponse> coreInfoListener($1.Empty request, {$grpc.CallOptions? options}) { return $createStreamingCall( _$coreInfoListener, $async.Stream.fromIterable([request]), options: options); } $grpc.ResponseStream<$0.OutboundGroupList> outboundsInfo($1.Empty request, {$grpc.CallOptions? options}) { return $createStreamingCall( _$outboundsInfo, $async.Stream.fromIterable([request]), options: options); } $grpc.ResponseStream<$0.OutboundGroupList> mainOutboundsInfo($1.Empty request, {$grpc.CallOptions? options}) { return $createStreamingCall( _$mainOutboundsInfo, $async.Stream.fromIterable([request]), options: options); } $grpc.ResponseFuture<$0.SystemInfo> getSystemInfo($1.Empty request, {$grpc.CallOptions? options}) { return $createUnaryCall(_$getSystemInfo, request, options: options); } $grpc.ResponseStream<$0.SystemInfo> getSystemInfoStream($1.Empty request, {$grpc.CallOptions? options}) { return $createStreamingCall( _$getSystemInfoStream, $async.Stream.fromIterable([request]), options: options); } $grpc.ResponseFuture<$1.Response> setup($0.SetupRequest request, {$grpc.CallOptions? options}) { return $createUnaryCall(_$setup, request, options: options); } $grpc.ResponseFuture<$0.ParseResponse> parse($0.ParseRequest request, {$grpc.CallOptions? options}) { return $createUnaryCall(_$parse, request, options: options); } $grpc.ResponseFuture<$0.CoreInfoResponse> changeHiddifySettings( $0.ChangeHiddifySettingsRequest request, {$grpc.CallOptions? options}) { return $createUnaryCall(_$changeHiddifySettings, request, options: options); } $grpc.ResponseFuture<$0.CoreInfoResponse> startService( $0.StartRequest request, {$grpc.CallOptions? options}) { return $createUnaryCall(_$startService, request, options: options); } $grpc.ResponseFuture<$0.CoreInfoResponse> stop($1.Empty request, {$grpc.CallOptions? options}) { return $createUnaryCall(_$stop, request, options: options); } $grpc.ResponseFuture<$0.CoreInfoResponse> restart($0.StartRequest request, {$grpc.CallOptions? options}) { return $createUnaryCall(_$restart, request, options: options); } $grpc.ResponseFuture<$1.Response> selectOutbound( $0.SelectOutboundRequest request, {$grpc.CallOptions? options}) { return $createUnaryCall(_$selectOutbound, request, options: options); } $grpc.ResponseFuture<$1.Response> urlTest($0.UrlTestRequest request, {$grpc.CallOptions? options}) { return $createUnaryCall(_$urlTest, request, options: options); } $grpc.ResponseFuture<$1.Response> urlTestActive($1.Empty request, {$grpc.CallOptions? options}) { return $createUnaryCall(_$urlTestActive, request, options: options); } $grpc.ResponseFuture<$0.WarpGenerationResponse> generateWarpConfig( $0.GenerateWarpConfigRequest request, {$grpc.CallOptions? options}) { return $createUnaryCall(_$generateWarpConfig, request, options: options); } $grpc.ResponseFuture<$0.SystemProxyStatus> getSystemProxyStatus( $1.Empty request, {$grpc.CallOptions? options}) { return $createUnaryCall(_$getSystemProxyStatus, request, options: options); } $grpc.ResponseFuture<$1.Response> setSystemProxyEnabled( $0.SetSystemProxyEnabledRequest request, {$grpc.CallOptions? options}) { return $createUnaryCall(_$setSystemProxyEnabled, request, options: options); } $grpc.ResponseStream<$0.LogMessage> logListener($0.LogRequest request, {$grpc.CallOptions? options}) { return $createStreamingCall( _$logListener, $async.Stream.fromIterable([request]), options: options); } $grpc.ResponseFuture<$1.Empty> close($0.CloseRequest request, {$grpc.CallOptions? options}) { return $createUnaryCall(_$close, request, options: options); } } abstract class CoreServiceBase extends $grpc.Service { $core.String get $name => 'hcore.Core'; CoreServiceBase() { $addMethod($grpc.ServiceMethod<$0.StartRequest, $0.CoreInfoResponse>( 'Start', start_Pre, false, false, ($core.List<$core.int> value) => $0.StartRequest.fromBuffer(value), ($0.CoreInfoResponse value) => value.writeToBuffer())); $addMethod($grpc.ServiceMethod<$1.Empty, $0.CoreInfoResponse>( 'CoreInfoListener', coreInfoListener_Pre, false, true, ($core.List<$core.int> value) => $1.Empty.fromBuffer(value), ($0.CoreInfoResponse value) => value.writeToBuffer())); $addMethod($grpc.ServiceMethod<$1.Empty, $0.OutboundGroupList>( 'OutboundsInfo', outboundsInfo_Pre, false, true, ($core.List<$core.int> value) => $1.Empty.fromBuffer(value), ($0.OutboundGroupList value) => value.writeToBuffer())); $addMethod($grpc.ServiceMethod<$1.Empty, $0.OutboundGroupList>( 'MainOutboundsInfo', mainOutboundsInfo_Pre, false, true, ($core.List<$core.int> value) => $1.Empty.fromBuffer(value), ($0.OutboundGroupList value) => value.writeToBuffer())); $addMethod($grpc.ServiceMethod<$1.Empty, $0.SystemInfo>( 'GetSystemInfo', getSystemInfo_Pre, false, false, ($core.List<$core.int> value) => $1.Empty.fromBuffer(value), ($0.SystemInfo value) => value.writeToBuffer())); $addMethod($grpc.ServiceMethod<$1.Empty, $0.SystemInfo>( 'GetSystemInfoStream', getSystemInfoStream_Pre, false, true, ($core.List<$core.int> value) => $1.Empty.fromBuffer(value), ($0.SystemInfo value) => value.writeToBuffer())); $addMethod($grpc.ServiceMethod<$0.SetupRequest, $1.Response>( 'Setup', setup_Pre, false, false, ($core.List<$core.int> value) => $0.SetupRequest.fromBuffer(value), ($1.Response value) => value.writeToBuffer())); $addMethod($grpc.ServiceMethod<$0.ParseRequest, $0.ParseResponse>( 'Parse', parse_Pre, false, false, ($core.List<$core.int> value) => $0.ParseRequest.fromBuffer(value), ($0.ParseResponse value) => value.writeToBuffer())); $addMethod($grpc.ServiceMethod<$0.ChangeHiddifySettingsRequest, $0.CoreInfoResponse>( 'ChangeHiddifySettings', changeHiddifySettings_Pre, false, false, ($core.List<$core.int> value) => $0.ChangeHiddifySettingsRequest.fromBuffer(value), ($0.CoreInfoResponse value) => value.writeToBuffer())); $addMethod($grpc.ServiceMethod<$0.StartRequest, $0.CoreInfoResponse>( 'StartService', startService_Pre, false, false, ($core.List<$core.int> value) => $0.StartRequest.fromBuffer(value), ($0.CoreInfoResponse value) => value.writeToBuffer())); $addMethod($grpc.ServiceMethod<$1.Empty, $0.CoreInfoResponse>( 'Stop', stop_Pre, false, false, ($core.List<$core.int> value) => $1.Empty.fromBuffer(value), ($0.CoreInfoResponse value) => value.writeToBuffer())); $addMethod($grpc.ServiceMethod<$0.StartRequest, $0.CoreInfoResponse>( 'Restart', restart_Pre, false, false, ($core.List<$core.int> value) => $0.StartRequest.fromBuffer(value), ($0.CoreInfoResponse value) => value.writeToBuffer())); $addMethod($grpc.ServiceMethod<$0.SelectOutboundRequest, $1.Response>( 'SelectOutbound', selectOutbound_Pre, false, false, ($core.List<$core.int> value) => $0.SelectOutboundRequest.fromBuffer(value), ($1.Response value) => value.writeToBuffer())); $addMethod($grpc.ServiceMethod<$0.UrlTestRequest, $1.Response>( 'UrlTest', urlTest_Pre, false, false, ($core.List<$core.int> value) => $0.UrlTestRequest.fromBuffer(value), ($1.Response value) => value.writeToBuffer())); $addMethod($grpc.ServiceMethod<$1.Empty, $1.Response>( 'UrlTestActive', urlTestActive_Pre, false, false, ($core.List<$core.int> value) => $1.Empty.fromBuffer(value), ($1.Response value) => value.writeToBuffer())); $addMethod($grpc.ServiceMethod<$0.GenerateWarpConfigRequest, $0.WarpGenerationResponse>( 'GenerateWarpConfig', generateWarpConfig_Pre, false, false, ($core.List<$core.int> value) => $0.GenerateWarpConfigRequest.fromBuffer(value), ($0.WarpGenerationResponse value) => value.writeToBuffer())); $addMethod($grpc.ServiceMethod<$1.Empty, $0.SystemProxyStatus>( 'GetSystemProxyStatus', getSystemProxyStatus_Pre, false, false, ($core.List<$core.int> value) => $1.Empty.fromBuffer(value), ($0.SystemProxyStatus value) => value.writeToBuffer())); $addMethod( $grpc.ServiceMethod<$0.SetSystemProxyEnabledRequest, $1.Response>( 'SetSystemProxyEnabled', setSystemProxyEnabled_Pre, false, false, ($core.List<$core.int> value) => $0.SetSystemProxyEnabledRequest.fromBuffer(value), ($1.Response value) => value.writeToBuffer())); $addMethod($grpc.ServiceMethod<$0.LogRequest, $0.LogMessage>( 'LogListener', logListener_Pre, false, true, ($core.List<$core.int> value) => $0.LogRequest.fromBuffer(value), ($0.LogMessage value) => value.writeToBuffer())); $addMethod($grpc.ServiceMethod<$0.CloseRequest, $1.Empty>( 'Close', close_Pre, false, false, ($core.List<$core.int> value) => $0.CloseRequest.fromBuffer(value), ($1.Empty value) => value.writeToBuffer())); } $async.Future<$0.CoreInfoResponse> start_Pre( $grpc.ServiceCall call, $async.Future<$0.StartRequest> request) async { return start(call, await request); } $async.Stream<$0.CoreInfoResponse> coreInfoListener_Pre( $grpc.ServiceCall call, $async.Future<$1.Empty> request) async* { yield* coreInfoListener(call, await request); } $async.Stream<$0.OutboundGroupList> outboundsInfo_Pre( $grpc.ServiceCall call, $async.Future<$1.Empty> request) async* { yield* outboundsInfo(call, await request); } $async.Stream<$0.OutboundGroupList> mainOutboundsInfo_Pre( $grpc.ServiceCall call, $async.Future<$1.Empty> request) async* { yield* mainOutboundsInfo(call, await request); } $async.Future<$0.SystemInfo> getSystemInfo_Pre( $grpc.ServiceCall call, $async.Future<$1.Empty> request) async { return getSystemInfo(call, await request); } $async.Stream<$0.SystemInfo> getSystemInfoStream_Pre( $grpc.ServiceCall call, $async.Future<$1.Empty> request) async* { yield* getSystemInfoStream(call, await request); } $async.Future<$1.Response> setup_Pre( $grpc.ServiceCall call, $async.Future<$0.SetupRequest> request) async { return setup(call, await request); } $async.Future<$0.ParseResponse> parse_Pre( $grpc.ServiceCall call, $async.Future<$0.ParseRequest> request) async { return parse(call, await request); } $async.Future<$0.CoreInfoResponse> changeHiddifySettings_Pre( $grpc.ServiceCall call, $async.Future<$0.ChangeHiddifySettingsRequest> request) async { return changeHiddifySettings(call, await request); } $async.Future<$0.CoreInfoResponse> startService_Pre( $grpc.ServiceCall call, $async.Future<$0.StartRequest> request) async { return startService(call, await request); } $async.Future<$0.CoreInfoResponse> stop_Pre( $grpc.ServiceCall call, $async.Future<$1.Empty> request) async { return stop(call, await request); } $async.Future<$0.CoreInfoResponse> restart_Pre( $grpc.ServiceCall call, $async.Future<$0.StartRequest> request) async { return restart(call, await request); } $async.Future<$1.Response> selectOutbound_Pre($grpc.ServiceCall call, $async.Future<$0.SelectOutboundRequest> request) async { return selectOutbound(call, await request); } $async.Future<$1.Response> urlTest_Pre( $grpc.ServiceCall call, $async.Future<$0.UrlTestRequest> request) async { return urlTest(call, await request); } $async.Future<$1.Response> urlTestActive_Pre( $grpc.ServiceCall call, $async.Future<$1.Empty> request) async { return urlTestActive(call, await request); } $async.Future<$0.WarpGenerationResponse> generateWarpConfig_Pre( $grpc.ServiceCall call, $async.Future<$0.GenerateWarpConfigRequest> request) async { return generateWarpConfig(call, await request); } $async.Future<$0.SystemProxyStatus> getSystemProxyStatus_Pre( $grpc.ServiceCall call, $async.Future<$1.Empty> request) async { return getSystemProxyStatus(call, await request); } $async.Future<$1.Response> setSystemProxyEnabled_Pre($grpc.ServiceCall call, $async.Future<$0.SetSystemProxyEnabledRequest> request) async { return setSystemProxyEnabled(call, await request); } $async.Stream<$0.LogMessage> logListener_Pre( $grpc.ServiceCall call, $async.Future<$0.LogRequest> request) async* { yield* logListener(call, await request); } $async.Future<$1.Empty> close_Pre( $grpc.ServiceCall call, $async.Future<$0.CloseRequest> request) async { return close(call, await request); } $async.Future<$0.CoreInfoResponse> start( $grpc.ServiceCall call, $0.StartRequest request); $async.Stream<$0.CoreInfoResponse> coreInfoListener( $grpc.ServiceCall call, $1.Empty request); $async.Stream<$0.OutboundGroupList> outboundsInfo( $grpc.ServiceCall call, $1.Empty request); $async.Stream<$0.OutboundGroupList> mainOutboundsInfo( $grpc.ServiceCall call, $1.Empty request); $async.Future<$0.SystemInfo> getSystemInfo( $grpc.ServiceCall call, $1.Empty request); $async.Stream<$0.SystemInfo> getSystemInfoStream( $grpc.ServiceCall call, $1.Empty request); $async.Future<$1.Response> setup( $grpc.ServiceCall call, $0.SetupRequest request); $async.Future<$0.ParseResponse> parse( $grpc.ServiceCall call, $0.ParseRequest request); $async.Future<$0.CoreInfoResponse> changeHiddifySettings( $grpc.ServiceCall call, $0.ChangeHiddifySettingsRequest request); $async.Future<$0.CoreInfoResponse> startService( $grpc.ServiceCall call, $0.StartRequest request); $async.Future<$0.CoreInfoResponse> stop( $grpc.ServiceCall call, $1.Empty request); $async.Future<$0.CoreInfoResponse> restart( $grpc.ServiceCall call, $0.StartRequest request); $async.Future<$1.Response> selectOutbound( $grpc.ServiceCall call, $0.SelectOutboundRequest request); $async.Future<$1.Response> urlTest( $grpc.ServiceCall call, $0.UrlTestRequest request); $async.Future<$1.Response> urlTestActive( $grpc.ServiceCall call, $1.Empty request); $async.Future<$0.WarpGenerationResponse> generateWarpConfig( $grpc.ServiceCall call, $0.GenerateWarpConfigRequest request); $async.Future<$0.SystemProxyStatus> getSystemProxyStatus( $grpc.ServiceCall call, $1.Empty request); $async.Future<$1.Response> setSystemProxyEnabled( $grpc.ServiceCall call, $0.SetSystemProxyEnabledRequest request); $async.Stream<$0.LogMessage> logListener( $grpc.ServiceCall call, $0.LogRequest request); $async.Future<$1.Empty> close( $grpc.ServiceCall call, $0.CloseRequest request); } ================================================ FILE: lib/hiddifycore/generated/v2/hcore/hcore_service.pbjson.dart ================================================ /// // Generated code. Do not modify. // source: v2/hcore/hcore_service.proto // // @dart = 2.12 // ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,deprecated_member_use_from_same_package,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name import 'dart:core' as $core; import 'dart:convert' as $convert; import 'dart:typed_data' as $typed_data; ================================================ FILE: lib/hiddifycore/generated/v2/hcore/tunnelservice/tunnel.pb.dart ================================================ /// // Generated code. Do not modify. // source: v2/hcore/tunnelservice/tunnel.proto // // @dart = 2.12 // ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name import 'dart:core' as $core; import 'package:protobuf/protobuf.dart' as $pb; class TunnelStartRequest extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'TunnelStartRequest', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'tunnelservice'), createEmptyInstance: create) ..aOB(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'ipv6') ..a<$core.int>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'serverPort', $pb.PbFieldType.O3) ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'serverUsername') ..aOS(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'serverPassword') ..aOB(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'strictRoute') ..aOB(6, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'endpointIndependentNat') ..aOS(7, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'stack') ..hasRequiredFields = false ; TunnelStartRequest._() : super(); factory TunnelStartRequest({ $core.bool? ipv6, $core.int? serverPort, $core.String? serverUsername, $core.String? serverPassword, $core.bool? strictRoute, $core.bool? endpointIndependentNat, $core.String? stack, }) { final _result = create(); if (ipv6 != null) { _result.ipv6 = ipv6; } if (serverPort != null) { _result.serverPort = serverPort; } if (serverUsername != null) { _result.serverUsername = serverUsername; } if (serverPassword != null) { _result.serverPassword = serverPassword; } if (strictRoute != null) { _result.strictRoute = strictRoute; } if (endpointIndependentNat != null) { _result.endpointIndependentNat = endpointIndependentNat; } if (stack != null) { _result.stack = stack; } return _result; } factory TunnelStartRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory TunnelStartRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') TunnelStartRequest clone() => TunnelStartRequest()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') TunnelStartRequest copyWith(void Function(TunnelStartRequest) updates) => super.copyWith((message) => updates(message as TunnelStartRequest)) as TunnelStartRequest; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static TunnelStartRequest create() => TunnelStartRequest._(); TunnelStartRequest createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static TunnelStartRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static TunnelStartRequest? _defaultInstance; @$pb.TagNumber(1) $core.bool get ipv6 => $_getBF(0); @$pb.TagNumber(1) set ipv6($core.bool v) { $_setBool(0, v); } @$pb.TagNumber(1) $core.bool hasIpv6() => $_has(0); @$pb.TagNumber(1) void clearIpv6() => clearField(1); @$pb.TagNumber(2) $core.int get serverPort => $_getIZ(1); @$pb.TagNumber(2) set serverPort($core.int v) { $_setSignedInt32(1, v); } @$pb.TagNumber(2) $core.bool hasServerPort() => $_has(1); @$pb.TagNumber(2) void clearServerPort() => clearField(2); @$pb.TagNumber(3) $core.String get serverUsername => $_getSZ(2); @$pb.TagNumber(3) set serverUsername($core.String v) { $_setString(2, v); } @$pb.TagNumber(3) $core.bool hasServerUsername() => $_has(2); @$pb.TagNumber(3) void clearServerUsername() => clearField(3); @$pb.TagNumber(4) $core.String get serverPassword => $_getSZ(3); @$pb.TagNumber(4) set serverPassword($core.String v) { $_setString(3, v); } @$pb.TagNumber(4) $core.bool hasServerPassword() => $_has(3); @$pb.TagNumber(4) void clearServerPassword() => clearField(4); @$pb.TagNumber(5) $core.bool get strictRoute => $_getBF(4); @$pb.TagNumber(5) set strictRoute($core.bool v) { $_setBool(4, v); } @$pb.TagNumber(5) $core.bool hasStrictRoute() => $_has(4); @$pb.TagNumber(5) void clearStrictRoute() => clearField(5); @$pb.TagNumber(6) $core.bool get endpointIndependentNat => $_getBF(5); @$pb.TagNumber(6) set endpointIndependentNat($core.bool v) { $_setBool(5, v); } @$pb.TagNumber(6) $core.bool hasEndpointIndependentNat() => $_has(5); @$pb.TagNumber(6) void clearEndpointIndependentNat() => clearField(6); @$pb.TagNumber(7) $core.String get stack => $_getSZ(6); @$pb.TagNumber(7) set stack($core.String v) { $_setString(6, v); } @$pb.TagNumber(7) $core.bool hasStack() => $_has(6); @$pb.TagNumber(7) void clearStack() => clearField(7); } class TunnelResponse extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'TunnelResponse', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'tunnelservice'), createEmptyInstance: create) ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'message') ..hasRequiredFields = false ; TunnelResponse._() : super(); factory TunnelResponse({ $core.String? message, }) { final _result = create(); if (message != null) { _result.message = message; } return _result; } factory TunnelResponse.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory TunnelResponse.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') TunnelResponse clone() => TunnelResponse()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') TunnelResponse copyWith(void Function(TunnelResponse) updates) => super.copyWith((message) => updates(message as TunnelResponse)) as TunnelResponse; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static TunnelResponse create() => TunnelResponse._(); TunnelResponse createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static TunnelResponse getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static TunnelResponse? _defaultInstance; @$pb.TagNumber(1) $core.String get message => $_getSZ(0); @$pb.TagNumber(1) set message($core.String v) { $_setString(0, v); } @$pb.TagNumber(1) $core.bool hasMessage() => $_has(0); @$pb.TagNumber(1) void clearMessage() => clearField(1); } ================================================ FILE: lib/hiddifycore/generated/v2/hcore/tunnelservice/tunnel.pbenum.dart ================================================ /// // Generated code. Do not modify. // source: v2/hcore/tunnelservice/tunnel.proto // // @dart = 2.12 // ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name ================================================ FILE: lib/hiddifycore/generated/v2/hcore/tunnelservice/tunnel.pbjson.dart ================================================ /// // Generated code. Do not modify. // source: v2/hcore/tunnelservice/tunnel.proto // // @dart = 2.12 // ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,deprecated_member_use_from_same_package,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name import 'dart:core' as $core; import 'dart:convert' as $convert; import 'dart:typed_data' as $typed_data; @$core.Deprecated('Use tunnelStartRequestDescriptor instead') const TunnelStartRequest$json = const { '1': 'TunnelStartRequest', '2': const [ const {'1': 'ipv6', '3': 1, '4': 1, '5': 8, '10': 'ipv6'}, const {'1': 'server_port', '3': 2, '4': 1, '5': 5, '10': 'serverPort'}, const {'1': 'server_username', '3': 3, '4': 1, '5': 9, '10': 'serverUsername'}, const {'1': 'server_password', '3': 4, '4': 1, '5': 9, '10': 'serverPassword'}, const {'1': 'strict_route', '3': 5, '4': 1, '5': 8, '10': 'strictRoute'}, const {'1': 'endpoint_independent_nat', '3': 6, '4': 1, '5': 8, '10': 'endpointIndependentNat'}, const {'1': 'stack', '3': 7, '4': 1, '5': 9, '10': 'stack'}, ], }; /// Descriptor for `TunnelStartRequest`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List tunnelStartRequestDescriptor = $convert.base64Decode('ChJUdW5uZWxTdGFydFJlcXVlc3QSEgoEaXB2NhgBIAEoCFIEaXB2NhIfCgtzZXJ2ZXJfcG9ydBgCIAEoBVIKc2VydmVyUG9ydBInCg9zZXJ2ZXJfdXNlcm5hbWUYAyABKAlSDnNlcnZlclVzZXJuYW1lEicKD3NlcnZlcl9wYXNzd29yZBgEIAEoCVIOc2VydmVyUGFzc3dvcmQSIQoMc3RyaWN0X3JvdXRlGAUgASgIUgtzdHJpY3RSb3V0ZRI4ChhlbmRwb2ludF9pbmRlcGVuZGVudF9uYXQYBiABKAhSFmVuZHBvaW50SW5kZXBlbmRlbnROYXQSFAoFc3RhY2sYByABKAlSBXN0YWNr'); @$core.Deprecated('Use tunnelResponseDescriptor instead') const TunnelResponse$json = const { '1': 'TunnelResponse', '2': const [ const {'1': 'message', '3': 1, '4': 1, '5': 9, '10': 'message'}, ], }; /// Descriptor for `TunnelResponse`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List tunnelResponseDescriptor = $convert.base64Decode('Cg5UdW5uZWxSZXNwb25zZRIYCgdtZXNzYWdlGAEgASgJUgdtZXNzYWdl'); ================================================ FILE: lib/hiddifycore/generated/v2/hcore/tunnelservice/tunnel_service.pb.dart ================================================ /// // Generated code. Do not modify. // source: v2/hcore/tunnelservice/tunnel_service.proto // // @dart = 2.12 // ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name import 'dart:core' as $core; ================================================ FILE: lib/hiddifycore/generated/v2/hcore/tunnelservice/tunnel_service.pbenum.dart ================================================ /// // Generated code. Do not modify. // source: v2/hcore/tunnelservice/tunnel_service.proto // // @dart = 2.12 // ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name ================================================ FILE: lib/hiddifycore/generated/v2/hcore/tunnelservice/tunnel_service.pbgrpc.dart ================================================ /// // Generated code. Do not modify. // source: v2/hcore/tunnelservice/tunnel_service.proto // // @dart = 2.12 // ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name import 'dart:async' as $async; import 'dart:core' as $core; import 'package:grpc/service_api.dart' as $grpc; import 'tunnel.pb.dart' as $2; import '../../hcommon/common.pb.dart' as $1; export 'tunnel_service.pb.dart'; class TunnelServiceClient extends $grpc.Client { static final _$start = $grpc.ClientMethod<$2.TunnelStartRequest, $2.TunnelResponse>( '/tunnelservice.TunnelService/Start', ($2.TunnelStartRequest value) => value.writeToBuffer(), ($core.List<$core.int> value) => $2.TunnelResponse.fromBuffer(value)); static final _$stop = $grpc.ClientMethod<$1.Empty, $2.TunnelResponse>( '/tunnelservice.TunnelService/Stop', ($1.Empty value) => value.writeToBuffer(), ($core.List<$core.int> value) => $2.TunnelResponse.fromBuffer(value)); static final _$status = $grpc.ClientMethod<$1.Empty, $2.TunnelResponse>( '/tunnelservice.TunnelService/Status', ($1.Empty value) => value.writeToBuffer(), ($core.List<$core.int> value) => $2.TunnelResponse.fromBuffer(value)); static final _$exit = $grpc.ClientMethod<$1.Empty, $2.TunnelResponse>( '/tunnelservice.TunnelService/Exit', ($1.Empty value) => value.writeToBuffer(), ($core.List<$core.int> value) => $2.TunnelResponse.fromBuffer(value)); TunnelServiceClient($grpc.ClientChannel channel, {$grpc.CallOptions? options, $core.Iterable<$grpc.ClientInterceptor>? interceptors}) : super(channel, options: options, interceptors: interceptors); $grpc.ResponseFuture<$2.TunnelResponse> start($2.TunnelStartRequest request, {$grpc.CallOptions? options}) { return $createUnaryCall(_$start, request, options: options); } $grpc.ResponseFuture<$2.TunnelResponse> stop($1.Empty request, {$grpc.CallOptions? options}) { return $createUnaryCall(_$stop, request, options: options); } $grpc.ResponseFuture<$2.TunnelResponse> status($1.Empty request, {$grpc.CallOptions? options}) { return $createUnaryCall(_$status, request, options: options); } $grpc.ResponseFuture<$2.TunnelResponse> exit($1.Empty request, {$grpc.CallOptions? options}) { return $createUnaryCall(_$exit, request, options: options); } } abstract class TunnelServiceBase extends $grpc.Service { $core.String get $name => 'tunnelservice.TunnelService'; TunnelServiceBase() { $addMethod($grpc.ServiceMethod<$2.TunnelStartRequest, $2.TunnelResponse>( 'Start', start_Pre, false, false, ($core.List<$core.int> value) => $2.TunnelStartRequest.fromBuffer(value), ($2.TunnelResponse value) => value.writeToBuffer())); $addMethod($grpc.ServiceMethod<$1.Empty, $2.TunnelResponse>( 'Stop', stop_Pre, false, false, ($core.List<$core.int> value) => $1.Empty.fromBuffer(value), ($2.TunnelResponse value) => value.writeToBuffer())); $addMethod($grpc.ServiceMethod<$1.Empty, $2.TunnelResponse>( 'Status', status_Pre, false, false, ($core.List<$core.int> value) => $1.Empty.fromBuffer(value), ($2.TunnelResponse value) => value.writeToBuffer())); $addMethod($grpc.ServiceMethod<$1.Empty, $2.TunnelResponse>( 'Exit', exit_Pre, false, false, ($core.List<$core.int> value) => $1.Empty.fromBuffer(value), ($2.TunnelResponse value) => value.writeToBuffer())); } $async.Future<$2.TunnelResponse> start_Pre($grpc.ServiceCall call, $async.Future<$2.TunnelStartRequest> request) async { return start(call, await request); } $async.Future<$2.TunnelResponse> stop_Pre( $grpc.ServiceCall call, $async.Future<$1.Empty> request) async { return stop(call, await request); } $async.Future<$2.TunnelResponse> status_Pre( $grpc.ServiceCall call, $async.Future<$1.Empty> request) async { return status(call, await request); } $async.Future<$2.TunnelResponse> exit_Pre( $grpc.ServiceCall call, $async.Future<$1.Empty> request) async { return exit(call, await request); } $async.Future<$2.TunnelResponse> start( $grpc.ServiceCall call, $2.TunnelStartRequest request); $async.Future<$2.TunnelResponse> stop( $grpc.ServiceCall call, $1.Empty request); $async.Future<$2.TunnelResponse> status( $grpc.ServiceCall call, $1.Empty request); $async.Future<$2.TunnelResponse> exit( $grpc.ServiceCall call, $1.Empty request); } ================================================ FILE: lib/hiddifycore/generated/v2/hcore/tunnelservice/tunnel_service.pbjson.dart ================================================ /// // Generated code. Do not modify. // source: v2/hcore/tunnelservice/tunnel_service.proto // // @dart = 2.12 // ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,deprecated_member_use_from_same_package,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name import 'dart:core' as $core; import 'dart:convert' as $convert; import 'dart:typed_data' as $typed_data; ================================================ FILE: lib/hiddifycore/generated/v2/hello/hello.pb.dart ================================================ /// // Generated code. Do not modify. // source: v2/hello/hello.proto // // @dart = 2.12 // ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name import 'dart:core' as $core; import 'package:protobuf/protobuf.dart' as $pb; class HelloRequest extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'HelloRequest', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'hello'), createEmptyInstance: create) ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'name') ..hasRequiredFields = false ; HelloRequest._() : super(); factory HelloRequest({ $core.String? name, }) { final _result = create(); if (name != null) { _result.name = name; } return _result; } factory HelloRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory HelloRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') HelloRequest clone() => HelloRequest()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') HelloRequest copyWith(void Function(HelloRequest) updates) => super.copyWith((message) => updates(message as HelloRequest)) as HelloRequest; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static HelloRequest create() => HelloRequest._(); HelloRequest createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static HelloRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static HelloRequest? _defaultInstance; @$pb.TagNumber(1) $core.String get name => $_getSZ(0); @$pb.TagNumber(1) set name($core.String v) { $_setString(0, v); } @$pb.TagNumber(1) $core.bool hasName() => $_has(0); @$pb.TagNumber(1) void clearName() => clearField(1); } class HelloResponse extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'HelloResponse', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'hello'), createEmptyInstance: create) ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'message') ..hasRequiredFields = false ; HelloResponse._() : super(); factory HelloResponse({ $core.String? message, }) { final _result = create(); if (message != null) { _result.message = message; } return _result; } factory HelloResponse.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory HelloResponse.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') HelloResponse clone() => HelloResponse()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') HelloResponse copyWith(void Function(HelloResponse) updates) => super.copyWith((message) => updates(message as HelloResponse)) as HelloResponse; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static HelloResponse create() => HelloResponse._(); HelloResponse createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static HelloResponse getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static HelloResponse? _defaultInstance; @$pb.TagNumber(1) $core.String get message => $_getSZ(0); @$pb.TagNumber(1) set message($core.String v) { $_setString(0, v); } @$pb.TagNumber(1) $core.bool hasMessage() => $_has(0); @$pb.TagNumber(1) void clearMessage() => clearField(1); } ================================================ FILE: lib/hiddifycore/generated/v2/hello/hello.pbenum.dart ================================================ /// // Generated code. Do not modify. // source: v2/hello/hello.proto // // @dart = 2.12 // ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name ================================================ FILE: lib/hiddifycore/generated/v2/hello/hello.pbjson.dart ================================================ /// // Generated code. Do not modify. // source: v2/hello/hello.proto // // @dart = 2.12 // ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,deprecated_member_use_from_same_package,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name import 'dart:core' as $core; import 'dart:convert' as $convert; import 'dart:typed_data' as $typed_data; @$core.Deprecated('Use helloRequestDescriptor instead') const HelloRequest$json = const { '1': 'HelloRequest', '2': const [ const {'1': 'name', '3': 1, '4': 1, '5': 9, '10': 'name'}, ], }; /// Descriptor for `HelloRequest`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List helloRequestDescriptor = $convert.base64Decode('CgxIZWxsb1JlcXVlc3QSEgoEbmFtZRgBIAEoCVIEbmFtZQ=='); @$core.Deprecated('Use helloResponseDescriptor instead') const HelloResponse$json = const { '1': 'HelloResponse', '2': const [ const {'1': 'message', '3': 1, '4': 1, '5': 9, '10': 'message'}, ], }; /// Descriptor for `HelloResponse`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List helloResponseDescriptor = $convert.base64Decode('Cg1IZWxsb1Jlc3BvbnNlEhgKB21lc3NhZ2UYASABKAlSB21lc3NhZ2U='); ================================================ FILE: lib/hiddifycore/generated/v2/hello/hello_service.pb.dart ================================================ /// // Generated code. Do not modify. // source: v2/hello/hello_service.proto // // @dart = 2.12 // ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name import 'dart:core' as $core; ================================================ FILE: lib/hiddifycore/generated/v2/hello/hello_service.pbenum.dart ================================================ /// // Generated code. Do not modify. // source: v2/hello/hello_service.proto // // @dart = 2.12 // ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name ================================================ FILE: lib/hiddifycore/generated/v2/hello/hello_service.pbgrpc.dart ================================================ /// // Generated code. Do not modify. // source: v2/hello/hello_service.proto // // @dart = 2.12 // ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name import 'dart:async' as $async; import 'dart:core' as $core; import 'package:grpc/service_api.dart' as $grpc; import 'hello.pb.dart' as $5; export 'hello_service.pb.dart'; class HelloClient extends $grpc.Client { static final _$sayHello = $grpc.ClientMethod<$5.HelloRequest, $5.HelloResponse>( '/hello.Hello/SayHello', ($5.HelloRequest value) => value.writeToBuffer(), ($core.List<$core.int> value) => $5.HelloResponse.fromBuffer(value)); static final _$sayHelloStream = $grpc.ClientMethod<$5.HelloRequest, $5.HelloResponse>( '/hello.Hello/SayHelloStream', ($5.HelloRequest value) => value.writeToBuffer(), ($core.List<$core.int> value) => $5.HelloResponse.fromBuffer(value)); HelloClient($grpc.ClientChannel channel, {$grpc.CallOptions? options, $core.Iterable<$grpc.ClientInterceptor>? interceptors}) : super(channel, options: options, interceptors: interceptors); $grpc.ResponseFuture<$5.HelloResponse> sayHello($5.HelloRequest request, {$grpc.CallOptions? options}) { return $createUnaryCall(_$sayHello, request, options: options); } $grpc.ResponseStream<$5.HelloResponse> sayHelloStream( $async.Stream<$5.HelloRequest> request, {$grpc.CallOptions? options}) { return $createStreamingCall(_$sayHelloStream, request, options: options); } } abstract class HelloServiceBase extends $grpc.Service { $core.String get $name => 'hello.Hello'; HelloServiceBase() { $addMethod($grpc.ServiceMethod<$5.HelloRequest, $5.HelloResponse>( 'SayHello', sayHello_Pre, false, false, ($core.List<$core.int> value) => $5.HelloRequest.fromBuffer(value), ($5.HelloResponse value) => value.writeToBuffer())); $addMethod($grpc.ServiceMethod<$5.HelloRequest, $5.HelloResponse>( 'SayHelloStream', sayHelloStream, true, true, ($core.List<$core.int> value) => $5.HelloRequest.fromBuffer(value), ($5.HelloResponse value) => value.writeToBuffer())); } $async.Future<$5.HelloResponse> sayHello_Pre( $grpc.ServiceCall call, $async.Future<$5.HelloRequest> request) async { return sayHello(call, await request); } $async.Future<$5.HelloResponse> sayHello( $grpc.ServiceCall call, $5.HelloRequest request); $async.Stream<$5.HelloResponse> sayHelloStream( $grpc.ServiceCall call, $async.Stream<$5.HelloRequest> request); } ================================================ FILE: lib/hiddifycore/generated/v2/hello/hello_service.pbjson.dart ================================================ /// // Generated code. Do not modify. // source: v2/hello/hello_service.proto // // @dart = 2.12 // ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,deprecated_member_use_from_same_package,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name import 'dart:core' as $core; import 'dart:convert' as $convert; import 'dart:typed_data' as $typed_data; ================================================ FILE: lib/hiddifycore/generated/v2/hiddifyoptions/hiddify_options.pb.dart ================================================ /// // Generated code. Do not modify. // source: v2/hiddifyoptions/hiddify_options.proto // // @dart = 2.12 // ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name import 'dart:core' as $core; import 'package:fixnum/fixnum.dart' as $fixnum; import 'package:protobuf/protobuf.dart' as $pb; import 'hiddify_options.pbenum.dart'; export 'hiddify_options.pbenum.dart'; class HiddifyOptions extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'HiddifyOptions', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'hiddifyoptions'), createEmptyInstance: create) ..aOB(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'enableFullConfig') ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'logLevel') ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'logFile') ..aOB(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'enableClashApi') ..a<$core.int>(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'clashApiPort', $pb.PbFieldType.OU3) ..aOS(6, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'webSecret') ..aOS(7, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'region') ..aOB(8, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'blockAds') ..aOB(9, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'useXrayCoreWhenPossible') ..pc(10, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'rules', $pb.PbFieldType.PM, subBuilder: Rule.create) ..aOM(11, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'warp', subBuilder: WarpOptions.create) ..aOM(12, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'warp2', subBuilder: WarpOptions.create) ..aOM(13, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'mux', subBuilder: MuxOptions.create) ..aOM(14, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'tlsTricks', subBuilder: TLSTricks.create) ..aOM(15, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'dnsOptions', subBuilder: DNSOptions.create) ..aOM(16, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'inboundOptions', subBuilder: InboundOptions.create) ..aOM(17, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'urlTestOptions', subBuilder: URLTestOptions.create) ..aOM(18, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'routeOptions', subBuilder: RouteOptions.create) ..hasRequiredFields = false ; HiddifyOptions._() : super(); factory HiddifyOptions({ $core.bool? enableFullConfig, $core.String? logLevel, $core.String? logFile, $core.bool? enableClashApi, $core.int? clashApiPort, $core.String? webSecret, $core.String? region, $core.bool? blockAds, $core.bool? useXrayCoreWhenPossible, $core.Iterable? rules, WarpOptions? warp, WarpOptions? warp2, MuxOptions? mux, TLSTricks? tlsTricks, DNSOptions? dnsOptions, InboundOptions? inboundOptions, URLTestOptions? urlTestOptions, RouteOptions? routeOptions, }) { final _result = create(); if (enableFullConfig != null) { _result.enableFullConfig = enableFullConfig; } if (logLevel != null) { _result.logLevel = logLevel; } if (logFile != null) { _result.logFile = logFile; } if (enableClashApi != null) { _result.enableClashApi = enableClashApi; } if (clashApiPort != null) { _result.clashApiPort = clashApiPort; } if (webSecret != null) { _result.webSecret = webSecret; } if (region != null) { _result.region = region; } if (blockAds != null) { _result.blockAds = blockAds; } if (useXrayCoreWhenPossible != null) { _result.useXrayCoreWhenPossible = useXrayCoreWhenPossible; } if (rules != null) { _result.rules.addAll(rules); } if (warp != null) { _result.warp = warp; } if (warp2 != null) { _result.warp2 = warp2; } if (mux != null) { _result.mux = mux; } if (tlsTricks != null) { _result.tlsTricks = tlsTricks; } if (dnsOptions != null) { _result.dnsOptions = dnsOptions; } if (inboundOptions != null) { _result.inboundOptions = inboundOptions; } if (urlTestOptions != null) { _result.urlTestOptions = urlTestOptions; } if (routeOptions != null) { _result.routeOptions = routeOptions; } return _result; } factory HiddifyOptions.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory HiddifyOptions.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') HiddifyOptions clone() => HiddifyOptions()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') HiddifyOptions copyWith(void Function(HiddifyOptions) updates) => super.copyWith((message) => updates(message as HiddifyOptions)) as HiddifyOptions; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static HiddifyOptions create() => HiddifyOptions._(); HiddifyOptions createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static HiddifyOptions getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static HiddifyOptions? _defaultInstance; @$pb.TagNumber(1) $core.bool get enableFullConfig => $_getBF(0); @$pb.TagNumber(1) set enableFullConfig($core.bool v) { $_setBool(0, v); } @$pb.TagNumber(1) $core.bool hasEnableFullConfig() => $_has(0); @$pb.TagNumber(1) void clearEnableFullConfig() => clearField(1); @$pb.TagNumber(2) $core.String get logLevel => $_getSZ(1); @$pb.TagNumber(2) set logLevel($core.String v) { $_setString(1, v); } @$pb.TagNumber(2) $core.bool hasLogLevel() => $_has(1); @$pb.TagNumber(2) void clearLogLevel() => clearField(2); @$pb.TagNumber(3) $core.String get logFile => $_getSZ(2); @$pb.TagNumber(3) set logFile($core.String v) { $_setString(2, v); } @$pb.TagNumber(3) $core.bool hasLogFile() => $_has(2); @$pb.TagNumber(3) void clearLogFile() => clearField(3); @$pb.TagNumber(4) $core.bool get enableClashApi => $_getBF(3); @$pb.TagNumber(4) set enableClashApi($core.bool v) { $_setBool(3, v); } @$pb.TagNumber(4) $core.bool hasEnableClashApi() => $_has(3); @$pb.TagNumber(4) void clearEnableClashApi() => clearField(4); @$pb.TagNumber(5) $core.int get clashApiPort => $_getIZ(4); @$pb.TagNumber(5) set clashApiPort($core.int v) { $_setUnsignedInt32(4, v); } @$pb.TagNumber(5) $core.bool hasClashApiPort() => $_has(4); @$pb.TagNumber(5) void clearClashApiPort() => clearField(5); @$pb.TagNumber(6) $core.String get webSecret => $_getSZ(5); @$pb.TagNumber(6) set webSecret($core.String v) { $_setString(5, v); } @$pb.TagNumber(6) $core.bool hasWebSecret() => $_has(5); @$pb.TagNumber(6) void clearWebSecret() => clearField(6); @$pb.TagNumber(7) $core.String get region => $_getSZ(6); @$pb.TagNumber(7) set region($core.String v) { $_setString(6, v); } @$pb.TagNumber(7) $core.bool hasRegion() => $_has(6); @$pb.TagNumber(7) void clearRegion() => clearField(7); @$pb.TagNumber(8) $core.bool get blockAds => $_getBF(7); @$pb.TagNumber(8) set blockAds($core.bool v) { $_setBool(7, v); } @$pb.TagNumber(8) $core.bool hasBlockAds() => $_has(7); @$pb.TagNumber(8) void clearBlockAds() => clearField(8); @$pb.TagNumber(9) $core.bool get useXrayCoreWhenPossible => $_getBF(8); @$pb.TagNumber(9) set useXrayCoreWhenPossible($core.bool v) { $_setBool(8, v); } @$pb.TagNumber(9) $core.bool hasUseXrayCoreWhenPossible() => $_has(8); @$pb.TagNumber(9) void clearUseXrayCoreWhenPossible() => clearField(9); @$pb.TagNumber(10) $core.List get rules => $_getList(9); @$pb.TagNumber(11) WarpOptions get warp => $_getN(10); @$pb.TagNumber(11) set warp(WarpOptions v) { setField(11, v); } @$pb.TagNumber(11) $core.bool hasWarp() => $_has(10); @$pb.TagNumber(11) void clearWarp() => clearField(11); @$pb.TagNumber(11) WarpOptions ensureWarp() => $_ensure(10); @$pb.TagNumber(12) WarpOptions get warp2 => $_getN(11); @$pb.TagNumber(12) set warp2(WarpOptions v) { setField(12, v); } @$pb.TagNumber(12) $core.bool hasWarp2() => $_has(11); @$pb.TagNumber(12) void clearWarp2() => clearField(12); @$pb.TagNumber(12) WarpOptions ensureWarp2() => $_ensure(11); @$pb.TagNumber(13) MuxOptions get mux => $_getN(12); @$pb.TagNumber(13) set mux(MuxOptions v) { setField(13, v); } @$pb.TagNumber(13) $core.bool hasMux() => $_has(12); @$pb.TagNumber(13) void clearMux() => clearField(13); @$pb.TagNumber(13) MuxOptions ensureMux() => $_ensure(12); @$pb.TagNumber(14) TLSTricks get tlsTricks => $_getN(13); @$pb.TagNumber(14) set tlsTricks(TLSTricks v) { setField(14, v); } @$pb.TagNumber(14) $core.bool hasTlsTricks() => $_has(13); @$pb.TagNumber(14) void clearTlsTricks() => clearField(14); @$pb.TagNumber(14) TLSTricks ensureTlsTricks() => $_ensure(13); @$pb.TagNumber(15) DNSOptions get dnsOptions => $_getN(14); @$pb.TagNumber(15) set dnsOptions(DNSOptions v) { setField(15, v); } @$pb.TagNumber(15) $core.bool hasDnsOptions() => $_has(14); @$pb.TagNumber(15) void clearDnsOptions() => clearField(15); @$pb.TagNumber(15) DNSOptions ensureDnsOptions() => $_ensure(14); @$pb.TagNumber(16) InboundOptions get inboundOptions => $_getN(15); @$pb.TagNumber(16) set inboundOptions(InboundOptions v) { setField(16, v); } @$pb.TagNumber(16) $core.bool hasInboundOptions() => $_has(15); @$pb.TagNumber(16) void clearInboundOptions() => clearField(16); @$pb.TagNumber(16) InboundOptions ensureInboundOptions() => $_ensure(15); @$pb.TagNumber(17) URLTestOptions get urlTestOptions => $_getN(16); @$pb.TagNumber(17) set urlTestOptions(URLTestOptions v) { setField(17, v); } @$pb.TagNumber(17) $core.bool hasUrlTestOptions() => $_has(16); @$pb.TagNumber(17) void clearUrlTestOptions() => clearField(17); @$pb.TagNumber(17) URLTestOptions ensureUrlTestOptions() => $_ensure(16); @$pb.TagNumber(18) RouteOptions get routeOptions => $_getN(17); @$pb.TagNumber(18) set routeOptions(RouteOptions v) { setField(18, v); } @$pb.TagNumber(18) $core.bool hasRouteOptions() => $_has(17); @$pb.TagNumber(18) void clearRouteOptions() => clearField(18); @$pb.TagNumber(18) RouteOptions ensureRouteOptions() => $_ensure(17); } class IntRange extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'IntRange', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'hiddifyoptions'), createEmptyInstance: create) ..a<$core.int>(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'from', $pb.PbFieldType.O3) ..a<$core.int>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'to', $pb.PbFieldType.O3) ..hasRequiredFields = false ; IntRange._() : super(); factory IntRange({ $core.int? from, $core.int? to, }) { final _result = create(); if (from != null) { _result.from = from; } if (to != null) { _result.to = to; } return _result; } factory IntRange.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory IntRange.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') IntRange clone() => IntRange()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') IntRange copyWith(void Function(IntRange) updates) => super.copyWith((message) => updates(message as IntRange)) as IntRange; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static IntRange create() => IntRange._(); IntRange createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static IntRange getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static IntRange? _defaultInstance; @$pb.TagNumber(1) $core.int get from => $_getIZ(0); @$pb.TagNumber(1) set from($core.int v) { $_setSignedInt32(0, v); } @$pb.TagNumber(1) $core.bool hasFrom() => $_has(0); @$pb.TagNumber(1) void clearFrom() => clearField(1); @$pb.TagNumber(2) $core.int get to => $_getIZ(1); @$pb.TagNumber(2) set to($core.int v) { $_setSignedInt32(1, v); } @$pb.TagNumber(2) $core.bool hasTo() => $_has(1); @$pb.TagNumber(2) void clearTo() => clearField(2); } class DNSOptions extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'DNSOptions', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'hiddifyoptions'), createEmptyInstance: create) ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'remoteDnsAddress') ..e(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'remoteDnsDomainStrategy', $pb.PbFieldType.OE, defaultOrMaker: DomainStrategy.as_is, valueOf: DomainStrategy.valueOf, enumValues: DomainStrategy.values) ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'directDnsAddress') ..e(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'directDnsDomainStrategy', $pb.PbFieldType.OE, defaultOrMaker: DomainStrategy.as_is, valueOf: DomainStrategy.valueOf, enumValues: DomainStrategy.values) ..aOB(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'independentDnsCache') ..aOB(6, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'enableFakeDns') ..aOB(7, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'enableDnsRouting') ..hasRequiredFields = false ; DNSOptions._() : super(); factory DNSOptions({ $core.String? remoteDnsAddress, DomainStrategy? remoteDnsDomainStrategy, $core.String? directDnsAddress, DomainStrategy? directDnsDomainStrategy, $core.bool? independentDnsCache, $core.bool? enableFakeDns, $core.bool? enableDnsRouting, }) { final _result = create(); if (remoteDnsAddress != null) { _result.remoteDnsAddress = remoteDnsAddress; } if (remoteDnsDomainStrategy != null) { _result.remoteDnsDomainStrategy = remoteDnsDomainStrategy; } if (directDnsAddress != null) { _result.directDnsAddress = directDnsAddress; } if (directDnsDomainStrategy != null) { _result.directDnsDomainStrategy = directDnsDomainStrategy; } if (independentDnsCache != null) { _result.independentDnsCache = independentDnsCache; } if (enableFakeDns != null) { _result.enableFakeDns = enableFakeDns; } if (enableDnsRouting != null) { _result.enableDnsRouting = enableDnsRouting; } return _result; } factory DNSOptions.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory DNSOptions.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') DNSOptions clone() => DNSOptions()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') DNSOptions copyWith(void Function(DNSOptions) updates) => super.copyWith((message) => updates(message as DNSOptions)) as DNSOptions; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static DNSOptions create() => DNSOptions._(); DNSOptions createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static DNSOptions getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static DNSOptions? _defaultInstance; @$pb.TagNumber(1) $core.String get remoteDnsAddress => $_getSZ(0); @$pb.TagNumber(1) set remoteDnsAddress($core.String v) { $_setString(0, v); } @$pb.TagNumber(1) $core.bool hasRemoteDnsAddress() => $_has(0); @$pb.TagNumber(1) void clearRemoteDnsAddress() => clearField(1); @$pb.TagNumber(2) DomainStrategy get remoteDnsDomainStrategy => $_getN(1); @$pb.TagNumber(2) set remoteDnsDomainStrategy(DomainStrategy v) { setField(2, v); } @$pb.TagNumber(2) $core.bool hasRemoteDnsDomainStrategy() => $_has(1); @$pb.TagNumber(2) void clearRemoteDnsDomainStrategy() => clearField(2); @$pb.TagNumber(3) $core.String get directDnsAddress => $_getSZ(2); @$pb.TagNumber(3) set directDnsAddress($core.String v) { $_setString(2, v); } @$pb.TagNumber(3) $core.bool hasDirectDnsAddress() => $_has(2); @$pb.TagNumber(3) void clearDirectDnsAddress() => clearField(3); @$pb.TagNumber(4) DomainStrategy get directDnsDomainStrategy => $_getN(3); @$pb.TagNumber(4) set directDnsDomainStrategy(DomainStrategy v) { setField(4, v); } @$pb.TagNumber(4) $core.bool hasDirectDnsDomainStrategy() => $_has(3); @$pb.TagNumber(4) void clearDirectDnsDomainStrategy() => clearField(4); @$pb.TagNumber(5) $core.bool get independentDnsCache => $_getBF(4); @$pb.TagNumber(5) set independentDnsCache($core.bool v) { $_setBool(4, v); } @$pb.TagNumber(5) $core.bool hasIndependentDnsCache() => $_has(4); @$pb.TagNumber(5) void clearIndependentDnsCache() => clearField(5); @$pb.TagNumber(6) $core.bool get enableFakeDns => $_getBF(5); @$pb.TagNumber(6) set enableFakeDns($core.bool v) { $_setBool(5, v); } @$pb.TagNumber(6) $core.bool hasEnableFakeDns() => $_has(5); @$pb.TagNumber(6) void clearEnableFakeDns() => clearField(6); @$pb.TagNumber(7) $core.bool get enableDnsRouting => $_getBF(6); @$pb.TagNumber(7) set enableDnsRouting($core.bool v) { $_setBool(6, v); } @$pb.TagNumber(7) $core.bool hasEnableDnsRouting() => $_has(6); @$pb.TagNumber(7) void clearEnableDnsRouting() => clearField(7); } class InboundOptions extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'InboundOptions', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'hiddifyoptions'), createEmptyInstance: create) ..aOB(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'enableTun') ..aOB(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'enableTunService') ..aOB(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'setSystemProxy') ..a<$core.int>(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'mixedPort', $pb.PbFieldType.OU3) ..a<$core.int>(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'tproxyPort', $pb.PbFieldType.OU3) ..a<$core.int>(6, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'directPort', $pb.PbFieldType.OU3) ..a<$core.int>(7, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'mtu', $pb.PbFieldType.OU3) ..aOB(8, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'strictRoute') ..aOS(9, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'tunStack') ..a<$core.int>(10, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'redirectPort', $pb.PbFieldType.OU3) ..hasRequiredFields = false ; InboundOptions._() : super(); factory InboundOptions({ $core.bool? enableTun, $core.bool? enableTunService, $core.bool? setSystemProxy, $core.int? mixedPort, $core.int? tproxyPort, $core.int? directPort, $core.int? mtu, $core.bool? strictRoute, $core.String? tunStack, $core.int? redirectPort, }) { final _result = create(); if (enableTun != null) { _result.enableTun = enableTun; } if (enableTunService != null) { _result.enableTunService = enableTunService; } if (setSystemProxy != null) { _result.setSystemProxy = setSystemProxy; } if (mixedPort != null) { _result.mixedPort = mixedPort; } if (tproxyPort != null) { _result.tproxyPort = tproxyPort; } if (directPort != null) { _result.directPort = directPort; } if (mtu != null) { _result.mtu = mtu; } if (strictRoute != null) { _result.strictRoute = strictRoute; } if (tunStack != null) { _result.tunStack = tunStack; } if (redirectPort != null) { _result.redirectPort = redirectPort; } return _result; } factory InboundOptions.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory InboundOptions.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') InboundOptions clone() => InboundOptions()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') InboundOptions copyWith(void Function(InboundOptions) updates) => super.copyWith((message) => updates(message as InboundOptions)) as InboundOptions; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static InboundOptions create() => InboundOptions._(); InboundOptions createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static InboundOptions getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static InboundOptions? _defaultInstance; @$pb.TagNumber(1) $core.bool get enableTun => $_getBF(0); @$pb.TagNumber(1) set enableTun($core.bool v) { $_setBool(0, v); } @$pb.TagNumber(1) $core.bool hasEnableTun() => $_has(0); @$pb.TagNumber(1) void clearEnableTun() => clearField(1); @$pb.TagNumber(2) $core.bool get enableTunService => $_getBF(1); @$pb.TagNumber(2) set enableTunService($core.bool v) { $_setBool(1, v); } @$pb.TagNumber(2) $core.bool hasEnableTunService() => $_has(1); @$pb.TagNumber(2) void clearEnableTunService() => clearField(2); @$pb.TagNumber(3) $core.bool get setSystemProxy => $_getBF(2); @$pb.TagNumber(3) set setSystemProxy($core.bool v) { $_setBool(2, v); } @$pb.TagNumber(3) $core.bool hasSetSystemProxy() => $_has(2); @$pb.TagNumber(3) void clearSetSystemProxy() => clearField(3); @$pb.TagNumber(4) $core.int get mixedPort => $_getIZ(3); @$pb.TagNumber(4) set mixedPort($core.int v) { $_setUnsignedInt32(3, v); } @$pb.TagNumber(4) $core.bool hasMixedPort() => $_has(3); @$pb.TagNumber(4) void clearMixedPort() => clearField(4); @$pb.TagNumber(5) $core.int get tproxyPort => $_getIZ(4); @$pb.TagNumber(5) set tproxyPort($core.int v) { $_setUnsignedInt32(4, v); } @$pb.TagNumber(5) $core.bool hasTproxyPort() => $_has(4); @$pb.TagNumber(5) void clearTproxyPort() => clearField(5); @$pb.TagNumber(6) $core.int get directPort => $_getIZ(5); @$pb.TagNumber(6) set directPort($core.int v) { $_setUnsignedInt32(5, v); } @$pb.TagNumber(6) $core.bool hasDirectPort() => $_has(5); @$pb.TagNumber(6) void clearDirectPort() => clearField(6); @$pb.TagNumber(7) $core.int get mtu => $_getIZ(6); @$pb.TagNumber(7) set mtu($core.int v) { $_setUnsignedInt32(6, v); } @$pb.TagNumber(7) $core.bool hasMtu() => $_has(6); @$pb.TagNumber(7) void clearMtu() => clearField(7); @$pb.TagNumber(8) $core.bool get strictRoute => $_getBF(7); @$pb.TagNumber(8) set strictRoute($core.bool v) { $_setBool(7, v); } @$pb.TagNumber(8) $core.bool hasStrictRoute() => $_has(7); @$pb.TagNumber(8) void clearStrictRoute() => clearField(8); @$pb.TagNumber(9) $core.String get tunStack => $_getSZ(8); @$pb.TagNumber(9) set tunStack($core.String v) { $_setString(8, v); } @$pb.TagNumber(9) $core.bool hasTunStack() => $_has(8); @$pb.TagNumber(9) void clearTunStack() => clearField(9); @$pb.TagNumber(10) $core.int get redirectPort => $_getIZ(9); @$pb.TagNumber(10) set redirectPort($core.int v) { $_setUnsignedInt32(9, v); } @$pb.TagNumber(10) $core.bool hasRedirectPort() => $_has(9); @$pb.TagNumber(10) void clearRedirectPort() => clearField(10); } class URLTestOptions extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'URLTestOptions', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'hiddifyoptions'), createEmptyInstance: create) ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'connectionTestUrl') ..aInt64(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'urlTestInterval') ..hasRequiredFields = false ; URLTestOptions._() : super(); factory URLTestOptions({ $core.String? connectionTestUrl, $fixnum.Int64? urlTestInterval, }) { final _result = create(); if (connectionTestUrl != null) { _result.connectionTestUrl = connectionTestUrl; } if (urlTestInterval != null) { _result.urlTestInterval = urlTestInterval; } return _result; } factory URLTestOptions.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory URLTestOptions.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') URLTestOptions clone() => URLTestOptions()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') URLTestOptions copyWith(void Function(URLTestOptions) updates) => super.copyWith((message) => updates(message as URLTestOptions)) as URLTestOptions; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static URLTestOptions create() => URLTestOptions._(); URLTestOptions createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static URLTestOptions getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static URLTestOptions? _defaultInstance; @$pb.TagNumber(1) $core.String get connectionTestUrl => $_getSZ(0); @$pb.TagNumber(1) set connectionTestUrl($core.String v) { $_setString(0, v); } @$pb.TagNumber(1) $core.bool hasConnectionTestUrl() => $_has(0); @$pb.TagNumber(1) void clearConnectionTestUrl() => clearField(1); @$pb.TagNumber(2) $fixnum.Int64 get urlTestInterval => $_getI64(1); @$pb.TagNumber(2) set urlTestInterval($fixnum.Int64 v) { $_setInt64(1, v); } @$pb.TagNumber(2) $core.bool hasUrlTestInterval() => $_has(1); @$pb.TagNumber(2) void clearUrlTestInterval() => clearField(2); } class RouteOptions extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'RouteOptions', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'hiddifyoptions'), createEmptyInstance: create) ..aOB(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'resolveDestination') ..e(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'ipv6Mode', $pb.PbFieldType.OE, defaultOrMaker: DomainStrategy.as_is, valueOf: DomainStrategy.valueOf, enumValues: DomainStrategy.values) ..aOB(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'bypassLan') ..aOB(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'allowConnectionFromLan') ..hasRequiredFields = false ; RouteOptions._() : super(); factory RouteOptions({ $core.bool? resolveDestination, DomainStrategy? ipv6Mode, $core.bool? bypassLan, $core.bool? allowConnectionFromLan, }) { final _result = create(); if (resolveDestination != null) { _result.resolveDestination = resolveDestination; } if (ipv6Mode != null) { _result.ipv6Mode = ipv6Mode; } if (bypassLan != null) { _result.bypassLan = bypassLan; } if (allowConnectionFromLan != null) { _result.allowConnectionFromLan = allowConnectionFromLan; } return _result; } factory RouteOptions.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory RouteOptions.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') RouteOptions clone() => RouteOptions()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') RouteOptions copyWith(void Function(RouteOptions) updates) => super.copyWith((message) => updates(message as RouteOptions)) as RouteOptions; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static RouteOptions create() => RouteOptions._(); RouteOptions createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static RouteOptions getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static RouteOptions? _defaultInstance; @$pb.TagNumber(1) $core.bool get resolveDestination => $_getBF(0); @$pb.TagNumber(1) set resolveDestination($core.bool v) { $_setBool(0, v); } @$pb.TagNumber(1) $core.bool hasResolveDestination() => $_has(0); @$pb.TagNumber(1) void clearResolveDestination() => clearField(1); @$pb.TagNumber(2) DomainStrategy get ipv6Mode => $_getN(1); @$pb.TagNumber(2) set ipv6Mode(DomainStrategy v) { setField(2, v); } @$pb.TagNumber(2) $core.bool hasIpv6Mode() => $_has(1); @$pb.TagNumber(2) void clearIpv6Mode() => clearField(2); @$pb.TagNumber(3) $core.bool get bypassLan => $_getBF(2); @$pb.TagNumber(3) set bypassLan($core.bool v) { $_setBool(2, v); } @$pb.TagNumber(3) $core.bool hasBypassLan() => $_has(2); @$pb.TagNumber(3) void clearBypassLan() => clearField(3); @$pb.TagNumber(4) $core.bool get allowConnectionFromLan => $_getBF(3); @$pb.TagNumber(4) set allowConnectionFromLan($core.bool v) { $_setBool(3, v); } @$pb.TagNumber(4) $core.bool hasAllowConnectionFromLan() => $_has(3); @$pb.TagNumber(4) void clearAllowConnectionFromLan() => clearField(4); } class TLSTricks extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'TLSTricks', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'hiddifyoptions'), createEmptyInstance: create) ..aOB(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'enableFragment') ..aOM(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'fragmentSize', subBuilder: IntRange.create) ..aOM(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'fragmentSleep', subBuilder: IntRange.create) ..aOB(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'mixedSniCase') ..aOB(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'enablePadding') ..aOM(6, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'paddingSize', subBuilder: IntRange.create) ..hasRequiredFields = false ; TLSTricks._() : super(); factory TLSTricks({ $core.bool? enableFragment, IntRange? fragmentSize, IntRange? fragmentSleep, $core.bool? mixedSniCase, $core.bool? enablePadding, IntRange? paddingSize, }) { final _result = create(); if (enableFragment != null) { _result.enableFragment = enableFragment; } if (fragmentSize != null) { _result.fragmentSize = fragmentSize; } if (fragmentSleep != null) { _result.fragmentSleep = fragmentSleep; } if (mixedSniCase != null) { _result.mixedSniCase = mixedSniCase; } if (enablePadding != null) { _result.enablePadding = enablePadding; } if (paddingSize != null) { _result.paddingSize = paddingSize; } return _result; } factory TLSTricks.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory TLSTricks.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') TLSTricks clone() => TLSTricks()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') TLSTricks copyWith(void Function(TLSTricks) updates) => super.copyWith((message) => updates(message as TLSTricks)) as TLSTricks; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static TLSTricks create() => TLSTricks._(); TLSTricks createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static TLSTricks getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static TLSTricks? _defaultInstance; @$pb.TagNumber(1) $core.bool get enableFragment => $_getBF(0); @$pb.TagNumber(1) set enableFragment($core.bool v) { $_setBool(0, v); } @$pb.TagNumber(1) $core.bool hasEnableFragment() => $_has(0); @$pb.TagNumber(1) void clearEnableFragment() => clearField(1); @$pb.TagNumber(2) IntRange get fragmentSize => $_getN(1); @$pb.TagNumber(2) set fragmentSize(IntRange v) { setField(2, v); } @$pb.TagNumber(2) $core.bool hasFragmentSize() => $_has(1); @$pb.TagNumber(2) void clearFragmentSize() => clearField(2); @$pb.TagNumber(2) IntRange ensureFragmentSize() => $_ensure(1); @$pb.TagNumber(3) IntRange get fragmentSleep => $_getN(2); @$pb.TagNumber(3) set fragmentSleep(IntRange v) { setField(3, v); } @$pb.TagNumber(3) $core.bool hasFragmentSleep() => $_has(2); @$pb.TagNumber(3) void clearFragmentSleep() => clearField(3); @$pb.TagNumber(3) IntRange ensureFragmentSleep() => $_ensure(2); @$pb.TagNumber(4) $core.bool get mixedSniCase => $_getBF(3); @$pb.TagNumber(4) set mixedSniCase($core.bool v) { $_setBool(3, v); } @$pb.TagNumber(4) $core.bool hasMixedSniCase() => $_has(3); @$pb.TagNumber(4) void clearMixedSniCase() => clearField(4); @$pb.TagNumber(5) $core.bool get enablePadding => $_getBF(4); @$pb.TagNumber(5) set enablePadding($core.bool v) { $_setBool(4, v); } @$pb.TagNumber(5) $core.bool hasEnablePadding() => $_has(4); @$pb.TagNumber(5) void clearEnablePadding() => clearField(5); @$pb.TagNumber(6) IntRange get paddingSize => $_getN(5); @$pb.TagNumber(6) set paddingSize(IntRange v) { setField(6, v); } @$pb.TagNumber(6) $core.bool hasPaddingSize() => $_has(5); @$pb.TagNumber(6) void clearPaddingSize() => clearField(6); @$pb.TagNumber(6) IntRange ensurePaddingSize() => $_ensure(5); } class MuxOptions extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'MuxOptions', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'hiddifyoptions'), createEmptyInstance: create) ..aOB(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'enable') ..aOB(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'padding') ..a<$core.int>(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'maxStreams', $pb.PbFieldType.O3) ..aOS(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'protocol') ..hasRequiredFields = false ; MuxOptions._() : super(); factory MuxOptions({ $core.bool? enable, $core.bool? padding, $core.int? maxStreams, $core.String? protocol, }) { final _result = create(); if (enable != null) { _result.enable = enable; } if (padding != null) { _result.padding = padding; } if (maxStreams != null) { _result.maxStreams = maxStreams; } if (protocol != null) { _result.protocol = protocol; } return _result; } factory MuxOptions.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory MuxOptions.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') MuxOptions clone() => MuxOptions()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') MuxOptions copyWith(void Function(MuxOptions) updates) => super.copyWith((message) => updates(message as MuxOptions)) as MuxOptions; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static MuxOptions create() => MuxOptions._(); MuxOptions createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static MuxOptions getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static MuxOptions? _defaultInstance; @$pb.TagNumber(1) $core.bool get enable => $_getBF(0); @$pb.TagNumber(1) set enable($core.bool v) { $_setBool(0, v); } @$pb.TagNumber(1) $core.bool hasEnable() => $_has(0); @$pb.TagNumber(1) void clearEnable() => clearField(1); @$pb.TagNumber(2) $core.bool get padding => $_getBF(1); @$pb.TagNumber(2) set padding($core.bool v) { $_setBool(1, v); } @$pb.TagNumber(2) $core.bool hasPadding() => $_has(1); @$pb.TagNumber(2) void clearPadding() => clearField(2); @$pb.TagNumber(3) $core.int get maxStreams => $_getIZ(2); @$pb.TagNumber(3) set maxStreams($core.int v) { $_setSignedInt32(2, v); } @$pb.TagNumber(3) $core.bool hasMaxStreams() => $_has(2); @$pb.TagNumber(3) void clearMaxStreams() => clearField(3); @$pb.TagNumber(4) $core.String get protocol => $_getSZ(3); @$pb.TagNumber(4) set protocol($core.String v) { $_setString(3, v); } @$pb.TagNumber(4) $core.bool hasProtocol() => $_has(3); @$pb.TagNumber(4) void clearProtocol() => clearField(4); } class WarpOptions extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'WarpOptions', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'hiddifyoptions'), createEmptyInstance: create) ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'id') ..aOB(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'enableWarp') ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'mode') ..aOM(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'wireguardConfig', subBuilder: WarpWireguardConfig.create) ..aOS(6, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'fakePackets') ..aOM(7, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'fakePacketSize', subBuilder: IntRange.create) ..aOM(8, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'fakePacketDelay', subBuilder: IntRange.create) ..aOS(9, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'fakePacketMode') ..aOS(10, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'cleanIp') ..a<$core.int>(11, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'cleanPort', $pb.PbFieldType.OU3) ..aOM(12, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'account', subBuilder: WarpAccount.create) ..hasRequiredFields = false ; WarpOptions._() : super(); factory WarpOptions({ $core.String? id, $core.bool? enableWarp, $core.String? mode, WarpWireguardConfig? wireguardConfig, $core.String? fakePackets, IntRange? fakePacketSize, IntRange? fakePacketDelay, $core.String? fakePacketMode, $core.String? cleanIp, $core.int? cleanPort, WarpAccount? account, }) { final _result = create(); if (id != null) { _result.id = id; } if (enableWarp != null) { _result.enableWarp = enableWarp; } if (mode != null) { _result.mode = mode; } if (wireguardConfig != null) { _result.wireguardConfig = wireguardConfig; } if (fakePackets != null) { _result.fakePackets = fakePackets; } if (fakePacketSize != null) { _result.fakePacketSize = fakePacketSize; } if (fakePacketDelay != null) { _result.fakePacketDelay = fakePacketDelay; } if (fakePacketMode != null) { _result.fakePacketMode = fakePacketMode; } if (cleanIp != null) { _result.cleanIp = cleanIp; } if (cleanPort != null) { _result.cleanPort = cleanPort; } if (account != null) { _result.account = account; } return _result; } factory WarpOptions.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory WarpOptions.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') WarpOptions clone() => WarpOptions()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') WarpOptions copyWith(void Function(WarpOptions) updates) => super.copyWith((message) => updates(message as WarpOptions)) as WarpOptions; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static WarpOptions create() => WarpOptions._(); WarpOptions createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static WarpOptions getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static WarpOptions? _defaultInstance; @$pb.TagNumber(1) $core.String get id => $_getSZ(0); @$pb.TagNumber(1) set id($core.String v) { $_setString(0, v); } @$pb.TagNumber(1) $core.bool hasId() => $_has(0); @$pb.TagNumber(1) void clearId() => clearField(1); @$pb.TagNumber(2) $core.bool get enableWarp => $_getBF(1); @$pb.TagNumber(2) set enableWarp($core.bool v) { $_setBool(1, v); } @$pb.TagNumber(2) $core.bool hasEnableWarp() => $_has(1); @$pb.TagNumber(2) void clearEnableWarp() => clearField(2); @$pb.TagNumber(3) $core.String get mode => $_getSZ(2); @$pb.TagNumber(3) set mode($core.String v) { $_setString(2, v); } @$pb.TagNumber(3) $core.bool hasMode() => $_has(2); @$pb.TagNumber(3) void clearMode() => clearField(3); @$pb.TagNumber(5) WarpWireguardConfig get wireguardConfig => $_getN(3); @$pb.TagNumber(5) set wireguardConfig(WarpWireguardConfig v) { setField(5, v); } @$pb.TagNumber(5) $core.bool hasWireguardConfig() => $_has(3); @$pb.TagNumber(5) void clearWireguardConfig() => clearField(5); @$pb.TagNumber(5) WarpWireguardConfig ensureWireguardConfig() => $_ensure(3); @$pb.TagNumber(6) $core.String get fakePackets => $_getSZ(4); @$pb.TagNumber(6) set fakePackets($core.String v) { $_setString(4, v); } @$pb.TagNumber(6) $core.bool hasFakePackets() => $_has(4); @$pb.TagNumber(6) void clearFakePackets() => clearField(6); @$pb.TagNumber(7) IntRange get fakePacketSize => $_getN(5); @$pb.TagNumber(7) set fakePacketSize(IntRange v) { setField(7, v); } @$pb.TagNumber(7) $core.bool hasFakePacketSize() => $_has(5); @$pb.TagNumber(7) void clearFakePacketSize() => clearField(7); @$pb.TagNumber(7) IntRange ensureFakePacketSize() => $_ensure(5); @$pb.TagNumber(8) IntRange get fakePacketDelay => $_getN(6); @$pb.TagNumber(8) set fakePacketDelay(IntRange v) { setField(8, v); } @$pb.TagNumber(8) $core.bool hasFakePacketDelay() => $_has(6); @$pb.TagNumber(8) void clearFakePacketDelay() => clearField(8); @$pb.TagNumber(8) IntRange ensureFakePacketDelay() => $_ensure(6); @$pb.TagNumber(9) $core.String get fakePacketMode => $_getSZ(7); @$pb.TagNumber(9) set fakePacketMode($core.String v) { $_setString(7, v); } @$pb.TagNumber(9) $core.bool hasFakePacketMode() => $_has(7); @$pb.TagNumber(9) void clearFakePacketMode() => clearField(9); @$pb.TagNumber(10) $core.String get cleanIp => $_getSZ(8); @$pb.TagNumber(10) set cleanIp($core.String v) { $_setString(8, v); } @$pb.TagNumber(10) $core.bool hasCleanIp() => $_has(8); @$pb.TagNumber(10) void clearCleanIp() => clearField(10); @$pb.TagNumber(11) $core.int get cleanPort => $_getIZ(9); @$pb.TagNumber(11) set cleanPort($core.int v) { $_setUnsignedInt32(9, v); } @$pb.TagNumber(11) $core.bool hasCleanPort() => $_has(9); @$pb.TagNumber(11) void clearCleanPort() => clearField(11); @$pb.TagNumber(12) WarpAccount get account => $_getN(10); @$pb.TagNumber(12) set account(WarpAccount v) { setField(12, v); } @$pb.TagNumber(12) $core.bool hasAccount() => $_has(10); @$pb.TagNumber(12) void clearAccount() => clearField(12); @$pb.TagNumber(12) WarpAccount ensureAccount() => $_ensure(10); } class WarpAccount extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'WarpAccount', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'hiddifyoptions'), createEmptyInstance: create) ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'accountId') ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'accessToken') ..hasRequiredFields = false ; WarpAccount._() : super(); factory WarpAccount({ $core.String? accountId, $core.String? accessToken, }) { final _result = create(); if (accountId != null) { _result.accountId = accountId; } if (accessToken != null) { _result.accessToken = accessToken; } return _result; } factory WarpAccount.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory WarpAccount.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') WarpAccount clone() => WarpAccount()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') WarpAccount copyWith(void Function(WarpAccount) updates) => super.copyWith((message) => updates(message as WarpAccount)) as WarpAccount; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static WarpAccount create() => WarpAccount._(); WarpAccount createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static WarpAccount getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static WarpAccount? _defaultInstance; @$pb.TagNumber(1) $core.String get accountId => $_getSZ(0); @$pb.TagNumber(1) set accountId($core.String v) { $_setString(0, v); } @$pb.TagNumber(1) $core.bool hasAccountId() => $_has(0); @$pb.TagNumber(1) void clearAccountId() => clearField(1); @$pb.TagNumber(2) $core.String get accessToken => $_getSZ(1); @$pb.TagNumber(2) set accessToken($core.String v) { $_setString(1, v); } @$pb.TagNumber(2) $core.bool hasAccessToken() => $_has(1); @$pb.TagNumber(2) void clearAccessToken() => clearField(2); } class WarpWireguardConfig extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'WarpWireguardConfig', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'hiddifyoptions'), createEmptyInstance: create) ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'privateKey') ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'localAddressIpv4') ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'localAddressIpv6') ..aOS(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'peerPublicKey') ..aOS(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'clientId') ..hasRequiredFields = false ; WarpWireguardConfig._() : super(); factory WarpWireguardConfig({ $core.String? privateKey, $core.String? localAddressIpv4, $core.String? localAddressIpv6, $core.String? peerPublicKey, $core.String? clientId, }) { final _result = create(); if (privateKey != null) { _result.privateKey = privateKey; } if (localAddressIpv4 != null) { _result.localAddressIpv4 = localAddressIpv4; } if (localAddressIpv6 != null) { _result.localAddressIpv6 = localAddressIpv6; } if (peerPublicKey != null) { _result.peerPublicKey = peerPublicKey; } if (clientId != null) { _result.clientId = clientId; } return _result; } factory WarpWireguardConfig.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory WarpWireguardConfig.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') WarpWireguardConfig clone() => WarpWireguardConfig()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') WarpWireguardConfig copyWith(void Function(WarpWireguardConfig) updates) => super.copyWith((message) => updates(message as WarpWireguardConfig)) as WarpWireguardConfig; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static WarpWireguardConfig create() => WarpWireguardConfig._(); WarpWireguardConfig createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static WarpWireguardConfig getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static WarpWireguardConfig? _defaultInstance; @$pb.TagNumber(1) $core.String get privateKey => $_getSZ(0); @$pb.TagNumber(1) set privateKey($core.String v) { $_setString(0, v); } @$pb.TagNumber(1) $core.bool hasPrivateKey() => $_has(0); @$pb.TagNumber(1) void clearPrivateKey() => clearField(1); @$pb.TagNumber(2) $core.String get localAddressIpv4 => $_getSZ(1); @$pb.TagNumber(2) set localAddressIpv4($core.String v) { $_setString(1, v); } @$pb.TagNumber(2) $core.bool hasLocalAddressIpv4() => $_has(1); @$pb.TagNumber(2) void clearLocalAddressIpv4() => clearField(2); @$pb.TagNumber(3) $core.String get localAddressIpv6 => $_getSZ(2); @$pb.TagNumber(3) set localAddressIpv6($core.String v) { $_setString(2, v); } @$pb.TagNumber(3) $core.bool hasLocalAddressIpv6() => $_has(2); @$pb.TagNumber(3) void clearLocalAddressIpv6() => clearField(3); @$pb.TagNumber(4) $core.String get peerPublicKey => $_getSZ(3); @$pb.TagNumber(4) set peerPublicKey($core.String v) { $_setString(3, v); } @$pb.TagNumber(4) $core.bool hasPeerPublicKey() => $_has(3); @$pb.TagNumber(4) void clearPeerPublicKey() => clearField(4); @$pb.TagNumber(5) $core.String get clientId => $_getSZ(4); @$pb.TagNumber(5) set clientId($core.String v) { $_setString(4, v); } @$pb.TagNumber(5) $core.bool hasClientId() => $_has(4); @$pb.TagNumber(5) void clearClientId() => clearField(5); } class Rule extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'Rule', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'hiddifyoptions'), createEmptyInstance: create) ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'ruleSetUrl') ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'domains') ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'ip') ..aOS(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'port') ..aOS(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'network') ..aOS(6, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'protocol') ..aOS(7, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'outbound') ..hasRequiredFields = false ; Rule._() : super(); factory Rule({ $core.String? ruleSetUrl, $core.String? domains, $core.String? ip, $core.String? port, $core.String? network, $core.String? protocol, $core.String? outbound, }) { final _result = create(); if (ruleSetUrl != null) { _result.ruleSetUrl = ruleSetUrl; } if (domains != null) { _result.domains = domains; } if (ip != null) { _result.ip = ip; } if (port != null) { _result.port = port; } if (network != null) { _result.network = network; } if (protocol != null) { _result.protocol = protocol; } if (outbound != null) { _result.outbound = outbound; } return _result; } factory Rule.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory Rule.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') Rule clone() => Rule()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') Rule copyWith(void Function(Rule) updates) => super.copyWith((message) => updates(message as Rule)) as Rule; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static Rule create() => Rule._(); Rule createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static Rule getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static Rule? _defaultInstance; @$pb.TagNumber(1) $core.String get ruleSetUrl => $_getSZ(0); @$pb.TagNumber(1) set ruleSetUrl($core.String v) { $_setString(0, v); } @$pb.TagNumber(1) $core.bool hasRuleSetUrl() => $_has(0); @$pb.TagNumber(1) void clearRuleSetUrl() => clearField(1); @$pb.TagNumber(2) $core.String get domains => $_getSZ(1); @$pb.TagNumber(2) set domains($core.String v) { $_setString(1, v); } @$pb.TagNumber(2) $core.bool hasDomains() => $_has(1); @$pb.TagNumber(2) void clearDomains() => clearField(2); @$pb.TagNumber(3) $core.String get ip => $_getSZ(2); @$pb.TagNumber(3) set ip($core.String v) { $_setString(2, v); } @$pb.TagNumber(3) $core.bool hasIp() => $_has(2); @$pb.TagNumber(3) void clearIp() => clearField(3); @$pb.TagNumber(4) $core.String get port => $_getSZ(3); @$pb.TagNumber(4) set port($core.String v) { $_setString(3, v); } @$pb.TagNumber(4) $core.bool hasPort() => $_has(3); @$pb.TagNumber(4) void clearPort() => clearField(4); @$pb.TagNumber(5) $core.String get network => $_getSZ(4); @$pb.TagNumber(5) set network($core.String v) { $_setString(4, v); } @$pb.TagNumber(5) $core.bool hasNetwork() => $_has(4); @$pb.TagNumber(5) void clearNetwork() => clearField(5); @$pb.TagNumber(6) $core.String get protocol => $_getSZ(5); @$pb.TagNumber(6) set protocol($core.String v) { $_setString(5, v); } @$pb.TagNumber(6) $core.bool hasProtocol() => $_has(5); @$pb.TagNumber(6) void clearProtocol() => clearField(6); @$pb.TagNumber(7) $core.String get outbound => $_getSZ(6); @$pb.TagNumber(7) set outbound($core.String v) { $_setString(6, v); } @$pb.TagNumber(7) $core.bool hasOutbound() => $_has(6); @$pb.TagNumber(7) void clearOutbound() => clearField(7); } ================================================ FILE: lib/hiddifycore/generated/v2/hiddifyoptions/hiddify_options.pbenum.dart ================================================ /// // Generated code. Do not modify. // source: v2/hiddifyoptions/hiddify_options.proto // // @dart = 2.12 // ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name // ignore_for_file: UNDEFINED_SHOWN_NAME import 'dart:core' as $core; import 'package:protobuf/protobuf.dart' as $pb; class DomainStrategy extends $pb.ProtobufEnum { static const DomainStrategy as_is = DomainStrategy._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'as_is'); static const DomainStrategy prefer_ipv4 = DomainStrategy._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'prefer_ipv4'); static const DomainStrategy prefer_ipv6 = DomainStrategy._(2, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'prefer_ipv6'); static const DomainStrategy ipv4_only = DomainStrategy._(3, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ipv4_only'); static const DomainStrategy ipv6_only = DomainStrategy._(4, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ipv6_only'); static const $core.List values = [ as_is, prefer_ipv4, prefer_ipv6, ipv4_only, ipv6_only, ]; static final $core.Map<$core.int, DomainStrategy> _byValue = $pb.ProtobufEnum.initByValue(values); static DomainStrategy? valueOf($core.int value) => _byValue[value]; const DomainStrategy._($core.int v, $core.String n) : super(v, n); } ================================================ FILE: lib/hiddifycore/generated/v2/hiddifyoptions/hiddify_options.pbjson.dart ================================================ /// // Generated code. Do not modify. // source: v2/hiddifyoptions/hiddify_options.proto // // @dart = 2.12 // ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,deprecated_member_use_from_same_package,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name import 'dart:core' as $core; import 'dart:convert' as $convert; import 'dart:typed_data' as $typed_data; @$core.Deprecated('Use domainStrategyDescriptor instead') const DomainStrategy$json = const { '1': 'DomainStrategy', '2': const [ const {'1': 'as_is', '2': 0}, const {'1': 'prefer_ipv4', '2': 1}, const {'1': 'prefer_ipv6', '2': 2}, const {'1': 'ipv4_only', '2': 3}, const {'1': 'ipv6_only', '2': 4}, ], }; /// Descriptor for `DomainStrategy`. Decode as a `google.protobuf.EnumDescriptorProto`. final $typed_data.Uint8List domainStrategyDescriptor = $convert.base64Decode('Cg5Eb21haW5TdHJhdGVneRIJCgVhc19pcxAAEg8KC3ByZWZlcl9pcHY0EAESDwoLcHJlZmVyX2lwdjYQAhINCglpcHY0X29ubHkQAxINCglpcHY2X29ubHkQBA=='); @$core.Deprecated('Use hiddifyOptionsDescriptor instead') const HiddifyOptions$json = const { '1': 'HiddifyOptions', '2': const [ const {'1': 'enable_full_config', '3': 1, '4': 1, '5': 8, '10': 'enableFullConfig'}, const {'1': 'log_level', '3': 2, '4': 1, '5': 9, '10': 'logLevel'}, const {'1': 'log_file', '3': 3, '4': 1, '5': 9, '10': 'logFile'}, const {'1': 'enable_clash_api', '3': 4, '4': 1, '5': 8, '10': 'enableClashApi'}, const {'1': 'clash_api_port', '3': 5, '4': 1, '5': 13, '10': 'clashApiPort'}, const {'1': 'web_secret', '3': 6, '4': 1, '5': 9, '10': 'webSecret'}, const {'1': 'region', '3': 7, '4': 1, '5': 9, '10': 'region'}, const {'1': 'block_ads', '3': 8, '4': 1, '5': 8, '10': 'blockAds'}, const {'1': 'use_xray_core_when_possible', '3': 9, '4': 1, '5': 8, '10': 'useXrayCoreWhenPossible'}, const {'1': 'rules', '3': 10, '4': 3, '5': 11, '6': '.hiddifyoptions.Rule', '10': 'rules'}, const {'1': 'warp', '3': 11, '4': 1, '5': 11, '6': '.hiddifyoptions.WarpOptions', '10': 'warp'}, const {'1': 'warp2', '3': 12, '4': 1, '5': 11, '6': '.hiddifyoptions.WarpOptions', '10': 'warp2'}, const {'1': 'mux', '3': 13, '4': 1, '5': 11, '6': '.hiddifyoptions.MuxOptions', '10': 'mux'}, const {'1': 'tls_tricks', '3': 14, '4': 1, '5': 11, '6': '.hiddifyoptions.TLSTricks', '10': 'tlsTricks'}, const {'1': 'dns_options', '3': 15, '4': 1, '5': 11, '6': '.hiddifyoptions.DNSOptions', '10': 'dnsOptions'}, const {'1': 'inbound_options', '3': 16, '4': 1, '5': 11, '6': '.hiddifyoptions.InboundOptions', '10': 'inboundOptions'}, const {'1': 'url_test_options', '3': 17, '4': 1, '5': 11, '6': '.hiddifyoptions.URLTestOptions', '10': 'urlTestOptions'}, const {'1': 'route_options', '3': 18, '4': 1, '5': 11, '6': '.hiddifyoptions.RouteOptions', '10': 'routeOptions'}, ], }; /// Descriptor for `HiddifyOptions`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List hiddifyOptionsDescriptor = $convert.base64Decode('Cg5IaWRkaWZ5T3B0aW9ucxIsChJlbmFibGVfZnVsbF9jb25maWcYASABKAhSEGVuYWJsZUZ1bGxDb25maWcSGwoJbG9nX2xldmVsGAIgASgJUghsb2dMZXZlbBIZCghsb2dfZmlsZRgDIAEoCVIHbG9nRmlsZRIoChBlbmFibGVfY2xhc2hfYXBpGAQgASgIUg5lbmFibGVDbGFzaEFwaRIkCg5jbGFzaF9hcGlfcG9ydBgFIAEoDVIMY2xhc2hBcGlQb3J0Eh0KCndlYl9zZWNyZXQYBiABKAlSCXdlYlNlY3JldBIWCgZyZWdpb24YByABKAlSBnJlZ2lvbhIbCglibG9ja19hZHMYCCABKAhSCGJsb2NrQWRzEjwKG3VzZV94cmF5X2NvcmVfd2hlbl9wb3NzaWJsZRgJIAEoCFIXdXNlWHJheUNvcmVXaGVuUG9zc2libGUSKgoFcnVsZXMYCiADKAsyFC5oaWRkaWZ5b3B0aW9ucy5SdWxlUgVydWxlcxIvCgR3YXJwGAsgASgLMhsuaGlkZGlmeW9wdGlvbnMuV2FycE9wdGlvbnNSBHdhcnASMQoFd2FycDIYDCABKAsyGy5oaWRkaWZ5b3B0aW9ucy5XYXJwT3B0aW9uc1IFd2FycDISLAoDbXV4GA0gASgLMhouaGlkZGlmeW9wdGlvbnMuTXV4T3B0aW9uc1IDbXV4EjgKCnRsc190cmlja3MYDiABKAsyGS5oaWRkaWZ5b3B0aW9ucy5UTFNUcmlja3NSCXRsc1RyaWNrcxI7CgtkbnNfb3B0aW9ucxgPIAEoCzIaLmhpZGRpZnlvcHRpb25zLkROU09wdGlvbnNSCmRuc09wdGlvbnMSRwoPaW5ib3VuZF9vcHRpb25zGBAgASgLMh4uaGlkZGlmeW9wdGlvbnMuSW5ib3VuZE9wdGlvbnNSDmluYm91bmRPcHRpb25zEkgKEHVybF90ZXN0X29wdGlvbnMYESABKAsyHi5oaWRkaWZ5b3B0aW9ucy5VUkxUZXN0T3B0aW9uc1IOdXJsVGVzdE9wdGlvbnMSQQoNcm91dGVfb3B0aW9ucxgSIAEoCzIcLmhpZGRpZnlvcHRpb25zLlJvdXRlT3B0aW9uc1IMcm91dGVPcHRpb25z'); @$core.Deprecated('Use intRangeDescriptor instead') const IntRange$json = const { '1': 'IntRange', '2': const [ const {'1': 'from', '3': 1, '4': 1, '5': 5, '10': 'from'}, const {'1': 'to', '3': 2, '4': 1, '5': 5, '10': 'to'}, ], }; /// Descriptor for `IntRange`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List intRangeDescriptor = $convert.base64Decode('CghJbnRSYW5nZRISCgRmcm9tGAEgASgFUgRmcm9tEg4KAnRvGAIgASgFUgJ0bw=='); @$core.Deprecated('Use dNSOptionsDescriptor instead') const DNSOptions$json = const { '1': 'DNSOptions', '2': const [ const {'1': 'remote_dns_address', '3': 1, '4': 1, '5': 9, '10': 'remoteDnsAddress'}, const {'1': 'remote_dns_domain_strategy', '3': 2, '4': 1, '5': 14, '6': '.hiddifyoptions.DomainStrategy', '10': 'remoteDnsDomainStrategy'}, const {'1': 'direct_dns_address', '3': 3, '4': 1, '5': 9, '10': 'directDnsAddress'}, const {'1': 'direct_dns_domain_strategy', '3': 4, '4': 1, '5': 14, '6': '.hiddifyoptions.DomainStrategy', '10': 'directDnsDomainStrategy'}, const {'1': 'independent_dns_cache', '3': 5, '4': 1, '5': 8, '10': 'independentDnsCache'}, const {'1': 'enable_fake_dns', '3': 6, '4': 1, '5': 8, '10': 'enableFakeDns'}, const {'1': 'enable_dns_routing', '3': 7, '4': 1, '5': 8, '10': 'enableDnsRouting'}, ], }; /// Descriptor for `DNSOptions`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List dNSOptionsDescriptor = $convert.base64Decode('CgpETlNPcHRpb25zEiwKEnJlbW90ZV9kbnNfYWRkcmVzcxgBIAEoCVIQcmVtb3RlRG5zQWRkcmVzcxJbChpyZW1vdGVfZG5zX2RvbWFpbl9zdHJhdGVneRgCIAEoDjIeLmhpZGRpZnlvcHRpb25zLkRvbWFpblN0cmF0ZWd5UhdyZW1vdGVEbnNEb21haW5TdHJhdGVneRIsChJkaXJlY3RfZG5zX2FkZHJlc3MYAyABKAlSEGRpcmVjdERuc0FkZHJlc3MSWwoaZGlyZWN0X2Ruc19kb21haW5fc3RyYXRlZ3kYBCABKA4yHi5oaWRkaWZ5b3B0aW9ucy5Eb21haW5TdHJhdGVneVIXZGlyZWN0RG5zRG9tYWluU3RyYXRlZ3kSMgoVaW5kZXBlbmRlbnRfZG5zX2NhY2hlGAUgASgIUhNpbmRlcGVuZGVudERuc0NhY2hlEiYKD2VuYWJsZV9mYWtlX2RucxgGIAEoCFINZW5hYmxlRmFrZURucxIsChJlbmFibGVfZG5zX3JvdXRpbmcYByABKAhSEGVuYWJsZURuc1JvdXRpbmc='); @$core.Deprecated('Use inboundOptionsDescriptor instead') const InboundOptions$json = const { '1': 'InboundOptions', '2': const [ const {'1': 'enable_tun', '3': 1, '4': 1, '5': 8, '10': 'enableTun'}, const {'1': 'enable_tun_service', '3': 2, '4': 1, '5': 8, '10': 'enableTunService'}, const {'1': 'set_system_proxy', '3': 3, '4': 1, '5': 8, '10': 'setSystemProxy'}, const {'1': 'mixed_port', '3': 4, '4': 1, '5': 13, '10': 'mixedPort'}, const {'1': 'tproxy_port', '3': 5, '4': 1, '5': 13, '10': 'tproxyPort'}, const {'1': 'redirect_port', '3': 10, '4': 1, '5': 13, '10': 'redirectPort'}, const {'1': 'direct_port', '3': 6, '4': 1, '5': 13, '10': 'directPort'}, const {'1': 'mtu', '3': 7, '4': 1, '5': 13, '10': 'mtu'}, const {'1': 'strict_route', '3': 8, '4': 1, '5': 8, '10': 'strictRoute'}, const {'1': 'tun_stack', '3': 9, '4': 1, '5': 9, '10': 'tunStack'}, ], }; /// Descriptor for `InboundOptions`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List inboundOptionsDescriptor = $convert.base64Decode('Cg5JbmJvdW5kT3B0aW9ucxIdCgplbmFibGVfdHVuGAEgASgIUgllbmFibGVUdW4SLAoSZW5hYmxlX3R1bl9zZXJ2aWNlGAIgASgIUhBlbmFibGVUdW5TZXJ2aWNlEigKEHNldF9zeXN0ZW1fcHJveHkYAyABKAhSDnNldFN5c3RlbVByb3h5Eh0KCm1peGVkX3BvcnQYBCABKA1SCW1peGVkUG9ydBIfCgt0cHJveHlfcG9ydBgFIAEoDVIKdHByb3h5UG9ydBIjCg1yZWRpcmVjdF9wb3J0GAogASgNUgxyZWRpcmVjdFBvcnQSHwoLZGlyZWN0X3BvcnQYBiABKA1SCmRpcmVjdFBvcnQSEAoDbXR1GAcgASgNUgNtdHUSIQoMc3RyaWN0X3JvdXRlGAggASgIUgtzdHJpY3RSb3V0ZRIbCgl0dW5fc3RhY2sYCSABKAlSCHR1blN0YWNr'); @$core.Deprecated('Use uRLTestOptionsDescriptor instead') const URLTestOptions$json = const { '1': 'URLTestOptions', '2': const [ const {'1': 'connection_test_url', '3': 1, '4': 1, '5': 9, '10': 'connectionTestUrl'}, const {'1': 'url_test_interval', '3': 2, '4': 1, '5': 3, '10': 'urlTestInterval'}, ], }; /// Descriptor for `URLTestOptions`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List uRLTestOptionsDescriptor = $convert.base64Decode('Cg5VUkxUZXN0T3B0aW9ucxIuChNjb25uZWN0aW9uX3Rlc3RfdXJsGAEgASgJUhFjb25uZWN0aW9uVGVzdFVybBIqChF1cmxfdGVzdF9pbnRlcnZhbBgCIAEoA1IPdXJsVGVzdEludGVydmFs'); @$core.Deprecated('Use routeOptionsDescriptor instead') const RouteOptions$json = const { '1': 'RouteOptions', '2': const [ const {'1': 'resolve_destination', '3': 1, '4': 1, '5': 8, '10': 'resolveDestination'}, const {'1': 'ipv6_mode', '3': 2, '4': 1, '5': 14, '6': '.hiddifyoptions.DomainStrategy', '10': 'ipv6Mode'}, const {'1': 'bypass_lan', '3': 3, '4': 1, '5': 8, '10': 'bypassLan'}, const {'1': 'allow_connection_from_lan', '3': 4, '4': 1, '5': 8, '10': 'allowConnectionFromLan'}, ], }; /// Descriptor for `RouteOptions`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List routeOptionsDescriptor = $convert.base64Decode('CgxSb3V0ZU9wdGlvbnMSLwoTcmVzb2x2ZV9kZXN0aW5hdGlvbhgBIAEoCFIScmVzb2x2ZURlc3RpbmF0aW9uEjsKCWlwdjZfbW9kZRgCIAEoDjIeLmhpZGRpZnlvcHRpb25zLkRvbWFpblN0cmF0ZWd5UghpcHY2TW9kZRIdCgpieXBhc3NfbGFuGAMgASgIUglieXBhc3NMYW4SOQoZYWxsb3dfY29ubmVjdGlvbl9mcm9tX2xhbhgEIAEoCFIWYWxsb3dDb25uZWN0aW9uRnJvbUxhbg=='); @$core.Deprecated('Use tLSTricksDescriptor instead') const TLSTricks$json = const { '1': 'TLSTricks', '2': const [ const {'1': 'enable_fragment', '3': 1, '4': 1, '5': 8, '10': 'enableFragment'}, const {'1': 'fragment_size', '3': 2, '4': 1, '5': 11, '6': '.hiddifyoptions.IntRange', '10': 'fragmentSize'}, const {'1': 'fragment_sleep', '3': 3, '4': 1, '5': 11, '6': '.hiddifyoptions.IntRange', '10': 'fragmentSleep'}, const {'1': 'mixed_sni_case', '3': 4, '4': 1, '5': 8, '10': 'mixedSniCase'}, const {'1': 'enable_padding', '3': 5, '4': 1, '5': 8, '10': 'enablePadding'}, const {'1': 'padding_size', '3': 6, '4': 1, '5': 11, '6': '.hiddifyoptions.IntRange', '10': 'paddingSize'}, ], }; /// Descriptor for `TLSTricks`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List tLSTricksDescriptor = $convert.base64Decode('CglUTFNUcmlja3MSJwoPZW5hYmxlX2ZyYWdtZW50GAEgASgIUg5lbmFibGVGcmFnbWVudBI9Cg1mcmFnbWVudF9zaXplGAIgASgLMhguaGlkZGlmeW9wdGlvbnMuSW50UmFuZ2VSDGZyYWdtZW50U2l6ZRI/Cg5mcmFnbWVudF9zbGVlcBgDIAEoCzIYLmhpZGRpZnlvcHRpb25zLkludFJhbmdlUg1mcmFnbWVudFNsZWVwEiQKDm1peGVkX3NuaV9jYXNlGAQgASgIUgxtaXhlZFNuaUNhc2USJQoOZW5hYmxlX3BhZGRpbmcYBSABKAhSDWVuYWJsZVBhZGRpbmcSOwoMcGFkZGluZ19zaXplGAYgASgLMhguaGlkZGlmeW9wdGlvbnMuSW50UmFuZ2VSC3BhZGRpbmdTaXpl'); @$core.Deprecated('Use muxOptionsDescriptor instead') const MuxOptions$json = const { '1': 'MuxOptions', '2': const [ const {'1': 'enable', '3': 1, '4': 1, '5': 8, '10': 'enable'}, const {'1': 'padding', '3': 2, '4': 1, '5': 8, '10': 'padding'}, const {'1': 'max_streams', '3': 3, '4': 1, '5': 5, '10': 'maxStreams'}, const {'1': 'protocol', '3': 4, '4': 1, '5': 9, '10': 'protocol'}, ], }; /// Descriptor for `MuxOptions`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List muxOptionsDescriptor = $convert.base64Decode('CgpNdXhPcHRpb25zEhYKBmVuYWJsZRgBIAEoCFIGZW5hYmxlEhgKB3BhZGRpbmcYAiABKAhSB3BhZGRpbmcSHwoLbWF4X3N0cmVhbXMYAyABKAVSCm1heFN0cmVhbXMSGgoIcHJvdG9jb2wYBCABKAlSCHByb3RvY29s'); @$core.Deprecated('Use warpOptionsDescriptor instead') const WarpOptions$json = const { '1': 'WarpOptions', '2': const [ const {'1': 'id', '3': 1, '4': 1, '5': 9, '10': 'id'}, const {'1': 'enable_warp', '3': 2, '4': 1, '5': 8, '10': 'enableWarp'}, const {'1': 'mode', '3': 3, '4': 1, '5': 9, '10': 'mode'}, const {'1': 'wireguard_config', '3': 5, '4': 1, '5': 11, '6': '.hiddifyoptions.WarpWireguardConfig', '10': 'wireguardConfig'}, const {'1': 'fake_packets', '3': 6, '4': 1, '5': 9, '10': 'fakePackets'}, const {'1': 'fake_packet_size', '3': 7, '4': 1, '5': 11, '6': '.hiddifyoptions.IntRange', '10': 'fakePacketSize'}, const {'1': 'fake_packet_delay', '3': 8, '4': 1, '5': 11, '6': '.hiddifyoptions.IntRange', '10': 'fakePacketDelay'}, const {'1': 'fake_packet_mode', '3': 9, '4': 1, '5': 9, '10': 'fakePacketMode'}, const {'1': 'clean_ip', '3': 10, '4': 1, '5': 9, '10': 'cleanIp'}, const {'1': 'clean_port', '3': 11, '4': 1, '5': 13, '10': 'cleanPort'}, const {'1': 'account', '3': 12, '4': 1, '5': 11, '6': '.hiddifyoptions.WarpAccount', '10': 'account'}, ], }; /// Descriptor for `WarpOptions`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List warpOptionsDescriptor = $convert.base64Decode('CgtXYXJwT3B0aW9ucxIOCgJpZBgBIAEoCVICaWQSHwoLZW5hYmxlX3dhcnAYAiABKAhSCmVuYWJsZVdhcnASEgoEbW9kZRgDIAEoCVIEbW9kZRJOChB3aXJlZ3VhcmRfY29uZmlnGAUgASgLMiMuaGlkZGlmeW9wdGlvbnMuV2FycFdpcmVndWFyZENvbmZpZ1IPd2lyZWd1YXJkQ29uZmlnEiEKDGZha2VfcGFja2V0cxgGIAEoCVILZmFrZVBhY2tldHMSQgoQZmFrZV9wYWNrZXRfc2l6ZRgHIAEoCzIYLmhpZGRpZnlvcHRpb25zLkludFJhbmdlUg5mYWtlUGFja2V0U2l6ZRJEChFmYWtlX3BhY2tldF9kZWxheRgIIAEoCzIYLmhpZGRpZnlvcHRpb25zLkludFJhbmdlUg9mYWtlUGFja2V0RGVsYXkSKAoQZmFrZV9wYWNrZXRfbW9kZRgJIAEoCVIOZmFrZVBhY2tldE1vZGUSGQoIY2xlYW5faXAYCiABKAlSB2NsZWFuSXASHQoKY2xlYW5fcG9ydBgLIAEoDVIJY2xlYW5Qb3J0EjUKB2FjY291bnQYDCABKAsyGy5oaWRkaWZ5b3B0aW9ucy5XYXJwQWNjb3VudFIHYWNjb3VudA=='); @$core.Deprecated('Use warpAccountDescriptor instead') const WarpAccount$json = const { '1': 'WarpAccount', '2': const [ const {'1': 'account_id', '3': 1, '4': 1, '5': 9, '10': 'accountId'}, const {'1': 'access_token', '3': 2, '4': 1, '5': 9, '10': 'accessToken'}, ], }; /// Descriptor for `WarpAccount`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List warpAccountDescriptor = $convert.base64Decode('CgtXYXJwQWNjb3VudBIdCgphY2NvdW50X2lkGAEgASgJUglhY2NvdW50SWQSIQoMYWNjZXNzX3Rva2VuGAIgASgJUgthY2Nlc3NUb2tlbg=='); @$core.Deprecated('Use warpWireguardConfigDescriptor instead') const WarpWireguardConfig$json = const { '1': 'WarpWireguardConfig', '2': const [ const {'1': 'private_key', '3': 1, '4': 1, '5': 9, '10': 'privateKey'}, const {'1': 'local_address_ipv4', '3': 2, '4': 1, '5': 9, '10': 'localAddressIpv4'}, const {'1': 'local_address_ipv6', '3': 3, '4': 1, '5': 9, '10': 'localAddressIpv6'}, const {'1': 'peer_public_key', '3': 4, '4': 1, '5': 9, '10': 'peerPublicKey'}, const {'1': 'client_id', '3': 5, '4': 1, '5': 9, '10': 'clientId'}, ], }; /// Descriptor for `WarpWireguardConfig`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List warpWireguardConfigDescriptor = $convert.base64Decode('ChNXYXJwV2lyZWd1YXJkQ29uZmlnEh8KC3ByaXZhdGVfa2V5GAEgASgJUgpwcml2YXRlS2V5EiwKEmxvY2FsX2FkZHJlc3NfaXB2NBgCIAEoCVIQbG9jYWxBZGRyZXNzSXB2NBIsChJsb2NhbF9hZGRyZXNzX2lwdjYYAyABKAlSEGxvY2FsQWRkcmVzc0lwdjYSJgoPcGVlcl9wdWJsaWNfa2V5GAQgASgJUg1wZWVyUHVibGljS2V5EhsKCWNsaWVudF9pZBgFIAEoCVIIY2xpZW50SWQ='); @$core.Deprecated('Use ruleDescriptor instead') const Rule$json = const { '1': 'Rule', '2': const [ const {'1': 'rule_set_url', '3': 1, '4': 1, '5': 9, '10': 'ruleSetUrl'}, const {'1': 'domains', '3': 2, '4': 1, '5': 9, '10': 'domains'}, const {'1': 'ip', '3': 3, '4': 1, '5': 9, '10': 'ip'}, const {'1': 'port', '3': 4, '4': 1, '5': 9, '10': 'port'}, const {'1': 'network', '3': 5, '4': 1, '5': 9, '10': 'network'}, const {'1': 'protocol', '3': 6, '4': 1, '5': 9, '10': 'protocol'}, const {'1': 'outbound', '3': 7, '4': 1, '5': 9, '10': 'outbound'}, ], }; /// Descriptor for `Rule`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List ruleDescriptor = $convert.base64Decode('CgRSdWxlEiAKDHJ1bGVfc2V0X3VybBgBIAEoCVIKcnVsZVNldFVybBIYCgdkb21haW5zGAIgASgJUgdkb21haW5zEg4KAmlwGAMgASgJUgJpcBISCgRwb3J0GAQgASgJUgRwb3J0EhgKB25ldHdvcmsYBSABKAlSB25ldHdvcmsSGgoIcHJvdG9jb2wYBiABKAlSCHByb3RvY29sEhoKCG91dGJvdW5kGAcgASgJUghvdXRib3VuZA=='); ================================================ FILE: lib/hiddifycore/generated/v2/profile/profile.pb.dart ================================================ /// // Generated code. Do not modify. // source: v2/profile/profile.proto // // @dart = 2.12 // ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name import 'dart:core' as $core; import 'package:fixnum/fixnum.dart' as $fixnum; import 'package:protobuf/protobuf.dart' as $pb; import '../hiddifyoptions/hiddify_options.pb.dart' as $8; class ProfileEntity extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'ProfileEntity', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'profile'), createEmptyInstance: create) ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'id') ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'name') ..aOS(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'url') ..aInt64(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'lastUpdate') ..aOM(6, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'options', subBuilder: ProfileOptions.create) ..aOM(7, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'subInfo', subBuilder: SubscriptionInfo.create) ..aOM<$8.HiddifyOptions>(8, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'overrideHiddifyOptions', subBuilder: $8.HiddifyOptions.create) ..hasRequiredFields = false ; ProfileEntity._() : super(); factory ProfileEntity({ $core.String? id, $core.String? name, $core.String? url, $fixnum.Int64? lastUpdate, ProfileOptions? options, SubscriptionInfo? subInfo, $8.HiddifyOptions? overrideHiddifyOptions, }) { final _result = create(); if (id != null) { _result.id = id; } if (name != null) { _result.name = name; } if (url != null) { _result.url = url; } if (lastUpdate != null) { _result.lastUpdate = lastUpdate; } if (options != null) { _result.options = options; } if (subInfo != null) { _result.subInfo = subInfo; } if (overrideHiddifyOptions != null) { _result.overrideHiddifyOptions = overrideHiddifyOptions; } return _result; } factory ProfileEntity.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory ProfileEntity.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') ProfileEntity clone() => ProfileEntity()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') ProfileEntity copyWith(void Function(ProfileEntity) updates) => super.copyWith((message) => updates(message as ProfileEntity)) as ProfileEntity; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static ProfileEntity create() => ProfileEntity._(); ProfileEntity createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static ProfileEntity getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static ProfileEntity? _defaultInstance; @$pb.TagNumber(1) $core.String get id => $_getSZ(0); @$pb.TagNumber(1) set id($core.String v) { $_setString(0, v); } @$pb.TagNumber(1) $core.bool hasId() => $_has(0); @$pb.TagNumber(1) void clearId() => clearField(1); @$pb.TagNumber(3) $core.String get name => $_getSZ(1); @$pb.TagNumber(3) set name($core.String v) { $_setString(1, v); } @$pb.TagNumber(3) $core.bool hasName() => $_has(1); @$pb.TagNumber(3) void clearName() => clearField(3); @$pb.TagNumber(4) $core.String get url => $_getSZ(2); @$pb.TagNumber(4) set url($core.String v) { $_setString(2, v); } @$pb.TagNumber(4) $core.bool hasUrl() => $_has(2); @$pb.TagNumber(4) void clearUrl() => clearField(4); @$pb.TagNumber(5) $fixnum.Int64 get lastUpdate => $_getI64(3); @$pb.TagNumber(5) set lastUpdate($fixnum.Int64 v) { $_setInt64(3, v); } @$pb.TagNumber(5) $core.bool hasLastUpdate() => $_has(3); @$pb.TagNumber(5) void clearLastUpdate() => clearField(5); @$pb.TagNumber(6) ProfileOptions get options => $_getN(4); @$pb.TagNumber(6) set options(ProfileOptions v) { setField(6, v); } @$pb.TagNumber(6) $core.bool hasOptions() => $_has(4); @$pb.TagNumber(6) void clearOptions() => clearField(6); @$pb.TagNumber(6) ProfileOptions ensureOptions() => $_ensure(4); @$pb.TagNumber(7) SubscriptionInfo get subInfo => $_getN(5); @$pb.TagNumber(7) set subInfo(SubscriptionInfo v) { setField(7, v); } @$pb.TagNumber(7) $core.bool hasSubInfo() => $_has(5); @$pb.TagNumber(7) void clearSubInfo() => clearField(7); @$pb.TagNumber(7) SubscriptionInfo ensureSubInfo() => $_ensure(5); @$pb.TagNumber(8) $8.HiddifyOptions get overrideHiddifyOptions => $_getN(6); @$pb.TagNumber(8) set overrideHiddifyOptions($8.HiddifyOptions v) { setField(8, v); } @$pb.TagNumber(8) $core.bool hasOverrideHiddifyOptions() => $_has(6); @$pb.TagNumber(8) void clearOverrideHiddifyOptions() => clearField(8); @$pb.TagNumber(8) $8.HiddifyOptions ensureOverrideHiddifyOptions() => $_ensure(6); } class ProfileOptions extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'ProfileOptions', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'profile'), createEmptyInstance: create) ..aInt64(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'updateInterval') ..hasRequiredFields = false ; ProfileOptions._() : super(); factory ProfileOptions({ $fixnum.Int64? updateInterval, }) { final _result = create(); if (updateInterval != null) { _result.updateInterval = updateInterval; } return _result; } factory ProfileOptions.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory ProfileOptions.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') ProfileOptions clone() => ProfileOptions()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') ProfileOptions copyWith(void Function(ProfileOptions) updates) => super.copyWith((message) => updates(message as ProfileOptions)) as ProfileOptions; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static ProfileOptions create() => ProfileOptions._(); ProfileOptions createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static ProfileOptions getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static ProfileOptions? _defaultInstance; @$pb.TagNumber(1) $fixnum.Int64 get updateInterval => $_getI64(0); @$pb.TagNumber(1) set updateInterval($fixnum.Int64 v) { $_setInt64(0, v); } @$pb.TagNumber(1) $core.bool hasUpdateInterval() => $_has(0); @$pb.TagNumber(1) void clearUpdateInterval() => clearField(1); } class SubscriptionInfo extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'SubscriptionInfo', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'profile'), createEmptyInstance: create) ..aInt64(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'upload') ..aInt64(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'download') ..aInt64(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'total') ..aInt64(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'expire') ..aOS(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'webPageUrl') ..aOS(6, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'supportUrl') ..hasRequiredFields = false ; SubscriptionInfo._() : super(); factory SubscriptionInfo({ $fixnum.Int64? upload, $fixnum.Int64? download, $fixnum.Int64? total, $fixnum.Int64? expire, $core.String? webPageUrl, $core.String? supportUrl, }) { final _result = create(); if (upload != null) { _result.upload = upload; } if (download != null) { _result.download = download; } if (total != null) { _result.total = total; } if (expire != null) { _result.expire = expire; } if (webPageUrl != null) { _result.webPageUrl = webPageUrl; } if (supportUrl != null) { _result.supportUrl = supportUrl; } return _result; } factory SubscriptionInfo.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory SubscriptionInfo.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') SubscriptionInfo clone() => SubscriptionInfo()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') SubscriptionInfo copyWith(void Function(SubscriptionInfo) updates) => super.copyWith((message) => updates(message as SubscriptionInfo)) as SubscriptionInfo; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static SubscriptionInfo create() => SubscriptionInfo._(); SubscriptionInfo createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static SubscriptionInfo getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static SubscriptionInfo? _defaultInstance; @$pb.TagNumber(1) $fixnum.Int64 get upload => $_getI64(0); @$pb.TagNumber(1) set upload($fixnum.Int64 v) { $_setInt64(0, v); } @$pb.TagNumber(1) $core.bool hasUpload() => $_has(0); @$pb.TagNumber(1) void clearUpload() => clearField(1); @$pb.TagNumber(2) $fixnum.Int64 get download => $_getI64(1); @$pb.TagNumber(2) set download($fixnum.Int64 v) { $_setInt64(1, v); } @$pb.TagNumber(2) $core.bool hasDownload() => $_has(1); @$pb.TagNumber(2) void clearDownload() => clearField(2); @$pb.TagNumber(3) $fixnum.Int64 get total => $_getI64(2); @$pb.TagNumber(3) set total($fixnum.Int64 v) { $_setInt64(2, v); } @$pb.TagNumber(3) $core.bool hasTotal() => $_has(2); @$pb.TagNumber(3) void clearTotal() => clearField(3); @$pb.TagNumber(4) $fixnum.Int64 get expire => $_getI64(3); @$pb.TagNumber(4) set expire($fixnum.Int64 v) { $_setInt64(3, v); } @$pb.TagNumber(4) $core.bool hasExpire() => $_has(3); @$pb.TagNumber(4) void clearExpire() => clearField(4); @$pb.TagNumber(5) $core.String get webPageUrl => $_getSZ(4); @$pb.TagNumber(5) set webPageUrl($core.String v) { $_setString(4, v); } @$pb.TagNumber(5) $core.bool hasWebPageUrl() => $_has(4); @$pb.TagNumber(5) void clearWebPageUrl() => clearField(5); @$pb.TagNumber(6) $core.String get supportUrl => $_getSZ(5); @$pb.TagNumber(6) set supportUrl($core.String v) { $_setString(5, v); } @$pb.TagNumber(6) $core.bool hasSupportUrl() => $_has(5); @$pb.TagNumber(6) void clearSupportUrl() => clearField(6); } ================================================ FILE: lib/hiddifycore/generated/v2/profile/profile.pbenum.dart ================================================ /// // Generated code. Do not modify. // source: v2/profile/profile.proto // // @dart = 2.12 // ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name ================================================ FILE: lib/hiddifycore/generated/v2/profile/profile.pbjson.dart ================================================ /// // Generated code. Do not modify. // source: v2/profile/profile.proto // // @dart = 2.12 // ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,deprecated_member_use_from_same_package,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name import 'dart:core' as $core; import 'dart:convert' as $convert; import 'dart:typed_data' as $typed_data; @$core.Deprecated('Use profileEntityDescriptor instead') const ProfileEntity$json = const { '1': 'ProfileEntity', '2': const [ const {'1': 'id', '3': 1, '4': 1, '5': 9, '10': 'id'}, const {'1': 'name', '3': 3, '4': 1, '5': 9, '10': 'name'}, const {'1': 'url', '3': 4, '4': 1, '5': 9, '10': 'url'}, const {'1': 'last_update', '3': 5, '4': 1, '5': 3, '10': 'lastUpdate'}, const {'1': 'options', '3': 6, '4': 1, '5': 11, '6': '.profile.ProfileOptions', '10': 'options'}, const {'1': 'sub_info', '3': 7, '4': 1, '5': 11, '6': '.profile.SubscriptionInfo', '10': 'subInfo'}, const {'1': 'override_hiddify_options', '3': 8, '4': 1, '5': 11, '6': '.hiddifyoptions.HiddifyOptions', '10': 'overrideHiddifyOptions'}, ], }; /// Descriptor for `ProfileEntity`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List profileEntityDescriptor = $convert.base64Decode('Cg1Qcm9maWxlRW50aXR5Eg4KAmlkGAEgASgJUgJpZBISCgRuYW1lGAMgASgJUgRuYW1lEhAKA3VybBgEIAEoCVIDdXJsEh8KC2xhc3RfdXBkYXRlGAUgASgDUgpsYXN0VXBkYXRlEjEKB29wdGlvbnMYBiABKAsyFy5wcm9maWxlLlByb2ZpbGVPcHRpb25zUgdvcHRpb25zEjQKCHN1Yl9pbmZvGAcgASgLMhkucHJvZmlsZS5TdWJzY3JpcHRpb25JbmZvUgdzdWJJbmZvElgKGG92ZXJyaWRlX2hpZGRpZnlfb3B0aW9ucxgIIAEoCzIeLmhpZGRpZnlvcHRpb25zLkhpZGRpZnlPcHRpb25zUhZvdmVycmlkZUhpZGRpZnlPcHRpb25z'); @$core.Deprecated('Use profileOptionsDescriptor instead') const ProfileOptions$json = const { '1': 'ProfileOptions', '2': const [ const {'1': 'update_interval', '3': 1, '4': 1, '5': 3, '10': 'updateInterval'}, ], }; /// Descriptor for `ProfileOptions`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List profileOptionsDescriptor = $convert.base64Decode('Cg5Qcm9maWxlT3B0aW9ucxInCg91cGRhdGVfaW50ZXJ2YWwYASABKANSDnVwZGF0ZUludGVydmFs'); @$core.Deprecated('Use subscriptionInfoDescriptor instead') const SubscriptionInfo$json = const { '1': 'SubscriptionInfo', '2': const [ const {'1': 'upload', '3': 1, '4': 1, '5': 3, '10': 'upload'}, const {'1': 'download', '3': 2, '4': 1, '5': 3, '10': 'download'}, const {'1': 'total', '3': 3, '4': 1, '5': 3, '10': 'total'}, const {'1': 'expire', '3': 4, '4': 1, '5': 3, '10': 'expire'}, const {'1': 'web_page_url', '3': 5, '4': 1, '5': 9, '10': 'webPageUrl'}, const {'1': 'support_url', '3': 6, '4': 1, '5': 9, '10': 'supportUrl'}, ], }; /// Descriptor for `SubscriptionInfo`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List subscriptionInfoDescriptor = $convert.base64Decode('ChBTdWJzY3JpcHRpb25JbmZvEhYKBnVwbG9hZBgBIAEoA1IGdXBsb2FkEhoKCGRvd25sb2FkGAIgASgDUghkb3dubG9hZBIUCgV0b3RhbBgDIAEoA1IFdG90YWwSFgoGZXhwaXJlGAQgASgDUgZleHBpcmUSIAoMd2ViX3BhZ2VfdXJsGAUgASgJUgp3ZWJQYWdlVXJsEh8KC3N1cHBvcnRfdXJsGAYgASgJUgpzdXBwb3J0VXJs'); ================================================ FILE: lib/hiddifycore/generated/v2/profile/profile_service.pb.dart ================================================ /// // Generated code. Do not modify. // source: v2/profile/profile_service.proto // // @dart = 2.12 // ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name import 'dart:core' as $core; import 'package:protobuf/protobuf.dart' as $pb; import 'profile.pb.dart' as $4; import '../hcommon/common.pbenum.dart' as $1; class ProfileRequest extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'ProfileRequest', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'profile'), createEmptyInstance: create) ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'id') ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'name') ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'url') ..hasRequiredFields = false ; ProfileRequest._() : super(); factory ProfileRequest({ $core.String? id, $core.String? name, $core.String? url, }) { final _result = create(); if (id != null) { _result.id = id; } if (name != null) { _result.name = name; } if (url != null) { _result.url = url; } return _result; } factory ProfileRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory ProfileRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') ProfileRequest clone() => ProfileRequest()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') ProfileRequest copyWith(void Function(ProfileRequest) updates) => super.copyWith((message) => updates(message as ProfileRequest)) as ProfileRequest; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static ProfileRequest create() => ProfileRequest._(); ProfileRequest createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static ProfileRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static ProfileRequest? _defaultInstance; @$pb.TagNumber(1) $core.String get id => $_getSZ(0); @$pb.TagNumber(1) set id($core.String v) { $_setString(0, v); } @$pb.TagNumber(1) $core.bool hasId() => $_has(0); @$pb.TagNumber(1) void clearId() => clearField(1); @$pb.TagNumber(2) $core.String get name => $_getSZ(1); @$pb.TagNumber(2) set name($core.String v) { $_setString(1, v); } @$pb.TagNumber(2) $core.bool hasName() => $_has(1); @$pb.TagNumber(2) void clearName() => clearField(2); @$pb.TagNumber(3) $core.String get url => $_getSZ(2); @$pb.TagNumber(3) set url($core.String v) { $_setString(2, v); } @$pb.TagNumber(3) $core.bool hasUrl() => $_has(2); @$pb.TagNumber(3) void clearUrl() => clearField(3); } class AddProfileRequest extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'AddProfileRequest', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'profile'), createEmptyInstance: create) ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'url') ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'content') ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'name') ..aOB(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'markAsActive') ..hasRequiredFields = false ; AddProfileRequest._() : super(); factory AddProfileRequest({ $core.String? url, $core.String? content, $core.String? name, $core.bool? markAsActive, }) { final _result = create(); if (url != null) { _result.url = url; } if (content != null) { _result.content = content; } if (name != null) { _result.name = name; } if (markAsActive != null) { _result.markAsActive = markAsActive; } return _result; } factory AddProfileRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory AddProfileRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') AddProfileRequest clone() => AddProfileRequest()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') AddProfileRequest copyWith(void Function(AddProfileRequest) updates) => super.copyWith((message) => updates(message as AddProfileRequest)) as AddProfileRequest; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static AddProfileRequest create() => AddProfileRequest._(); AddProfileRequest createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static AddProfileRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static AddProfileRequest? _defaultInstance; @$pb.TagNumber(1) $core.String get url => $_getSZ(0); @$pb.TagNumber(1) set url($core.String v) { $_setString(0, v); } @$pb.TagNumber(1) $core.bool hasUrl() => $_has(0); @$pb.TagNumber(1) void clearUrl() => clearField(1); @$pb.TagNumber(2) $core.String get content => $_getSZ(1); @$pb.TagNumber(2) set content($core.String v) { $_setString(1, v); } @$pb.TagNumber(2) $core.bool hasContent() => $_has(1); @$pb.TagNumber(2) void clearContent() => clearField(2); @$pb.TagNumber(3) $core.String get name => $_getSZ(2); @$pb.TagNumber(3) set name($core.String v) { $_setString(2, v); } @$pb.TagNumber(3) $core.bool hasName() => $_has(2); @$pb.TagNumber(3) void clearName() => clearField(3); @$pb.TagNumber(4) $core.bool get markAsActive => $_getBF(3); @$pb.TagNumber(4) set markAsActive($core.bool v) { $_setBool(3, v); } @$pb.TagNumber(4) $core.bool hasMarkAsActive() => $_has(3); @$pb.TagNumber(4) void clearMarkAsActive() => clearField(4); } class ProfileResponse extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'ProfileResponse', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'profile'), createEmptyInstance: create) ..aOM<$4.ProfileEntity>(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'profile', subBuilder: $4.ProfileEntity.create) ..e<$1.ResponseCode>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'responseCode', $pb.PbFieldType.OE, defaultOrMaker: $1.ResponseCode.OK, valueOf: $1.ResponseCode.valueOf, enumValues: $1.ResponseCode.values) ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'message') ..hasRequiredFields = false ; ProfileResponse._() : super(); factory ProfileResponse({ $4.ProfileEntity? profile, $1.ResponseCode? responseCode, $core.String? message, }) { final _result = create(); if (profile != null) { _result.profile = profile; } if (responseCode != null) { _result.responseCode = responseCode; } if (message != null) { _result.message = message; } return _result; } factory ProfileResponse.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory ProfileResponse.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') ProfileResponse clone() => ProfileResponse()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') ProfileResponse copyWith(void Function(ProfileResponse) updates) => super.copyWith((message) => updates(message as ProfileResponse)) as ProfileResponse; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static ProfileResponse create() => ProfileResponse._(); ProfileResponse createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static ProfileResponse getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static ProfileResponse? _defaultInstance; @$pb.TagNumber(1) $4.ProfileEntity get profile => $_getN(0); @$pb.TagNumber(1) set profile($4.ProfileEntity v) { setField(1, v); } @$pb.TagNumber(1) $core.bool hasProfile() => $_has(0); @$pb.TagNumber(1) void clearProfile() => clearField(1); @$pb.TagNumber(1) $4.ProfileEntity ensureProfile() => $_ensure(0); @$pb.TagNumber(2) $1.ResponseCode get responseCode => $_getN(1); @$pb.TagNumber(2) set responseCode($1.ResponseCode v) { setField(2, v); } @$pb.TagNumber(2) $core.bool hasResponseCode() => $_has(1); @$pb.TagNumber(2) void clearResponseCode() => clearField(2); @$pb.TagNumber(3) $core.String get message => $_getSZ(2); @$pb.TagNumber(3) set message($core.String v) { $_setString(2, v); } @$pb.TagNumber(3) $core.bool hasMessage() => $_has(2); @$pb.TagNumber(3) void clearMessage() => clearField(3); } class MultiProfilesResponse extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'MultiProfilesResponse', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'profile'), createEmptyInstance: create) ..pc<$4.ProfileEntity>(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'profiles', $pb.PbFieldType.PM, subBuilder: $4.ProfileEntity.create) ..e<$1.ResponseCode>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'responseCode', $pb.PbFieldType.OE, defaultOrMaker: $1.ResponseCode.OK, valueOf: $1.ResponseCode.valueOf, enumValues: $1.ResponseCode.values) ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'message') ..hasRequiredFields = false ; MultiProfilesResponse._() : super(); factory MultiProfilesResponse({ $core.Iterable<$4.ProfileEntity>? profiles, $1.ResponseCode? responseCode, $core.String? message, }) { final _result = create(); if (profiles != null) { _result.profiles.addAll(profiles); } if (responseCode != null) { _result.responseCode = responseCode; } if (message != null) { _result.message = message; } return _result; } factory MultiProfilesResponse.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory MultiProfilesResponse.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') MultiProfilesResponse clone() => MultiProfilesResponse()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') MultiProfilesResponse copyWith(void Function(MultiProfilesResponse) updates) => super.copyWith((message) => updates(message as MultiProfilesResponse)) as MultiProfilesResponse; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') static MultiProfilesResponse create() => MultiProfilesResponse._(); MultiProfilesResponse createEmptyInstance() => create(); static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') static MultiProfilesResponse getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static MultiProfilesResponse? _defaultInstance; @$pb.TagNumber(1) $core.List<$4.ProfileEntity> get profiles => $_getList(0); @$pb.TagNumber(2) $1.ResponseCode get responseCode => $_getN(1); @$pb.TagNumber(2) set responseCode($1.ResponseCode v) { setField(2, v); } @$pb.TagNumber(2) $core.bool hasResponseCode() => $_has(1); @$pb.TagNumber(2) void clearResponseCode() => clearField(2); @$pb.TagNumber(3) $core.String get message => $_getSZ(2); @$pb.TagNumber(3) set message($core.String v) { $_setString(2, v); } @$pb.TagNumber(3) $core.bool hasMessage() => $_has(2); @$pb.TagNumber(3) void clearMessage() => clearField(3); } ================================================ FILE: lib/hiddifycore/generated/v2/profile/profile_service.pbenum.dart ================================================ /// // Generated code. Do not modify. // source: v2/profile/profile_service.proto // // @dart = 2.12 // ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name ================================================ FILE: lib/hiddifycore/generated/v2/profile/profile_service.pbgrpc.dart ================================================ /// // Generated code. Do not modify. // source: v2/profile/profile_service.proto // // @dart = 2.12 // ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name import 'dart:async' as $async; import 'dart:core' as $core; import 'package:grpc/service_api.dart' as $grpc; import 'profile_service.pb.dart' as $3; import 'profile.pb.dart' as $4; import '../hcommon/common.pb.dart' as $1; export 'profile_service.pb.dart'; class ProfileServiceClient extends $grpc.Client { static final _$getProfile = $grpc.ClientMethod<$3.ProfileRequest, $3.ProfileResponse>( '/profile.ProfileService/GetProfile', ($3.ProfileRequest value) => value.writeToBuffer(), ($core.List<$core.int> value) => $3.ProfileResponse.fromBuffer(value)); static final _$updateProfile = $grpc.ClientMethod<$4.ProfileEntity, $3.ProfileResponse>( '/profile.ProfileService/UpdateProfile', ($4.ProfileEntity value) => value.writeToBuffer(), ($core.List<$core.int> value) => $3.ProfileResponse.fromBuffer(value)); static final _$getAllProfiles = $grpc.ClientMethod<$1.Empty, $3.MultiProfilesResponse>( '/profile.ProfileService/GetAllProfiles', ($1.Empty value) => value.writeToBuffer(), ($core.List<$core.int> value) => $3.MultiProfilesResponse.fromBuffer(value)); static final _$getActiveProfile = $grpc.ClientMethod<$1.Empty, $3.ProfileResponse>( '/profile.ProfileService/GetActiveProfile', ($1.Empty value) => value.writeToBuffer(), ($core.List<$core.int> value) => $3.ProfileResponse.fromBuffer(value)); static final _$setActiveProfile = $grpc.ClientMethod<$3.ProfileRequest, $1.Response>( '/profile.ProfileService/SetActiveProfile', ($3.ProfileRequest value) => value.writeToBuffer(), ($core.List<$core.int> value) => $1.Response.fromBuffer(value)); static final _$addProfile = $grpc.ClientMethod<$3.AddProfileRequest, $3.ProfileResponse>( '/profile.ProfileService/AddProfile', ($3.AddProfileRequest value) => value.writeToBuffer(), ($core.List<$core.int> value) => $3.ProfileResponse.fromBuffer(value)); static final _$deleteProfile = $grpc.ClientMethod<$3.ProfileRequest, $1.Response>( '/profile.ProfileService/DeleteProfile', ($3.ProfileRequest value) => value.writeToBuffer(), ($core.List<$core.int> value) => $1.Response.fromBuffer(value)); ProfileServiceClient($grpc.ClientChannel channel, {$grpc.CallOptions? options, $core.Iterable<$grpc.ClientInterceptor>? interceptors}) : super(channel, options: options, interceptors: interceptors); $grpc.ResponseFuture<$3.ProfileResponse> getProfile($3.ProfileRequest request, {$grpc.CallOptions? options}) { return $createUnaryCall(_$getProfile, request, options: options); } $grpc.ResponseFuture<$3.ProfileResponse> updateProfile( $4.ProfileEntity request, {$grpc.CallOptions? options}) { return $createUnaryCall(_$updateProfile, request, options: options); } $grpc.ResponseFuture<$3.MultiProfilesResponse> getAllProfiles( $1.Empty request, {$grpc.CallOptions? options}) { return $createUnaryCall(_$getAllProfiles, request, options: options); } $grpc.ResponseFuture<$3.ProfileResponse> getActiveProfile($1.Empty request, {$grpc.CallOptions? options}) { return $createUnaryCall(_$getActiveProfile, request, options: options); } $grpc.ResponseFuture<$1.Response> setActiveProfile($3.ProfileRequest request, {$grpc.CallOptions? options}) { return $createUnaryCall(_$setActiveProfile, request, options: options); } $grpc.ResponseFuture<$3.ProfileResponse> addProfile( $3.AddProfileRequest request, {$grpc.CallOptions? options}) { return $createUnaryCall(_$addProfile, request, options: options); } $grpc.ResponseFuture<$1.Response> deleteProfile($3.ProfileRequest request, {$grpc.CallOptions? options}) { return $createUnaryCall(_$deleteProfile, request, options: options); } } abstract class ProfileServiceBase extends $grpc.Service { $core.String get $name => 'profile.ProfileService'; ProfileServiceBase() { $addMethod($grpc.ServiceMethod<$3.ProfileRequest, $3.ProfileResponse>( 'GetProfile', getProfile_Pre, false, false, ($core.List<$core.int> value) => $3.ProfileRequest.fromBuffer(value), ($3.ProfileResponse value) => value.writeToBuffer())); $addMethod($grpc.ServiceMethod<$4.ProfileEntity, $3.ProfileResponse>( 'UpdateProfile', updateProfile_Pre, false, false, ($core.List<$core.int> value) => $4.ProfileEntity.fromBuffer(value), ($3.ProfileResponse value) => value.writeToBuffer())); $addMethod($grpc.ServiceMethod<$1.Empty, $3.MultiProfilesResponse>( 'GetAllProfiles', getAllProfiles_Pre, false, false, ($core.List<$core.int> value) => $1.Empty.fromBuffer(value), ($3.MultiProfilesResponse value) => value.writeToBuffer())); $addMethod($grpc.ServiceMethod<$1.Empty, $3.ProfileResponse>( 'GetActiveProfile', getActiveProfile_Pre, false, false, ($core.List<$core.int> value) => $1.Empty.fromBuffer(value), ($3.ProfileResponse value) => value.writeToBuffer())); $addMethod($grpc.ServiceMethod<$3.ProfileRequest, $1.Response>( 'SetActiveProfile', setActiveProfile_Pre, false, false, ($core.List<$core.int> value) => $3.ProfileRequest.fromBuffer(value), ($1.Response value) => value.writeToBuffer())); $addMethod($grpc.ServiceMethod<$3.AddProfileRequest, $3.ProfileResponse>( 'AddProfile', addProfile_Pre, false, false, ($core.List<$core.int> value) => $3.AddProfileRequest.fromBuffer(value), ($3.ProfileResponse value) => value.writeToBuffer())); $addMethod($grpc.ServiceMethod<$3.ProfileRequest, $1.Response>( 'DeleteProfile', deleteProfile_Pre, false, false, ($core.List<$core.int> value) => $3.ProfileRequest.fromBuffer(value), ($1.Response value) => value.writeToBuffer())); } $async.Future<$3.ProfileResponse> getProfile_Pre( $grpc.ServiceCall call, $async.Future<$3.ProfileRequest> request) async { return getProfile(call, await request); } $async.Future<$3.ProfileResponse> updateProfile_Pre( $grpc.ServiceCall call, $async.Future<$4.ProfileEntity> request) async { return updateProfile(call, await request); } $async.Future<$3.MultiProfilesResponse> getAllProfiles_Pre( $grpc.ServiceCall call, $async.Future<$1.Empty> request) async { return getAllProfiles(call, await request); } $async.Future<$3.ProfileResponse> getActiveProfile_Pre( $grpc.ServiceCall call, $async.Future<$1.Empty> request) async { return getActiveProfile(call, await request); } $async.Future<$1.Response> setActiveProfile_Pre( $grpc.ServiceCall call, $async.Future<$3.ProfileRequest> request) async { return setActiveProfile(call, await request); } $async.Future<$3.ProfileResponse> addProfile_Pre($grpc.ServiceCall call, $async.Future<$3.AddProfileRequest> request) async { return addProfile(call, await request); } $async.Future<$1.Response> deleteProfile_Pre( $grpc.ServiceCall call, $async.Future<$3.ProfileRequest> request) async { return deleteProfile(call, await request); } $async.Future<$3.ProfileResponse> getProfile( $grpc.ServiceCall call, $3.ProfileRequest request); $async.Future<$3.ProfileResponse> updateProfile( $grpc.ServiceCall call, $4.ProfileEntity request); $async.Future<$3.MultiProfilesResponse> getAllProfiles( $grpc.ServiceCall call, $1.Empty request); $async.Future<$3.ProfileResponse> getActiveProfile( $grpc.ServiceCall call, $1.Empty request); $async.Future<$1.Response> setActiveProfile( $grpc.ServiceCall call, $3.ProfileRequest request); $async.Future<$3.ProfileResponse> addProfile( $grpc.ServiceCall call, $3.AddProfileRequest request); $async.Future<$1.Response> deleteProfile( $grpc.ServiceCall call, $3.ProfileRequest request); } ================================================ FILE: lib/hiddifycore/generated/v2/profile/profile_service.pbjson.dart ================================================ /// // Generated code. Do not modify. // source: v2/profile/profile_service.proto // // @dart = 2.12 // ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,deprecated_member_use_from_same_package,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name import 'dart:core' as $core; import 'dart:convert' as $convert; import 'dart:typed_data' as $typed_data; @$core.Deprecated('Use profileRequestDescriptor instead') const ProfileRequest$json = const { '1': 'ProfileRequest', '2': const [ const {'1': 'id', '3': 1, '4': 1, '5': 9, '10': 'id'}, const {'1': 'name', '3': 2, '4': 1, '5': 9, '10': 'name'}, const {'1': 'url', '3': 3, '4': 1, '5': 9, '10': 'url'}, ], }; /// Descriptor for `ProfileRequest`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List profileRequestDescriptor = $convert.base64Decode('Cg5Qcm9maWxlUmVxdWVzdBIOCgJpZBgBIAEoCVICaWQSEgoEbmFtZRgCIAEoCVIEbmFtZRIQCgN1cmwYAyABKAlSA3VybA=='); @$core.Deprecated('Use addProfileRequestDescriptor instead') const AddProfileRequest$json = const { '1': 'AddProfileRequest', '2': const [ const {'1': 'url', '3': 1, '4': 1, '5': 9, '10': 'url'}, const {'1': 'content', '3': 2, '4': 1, '5': 9, '10': 'content'}, const {'1': 'name', '3': 3, '4': 1, '5': 9, '10': 'name'}, const {'1': 'mark_as_active', '3': 4, '4': 1, '5': 8, '10': 'markAsActive'}, ], }; /// Descriptor for `AddProfileRequest`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List addProfileRequestDescriptor = $convert.base64Decode('ChFBZGRQcm9maWxlUmVxdWVzdBIQCgN1cmwYASABKAlSA3VybBIYCgdjb250ZW50GAIgASgJUgdjb250ZW50EhIKBG5hbWUYAyABKAlSBG5hbWUSJAoObWFya19hc19hY3RpdmUYBCABKAhSDG1hcmtBc0FjdGl2ZQ=='); @$core.Deprecated('Use profileResponseDescriptor instead') const ProfileResponse$json = const { '1': 'ProfileResponse', '2': const [ const {'1': 'profile', '3': 1, '4': 1, '5': 11, '6': '.profile.ProfileEntity', '10': 'profile'}, const {'1': 'response_code', '3': 2, '4': 1, '5': 14, '6': '.hcommon.ResponseCode', '10': 'responseCode'}, const {'1': 'message', '3': 3, '4': 1, '5': 9, '10': 'message'}, ], }; /// Descriptor for `ProfileResponse`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List profileResponseDescriptor = $convert.base64Decode('Cg9Qcm9maWxlUmVzcG9uc2USMAoHcHJvZmlsZRgBIAEoCzIWLnByb2ZpbGUuUHJvZmlsZUVudGl0eVIHcHJvZmlsZRI6Cg1yZXNwb25zZV9jb2RlGAIgASgOMhUuaGNvbW1vbi5SZXNwb25zZUNvZGVSDHJlc3BvbnNlQ29kZRIYCgdtZXNzYWdlGAMgASgJUgdtZXNzYWdl'); @$core.Deprecated('Use multiProfilesResponseDescriptor instead') const MultiProfilesResponse$json = const { '1': 'MultiProfilesResponse', '2': const [ const {'1': 'profiles', '3': 1, '4': 3, '5': 11, '6': '.profile.ProfileEntity', '10': 'profiles'}, const {'1': 'response_code', '3': 2, '4': 1, '5': 14, '6': '.hcommon.ResponseCode', '10': 'responseCode'}, const {'1': 'message', '3': 3, '4': 1, '5': 9, '10': 'message'}, ], }; /// Descriptor for `MultiProfilesResponse`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List multiProfilesResponseDescriptor = $convert.base64Decode('ChVNdWx0aVByb2ZpbGVzUmVzcG9uc2USMgoIcHJvZmlsZXMYASADKAsyFi5wcm9maWxlLlByb2ZpbGVFbnRpdHlSCHByb2ZpbGVzEjoKDXJlc3BvbnNlX2NvZGUYAiABKA4yFS5oY29tbW9uLlJlc3BvbnNlQ29kZVIMcmVzcG9uc2VDb2RlEhgKB21lc3NhZ2UYAyABKAlSB21lc3NhZ2U='); ================================================ FILE: lib/hiddifycore/hiddify_core_service.dart ================================================ import 'dart:async'; import 'dart:convert'; import 'dart:math'; import 'package:fpdart/fpdart.dart'; import 'package:grpc/grpc.dart'; import 'package:hiddify/core/directories/directories_provider.dart'; import 'package:hiddify/core/model/directories.dart'; import 'package:hiddify/core/notification/in_app_notification_controller.dart'; import 'package:hiddify/core/preferences/general_preferences.dart'; import 'package:hiddify/features/connection/model/connection_failure.dart'; import 'package:hiddify/features/settings/data/config_option_repository.dart'; import 'package:hiddify/hiddifycore/core_interface/core_interface.dart'; import 'package:hiddify/hiddifycore/generated/v2/hcommon/common.pb.dart'; import 'package:hiddify/hiddifycore/generated/v2/hcore/hcore.pb.dart'; import 'package:hiddify/hiddifycore/generated/v2/hcore/hcore_service.pbgrpc.dart'; import 'package:hiddify/hiddifycore/init_signal.dart'; import 'package:hiddify/singbox/model/singbox_config_option.dart'; import 'package:hiddify/features/log/model/log_level.dart' as config_log_level; import 'package:hiddify/singbox/model/core_status.dart'; import 'package:hiddify/singbox/model/warp_account.dart'; import 'package:hiddify/hiddifycore/core_interface/core_interface_wrapper_stub.dart' if (dart.library.io) 'package:hiddify/hiddifycore/core_interface/core_interface_wrapper.dart'; import 'package:hiddify/utils/custom_loggers.dart'; import 'package:hiddify/utils/platform_utils.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:loggy/loggy.dart' as loggyl; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:rxdart/rxdart.dart'; class HiddifyCoreService with InfraLogger { HiddifyCoreService(this.ref); final Ref ref; // CoreHiddifyCoreService() {} final core = getCoreInterface(); CoreStatus currentState = const CoreStatus.stopped(); final statusController = BehaviorSubject(); final logController = BehaviorSubject>(); final CallOptions? grpcOptions = null; //CallOptions(timeout: const Duration(milliseconds: 10000)); final Map subscriptions = {}; List latest = []; Future init() async { await setup() .mapLeft((e) { loggy.error(e); if (PlatformUtils.isIOS) return; statusController.add(const CoreStatus.stopped()); ref.read(inAppNotificationControllerProvider).showErrorToast(e); }) .map((_) { loggy.info("Hiddify-core setup done"); ref.read(coreRestartSignalProvider.notifier).restart(); }) .run(); } /// validates config by path and save it /// /// [path] is used to save validated config /// [tempPath] includes base config, possibly invalid /// [debug] indicates if debug mode (avoid in prod) TaskEither validateConfigByPath(String path, String tempPath, bool debug) { return TaskEither(() async { try { final response = await core.fgClient.parse(ParseRequest(tempPath: tempPath, configPath: path, debug: false)); if (response.responseCode != ResponseCode.OK) return left("${response.responseCode} ${response.message}"); } catch (e) { await setup().run(); final response = await core.fgClient.parse(ParseRequest(tempPath: tempPath, configPath: path, debug: false)); if (response.responseCode != ResponseCode.OK) return left("${response.responseCode} ${response.message}"); } return right(unit); }); } TaskEither generateFullConfigByPath(String path) { return TaskEither(() async { final response = await core.fgClient.parse(ParseRequest(configPath: path, debug: false)); if (response.responseCode != ResponseCode.OK) return left("${response.responseCode} ${response.message}"); return right(response.content); }); } TaskEither setup() { return TaskEither(() async { try { final directories = ref.read(appDirectoriesProvider).requireValue; final debug = ref.read(debugModeNotifierProvider); final setupResponse = await core.setup(directories, debug, 3); if (setupResponse.isNotEmpty) { return left(setupResponse); } await startListeningLogs("fg", core.fgClient); // await startListeningStatus("fg", core.fgClient); if (!core.isSingleChannel()) { await startListeningLogs("bg", core.bgClient); } statusController.add(currentState); await startListeningStatus("bg", core.bgClient); // ref.read(coreRestartSignalProvider.notifier).restart(); return right(unit); } catch (e) { return left(e.toString()); } }); } TaskEither changeOptions(SingboxConfigOption options) { return TaskEither(() async { loggy.debug("changing options"); // latestOptions = options; try { final res = await core.fgClient.changeHiddifySettings( ChangeHiddifySettingsRequest(hiddifySettingsJson: jsonEncode(options.toJson())), ); if (res.messageType != MessageType.EMPTY) return left("${res.messageType} ${res.message}"); await core.bgClient.changeHiddifySettings( ChangeHiddifySettingsRequest(hiddifySettingsJson: jsonEncode(options.toJson())), ); } on GrpcError catch (e) { if (e.code == StatusCode.unavailable) { loggy.debug("background core is not started yet! $e"); } else { rethrow; } } return right(unit); }); } TaskEither start(String path, String name, bool disableMemoryLimit) { return TaskEither(() async { statusController.add(currentState = const CoreStatus.starting()); loggy.debug("starting"); final background = await core.setupBackground(path, name); if (background != const CoreStatus.started()) { statusController.add(currentState = const CoreStatus.stopped()); return left(background.getCoreAlert() ?? const ConnectionFailure.unexpected("failed to start core")); } if (!core.isSingleChannel()) { await startListeningLogs("bg", core.bgClient); await startListeningStatus("bg", core.bgClient); } // if (latestOptions != null) { // await core.bgClient.changeHiddifySettings( // ChangeHiddifySettingsRequest( // hiddifySettingsJson: jsonEncode(latestOptions!.toJson()), // ), // ); // } // final content = await File(path).readAsString(); // loggy.debug("starting with content: $content"); try { final res = await core.bgClient.start( StartRequest( configPath: path, configName: name, // configContent: content, disableMemoryLimit: disableMemoryLimit, ), ); ref.read(coreRestartSignalProvider.notifier).restart(); if (res.messageType != MessageType.ALREADY_STARTED && res.messageType != MessageType.EMPTY) { final alert = res.message.contains("denied") ? CoreAlert.requestVPNPermission : CoreAlert.startFailed; currentState = CoreStatus.stopped( alert: alert, message: "failed to start core ${res.messageType} ${res.message}", ); statusController.add(currentState); return left( currentState.getCoreAlert() ?? ConnectionFailure.unexpected("failed to start core ${res.messageType} ${res.message}"), ); } } on GrpcError catch (e) { loggy.error("failed to start bg core: $e"); ref.read(coreRestartSignalProvider.notifier).restart(); if (e.code == StatusCode.unavailable) { return left(const ConnectionFailure.unexpected("background core is not started yet!")); } // throw InvalidConfig(e.message); // throw DioException.connectionError(requestOptions: RequestOptions(), reason: e.codeName, error: e); // throw DioException(requestOptions: RequestOptions(), error: e); return left(const ConnectionFailure.unexpected("failed to start background core")); } // if (res.messageType != MessageType.EMPTY) return left(res); return right(unit); }); } TaskEither stop() { return TaskEither(() async { loggy.debug("stopping"); var errMsg = ""; try { final res = await core.bgClient.stop(Empty()); } on GrpcError catch (e) { if (e.code == StatusCode.unknown && !(e.message?.contains("HTTP/2") ?? false)) { errMsg = e.message ?? "failed to stop core: $e"; loggy.error("failed to stop bg core: $e"); } } catch (e) { loggy.error("failed to stop bg core: $e"); // left("failed to stop core: $e"); } if (!await core.stop()) {} statusController.add(currentState = const CoreStatus.stopped()); if (errMsg.isNotEmpty) return left(errMsg); return right(unit); }); } TaskEither restart(String path, String name, bool disableMemoryLimit) { return TaskEither(() async { loggy.debug("restarting"); // if (!await core.restart(path, name)) { try { final res = await core.bgClient.restart( StartRequest(configPath: path, configName: name, disableMemoryLimit: disableMemoryLimit, delayStart: true), ); if (res.messageType != MessageType.EMPTY) return left("${res.messageType} ${res.message}"); } on GrpcError catch (e) { loggy.error("failed to restart bg core: $e"); if (e.code == StatusCode.unknown && !(e.message?.contains("HTTP/2 error") ?? false)) { return left("${e.message}"); } } return right(unit); // await stop().run(); // return await start(path, name, disableMemoryLimit).run(); // } // if (!core.isSingleChannel()) { // await startListeningStatus("bg", core.bgClient); // await startListeningLogs("bg", core.bgClient); // } // return right(unit); }); } TaskEither resetTunnel() { return TaskEither(() async { // only available on iOS (and macOS later) if (!PlatformUtils.isIOS) { throw UnimplementedError("reset tunnel function unavailable on platform"); } // loggy.debug("resetting tunnel"); final res = await core.resetTunnel(); if (res) { return right(unit); } return left("failed to reset tunnel"); }); } // Stream> watchGroups() async* { // loggy.debug("watching groups"); // yield* core.bgClient.outboundsInfo(Empty()).map((event) => event.items); // // res?.cancel(); // } Stream watchGroup() async* { loggy.debug("watching group"); // interrupt managed by core if (!core.isInitialized()) { loggy.debug("core is not initialized, returning empty group stream"); return; } try { yield* core.bgClient.outboundsInfo(Empty()).map((event) => event.items.isEmpty ? null : event.items.first); } catch (e) { loggy.error("error watching group: $e"); rethrow; } // //emitting first event immediately // yield* core.bgClient.outboundsInfo(Empty()).take(1).map((event) => event.items.isEmpty ? null : event.items.first); // //emitting other event after every 4 seconds(latest event) // yield* core.bgClient.outboundsInfo(Empty()).throttleTime(const Duration(seconds: 4), leading: false, trailing: true).map((event) => event.items.isEmpty ? null : event.items.first); } Stream> watchActiveGroups() async* { loggy.info("watching active groups"); if (!core.isInitialized()) { loggy.debug("core is not initialized, returning empty group stream"); return; } try { yield* core.bgClient .mainOutboundsInfo(Empty()) .map((event) { return latest = event.items; }) .startWith(latest); } catch (e) { loggy.error("error watching active groups: $e"); rethrow; } } // // Stream watchStatus() => _status; ResponseStream watchStats() { loggy.debug("watching stats"); try { return core.bgClient.getSystemInfoStream(Empty()); } catch (e) { loggy.error("error watching stats: $e"); rethrow; } } TaskEither selectOutbound(String groupTag, String outboundTag) { return TaskEither(() async { loggy.debug("selecting outbound"); try { final res = await core.bgClient.selectOutbound( SelectOutboundRequest(groupTag: groupTag, outboundTag: outboundTag), options: CallOptions(timeout: const Duration(seconds: 1)), ); if (res.code != ResponseCode.OK) return left("${res.code} ${res.message}"); return right(unit); } catch (e) { loggy.error("error selecting outbound: $e"); rethrow; } }); } TaskEither urlTest(String tag) { return TaskEither(() async { loggy.debug("url test"); try { final res = await core.bgClient.urlTest(UrlTestRequest(tag: tag)); if (res.code != ResponseCode.OK) return left("${res.code} ${res.message}"); return right(unit); } catch (e) { loggy.error("error in url test: $e"); rethrow; } }); } List logBuffer = []; // SingboxConfigOption? latestOptions; Stream> watchLogs(String path) async* { if (!core.isInitialized()) { loggy.debug("core is not initialized, returning empty log stream"); return; } await startListeningLogs("bg", core.bgClient); await startListeningLogs("fg", core.fgClient); try { yield* logController.stream; } catch (e) { loggy.error("error watching logs: $e"); rethrow; } // Stream> logStream(CoreClient coreClient) { // return coreClient.logListener(Empty()).asBroadcastStream().map((event) => [event.message]).onErrorResume((error, stackTrace) { // loggy.debug('Error in $coreClient: $error, retrying...'); // final delay = (currentState == const SingboxStatus.stopped()) ? 5 : 1; // return const Stream>.empty().delay(Duration(seconds: delay)).concatWith([logStream(coreClient)]); // }); // } // // Create streams for both fg and bg clients with retry logic // final fgLogStream = logStream(core.fgClient); // if (core.bgClient == core.fgClient) { // yield* fgLogStream; // return; // } // final bgLogStream = logStream(core.bgClient); // yield* MergeStream([bgLogStream, fgLogStream]); } TaskEither clearLogs() { return TaskEither(() async { loggy.debug("clearing logs"); logBuffer.clear(); // final res = await core.bgClient(Empty()); // if (res.code != ResponseCode.OK) return left("${res.code} ${res.message}"); return right(unit); }); } TaskEither generateWarpConfig({ required String licenseKey, required String previousAccountId, required String previousAccessToken, }) { return TaskEither(() async { loggy.debug("generating warp config"); final warpConfig = await core.fgClient.generateWarpConfig( GenerateWarpConfigRequest( licenseKey: licenseKey, accountId: previousAccountId, accessToken: previousAccessToken, ), ); // if (warpConfig.code != ResponseCode.OK) return left("${warpConfig.code} ${warpConfig.message}"); final WarpResponse warp = ( log: warpConfig.log, accountId: warpConfig.account.accountId, accessToken: warpConfig.account.accessToken, wireguardConfig: jsonEncode(warpConfig.config.toProto3Json()), ); return right(warp); }); } Stream watchStatus() async* { await startListeningStatus("bg", core.bgClient); yield* statusController.stream; // .endWith(const CoreStatus.stopped()); } Future startListeningStatus(String key, CoreClient cc) async { await listenSingle( "${key}StatusListener", () => cc .coreInfoListener(Empty(), options: grpcOptions) .doOnCancel(() { loggy.error("status", "Canceld"); if (currentState == const CoreStatus.started()) currentState = const CoreStatus.stopped(); }) .doOnData((event) { loggy.debug("status", event); if (currentState == const CoreStatus.started()) currentState = const CoreStatus.stopped(); }) .doOnDone(() { loggy.error("status", "done"); if (currentState == const CoreStatus.started()) currentState = const CoreStatus.stopped(); }) .endWith(CoreInfoResponse(coreState: CoreStates.STOPPED)) .map((event) { currentState = CoreStatus.fromCoreInfo(event); statusController.add(currentState); return currentState; }), // .endWith(const CoreStatus.stopped()) onError: (error) { loggy.error("Stream error in ${key}StatusListener: $error"); // currentState = const CoreStatus.stopped(); // statusController.add(currentState); // startListeningStatus(key, cc); }, ); } Future startListeningLogs(String key, CoreClient cc) async { final logLevel = ref.read(ConfigOptions.logLevel); final coreLogLevel = getCoreLogLevel(logLevel); final listenKey = "${key}LogListener"; // await stopListenSingle(listenKey); await listenSingle(listenKey, () { return cc.logListener(LogRequest(level: coreLogLevel), options: grpcOptions).map((event) { // Handle incoming event logBuffer.add(event); if (logBuffer.length > 300) { logBuffer.removeAt(0); } logController.add(logBuffer); // loggy.log(getLogLevel(event.level), event.message); event.message.split('\n').forEach((line) { loggy.log(getLogLevel(event.level), line); }); return event; }); }); } Future stopListenSingle(String key) async { // Collect keys to remove first final keysToRemove = subscriptions.entries .where((entry) => entry.key.startsWith(key)) .map((entry) => entry.key) .toList(); // Cancel and remove for (final k in keysToRemove) { final sub = subscriptions[k]; await sub?.cancel(); // cancel the subscription subscriptions.remove(k); } } Future?> listenSingle( String key, Stream Function() stream, { Function(dynamic error)? onError, }) async { if (subscriptions.containsKey(key)) { // return subscriptions[key] as StreamSubscription?; await stopListenSingle(key); } subscriptions[key] = null; subscriptions[key] = stream().listen( (event) { // loggy.debug(event); }, cancelOnError: true, onError: (error) { loggy.log(loggyl.LogLevel.error, 'Stream error: $error'); onError?.call(error); subscriptions[key]?.cancel(); subscriptions.remove(key); }, ); return subscriptions[key] as StreamSubscription?; } loggyl.LogLevel getLogLevel(LogLevel level) { return switch (level) { LogLevel.DEBUG => loggyl.LogLevel.debug, LogLevel.INFO => loggyl.LogLevel.info, LogLevel.WARNING => loggyl.LogLevel.warning, LogLevel.ERROR => loggyl.LogLevel.error, LogLevel.FATAL => loggyl.LogLevel.error, _ => loggyl.LogLevel.info, // Default case }; } LogLevel getCoreLogLevel(config_log_level.LogLevel level) { return switch (level) { config_log_level.LogLevel.trace => LogLevel.TRACE, config_log_level.LogLevel.debug => LogLevel.DEBUG, config_log_level.LogLevel.info => LogLevel.INFO, config_log_level.LogLevel.warn => LogLevel.WARNING, config_log_level.LogLevel.error => LogLevel.ERROR, config_log_level.LogLevel.fatal => LogLevel.FATAL, config_log_level.LogLevel.panic => LogLevel.FATAL, _ => LogLevel.INFO, // Default case }; } Future closeFront() async { if (!core.isInitialized()) { return; } if (!core.isSingleChannel()) { await stopListenSingle("fg"); await stopListenSingle("bg"); try { await core.fgClient.close(CloseRequest(mode: SetupMode.GRPC_NORMAL_INSECURE)); } catch (e) {} try { await core.fgClient.close(CloseRequest(mode: SetupMode.GRPC_NORMAL)); } catch (e) {} } } } ================================================ FILE: lib/hiddifycore/hiddify_core_service_provider.dart ================================================ import 'package:hiddify/core/directories/directories_provider.dart'; import 'package:hiddify/core/notification/in_app_notification_controller.dart'; import 'package:hiddify/core/preferences/general_preferences.dart'; import 'package:hiddify/hiddifycore/hiddify_core_service.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'hiddify_core_service_provider.g.dart'; @Riverpod(keepAlive: true, dependencies: [AppDirectories, DebugModeNotifier, inAppNotificationController]) HiddifyCoreService hiddifyCoreService(Ref ref) { return HiddifyCoreService(ref); } ================================================ FILE: lib/hiddifycore/init_signal.dart ================================================ import 'package:hiddify/core/directories/directories_provider.dart'; import 'package:hiddify/core/notification/in_app_notification_controller.dart'; import 'package:hiddify/core/preferences/general_preferences.dart'; import 'package:hiddify/hiddifycore/hiddify_core_service.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'init_signal.g.dart'; @riverpod class CoreRestartSignal extends _$CoreRestartSignal { @override int build() => 0; void restart() => state++; } ================================================ FILE: lib/main.dart ================================================ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:hiddify/bootstrap.dart'; import 'package:hiddify/core/model/environment.dart'; Future main() async { final widgetsBinding = WidgetsFlutterBinding.ensureInitialized(); // final widgetsBinding = SentryWidgetsFlutterBinding.ensureInitialized(); // debugPaintSizeEnabled = true; SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); SystemChrome.setSystemUIOverlayStyle( const SystemUiOverlayStyle(statusBarColor: Colors.transparent, systemNavigationBarColor: Colors.transparent), ); return await lazyBootstrap(widgetsBinding, Environment.dev); } ================================================ FILE: lib/main_prod.dart ================================================ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:hiddify/bootstrap.dart'; import 'package:hiddify/core/model/environment.dart'; Future main() async { final widgetsBinding = WidgetsFlutterBinding.ensureInitialized(); SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); SystemChrome.setSystemUIOverlayStyle( const SystemUiOverlayStyle(statusBarColor: Colors.transparent, systemNavigationBarColor: Colors.transparent), ); return await lazyBootstrap(widgetsBinding, Environment.prod); } ================================================ FILE: lib/riverpod_observer.dart ================================================ import 'dart:developer'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class RiverpodObserver extends ProviderObserver { @override void didAddProvider(ProviderBase provider, Object? value, ProviderContainer container) { log('didAddProvider : ${provider.name ?? provider.runtimeType} : $value'); } @override void didDisposeProvider(ProviderBase provider, ProviderContainer container) { log('didDisposeProvider : ${provider.name ?? provider.runtimeType}'); } @override void didUpdateProvider( ProviderBase provider, Object? previousValue, Object? newValue, ProviderContainer container, ) { log('didUpdateProvider : ${provider.name ?? provider.runtimeType} : $previousValue -> $newValue'); } } ================================================ FILE: lib/singbox/model/core_status.dart ================================================ import 'package:dartx/dartx.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:hiddify/features/connection/model/connection_failure.dart'; import 'package:hiddify/hiddifycore/generated/v2/hcore/hcore.pb.dart'; part 'core_status.freezed.dart'; @freezed sealed class CoreStatus with _$CoreStatus { const CoreStatus._(); const factory CoreStatus.stopped({CoreAlert? alert, String? message}) = CoreStopped; const factory CoreStatus.starting() = CoreStarting; const factory CoreStatus.started() = CoreStarted; const factory CoreStatus.stopping() = CoreStopping; factory CoreStatus.fromEvent(dynamic event) { event = event as Map?; switch (event?["status"]) { case "Stopped": final alertstr = event?["alert"] as String?; var msgStr = event?["message"] as String?; var alert = CoreAlert.values.firstOrNullWhere((e) => alertstr?.toLowerCase() == e.name.toLowerCase()); if ((alert == null) && (alertstr ?? "") != "") { msgStr = ((msgStr ?? "") != "") ? "$alertstr: $msgStr" : alertstr; alert = CoreAlert.unknown; } return CoreStatus.stopped(alert: alert, message: msgStr); case "Starting": return const CoreStarting(); case "Started": return const CoreStarted(); case "Stopping": return const CoreStopping(); default: throw Exception("unexpected status [$event]"); } } factory CoreStatus.fromCoreInfo(CoreInfoResponse event) { switch (event.coreState) { case CoreStates.STOPPED: final CoreAlert? alert = switch (event.messageType) { MessageType.EMPTY => null, MessageType.ERROR_READING_CONFIG => CoreAlert.emptyConfiguration, MessageType.START_COMMAND_SERVER => CoreAlert.startCommandServer, MessageType.CREATE_SERVICE => CoreAlert.createService, MessageType.START_SERVICE => CoreAlert.startService, MessageType.UNEXPECTED_ERROR => CoreAlert.startService, MessageType.INSTANCE_NOT_STOPPED => CoreAlert.startService, MessageType.INSTANCE_NOT_STARTED => CoreAlert.startService, MessageType.INSTANCE_NOT_FOUND => CoreAlert.startService, MessageType.ERROR_PARSING_CONFIG => CoreAlert.emptyConfiguration, MessageType.ERROR_BUILDING_CONFIG => CoreAlert.emptyConfiguration, MessageType.EMPTY_CONFIGURATION => CoreAlert.emptyConfiguration, MessageType.ALREADY_STOPPED => CoreAlert.createService, MessageType.ALREADY_STARTED => CoreAlert.startService, // MessageType.REQUEST_VPN_PERMISSION => SingboxAlert.requestVPNPermission, // MessageType.REQUEST_NOTIFICATION_PERMISSION => SingboxAlert.requestNotificationPermission, _ => CoreAlert.emptyConfiguration, // Default case }; return CoreStatus.stopped(alert: alert, message: event.message); case CoreStates.STARTING: return const CoreStarting(); case CoreStates.STARTED: return const CoreStarted(); case CoreStates.STOPPING: return const CoreStopping(); default: throw Exception("unexpected status [$event]"); } } ConnectionFailure? getCoreAlert() { return switch (this) { CoreStopped(alert: final alert, message: final message) when alert != null => switch (alert) { CoreAlert.emptyConfiguration => ConnectionFailure.invalidConfig(message), CoreAlert.requestNotificationPermission => ConnectionFailure.missingNotificationPermission(message), CoreAlert.requestVPNPermission => ConnectionFailure.missingVpnPermission(message), CoreAlert.startCommandServer || CoreAlert.createService || CoreAlert.startService || CoreAlert.alreadyStarted || CoreAlert.startFailed => ConnectionFailure.unexpected("${alert.name} - $message"), _ => null, }, _ => null, }; } } enum CoreAlert { requestVPNPermission, requestNotificationPermission, emptyConfiguration, startCommandServer, createService, startService, alreadyStarted, startFailed, unknown, } ================================================ FILE: lib/singbox/model/singbox_config_enum.dart ================================================ import 'dart:io'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/utils/platform_utils.dart'; @JsonEnum(valueField: 'key') enum ServiceMode { proxy("proxy"), systemProxy("system-proxy"), tun("vpn") // tunService("vpn-service") ; const ServiceMode(this.key); final String key; static ServiceMode get defaultMode => PlatformUtils.isDesktop ? systemProxy : tun; /// supported service mode based on platform, use this instead of [values] in UI static List get choices { if (Platform.isWindows || Platform.isLinux) { return values; } else if (Platform.isMacOS) { return [proxy, systemProxy, tun]; } // mobile return [proxy, tun]; } // bool get isExperimental => switch (this) { // tun => PlatformUtils.isDesktop, // tunService => PlatformUtils.isDesktop, // _ => false, // }; String present(TranslationsEn t) => switch (this) { proxy => t.pages.settings.inbound.serviceModes.proxy, systemProxy => t.pages.settings.inbound.serviceModes.systemProxy, tun => t.pages.settings.inbound.serviceModes.tun, // tunService => t.pages.settings.inbound.serviceModes.tunService, }; String presentShort(TranslationsEn t) => switch (this) { proxy => t.pages.settings.inbound.shortServiceModes.proxy, systemProxy => t.pages.settings.inbound.shortServiceModes.systemProxy, tun => t.pages.settings.inbound.shortServiceModes.tun, // tunService => t.pages.settings.inbound.shortServiceModes.tunService, }; } @JsonEnum(valueField: 'key') enum BalancerStrategy { roundRobin("round-robin"), consistentHash("consistent-hashing"), stickySession("sticky-sessions"); const BalancerStrategy(this.key); final String key; String present(TranslationsEn t) => switch (this) { roundRobin => t.pages.settings.routing.balancerStrategy.roundRobin, consistentHash => t.pages.settings.routing.balancerStrategy.consistentHash, stickySession => t.pages.settings.routing.balancerStrategy.stickySession, }; } @JsonEnum(valueField: 'key') enum IPv6Mode { disable("ipv4_only"), enable("prefer_ipv4"), prefer("prefer_ipv6"), only("ipv6_only"); const IPv6Mode(this.key); final String key; String present(TranslationsEn t) => switch (this) { disable => t.pages.settings.routing.ipv6Modes.disable, enable => t.pages.settings.routing.ipv6Modes.enable, prefer => t.pages.settings.routing.ipv6Modes.prefer, only => t.pages.settings.routing.ipv6Modes.only, }; } @JsonEnum(valueField: 'key') enum DomainStrategy { auto(""), preferIpv6("prefer_ipv6"), preferIpv4("prefer_ipv4"), ipv4Only("ipv4_only"), ipv6Only("ipv6_only"); const DomainStrategy(this.key); final String key; String present(TranslationsEn t) => switch (this) { auto => t.pages.settings.dns.domainStrategy.auto, preferIpv6 => t.pages.settings.dns.domainStrategy.preferIpv6, preferIpv4 => t.pages.settings.dns.domainStrategy.preferIpv4, ipv4Only => t.pages.settings.dns.domainStrategy.ipv4Only, ipv6Only => t.pages.settings.dns.domainStrategy.ipv6Only, }; } enum TunImplementation { mixed, system, gvisor; String present(TranslationsEn t) => switch (this) { mixed => t.pages.settings.inbound.tunImplementations.mixed, system => t.pages.settings.inbound.tunImplementations.system, gvisor => t.pages.settings.inbound.tunImplementations.gvisor, }; } enum MuxProtocol { h2mux, smux, yamux } @JsonEnum(valueField: 'key') enum WarpDetourMode { proxyOverWarp("proxy_over_warp"), warpOverProxy("warp_over_proxy"); const WarpDetourMode(this.key); final String key; String present(TranslationsEn t) => switch (this) { proxyOverWarp => t.pages.settings.warp.detourModes.proxyOverWarp, warpOverProxy => t.pages.settings.warp.detourModes.warpOverProxy, }; String presentExplain(TranslationsEn t) => switch (this) { proxyOverWarp => t.pages.settings.warp.detourModes.proxyOverWarpExplain, warpOverProxy => t.pages.settings.warp.detourModes.warpOverProxyExplain, }; } ================================================ FILE: lib/singbox/model/singbox_config_option.dart ================================================ import 'dart:convert'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:hiddify/core/model/optional_range.dart'; import 'package:hiddify/core/utils/json_converters.dart'; import 'package:hiddify/features/log/model/log_level.dart'; import 'package:hiddify/singbox/model/singbox_config_enum.dart'; import 'package:hiddify/singbox/model/singbox_rule.dart'; part 'singbox_config_option.freezed.dart'; part 'singbox_config_option.g.dart'; @freezed class SingboxConfigOption with _$SingboxConfigOption { const SingboxConfigOption._(); @JsonSerializable(fieldRename: FieldRename.kebab) const factory SingboxConfigOption({ required String region, required BalancerStrategy balancerStrategy, required bool blockAds, required bool useXrayCoreWhenPossible, required bool executeConfigAsIs, required LogLevel logLevel, required bool resolveDestination, required IPv6Mode ipv6Mode, required String remoteDnsAddress, required DomainStrategy remoteDnsDomainStrategy, required String directDnsAddress, required DomainStrategy directDnsDomainStrategy, required int mixedPort, required int tproxyPort, required int directPort, required int redirectPort, required TunImplementation tunImplementation, required int mtu, required bool strictRoute, required String connectionTestUrl, @IntervalInSecondsConverter() required Duration urlTestInterval, required bool enableClashApi, required int clashApiPort, required bool enableTun, // required bool enableTunService, required bool setSystemProxy, required bool bypassLan, required bool allowConnectionFromLan, required bool enableFakeDns, // required bool enableDnsRouting, required bool independentDnsCache, required List rules, // required SingboxMuxOption mux, required SingboxTlsTricks tlsTricks, required SingboxWarpOption warp, required SingboxWarpOption warp2, }) = _SingboxConfigOption; String format() { const encoder = JsonEncoder.withIndent(' '); return encoder.convert(toJson()); } factory SingboxConfigOption.fromJson(Map json) => _$SingboxConfigOptionFromJson(json); } @freezed class SingboxWarpOption with _$SingboxWarpOption { @JsonSerializable(fieldRename: FieldRename.kebab) const factory SingboxWarpOption({ required bool enable, required WarpDetourMode mode, required String wireguardConfig, required String licenseKey, required String accountId, required String accessToken, required String cleanIp, required int cleanPort, @OptionalRangeJsonConverter() required OptionalRange noise, @OptionalRangeJsonConverter() required OptionalRange noiseSize, @OptionalRangeJsonConverter() required OptionalRange noiseDelay, @OptionalRangeJsonConverter() required String noiseMode, }) = _SingboxWarpOption; factory SingboxWarpOption.fromJson(Map json) => _$SingboxWarpOptionFromJson(json); } // @freezed // class SingboxMuxOption with _$SingboxMuxOption { // @JsonSerializable(fieldRename: FieldRename.kebab) // const factory SingboxMuxOption({ // required bool enable, // required bool padding, // required int maxStreams, // required MuxProtocol protocol, // }) = _SingboxMuxOption; // factory SingboxMuxOption.fromJson(Map json) => _$SingboxMuxOptionFromJson(json); // } @freezed class SingboxTlsTricks with _$SingboxTlsTricks { @JsonSerializable(fieldRename: FieldRename.kebab) const factory SingboxTlsTricks({ required bool enableFragment, @OptionalRangeJsonConverter() required OptionalRange fragmentSize, @OptionalRangeJsonConverter() required OptionalRange fragmentSleep, required bool mixedSniCase, required bool enablePadding, @OptionalRangeJsonConverter() required OptionalRange paddingSize, }) = _SingboxTlsTricks; factory SingboxTlsTricks.fromJson(Map json) => _$SingboxTlsTricksFromJson(json); } ================================================ FILE: lib/singbox/model/singbox_outbound.dart ================================================ import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:hiddify/singbox/model/singbox_proxy_type.dart'; part 'singbox_outbound.freezed.dart'; part 'singbox_outbound.g.dart'; @freezed class SingboxOutboundGroup with _$SingboxOutboundGroup { @JsonSerializable(fieldRename: FieldRename.kebab) const factory SingboxOutboundGroup({ required String tag, @JsonKey(fromJson: _typeFromJson) required ProxyType type, required String selected, @Default([]) List items, }) = _SingboxOutboundGroup; factory SingboxOutboundGroup.fromJson(Map json) => _$SingboxOutboundGroupFromJson(json); // factory SingboxOutboundGroup.fromGrpc(OutboundGroup og) => _$SingboxOutboundGroup(tag=og.tag, type=_keyMap[og.type]!, selected=og.selected, items=og.items.map((e) => SingboxOutboundGroupItem.fromGRPC(e)).toList()); } @freezed class SingboxOutboundGroupItem with _$SingboxOutboundGroupItem { const SingboxOutboundGroupItem._(); @JsonSerializable(fieldRename: FieldRename.kebab) const factory SingboxOutboundGroupItem({required String tag, required String type, required int urlTestDelay}) = _SingboxOutboundGroupItem; factory SingboxOutboundGroupItem.fromJson(Map json) => _$SingboxOutboundGroupItemFromJson(json); } final Map _keyMap = Map.fromEntries(ProxyType.values.map((e) => MapEntry(e.key, e))); ProxyType _typeFromJson(dynamic type) => ProxyType.fromJson(type); ================================================ FILE: lib/singbox/model/singbox_proxy_type.dart ================================================ enum ProxyType { direct("Direct"), block("Block"), dns("DNS"), socks("SOCKS"), http("HTTP"), shadowsocks("Shadowsocks"), vmess("VMess"), trojan("Trojan"), naive("Naive"), wireguard("WireGuard"), awg("AWG"), hysteria("Hysteria"), tor("Tor"), ssh("SSH"), shadowtls("ShadowTLS"), shadowsocksr("ShadowsocksR"), vless("VLESS"), tuic("TUIC"), hysteria2("Hysteria2"), mieru("Mieru"), selector("Selector"), urltest("URLTest"), balancer("Balancer"), warp("Warp"), xvless("xVLESS"), xvmess("xVMess"), xtrojan("xTrojan"), xfreedom("xFragment"), xshadowsocks("xShadowsocks"), xsocks("xSocks"), invalid("Invalid"), unknown("Unknown"); const ProxyType(this.label); final String label; String get key => name; static List groupValues = [selector, urltest, balancer]; bool get isGroup => ProxyType.groupValues.contains(this); static final Map _keyMap = Map.fromEntries(ProxyType.values.map((e) => MapEntry(e.key, e))); static ProxyType fromJson(dynamic type) => _keyMap[(type as String?)?.toLowerCase()] ?? ProxyType.unknown; } ================================================ FILE: lib/singbox/model/singbox_rule.dart ================================================ import 'package:freezed_annotation/freezed_annotation.dart'; part 'singbox_rule.freezed.dart'; part 'singbox_rule.g.dart'; @freezed class SingboxRule with _$SingboxRule { const SingboxRule._(); @JsonSerializable(fieldRename: FieldRename.kebab) const factory SingboxRule({ String? ruleSetUrl, String? domains, String? ip, String? port, String? protocol, @Default(RuleNetwork.tcpAndUdp) RuleNetwork network, @Default(RuleOutbound.proxy) RuleOutbound outbound, }) = _SingboxRule; factory SingboxRule.fromJson(Map json) => _$SingboxRuleFromJson(json); } enum RuleOutbound { proxy, bypass, block } @JsonEnum(valueField: 'key') enum RuleNetwork { tcpAndUdp(""), tcp("tcp"), udp("udp"); const RuleNetwork(this.key); final String? key; } ================================================ FILE: lib/singbox/model/singbox_stats.dart ================================================ import 'package:freezed_annotation/freezed_annotation.dart'; part 'singbox_stats.freezed.dart'; part 'singbox_stats.g.dart'; @freezed class SingboxStats with _$SingboxStats { const SingboxStats._(); @JsonSerializable(fieldRename: FieldRename.kebab) const factory SingboxStats({ required int connectionsIn, required int connectionsOut, required int uplink, required int downlink, required int uplinkTotal, required int downlinkTotal, }) = _SingboxStats; factory SingboxStats.fromJson(Map json) => _$SingboxStatsFromJson(json); } ================================================ FILE: lib/singbox/model/warp_account.dart ================================================ import 'dart:convert'; typedef WarpResponse = ({String log, String accountId, String accessToken, String wireguardConfig}); WarpResponse warpFromJson(dynamic json) { if (json case { "account-id": final String newAccountId, "access-token": final String newAccessToken, "log": final String log, "config": final Map wireguardConfig, }) { return ( log: log, accountId: newAccountId, accessToken: newAccessToken, wireguardConfig: jsonEncode(wireguardConfig), ); } throw Exception("invalid response"); } ================================================ FILE: lib/utils/alerts.dart ================================================ import 'package:fluentui_system_icons/fluentui_system_icons.dart'; import 'package:flutter/material.dart'; import 'package:toastification/toastification.dart'; enum AlertType { info, error, success; ToastificationType get _toastificationType => switch (this) { success => ToastificationType.success, error => ToastificationType.error, info => ToastificationType.info, }; } class CustomToast extends StatelessWidget { const CustomToast(this.message, {this.type = AlertType.info, this.icon, this.duration = const Duration(seconds: 3)}); const CustomToast.error(this.message, {this.duration = const Duration(seconds: 5)}) : type = AlertType.error, icon = FluentIcons.error_circle_24_regular; const CustomToast.success(this.message, {this.duration = const Duration(seconds: 3)}) : type = AlertType.success, icon = FluentIcons.checkmark_24_regular; final String message; final AlertType type; final IconData? icon; final Duration duration; @override Widget build(BuildContext context) { final scheme = Theme.of(context).colorScheme; final color = switch (type) { AlertType.info => null, AlertType.error => scheme.error, AlertType.success => scheme.tertiary, }; return Container( decoration: BoxDecoration( borderRadius: const BorderRadius.all(Radius.circular(4)), color: Theme.of(context).colorScheme.surface, ), padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), child: Row( mainAxisSize: MainAxisSize.min, children: [ if (icon != null) ...[Icon(icon, color: color), const SizedBox(width: 8)], Flexible(child: Text(message)), ], ), ); } void show(BuildContext context) { toastification.show( context: context, title: Text(message), type: type._toastificationType, alignment: Alignment.bottomLeft, autoCloseDuration: duration, style: ToastificationStyle.fillColored, pauseOnHover: true, showProgressBar: false, dragToClose: true, closeOnClick: true, closeButtonShowType: CloseButtonShowType.onHover, ); } } ================================================ FILE: lib/utils/async_mutation.dart ================================================ // ignore_for_file: unreachable_switch_case import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; part 'async_mutation.freezed.dart'; // TODO: test and improve @freezed class AsyncMutation with _$AsyncMutation { const AsyncMutation._(); const factory AsyncMutation.idle() = Idle; const factory AsyncMutation.inProgress() = InProgress; const factory AsyncMutation.fail(Object error, StackTrace stackTrace) = Fail; const factory AsyncMutation.success() = Success; bool get isInProgress => this is InProgress; } /// temporary(and hacky) way to manage async mutations ({AsyncMutation state, ValueChanged> setFuture, ValueChanged setOnFailure}) useMutation({void Function(Object error)? initialOnFailure, void Function()? initialOnSuccess}) { final mutationUpdate = useState?>(null); final mutationState = useFuture(mutationUpdate.value); final failureCallBack = useValueNotifier(initialOnFailure); final successCallBack = useValueNotifier(initialOnSuccess); final mapped = useMemoized(() { return switch (mutationState.connectionState) { ConnectionState.none => const Idle(), ConnectionState.waiting => const InProgress(), _ => mutationState.hasError ? Fail(mutationState.error!, mutationState.stackTrace!) : const Success(), }; }, [mutationState]); // one-of callback in failure useMemoized(() { if (mapped case Fail(:final error)) { // if callback tries to build widget(show snackbar for example) this will prevent exceptions WidgetsBinding.instance.addPostFrameCallback((_) => failureCallBack.value?.call(error)); } if (mapped case Success()) { WidgetsBinding.instance.addPostFrameCallback((_) => successCallBack.value?.call()); } }, [mapped, failureCallBack.value, successCallBack.value]); return ( state: mapped, setFuture: (future) => mutationUpdate.value = future, setOnFailure: (onFailure) => failureCallBack.value = onFailure, ); } ================================================ FILE: lib/utils/bottom_sheet_page.dart ================================================ import 'package:flutter/material.dart'; class BottomSheetPage extends Page { const BottomSheetPage({super.key, super.name, required this.builder, this.fixed = false}); final Widget Function(ScrollController? controller) builder; final bool fixed; @override Route createRoute(BuildContext context) { return ModalBottomSheetRoute( settings: this, isScrollControlled: true, useSafeArea: true, showDragHandle: true, builder: (_) { if (!fixed) { return DraggableScrollableSheet(expand: false, builder: (_, scrollController) => builder(scrollController)); } return builder(null); }, ); } } ================================================ FILE: lib/utils/callback_debouncer.dart ================================================ import 'dart:async'; import 'package:flutter/foundation.dart'; class CallbackDebouncer { CallbackDebouncer(this._delay); final Duration _delay; Timer? _timer; /// Calls the given [callback] after the given duration has passed. void call(VoidCallback callback) { if (_delay == Duration.zero) { callback(); } else { _timer?.cancel(); _timer = Timer(_delay, callback); } } /// Stops any running timers and disposes this instance. void dispose() { _timer?.cancel(); } } ================================================ FILE: lib/utils/custom_loggers.dart ================================================ import 'package:loggy/loggy.dart'; /// application layer logger /// /// used in notifiers and controllers mixin AppLogger implements LoggyType { @override Loggy get loggy => Loggy('$runtimeType'); } /// presentation layer logger /// /// used in widgets and ui mixin PresLogger implements LoggyType { @override Loggy get loggy => Loggy('$runtimeType'); } /// data layer logger /// /// used in Repositories, DAOs, Services mixin InfraLogger implements LoggyType { @override Loggy get loggy => Loggy('$runtimeType'); } abstract class LoggerMixin { LoggerMixin(this.loggy); final Loggy loggy; } ================================================ FILE: lib/utils/custom_text_form_field.dart ================================================ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hiddify/utils/text_utils.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class CustomTextFormField extends HookConsumerWidget { const CustomTextFormField({ super.key, this.onChanged, this.validator, this.controller, this.inputFormatters, this.initialValue = '', this.suffixIcon, this.label, this.hint, this.maxLines, this.isDense = false, this.autoValidate = false, this.autoCorrect = false, }); final ValueChanged? onChanged; final String? Function(String? value)? validator; final TextEditingController? controller; final List? inputFormatters; final String initialValue; final Widget? suffixIcon; final String? label; final String? hint; final int? maxLines; final bool isDense; final bool autoValidate; final bool autoCorrect; @override Widget build(BuildContext context, WidgetRef ref) { final textController = controller ?? useTextEditingController(text: initialValue); final effectiveConstraints = isDense ? const BoxConstraints(maxHeight: 56) : null; final effectiveBorder = isDense ? OutlineInputBorder(borderRadius: BorderRadius.circular(36), borderSide: BorderSide.none) : null; return TextFormField( controller: textController, textCapitalization: TextCapitalization.sentences, maxLines: maxLines, onChanged: onChanged, textDirection: textController.textDirection, decoration: InputDecoration( isDense: true, label: label != null ? Text(label!) : null, hintText: hint, hintStyle: Theme.of(context).textTheme.bodySmall, constraints: effectiveConstraints, suffixIcon: suffixIcon, border: effectiveBorder, enabledBorder: effectiveBorder, errorBorder: effectiveBorder, focusedBorder: effectiveBorder, focusedErrorBorder: effectiveBorder, ), validator: validator, textInputAction: TextInputAction.next, inputFormatters: inputFormatters, autovalidateMode: autoValidate ? AutovalidateMode.always : AutovalidateMode.disabled, autocorrect: autoCorrect, ); } } ================================================ FILE: lib/utils/date_time_formatter.dart ================================================ import 'package:intl/intl.dart'; extension DateTimeFormatter on DateTime { String format() { return DateFormat.yMMMd().add_Hm().format(this); } } ================================================ FILE: lib/utils/link_parsers.dart ================================================ import 'dart:convert'; import 'package:hiddify/utils/validators.dart'; typedef ProfileLink = ({String url, String name}); // TODO: test and improve abstract class LinkParser { static String generateSubShareLink(String url, [String? name]) { final uri = Uri.tryParse(url); if (uri == null) return ''; final modifiedUri = Uri( scheme: uri.scheme, host: uri.host, path: uri.path, query: uri.query, fragment: name ?? uri.fragment, ); // return 'hiddify://import/$modifiedUri'; return '$modifiedUri'; } // protocols schemas static const protocols = ['hiddify', 'v2ray', 'v2rayn', 'v2rayng', 'clash', 'clashmeta', 'sing-box']; static ProfileLink? parse(String link) { return simple(link) ?? deep(link); } static ProfileLink? simple(String link) { if (!isUrl(link)) return null; final uri = Uri.parse(link.trim()); return (url: uri.toString(), name: uri.queryParameters['name'] ?? ''); } static ProfileLink? deep(String link) { final uri = Uri.tryParse(link.trim()); if (uri == null || !uri.hasScheme || !uri.hasAuthority) return null; final queryParams = uri.queryParameters; switch (uri.scheme) { case 'hiddify': if (queryParams.containsKey('url')) { return (url: queryParams['url']!, name: queryParams['name'] ?? ''); } else { return (url: uri.path.substring(1) + (uri.hasQuery ? "?${uri.query}" : ""), name: uri.fragment); } case 'v2ray' || 'v2rayn' || 'v2rayng' || 'clash' || 'clashmeta' || 'sing-box': return queryParams.containsKey('url') ? (url: queryParams['url']!, name: queryParams['name'] ?? '') : null; default: return null; } } } String safeDecodeBase64(String str) { try { return utf8.decode(base64Decode(str)); } catch (e) { return str; } } ================================================ FILE: lib/utils/mutation_state.dart ================================================ import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:hiddify/core/model/failures.dart'; part 'mutation_state.freezed.dart'; // TODO: remove @freezed class MutationState with _$MutationState { const MutationState._(); const factory MutationState.initial() = MutationInitial; const factory MutationState.inProgress() = MutationInProgress; const factory MutationState.failure(Failure failure) = MutationFailure; const factory MutationState.success() = MutationSuccess; bool get isInProgress => this is MutationInProgress; } ================================================ FILE: lib/utils/number_formatters.dart ================================================ import 'package:humanizer/humanizer.dart'; extension ByteFormatter on int { String size() => bytes().toString(); static final _sizeOfFormat = InformationSizeFormat(permissibleValueUnits: {InformationUnit.gibibyte}); String sizeGB() => _sizeOfFormat.format(bytes()); String sizeOf(int total) => "${_sizeOfFormat.format(bytes())} / ${_sizeOfFormat.format(total.bytes())}"; bool isInfinitSize() => bytes().terabytes.toDouble() > 10; static final _rateFormat = InformationRateFormat(permissibleRateUnits: {RateUnit.second}); String speed() => _rateFormat.format(bytes().per(const Duration(seconds: 1))); } ================================================ FILE: lib/utils/placeholders.dart ================================================ import 'package:fluentui_system_icons/fluentui_system_icons.dart'; import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; // TODO: improve class SliverBodyPlaceholder extends HookConsumerWidget { const SliverBodyPlaceholder(this.children, {super.key}); final List children; @override Widget build(BuildContext context, WidgetRef ref) { return SliverFillRemaining( hasScrollBody: false, child: Column(mainAxisAlignment: MainAxisAlignment.center, children: children), ); } } class SliverLoadingBodyPlaceholder extends HookConsumerWidget { const SliverLoadingBodyPlaceholder({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { return const SliverFillRemaining( hasScrollBody: false, child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [CircularProgressIndicator()]), ); } } class SliverErrorBodyPlaceholder extends HookConsumerWidget { const SliverErrorBodyPlaceholder(this.msg, {super.key, this.icon = FluentIcons.error_circle_24_regular}); final String msg; final IconData? icon; @override Widget build(BuildContext context, WidgetRef ref) { return SliverFillRemaining( hasScrollBody: false, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ if (icon != null) ...[Icon(icon), const Gap(16)], Text(msg), ], ), ); } } ================================================ FILE: lib/utils/platform_utils.dart ================================================ import 'dart:io'; import 'package:flutter/foundation.dart'; abstract class PlatformUtils { static bool get isWindows => !kIsWeb && (Platform.isWindows); static bool get isDesktop => !kIsWeb && (Platform.isLinux || Platform.isWindows || Platform.isMacOS); static bool get isInAppStore => !kIsWeb && (Platform.isIOS); static bool get isMobile => !kIsWeb && (Platform.isAndroid || Platform.isIOS); static bool get isWeb => kIsWeb; static bool get isLinux => !kIsWeb && Platform.isLinux; static bool get isMacOS => !kIsWeb && Platform.isMacOS; static bool get isIOS => !kIsWeb && Platform.isIOS; static bool get isAndroid => !kIsWeb && Platform.isAndroid; } ================================================ FILE: lib/utils/riverpod_utils.dart ================================================ import 'dart:async'; import 'package:hooks_riverpod/hooks_riverpod.dart'; extension RefLifeCycle on AutoDisposeRef { void disposeDelay(Duration duration) { final link = keepAlive(); Timer? timer; onCancel(() { timer?.cancel(); timer = Timer(duration, link.close); }); onDispose(() { timer?.cancel(); }); onResume(() { timer?.cancel(); }); } } ================================================ FILE: lib/utils/sentry_riverpod_observer.dart ================================================ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; class SentryRiverpodObserver extends ProviderObserver { void addBreadcrumb(String message, {Map? data}) { Sentry.addBreadcrumb(Breadcrumb(category: "Provider", message: message, data: data)); } @override void didAddProvider(ProviderBase provider, Object? value, ProviderContainer container) { super.didAddProvider(provider, value, container); addBreadcrumb( 'Provider [${provider.name ?? provider.runtimeType}] was ADDED', data: value != null ? {"initial-value": value} : null, ); } @override void didUpdateProvider( ProviderBase provider, Object? previousValue, Object? newValue, ProviderContainer container, ) { super.didUpdateProvider(provider, previousValue, newValue, container); addBreadcrumb( 'Provider [${provider.name ?? provider.runtimeType}] was UPDATED', data: {"new-value": newValue, "old-value": previousValue}, ); } } ================================================ FILE: lib/utils/sentry_utils.dart ================================================ import 'dart:io'; import 'package:dio/dio.dart'; import 'package:hiddify/core/model/failures.dart'; import 'package:hiddify/features/proxy/model/proxy_failure.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, UnknownIp _ => false, HttpException _ => false, HandshakeException _ => false, ExpectedFailure _ => false, ExpectedMeasuredFailure _ => false, _ => true, }; } bool canLogEvent(dynamic throwable) => switch (throwable) { ExpectedMeasuredFailure _ => true, _ => canSendEvent(throwable), }; ================================================ FILE: lib/utils/text_utils.dart ================================================ import 'package:flutter/material.dart'; import 'package:intl/intl.dart' as intl; extension TextAlignX on BuildContext { bool get isRtl => Directionality.of(this) == TextDirection.rtl; TextAlign get textAlign { if (isRtl) { return TextAlign.right; } else { return TextAlign.left; } } } extension StringX on String { TextDirection get textDirection { return intl.Bidi.detectRtlDirectionality(this) ? TextDirection.rtl : TextDirection.ltr; } } extension TextEditingControllerX on TextEditingController { TextDirection? get textDirection { if (text.isEmpty) return null; return text.textDirection; } } ================================================ FILE: lib/utils/uri_utils.dart ================================================ import 'dart:io'; import 'package:hiddify/utils/custom_loggers.dart'; import 'package:loggy/loggy.dart'; import 'package:share_plus/share_plus.dart'; import 'package:url_launcher/url_launcher.dart'; abstract class UriUtils { static final loggy = Loggy("UriUtils"); static Future tryShareOrLaunchFile(Uri uri, {Uri? fileOrDir}) async { if (Platform.isWindows || Platform.isLinux) { return tryLaunch(fileOrDir ?? uri); } return tryShareFile(uri); } static Future tryLaunch(Uri uri) async { try { loggy.debug("launching [$uri]"); if (!await canLaunchUrl(uri)) { loggy.warning("can't launch [$uri]"); return false; } return launchUrl(uri, mode: LaunchMode.externalApplication); } catch (e, stackTrace) { loggy.warning("error launching [$uri]", e, stackTrace); return false; } } static Future tryShareFile(Uri uri, {String? mimeType}) async { try { loggy.debug("sharing [$uri]"); final file = XFile(uri.path, mimeType: mimeType); final result = await Share.shareXFiles([file]); loggy.debug("share result: ${result.raw}"); return result.status == ShareResultStatus.success; } catch (e, stackTrace) { loggy.warning("error sharing file [$uri]", e, stackTrace); return false; } } } ================================================ FILE: lib/utils/utils.dart ================================================ export 'alerts.dart'; export 'async_mutation.dart'; export 'bottom_sheet_page.dart'; export 'callback_debouncer.dart'; export 'custom_loggers.dart'; export 'custom_text_form_field.dart'; export 'date_time_formatter.dart'; export 'link_parsers.dart'; export 'mutation_state.dart'; export 'number_formatters.dart'; export 'placeholders.dart'; export 'platform_utils.dart'; export 'sentry_riverpod_observer.dart'; export 'sentry_utils.dart'; export 'text_utils.dart'; export 'uri_utils.dart'; export 'validators.dart'; ================================================ FILE: lib/utils/validators.dart ================================================ /// https://gist.github.com/dperini/729294 final _urlRegex = RegExp( // r"^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z0-9\u00a1-\uffff][a-z0-9\u00a1-\uffff_-]{0,62})?[a-z0-9\u00a1-\uffff]\.)+(?:[a-z\u00a1-\uffff]{2,}\.?))(?::\d{2,5})?(?:[/?#]\S*)?$", r'^(?:(?:https?|ftp):\/\/)(?:\S+(?::\S*)?@)?(?:(?:[0-9]{1,3}\.){3}[0-9]{1,3}|(?:(?:[a-z\u00a1-\uffff0-9]+-?)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]+-?)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:\/[^\s]*)?#?.*$', ); /// https://stackoverflow.com/a/12968117 final _portRegex = RegExp(r"^([1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$"); // android package-name // RegExp(r'^([A-Za-z]{1}[A-Za-z\d_]*\.)+[A-Za-z][A-Za-z\d_]*$'); // windows prcess name // RegExp(r"^(?!\s)[a-zA-Z\d\s\-\]\^!@#$%&()[+_=~'`.,;]+(?+_=|~'`".,:;]+(?+_=|~'`".,;]+(?:"\\/|?*]+(?:"\\/|?*]*(?:"\\/|?*]+(?>:-O3>") target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") endfunction() # Flutter library and tool build rules. set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") add_subdirectory(${FLUTTER_MANAGED_DIR}) # System-level dependencies. find_package(PkgConfig REQUIRED) pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") # Define the application target. To change its name, change BINARY_NAME above, # not the value here, or `flutter run` will no longer work. # # Any new source files that you add to the application should be added here. add_executable(${BINARY_NAME} "main.cc" "my_application.cc" "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" ) # Apply the standard set of build settings. This can be removed for applications # that need different build settings. apply_standard_settings(${BINARY_NAME}) # Add dependency libraries. Add any application-specific dependencies here. target_link_libraries(${BINARY_NAME} PRIVATE flutter) target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) # Run the Flutter tool portions of the build. This must not be removed. add_dependencies(${BINARY_NAME} flutter_assemble) # Only the install-generated bundle's copy of the executable will launch # correctly, since the resources must in the right relative locations. To avoid # people trying to run the unbundled copy, put it in a subdirectory instead of # the default top-level location. set_target_properties(${BINARY_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" ) # Generated plugin build rules, which manage building the plugins and adding # them to the application. include(flutter/generated_plugins.cmake) # === Installation === # By default, "installing" just makes a relocatable bundle in the build # directory. set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) endif() # Start with a clean build bundle directory every time. install(CODE " file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") " COMPONENT Runtime) set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") install(FILES "../hiddify-core/bin/lib/hiddify-core.so" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" COMPONENT Runtime) install( FILES "../hiddify-core/bin/HiddifyCli" DESTINATION "${CMAKE_INSTALL_PREFIX}" PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ WORLD_EXECUTE WORLD_READ COMPONENT Runtime ) install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" COMPONENT Runtime) install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" COMPONENT Runtime) foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES}) install(FILES "${bundled_library}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" COMPONENT Runtime) endforeach(bundled_library) # Copy the native assets provided by the build.dart from all packages. set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/linux/") install(DIRECTORY "${NATIVE_ASSETS_DIR}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" COMPONENT Runtime) # Fully re-copy the assets directory on each build to avoid having stale files # from a previous install. set(FLUTTER_ASSET_DIR_NAME "flutter_assets") install(CODE " file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") " COMPONENT Runtime) install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) # Install the AOT library on non-Debug builds only. if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" COMPONENT Runtime) endif() ================================================ FILE: linux/flutter/CMakeLists.txt ================================================ # This file controls Flutter-level build steps. It should not be edited. cmake_minimum_required(VERSION 3.10) set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") # Configuration provided via flutter tool. include(${EPHEMERAL_DIR}/generated_config.cmake) # TODO: Move the rest of this into files in ephemeral. See # https://github.com/flutter/flutter/issues/57146. # Serves the same purpose as list(TRANSFORM ... PREPEND ...), # which isn't available in 3.10. function(list_prepend LIST_NAME PREFIX) set(NEW_LIST "") foreach(element ${${LIST_NAME}}) list(APPEND NEW_LIST "${PREFIX}${element}") endforeach(element) set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) endfunction() # === Flutter Library === # System-level dependencies. find_package(PkgConfig REQUIRED) pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") # Published to parent scope for install step. set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) list(APPEND FLUTTER_LIBRARY_HEADERS "fl_basic_message_channel.h" "fl_binary_codec.h" "fl_binary_messenger.h" "fl_dart_project.h" "fl_engine.h" "fl_json_message_codec.h" "fl_json_method_codec.h" "fl_message_codec.h" "fl_method_call.h" "fl_method_channel.h" "fl_method_codec.h" "fl_method_response.h" "fl_plugin_registrar.h" "fl_plugin_registry.h" "fl_standard_message_codec.h" "fl_standard_method_codec.h" "fl_string_codec.h" "fl_value.h" "fl_view.h" "flutter_linux.h" ) list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") add_library(flutter INTERFACE) target_include_directories(flutter INTERFACE "${EPHEMERAL_DIR}" ) target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") target_link_libraries(flutter INTERFACE PkgConfig::GTK PkgConfig::GLIB PkgConfig::GIO ) add_dependencies(flutter flutter_assemble) # === Flutter tool backend === # _phony_ is a non-existent file to force this command to run every time, # since currently there's no way to get a full input/output list from the # flutter tool. add_custom_command( OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} ${CMAKE_CURRENT_BINARY_DIR}/_phony_ COMMAND ${CMAKE_COMMAND} -E env ${FLUTTER_TOOL_ENVIRONMENT} "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} VERBATIM ) add_custom_target(flutter_assemble DEPENDS "${FLUTTER_LIBRARY}" ${FLUTTER_LIBRARY_HEADERS} ) ================================================ FILE: linux/flutter/generated_plugin_registrant.cc ================================================ // // Generated file. Do not edit. // // clang-format off #include "generated_plugin_registrant.h" #include #include #include #include #include #include #include #include void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) dynamic_color_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "DynamicColorPlugin"); dynamic_color_plugin_register_with_registrar(dynamic_color_registrar); g_autoptr(FlPluginRegistrar) gtk_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "GtkPlugin"); gtk_plugin_register_with_registrar(gtk_registrar); g_autoptr(FlPluginRegistrar) screen_retriever_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverLinuxPlugin"); screen_retriever_linux_plugin_register_with_registrar(screen_retriever_linux_registrar); g_autoptr(FlPluginRegistrar) sentry_flutter_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "SentryFlutterPlugin"); sentry_flutter_plugin_register_with_registrar(sentry_flutter_registrar); g_autoptr(FlPluginRegistrar) sqlite3_flutter_libs_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "Sqlite3FlutterLibsPlugin"); sqlite3_flutter_libs_plugin_register_with_registrar(sqlite3_flutter_libs_registrar); g_autoptr(FlPluginRegistrar) tray_manager_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "TrayManagerPlugin"); tray_manager_plugin_register_with_registrar(tray_manager_registrar); g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); g_autoptr(FlPluginRegistrar) window_manager_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "WindowManagerPlugin"); window_manager_plugin_register_with_registrar(window_manager_registrar); } ================================================ FILE: linux/flutter/generated_plugin_registrant.h ================================================ // // Generated file. Do not edit. // // clang-format off #ifndef GENERATED_PLUGIN_REGISTRANT_ #define GENERATED_PLUGIN_REGISTRANT_ #include // Registers Flutter plugins. void fl_register_plugins(FlPluginRegistry* registry); #endif // GENERATED_PLUGIN_REGISTRANT_ ================================================ FILE: linux/flutter/generated_plugins.cmake ================================================ # # Generated file, do not edit. # list(APPEND FLUTTER_PLUGIN_LIST dynamic_color gtk screen_retriever_linux sentry_flutter sqlite3_flutter_libs tray_manager url_launcher_linux window_manager ) list(APPEND FLUTTER_FFI_PLUGIN_LIST ) set(PLUGIN_BUNDLED_LIBRARIES) foreach(plugin ${FLUTTER_PLUGIN_LIST}) add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) list(APPEND PLUGIN_BUNDLED_LIBRARIES $) list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) endforeach(plugin) foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) endforeach(ffi_plugin) ================================================ FILE: linux/main.cc ================================================ #include "my_application.h" int main(int argc, char** argv) { g_autoptr(MyApplication) app = my_application_new(); return g_application_run(G_APPLICATION(app), argc, argv); } ================================================ FILE: linux/my_application.cc ================================================ #include "my_application.h" #include #ifdef GDK_WINDOWING_X11 #include #endif #include "flutter/generated_plugin_registrant.h" struct _MyApplication { GtkApplication parent_instance; char **dart_entrypoint_arguments; }; G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) #define ICON_PATH "./hiddify.png" // Implements GApplication::activate. static void my_application_activate(GApplication *application) { MyApplication *self = MY_APPLICATION(application); GList *windows = gtk_application_get_windows(GTK_APPLICATION(application)); if (windows) { gtk_window_present(GTK_WINDOW(windows->data)); return; } GtkWindow *window = GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); gtk_window_set_icon_from_file(window, ICON_PATH, NULL); // Use a header bar when running in GNOME as this is the common style used // by applications and is the setup most users will be using (e.g. Ubuntu // desktop). // If running on X and not using GNOME then just use a traditional title bar // in case the window manager does more exotic layout, e.g. tiling. // If running on Wayland assume the header bar will work (may need changing // if future cases occur). gboolean use_header_bar = TRUE; #ifdef GDK_WINDOWING_X11 GdkScreen *screen = gtk_window_get_screen(window); if (GDK_IS_X11_SCREEN(screen)) { const gchar *wm_name = gdk_x11_screen_get_window_manager_name(screen); if (g_strcmp0(wm_name, "GNOME Shell") != 0) { use_header_bar = FALSE; } } #endif if (use_header_bar) { GtkHeaderBar *header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); gtk_widget_show(GTK_WIDGET(header_bar)); gtk_header_bar_set_title(header_bar, "Hiddify"); gtk_header_bar_set_show_close_button(header_bar, TRUE); gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); } else { gtk_window_set_title(window, "Hiddify"); } gtk_window_set_default_size(window, 1280, 720); gtk_widget_show(GTK_WIDGET(window)); g_autoptr(FlDartProject) project = fl_dart_project_new(); fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments); FlView *view = fl_view_new(project); gtk_widget_show(GTK_WIDGET(view)); gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); fl_register_plugins(FL_PLUGIN_REGISTRY(view)); // Flutter 3.24 breaks the gtk_widget_realize solution: https://github.com/leanflutter/window_manager/issues/179#issuecomment-2299534856 gtk_widget_hide(GTK_WIDGET(window)); gtk_widget_grab_focus(GTK_WIDGET(view)); } // Implements GApplication::local_command_line. static gboolean my_application_local_command_line(GApplication *application, gchar ***arguments, int *exit_status) { MyApplication *self = MY_APPLICATION(application); // Strip out the first argument as it is the binary name. self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); g_autoptr(GError) error = nullptr; if (!g_application_register(application, nullptr, &error)) { g_warning("Failed to register: %s", error->message); *exit_status = 1; return TRUE; } g_application_activate(application); *exit_status = 0; return FALSE; } // Implements GApplication::startup. static void my_application_startup(GApplication *application) { // MyApplication* self = MY_APPLICATION(object); // Perform any actions required at application startup. G_APPLICATION_CLASS(my_application_parent_class)->startup(application); } // Implements GApplication::shutdown. static void my_application_shutdown(GApplication *application) { // MyApplication* self = MY_APPLICATION(object); // Perform any actions required at application shutdown. G_APPLICATION_CLASS(my_application_parent_class)->shutdown(application); } // Implements GObject::dispose. static void my_application_dispose(GObject *object) { MyApplication *self = MY_APPLICATION(object); g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); G_OBJECT_CLASS(my_application_parent_class)->dispose(object); } static void my_application_class_init(MyApplicationClass *klass) { G_APPLICATION_CLASS(klass)->activate = my_application_activate; G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; G_APPLICATION_CLASS(klass)->startup = my_application_startup; G_APPLICATION_CLASS(klass)->shutdown = my_application_shutdown; G_OBJECT_CLASS(klass)->dispose = my_application_dispose; } static void my_application_init(MyApplication *self) {} MyApplication *my_application_new() { g_set_prgname(APPLICATION_ID); return MY_APPLICATION(g_object_new(my_application_get_type(), "application-id", APPLICATION_ID, "flags", G_APPLICATION_HANDLES_COMMAND_LINE | G_APPLICATION_HANDLES_OPEN, nullptr)); } ================================================ FILE: linux/my_application.h ================================================ #ifndef FLUTTER_MY_APPLICATION_H_ #define FLUTTER_MY_APPLICATION_H_ #include G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, GtkApplication) /** * my_application_new: * * Creates a new Flutter-based application. * * Returns: a new #MyApplication. */ MyApplication* my_application_new(); #endif // FLUTTER_MY_APPLICATION_H_ ================================================ FILE: linux/packaging/app.hiddify.com.appdata.xml ================================================ app.hiddify.com CC0-1.0 GPL-3.0-or-later Hiddify Modern Proxy and VPN Manager for Linux Hiddify linux@hiddify.com

Hiddify is an advanced proxy and VPN client built for privacy and control. It supports multiple backend protocols including V2Ray, Xray, Clash, and Sing-box, providing flexibility and performance for all kinds of network configurations.

Designed with simplicity in mind, Hiddify helps users connect securely and manage complex proxy configurations through a clean graphical interface.

https://hiddify.com/assets/hiddify-next-mobile.svg Main Interface of Hiddify hiddify.desktop hiddify Network Hiddify Proxy VPN V2Ray Nekoray Xray Psiphon OpenVPN x-scheme-handler/hiddify x-scheme-handler/v2ray x-scheme-handler/v2rayn x-scheme-handler/v2rayng x-scheme-handler/clash x-scheme-handler/clashmeta x-scheme-handler/sing-box https://hiddify.com https://github.com/hiddify/hiddify-app/issues https://docs.hiddify.com hiddify

Initial Linux release of Hiddify with multi-protocol support and modern UI.

hiddify hiddify optional x11 6604 false
================================================ FILE: linux/packaging/appimage/AppRun ================================================ #!/bin/bash cd "$(dirname "$0")" export LD_LIBRARY_PATH=usr/lib # Handles desktop integration: # 1. Copies app icons to ~/.local/share/icons # 2. Ensures icon theme index exists # 3. Creates a .desktop file pointing to this AppImage # 4. Skips if a system-wide installation (/usr/share/applications) is detected install_integration() { if [[ -z "$APPIMAGE" ]]; then return fi REAL_HOME=$(getent passwd "$USER" | cut -d: -f6) if [ -z "$REAL_HOME" ]; then REAL_HOME="$HOME" fi APP_NAME="hiddify" TARGET_DESKTOP="$REAL_HOME/.local/share/applications/hiddify.desktop" LOCAL_HICOLOR="$REAL_HOME/.local/share/icons/hicolor" if [ ! -f "$LOCAL_HICOLOR/index.theme" ]; then mkdir -p "$LOCAL_HICOLOR" if [ -f "/usr/share/icons/hicolor/index.theme" ]; then cp "/usr/share/icons/hicolor/index.theme" "$LOCAL_HICOLOR/" else echo -e "[Icon Theme]\nName=Hicolor\nDirectories=128x128/apps,256x256/apps" > "$LOCAL_HICOLOR/index.theme" fi fi if [[ -f "./hiddify.png" ]]; then mkdir -p "$LOCAL_HICOLOR/128x128/apps" cp "./hiddify.png" "$LOCAL_HICOLOR/128x128/apps/hiddify.png" mkdir -p "$LOCAL_HICOLOR/256x256/apps" cp "./hiddify.png" "$LOCAL_HICOLOR/256x256/apps/hiddify.png" gtk-update-icon-cache -f -t "$LOCAL_HICOLOR" 2>/dev/null || true fi if [ -f "/usr/share/applications/hiddify.desktop" ]; then return fi mkdir -p "$(dirname "$TARGET_DESKTOP")" cat > "$TARGET_DESKTOP" </dev/null || true } # Usage info show_help() { cat << EOF Usage: ${0##*/} ... start Hiddify or HiddifyCli, when no parameter is given, Hiddify is executed. -v show version EOF } show_version() { printf "Hiddify version " jq .version <./data/flutter_assets/version.json } # Initialize variables: service=0 #declare -i service OPTIND=1 # Resetting OPTIND is necessary if getopts was used previously in the script. # It is a good idea to make OPTIND local if you process options in a function. # if no arg is provided, execute hiddify app if [[ $# == 0 ]];then install_integration exec ./hiddify else # processing arguments case $1 in HiddifyCli) exec ./HiddifyCli ${@:3} exit 0 ;; h) show_help exit 0 ;; v) show_version exit 0 ;; *) show_help >&2 exit 1 ;; esac fi ================================================ FILE: linux/packaging/appimage/make_config.yaml ================================================ display_name: Hiddify icon: assets/images/source/ic_launcher_border.png keywords: - Hiddify - Proxy - VPN - V2ray - Nekoray - Xray - Psiphon - OpenVPN generic_name: Hiddify actions: - name: Start label: start arguments: - --start - name: Stop label: stop arguments: - --stop categories: - Network startup_notify: true # app_links setup for Linux # https://github.com/llfbandit/app_links/blob/master/doc/README_linux.md supported_mime_type: - x-scheme-handler/hiddify - x-scheme-handler/v2ray - x-scheme-handler/v2rayn - x-scheme-handler/v2rayng - x-scheme-handler/clash - x-scheme-handler/clashmeta - x-scheme-handler/sing-box # You can specify the shared libraries that you want to bundle with your app # # flutter_distributor automatically detects the shared libraries that your app # depends on, but you can also specify them manually here. # # The following example shows how to bundle the libcurl library with your app. # # include: # - libcurl.so.4 include: [] metainfo: linux/packaging/app.hiddify.com.appdata.xml ================================================ FILE: linux/packaging/deb/make_config.yaml ================================================ display_name: Hiddify package_name: hiddify maintainer: name: hiddify email: linux@hiddify.com priority: optional section: x11 installed_size: 6604 essential: false icon: assets/images/source/ic_launcher_border.png # Post-installation tasks: # 1. Inject StartupWMClass into the system .desktop file for correct icon association # 2. Remove conflicting hiddify.desktop files from users' .local directories (cleanup AppImage artifacts) # 3. Update the desktop database to apply changes immediately postinstall_scripts: - echo "Setting up Hiddify..." - sudo sed -i '/^\[Desktop Entry\]/a StartupWMClass=app.hiddify.com' "/usr/share/applications/hiddify.desktop" - echo "Search and remove hiddify.desktop from .local" - for user_dir in /home/*; do if [ -f "$user_dir/.local/share/applications/hiddify.desktop" ]; then rm -f "$user_dir/.local/share/applications/hiddify.desktop"; echo "Found and removed hiddify.desktop from $user_dir .local"; fi; done - update-desktop-database /usr/share/applications - echo "Hiddify is ready to use :)" # Post-uninstallation tasks: # 1. Iterate through all user directories to remove Hiddify configuration and data # 2. Clean up root user data to ensure a clean removal postuninstall_scripts: - echo "Removing Hiddify user data..." - for user_dir in /home/*; do if [ -d "$user_dir/.local/share/hiddify" ]; then rm -rf "$user_dir/.local/share/hiddify"; echo "Removed data from $user_dir"; fi; done - rm -rf /root/.local/share/hiddify - echo "Hiddify data cleanup complete." keywords: - Hiddify - Proxy - VPN - V2ray - Nekoray - Xray - Psiphon - OpenVPN generic_name: Hiddify categories: - Network startup_notify: true # app_links setup for Linux # https://github.com/llfbandit/app_links/blob/master/doc/README_linux.md supported_mime_type: - x-scheme-handler/hiddify - x-scheme-handler/v2ray - x-scheme-handler/v2rayn - x-scheme-handler/v2rayng - x-scheme-handler/clash - x-scheme-handler/clashmeta - x-scheme-handler/sing-box metainfo: linux/packaging/app.hiddify.com.appdata.xml ================================================ FILE: linux/packaging/rpm/make_config.yaml ================================================ # <<< RPM NOT SUPPORTED >>> # We decided to remove RPM packaging for the following reasons: # - Flutter officially supports Debian-based systems only. # - Persistent issues with dependency linking. # - High maintenance overhead (requires Fedora environment setup). # - Successful builds do not guarantee runtime stability on Red Hat-based systems. # display_name: Hiddify # url: https://github.com/hiddify/hiddify-app # license: Other # packager: hiddify # packagerEmail: linux@hiddify.com # icon: assets/images/source/ic_launcher_border.png # keywords: # - Hiddify # - Proxy # - VPN # - V2ray # - Nekoray # - Xray # - Psiphon # - OpenVPN # generic_name: Hiddify # requires: # - libayatana-appindicator-gtk3 # # - libcurl # # - openssl # categories: # - Network # startup_notify: true # # app_links setup for Linux # # https://github.com/llfbandit/app_links/blob/master/doc/README_linux.md # supported_mime_type: # - x-scheme-handler/hiddify # - x-scheme-handler/v2ray # - x-scheme-handler/v2rayn # - x-scheme-handler/v2rayng # - x-scheme-handler/clash # - x-scheme-handler/clashmeta # - x-scheme-handler/sing-box ================================================ FILE: linux_deps.list ================================================ # ============================================================== # ⚠️ USAGE: SHARED BY MAKEFILE & DOCKERFILE # This list defines dependencies for both build environments. # ============================================================== # flutter sdk https://docs.flutter.dev/install/manual curl git unzip xz-utils zip libglu1-mesa # linux development https://docs.flutter.dev/platform-integration/linux/setup clang cmake ninja-build pkg-config libgtk-3-dev libstdc++-12-dev # build & run https://docs.flutter.dev/platform-integration/linux/building libgtk-3-0 libblkid1 liblzma5 # fastforge rpm https://fastforge.dev/makers/rpm rpm patchelf # fastforge appimage https://fastforge.dev/makers/appimage locate # tray_manager https://pub.dev/packages/tray_manager libayatana-appindicator3-dev # for running .appimage fuse # Dockerfile make sudo wget grep sed ca-certificates # Unknown Purpose libcurl4-openssl-dev libglib2.0-dev file appstream ================================================ FILE: macos/.gitignore ================================================ # Flutter-related **/Flutter/ephemeral/ **/Pods/ # Xcode-related **/dgph **/xcuserdata/ ================================================ FILE: macos/.stignore ================================================ **/Flutter/ephemeral/ **/Pods/ **/dgph **/xcuserdata/ ================================================ FILE: macos/Flutter/Flutter-Debug.xcconfig ================================================ #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" #include "ephemeral/Flutter-Generated.xcconfig" ================================================ FILE: macos/Flutter/Flutter-Release.xcconfig ================================================ #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" #include "ephemeral/Flutter-Generated.xcconfig" ================================================ FILE: macos/Flutter/GeneratedPluginRegistrant.swift ================================================ // // Generated file. Do not edit. // import FlutterMacOS import Foundation import app_links import device_info_plus import dynamic_color import file_picker import in_app_review import mobile_scanner import network_info_plus import package_info_plus import path_provider_foundation import screen_retriever_macos import sentry_flutter import share_plus import shared_preferences_foundation import sqlite3_flutter_libs import tray_manager import url_launcher_macos import window_manager func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { AppLinksMacosPlugin.register(with: registry.registrar(forPlugin: "AppLinksMacosPlugin")) DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) DynamicColorPlugin.register(with: registry.registrar(forPlugin: "DynamicColorPlugin")) FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin")) InAppReviewPlugin.register(with: registry.registrar(forPlugin: "InAppReviewPlugin")) MobileScannerPlugin.register(with: registry.registrar(forPlugin: "MobileScannerPlugin")) NetworkInfoPlusPlugin.register(with: registry.registrar(forPlugin: "NetworkInfoPlusPlugin")) FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) ScreenRetrieverMacosPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverMacosPlugin")) SentryFlutterPlugin.register(with: registry.registrar(forPlugin: "SentryFlutterPlugin")) SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) Sqlite3FlutterLibsPlugin.register(with: registry.registrar(forPlugin: "Sqlite3FlutterLibsPlugin")) TrayManagerPlugin.register(with: registry.registrar(forPlugin: "TrayManagerPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin")) } ================================================ FILE: macos/Podfile ================================================ platform :osx, '10.15' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' project 'Runner', { 'Debug' => :debug, 'Profile' => :release, 'Release' => :release, } def flutter_root generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) unless File.exist?(generated_xcode_build_settings_path) raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" end File.foreach(generated_xcode_build_settings_path) do |line| matches = line.match(/FLUTTER_ROOT\=(.*)/) return matches[1].strip if matches end raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" end require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) flutter_macos_podfile_setup target 'Runner' do use_frameworks! use_modular_headers! flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) target 'RunnerTests' do inherit! :search_paths end end post_install do |installer| # Ensure pods also use the minimum deployment target set above # https://stackoverflow.com/a/64385584/436422 puts 'Determining pod project minimum deployment target' pods_project = installer.pods_project deployment_target_key = 'MACOSX_DEPLOYMENT_TARGET' deployment_targets = pods_project.build_configurations.map{ |config| config.build_settings[deployment_target_key] } minimum_deployment_target = deployment_targets.min_by{ |version| Gem::Version.new(version) } puts 'Minimal deployment target is ' + minimum_deployment_target puts 'Setting each pod deployment target to ' + minimum_deployment_target installer.pods_project.targets.each do |target| flutter_additional_macos_build_settings(target) target.build_configurations.each do |config| config.build_settings[deployment_target_key] = minimum_deployment_target end end end ================================================ FILE: macos/Runner/AppDelegate.swift ================================================ import Cocoa import FlutterMacOS import UserNotifications @main class AppDelegate: FlutterAppDelegate { override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { // https://github.com/leanflutter/window_manager/issues/214 return false } override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { return true } override func applicationDidFinishLaunching(_ aNotification: Notification) { // Request notification authorization UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge]) { granted, error in if let error = error { print("Error requesting notification authorization: \(error)") } } } // // window manager restore from dock: https://leanflutter.dev/blog/click-dock-icon-to-restore-after-closing-the-window // override func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool { // if !flag { // for window in NSApp.windows { // if !window.isVisible { // window.setIsVisible(true) // } // window.makeKeyAndOrderFront(self) // NSApp.activate(ignoringOtherApps: true) // } // } // return true // } } ================================================ FILE: macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json ================================================ { "images" : [ { "filename" : "app-icon-16.png", "idiom" : "mac", "scale" : "1x", "size" : "16x16" }, { "filename" : "app-icon-16@2x.png", "idiom" : "mac", "scale" : "2x", "size" : "16x16" }, { "filename" : "app-icon-32.png", "idiom" : "mac", "scale" : "1x", "size" : "32x32" }, { "filename" : "app-icon-32@2x.png", "idiom" : "mac", "scale" : "2x", "size" : "32x32" }, { "filename" : "app-icon-128.png", "idiom" : "mac", "scale" : "1x", "size" : "128x128" }, { "filename" : "app-icon-128@2x.png", "idiom" : "mac", "scale" : "2x", "size" : "128x128" }, { "filename" : "app-icon-256.png", "idiom" : "mac", "scale" : "1x", "size" : "256x256" }, { "filename" : "app-icon-256@2x.png", "idiom" : "mac", "scale" : "2x", "size" : "256x256" }, { "filename" : "app-icon-512.png", "idiom" : "mac", "scale" : "1x", "size" : "512x512" }, { "filename" : "app-icon-512@2x.png", "idiom" : "mac", "scale" : "2x", "size" : "512x512" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: macos/Runner/Base.lproj/MainMenu.xib ================================================ ================================================ FILE: macos/Runner/Configs/AppInfo.xcconfig ================================================ // Application-level settings for the Runner target. // // This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the // future. If not, the values below would default to using the project name when this becomes a // 'flutter create' template. // The application's name. By default this is also the title of the Flutter window. PRODUCT_NAME = Hiddify // The application's bundle identifier PRODUCT_BUNDLE_IDENTIFIER = app.hiddify.com // The copyright displayed in application information PRODUCT_COPYRIGHT = Copyright © 2023 hiddify.com. All rights reserved. ================================================ FILE: macos/Runner/Configs/Debug.xcconfig ================================================ #include "../../Flutter/Flutter-Debug.xcconfig" #include "Warnings.xcconfig" ================================================ FILE: macos/Runner/Configs/Release.xcconfig ================================================ #include "../../Flutter/Flutter-Release.xcconfig" #include "Warnings.xcconfig" ================================================ FILE: macos/Runner/Configs/Warnings.xcconfig ================================================ WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings GCC_WARN_UNDECLARED_SELECTOR = YES CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE CLANG_WARN__DUPLICATE_METHOD_MATCH = YES CLANG_WARN_PRAGMA_PACK = YES CLANG_WARN_STRICT_PROTOTYPES = YES CLANG_WARN_COMMA = YES GCC_WARN_STRICT_SELECTOR_MATCH = YES CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES GCC_WARN_SHADOW = YES CLANG_WARN_UNREACHABLE_CODE = YES ================================================ FILE: macos/Runner/DebugProfile.entitlements ================================================ com.apple.security.network.client com.apple.security.app-sandbox com.apple.security.cs.allow-jit com.apple.security.network.server ================================================ FILE: macos/Runner/Info.plist ================================================ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIconFile CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType APPL CFBundleShortVersionString $(FLUTTER_BUILD_NAME) CFBundleVersion $(FLUTTER_BUILD_NUMBER) LSMinimumSystemVersion $(MACOSX_DEPLOYMENT_TARGET) NSHumanReadableCopyright $(PRODUCT_COPYRIGHT) NSMainNibFile MainMenu CFBundleURLTypes CFBundleTypeRole Editor CFBundleURLName CFBundleURLSchemes hiddify v2ray v2rayn v2rayng clash clashmeta sing-box NSPrincipalClass NSApplication CFBundleLocalizations ar en es fa fr id pt-BR ru tr zh-CN zh-TW ================================================ FILE: macos/Runner/MainFlutterWindow.swift ================================================ import Cocoa import FlutterMacOS import window_manager import LaunchAtLogin class MainFlutterWindow: NSWindow { override func awakeFromNib() { let flutterViewController = FlutterViewController() let windowFrame = self.frame self.contentViewController = flutterViewController self.setFrame(windowFrame, display: true) // Add FlutterMethodChannel platform code FlutterMethodChannel( name: "launch_at_startup", binaryMessenger: flutterViewController.engine.binaryMessenger ) .setMethodCallHandler { (_ call: FlutterMethodCall, result: @escaping FlutterResult) in switch call.method { case "launchAtStartupIsEnabled": result(LaunchAtLogin.isEnabled) case "launchAtStartupSetEnabled": if let arguments = call.arguments as? [String: Any] { LaunchAtLogin.isEnabled = arguments["setEnabledValue"] as! Bool } result(nil) default: result(FlutterMethodNotImplemented) } } // RegisterGeneratedPlugins(registry: flutterViewController) super.awakeFromNib() } // window manager hidden at launch override public func order(_ place: NSWindow.OrderingMode, relativeTo otherWin: Int) { super.order(place, relativeTo: otherWin) hiddenWindowAtLaunch() } } ================================================ FILE: macos/Runner/Release.entitlements ================================================ com.apple.security.network.client com.apple.security.app-sandbox com.apple.security.cs.allow-jit com.apple.security.network.server ================================================ FILE: macos/Runner.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 54; objects = { /* Begin PBXAggregateTarget section */ 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { isa = PBXAggregateTarget; buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; buildPhases = ( 33CC111E2044C6BF0003C045 /* ShellScript */, ); dependencies = ( ); name = "Flutter Assemble"; productName = FLX; }; /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ 27D5969F2A92B6AB00E2BE2B /* hiddify-core.dylib in Bundle Framework */ = {isa = PBXBuildFile; fileRef = 27D5969E2A92B6AB00E2BE2B /* hiddify-core.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; }; 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; 414949DC2CCE3D370069D5BA /* LaunchAtLogin in Frameworks */ = {isa = PBXBuildFile; productRef = 414949DB2CCE3D370069D5BA /* LaunchAtLogin */; }; C2CEC907B854CCA50E3CB29E /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7774D5331C4F7054E7047DF2 /* Pods_RunnerTests.framework */; }; FEE8413B259D2FDED8BE933A /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1A1027315C9B3E3F83410A09 /* Pods_Runner.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 33CC10E52044A3C60003C045 /* Project object */; proxyType = 1; remoteGlobalIDString = 33CC10EC2044A3C60003C045; remoteInfo = Runner; }; 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 33CC10E52044A3C60003C045 /* Project object */; proxyType = 1; remoteGlobalIDString = 33CC111A2044C6BA0003C045; remoteInfo = FLX; }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ 33CC110E2044A8840003C045 /* Bundle Framework */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 12; dstPath = Contents/Frameworks; dstSubfolderSpec = 1; files = ( 27D5969F2A92B6AB00E2BE2B /* hiddify-core.dylib in Bundle Framework */, ); name = "Bundle Framework"; runOnlyForDeploymentPostprocessing = 0; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ 1A1027315C9B3E3F83410A09 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 1C05D6984C93FE0E5C9038D0 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; 27D5969E2A92B6AB00E2BE2B /* hiddify-core.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = "hiddify-core.dylib"; path = "../hiddify-core/bin/hiddify-core.dylib"; sourceTree = ""; }; 331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; 33CC10ED2044A3C60003C045 /* Hiddify.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Hiddify.app; sourceTree = BUILT_PRODUCTS_DIR; }; 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; 3E235443DDCB73D48BD2341C /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 49CD96905E177C4420930499 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 7774D5331C4F7054E7047DF2 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; 7C0B4F177A93E89661E9B0CB /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; D96FD811DCD14FAF8EC1F071 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; DBFB795F5E72237D66B9D391 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 331C80D2294CF70F00263BE5 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( C2CEC907B854CCA50E3CB29E /* Pods_RunnerTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; 33CC10EA2044A3C60003C045 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 414949DC2CCE3D370069D5BA /* LaunchAtLogin in Frameworks */, FEE8413B259D2FDED8BE933A /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 0B357514C188DFB6739B421A /* Pods */ = { isa = PBXGroup; children = ( 3E235443DDCB73D48BD2341C /* Pods-Runner.debug.xcconfig */, DBFB795F5E72237D66B9D391 /* Pods-Runner.release.xcconfig */, 49CD96905E177C4420930499 /* Pods-Runner.profile.xcconfig */, 7C0B4F177A93E89661E9B0CB /* Pods-RunnerTests.debug.xcconfig */, 1C05D6984C93FE0E5C9038D0 /* Pods-RunnerTests.release.xcconfig */, D96FD811DCD14FAF8EC1F071 /* Pods-RunnerTests.profile.xcconfig */, ); path = Pods; sourceTree = ""; }; 331C80D6294CF71000263BE5 /* RunnerTests */ = { isa = PBXGroup; children = ( 331C80D7294CF71000263BE5 /* RunnerTests.swift */, ); path = RunnerTests; sourceTree = ""; }; 33BA886A226E78AF003329D5 /* Configs */ = { isa = PBXGroup; children = ( 33E5194F232828860026EE4D /* AppInfo.xcconfig */, 9740EEB21CF90195004384FC /* Debug.xcconfig */, 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, ); path = Configs; sourceTree = ""; }; 33CC10E42044A3C60003C045 = { isa = PBXGroup; children = ( 27D5969E2A92B6AB00E2BE2B /* hiddify-core.dylib */, 33FAB671232836740065AC1E /* Runner */, 33CEB47122A05771004F2AC0 /* Flutter */, 331C80D6294CF71000263BE5 /* RunnerTests */, 33CC10EE2044A3C60003C045 /* Products */, D73912EC22F37F3D000D13A0 /* Frameworks */, 0B357514C188DFB6739B421A /* Pods */, ); sourceTree = ""; }; 33CC10EE2044A3C60003C045 /* Products */ = { isa = PBXGroup; children = ( 33CC10ED2044A3C60003C045 /* Hiddify.app */, 331C80D5294CF71000263BE5 /* RunnerTests.xctest */, ); name = Products; sourceTree = ""; }; 33CC11242044D66E0003C045 /* Resources */ = { isa = PBXGroup; children = ( 33CC10F22044A3C60003C045 /* Assets.xcassets */, 33CC10F42044A3C60003C045 /* MainMenu.xib */, 33CC10F72044A3C60003C045 /* Info.plist */, ); name = Resources; path = ..; sourceTree = ""; }; 33CEB47122A05771004F2AC0 /* Flutter */ = { isa = PBXGroup; children = ( 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, ); path = Flutter; sourceTree = ""; }; 33FAB671232836740065AC1E /* Runner */ = { isa = PBXGroup; children = ( 33CC10F02044A3C60003C045 /* AppDelegate.swift */, 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, 33E51913231747F40026EE4D /* DebugProfile.entitlements */, 33E51914231749380026EE4D /* Release.entitlements */, 33CC11242044D66E0003C045 /* Resources */, 33BA886A226E78AF003329D5 /* Configs */, ); path = Runner; sourceTree = ""; }; D73912EC22F37F3D000D13A0 /* Frameworks */ = { isa = PBXGroup; children = ( 1A1027315C9B3E3F83410A09 /* Pods_Runner.framework */, 7774D5331C4F7054E7047DF2 /* Pods_RunnerTests.framework */, ); name = Frameworks; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ 331C80D4294CF70F00263BE5 /* RunnerTests */ = { isa = PBXNativeTarget; buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; buildPhases = ( 33C54E5BCA15F9B3C8DDCCD2 /* [CP] Check Pods Manifest.lock */, 331C80D1294CF70F00263BE5 /* Sources */, 331C80D2294CF70F00263BE5 /* Frameworks */, 331C80D3294CF70F00263BE5 /* Resources */, ); buildRules = ( ); dependencies = ( 331C80DA294CF71000263BE5 /* PBXTargetDependency */, ); name = RunnerTests; productName = RunnerTests; productReference = 331C80D5294CF71000263BE5 /* RunnerTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; 33CC10EC2044A3C60003C045 /* Runner */ = { isa = PBXNativeTarget; buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( 40CB9C5051F17943B75EF3A9 /* [CP] Check Pods Manifest.lock */, 33CC10E92044A3C60003C045 /* Sources */, 33CC10EA2044A3C60003C045 /* Frameworks */, 33CC10EB2044A3C60003C045 /* Resources */, 33CC110E2044A8840003C045 /* Bundle Framework */, 3399D490228B24CF009A79C7 /* ShellScript */, DDB714AFDEF721265C0C3AF7 /* [CP] Embed Pods Frameworks */, 414949D92CCE3C2C0069D5BA /* ShellScript */, ); buildRules = ( ); dependencies = ( 33CC11202044C79F0003C045 /* PBXTargetDependency */, ); name = Runner; packageProductDependencies = ( 414949DB2CCE3D370069D5BA /* LaunchAtLogin */, ); productName = Runner; productReference = 33CC10ED2044A3C60003C045 /* Hiddify.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 33CC10E52044A3C60003C045 /* Project object */ = { isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0920; LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 331C80D4294CF70F00263BE5 = { CreatedOnToolsVersion = 14.0; TestTargetID = 33CC10EC2044A3C60003C045; }; 33CC10EC2044A3C60003C045 = { CreatedOnToolsVersion = 9.2; LastSwiftMigration = 1100; ProvisioningStyle = Automatic; SystemCapabilities = { com.apple.Sandbox = { enabled = 1; }; }; }; 33CC111A2044C6BA0003C045 = { CreatedOnToolsVersion = 9.2; ProvisioningStyle = Manual; }; }; }; buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; compatibilityVersion = "Xcode 9.3"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = 33CC10E42044A3C60003C045; packageReferences = ( 414949DA2CCE3D370069D5BA /* XCRemoteSwiftPackageReference "LaunchAtLogin-Legacy" */, ); productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( 33CC10EC2044A3C60003C045 /* Runner */, 331C80D4294CF70F00263BE5 /* RunnerTests */, 33CC111A2044C6BA0003C045 /* Flutter Assemble */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ 331C80D3294CF70F00263BE5 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; 33CC10EB2044A3C60003C045 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ 3399D490228B24CF009A79C7 /* ShellScript */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( ); inputPaths = ( ); outputFileListPaths = ( ); outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; }; 33C54E5BCA15F9B3C8DDCCD2 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( ); inputPaths = ( "${PODS_PODFILE_DIR_PATH}/Podfile.lock", "${PODS_ROOT}/Manifest.lock", ); name = "[CP] Check Pods Manifest.lock"; outputFileListPaths = ( ); outputPaths = ( "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; 33CC111E2044C6BF0003C045 /* ShellScript */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( Flutter/ephemeral/FlutterInputs.xcfilelist, ); inputPaths = ( Flutter/ephemeral/tripwire, ); outputFileListPaths = ( Flutter/ephemeral/FlutterOutputs.xcfilelist, ); outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; }; 40CB9C5051F17943B75EF3A9 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( ); inputPaths = ( "${PODS_PODFILE_DIR_PATH}/Podfile.lock", "${PODS_ROOT}/Manifest.lock", ); name = "[CP] Check Pods Manifest.lock"; outputFileListPaths = ( ); outputPaths = ( "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; 414949D92CCE3C2C0069D5BA /* ShellScript */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( ); inputPaths = ( ); outputFileListPaths = ( ); outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "${BUILT_PRODUCTS_DIR}/LaunchAtLogin_LaunchAtLogin.bundle/Contents/Resources/copy-helper-swiftpm.sh\n"; }; DDB714AFDEF721265C0C3AF7 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); name = "[CP] Embed Pods Frameworks"; outputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ 331C80D1294CF70F00263BE5 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 33CC10E92044A3C60003C045 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ 331C80DA294CF71000263BE5 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 33CC10EC2044A3C60003C045 /* Runner */; targetProxy = 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */; }; 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { isa = PBXVariantGroup; children = ( 33CC10F52044A3C60003C045 /* Base */, ); name = MainMenu.xib; path = Runner; sourceTree = ""; }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ 331C80DB294CF71000263BE5 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 7C0B4F177A93E89661E9B0CB /* Pods-RunnerTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.hiddify.hiddify.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/hiddify.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/hiddify"; }; name = Debug; }; 331C80DC294CF71000263BE5 /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 1C05D6984C93FE0E5C9038D0 /* Pods-RunnerTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.hiddify.hiddify.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/hiddify.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/hiddify"; }; name = Release; }; 331C80DD294CF71000263BE5 /* Profile */ = { isa = XCBuildConfiguration; baseConfigurationReference = D96FD811DCD14FAF8EC1F071 /* Pods-RunnerTests.profile.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.hiddify.hiddify.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/hiddify.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/hiddify"; }; name = Profile; }; 338D0CE9231458BD00FA5F75 /* Profile */ = { isa = XCBuildConfiguration; baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CODE_SIGN_IDENTITY = "-"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; MACOSX_DEPLOYMENT_TARGET = 10.15; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; }; name = Profile; }; 338D0CEA231458BD00FA5F75 /* Profile */ = { isa = XCBuildConfiguration; baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = Hiddify; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.15; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_VERSION = 5.0; }; name = Profile; }; 338D0CEB231458BD00FA5F75 /* Profile */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_STYLE = Manual; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Profile; }; 33CC10F92044A3C60003C045 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CODE_SIGN_IDENTITY = "-"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; MACOSX_DEPLOYMENT_TARGET = 10.15; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; name = Debug; }; 33CC10FA2044A3C60003C045 /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CODE_SIGN_IDENTITY = "-"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; MACOSX_DEPLOYMENT_TARGET = 10.15; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; }; name = Release; }; 33CC10FC2044A3C60003C045 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = Hiddify; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.15; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; }; name = Debug; }; 33CC10FD2044A3C60003C045 /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = Hiddify; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.15; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_VERSION = 5.0; }; name = Release; }; 33CC111C2044C6BA0003C045 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_STYLE = Manual; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; }; 33CC111D2044C6BA0003C045 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_STYLE = Automatic; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { isa = XCConfigurationList; buildConfigurations = ( 331C80DB294CF71000263BE5 /* Debug */, 331C80DC294CF71000263BE5 /* Release */, 331C80DD294CF71000263BE5 /* Profile */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { isa = XCConfigurationList; buildConfigurations = ( 33CC10F92044A3C60003C045 /* Debug */, 33CC10FA2044A3C60003C045 /* Release */, 338D0CE9231458BD00FA5F75 /* Profile */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { isa = XCConfigurationList; buildConfigurations = ( 33CC10FC2044A3C60003C045 /* Debug */, 33CC10FD2044A3C60003C045 /* Release */, 338D0CEA231458BD00FA5F75 /* Profile */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { isa = XCConfigurationList; buildConfigurations = ( 33CC111C2044C6BA0003C045 /* Debug */, 33CC111D2044C6BA0003C045 /* Release */, 338D0CEB231458BD00FA5F75 /* Profile */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ 414949DA2CCE3D370069D5BA /* XCRemoteSwiftPackageReference "LaunchAtLogin-Legacy" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/sindresorhus/LaunchAtLogin-Legacy"; requirement = { branch = main; kind = branch; }; }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ 414949DB2CCE3D370069D5BA /* LaunchAtLogin */ = { isa = XCSwiftPackageProductDependency; package = 414949DA2CCE3D370069D5BA /* XCRemoteSwiftPackageReference "LaunchAtLogin-Legacy" */; productName = LaunchAtLogin; }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 33CC10E52044A3C60003C045 /* Project object */; } ================================================ FILE: macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist ================================================ IDEDidComputeMac32BitWarning ================================================ FILE: macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved ================================================ { "originHash" : "78526aafdff3d7bb5fccbf1f4499af4d3ea88e16e8819bf30db26c60f51192d8", "pins" : [ { "identity" : "launchatlogin-legacy", "kind" : "remoteSourceControl", "location" : "https://github.com/sindresorhus/LaunchAtLogin-Legacy", "state" : { "branch" : "main", "revision" : "9a894d799269cb591037f9f9cb0961510d4dca81" } } ], "version" : 2 } ================================================ FILE: macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme ================================================ ================================================ FILE: macos/Runner.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist ================================================ IDEDidComputeMac32BitWarning ================================================ FILE: macos/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved ================================================ { "originHash" : "78526aafdff3d7bb5fccbf1f4499af4d3ea88e16e8819bf30db26c60f51192d8", "pins" : [ { "identity" : "launchatlogin-legacy", "kind" : "remoteSourceControl", "location" : "https://github.com/sindresorhus/LaunchAtLogin-Legacy", "state" : { "branch" : "main", "revision" : "9a894d799269cb591037f9f9cb0961510d4dca81" } } ], "version" : 2 } ================================================ FILE: macos/RunnerTests/RunnerTests.swift ================================================ import FlutterMacOS import Cocoa import XCTest class RunnerTests: XCTestCase { func testExample() { // If you add code to the Runner application, consider adding tests here. // See https://developer.apple.com/documentation/xctest for more information about using XCTest. } } ================================================ FILE: macos/packaging/dmg/make_config.yaml ================================================ title: Hiddify background: "background.png" window: size: width: 600 height: 400 contents: - x: 440 y: 230 type: link path: "/Applications" - x: 160 y: 230 type: file path: Hiddify.app ================================================ FILE: macos/packaging/pkg/make_config.yaml ================================================ install-path: /Applications #sign-identity: "Apple Development: app hiddify (VS8XK8XGMM)" ================================================ FILE: project.inlang/project_id ================================================ 33134c0ff949ed5f9d94c105afe0750868db4328feb5f504f9c62db06940f891 ================================================ FILE: project.inlang/settings.json ================================================ { "$schema": "https://inlang.com/schema/project-settings", "sourceLanguageTag": "en", "languageTags": [ "en", "ar", "ckb-KUR", "es", "fa", "fr", "id", "pt-BR", "ru", "tr", "zh-CN", "zh-TW", "hi" ], "modules": [ "https://cdn.jsdelivr.net/npm/@inlang/plugin-i18next@4/dist/index.js", "https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-empty-pattern@1/dist/index.js", "https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-identical-pattern@1/dist/index.js", "https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-without-source@1/dist/index.js", "https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-missing-translation@1/dist/index.js", "https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-missing-translation@latest/dist/index.js" ], "plugin.inlang.i18next": { "pathPattern": "./assets/translations/strings_{languageTag}.i18n.json", "variableReferencePattern": [ "@:" ] } } ================================================ FILE: pubspec.yaml ================================================ name: hiddify description: Cross Platform Multi Protocol Proxy Frontend. publish_to: "none" version: 4.1.2+40102 environment: sdk: ^3.10.4 # ! IMPORTANT ! # This SDK version is strictly required and must not be removed. # It is parsed/grep-ed by Makefile and Dockerfile to configure the build environment. # Ensure this matches the target Flutter version. flutter: ^3.38.5 dependencies: flutter: sdk: flutter flutter_localizations: sdk: flutter intl: ^0.20.2 # lucy_editor: ^1.0.5 # re_highlight: ^0.0.3 # json_editor_flutter: ^1.4.2 slang: ^4.8.1 slang_flutter: ^4.8.0 fpdart: ^1.1.0 freezed_annotation: ^2.4.1 json_annotation: ^4.9.0 hooks_riverpod: ^2.4.10 flutter_hooks: ^0.20.5 riverpod_annotation: ^2.3.4 rxdart: ^0.28.0 drift: ^2.21.0 drift_flutter: ^0.2.1 shared_preferences: ^2.5.2 # shared_preferences_android: ^2.4.8 dio: ^5.4.1 ffi: ^2.1.2 path_provider: ^2.1.1 mobile_scanner: ^7.2.0 # protocol_handler: ^0.2.0 flutter_native_splash: ^2.3.10 share_plus: ^10.1.0 window_manager: ^0.5.0 tray_manager: ^0.5.0 # get list of connected monitors (on Desktops), including their size and position screen_retriever: ^0.2.0 package_info_plus: ^8.1.0 url_launcher: ^6.2.5 vclibs: ^0.1.2 launch_at_startup: ^0.5.1 sentry_flutter: ^8.14.0 sentry_dart_plugin: ^2.4.1 sqlite3_flutter_libs: ^0.5.28 combine: ^0.5.7 path: ^1.9.0 loggy: ^2.0.3 flutter_loggy: ^2.0.2 meta: ^1.15.0 dartx: ^1.2.0 uuid: ^4.3.3 tint: ^2.0.1 accessibility_tools: ^2.2.3 neat_periodic_task: ^2.0.1 watcher: ^1.1.0 go_router: ^16.2.4 flutter_animate: ^4.5.0 flutter_svg: ^2.0.10+1 gap: ^3.0.1 # percent_indicator_premium: ^0.0.2 sliver_tools: ^0.2.12 font_awesome_flutter: ^10.7.0 upgrader: ^11.3.0 toastification: ^2.3.0 version: ^3.0.2 posix: ^6.0.1 qr_flutter: ^4.1.0 flutter_displaymode: ^0.6.0 flutter_loggy_dio: ^3.1.0 dio_smart_retry: ^7.0.1 cupertino_http: ^2.0.1 dart_mappable: ^4.2.1 fluentui_system_icons: ^1.1.229 json_path: ^0.7.1 # permission_handler: ^11.3.0 # is not compatible with windows #flutter_easy_permission: ^1.1.2 # flutter_easy_permission: # git: https://github.com/MichaelStH/flutter_easy_permission.git in_app_review: ^2.0.10 # circle_flags: ^4.0.2 circle_flags: git: https://github.com/hiddify-com/flutter_circle_flags.git fixnum: ^1.1.0 grpc: ^4.0.1 bottom_navy_bar: ^6.1.0 # # for using protobuf install protoc from this link -> https://github.com/protocolbuffers/protobuf/releases # # then add path to "environment variable" e.g. -> C:\src\protoc-28.3-win64\bin # # then active "protoc_plugin" by running this command -> dart pub global activate protoc_plugin # # then add path to "environment variable" e.g. -> C:\Users\%USERPROFILE%\AppData\Local\Pub\Cache\global_packages\protoc_plugin\bin protobuf: ^2.0.0 dynamic_color: ^1.7.0 flutter_typeahead: ^5.0.1 # flutter_platform_widgets: ^7.0.1 simple_icons: ^10.1.3 animated_notch_bottom_bar: ^1.0.0 basic_utils: 5.7.0 showcaseview: 3.0.0 text_scroll: ^0.2.0 flutter_markdown_plus: ^1.0.6 # platform-specific settings # https://github.com/miguelpruivo/flutter_file_picker/wiki/Setup file_picker: ^8.1.7 installed_apps: # ^1.5.2 git: https://github.com/VB10/installed_apps # change case of text # for example camelCase to snake_case # helloWorld -> hello_worlds recase: ^4.1.0 # Used for managing deep links on desktop platforms # platform-specific settings # https://github.com/llfbandit/app_links/tree/master/doc app_links: ^6.4.0 # Used for registering custom schemes in the Windows registry win32: ^5.12.0 # Working with Wifi(get LAN IP address) # Web platform not supported # platform-specific settings network_info_plus: ^6.1.4 humanizer: ^3.0.1 dependency_validator: ^5.0.2 dev_dependencies: # flutter_test: # sdk: flutter lint: ^2.3.0 build_web_compilers: ^4.0.11 build_runner: ^2.4.13 json_serializable: ^6.7.1 freezed: ^2.4.7 riverpod_generator: ^2.4.3 drift_dev: ^2.21.0 ffigen: ^19.1.0 slang_build_runner: ^4.4.0 flutter_gen_runner: ^5.4.0 dart_mappable_builder: ^4.2.1 # protoc_builder: ^0.4.1 # google_protobuf: ^3.20.1; flutter: uses-material-design: true assets: # - assets/core/geoip.db # - assets/core/geosite.db - assets/images/logo.svg - assets/images/tray_icon.ico - assets/images/tray_icon.png - assets/images/tray_icon_dark.ico - assets/images/tray_icon_dark.png - assets/images/tray_icon_connected.ico - assets/images/tray_icon_connected.png - assets/images/tray_icon_disconnected.ico - assets/images/tray_icon_disconnected.png - assets/images/connect_norouz.PNG - assets/images/disconnect_norouz.PNG - assets/images/world_map.png - assets/images/source/ic_launcher_border.png fonts: - family: Shabnam fonts: - asset: assets/fonts/Shabnam.ttf # - family: Roboto # fonts: # - asset: assets/fonts/Roboto.ttf - family: Emoji fonts: - asset: assets/fonts/Emoji.ttf flutter_gen: output: lib/gen/ integrations: flutter_svg: true flutter_native_splash: color: "#ffffff" image: assets/images/source/ic_launcher_splash.png android_gravity: center android_12: color: "#ffffff" image: assets/images/source/ic_launcher_foreground.png ffigen: name: "HiddifyCoreNativeLibrary" description: "Bindings to Hiddify Core Library" output: "lib/gen/hiddify_core_generated_bindings.dart" headers: entry-points: - "hiddify-core/bin/desktop.h" sentry: upload_debug_symbols: true upload_source_maps: true upload_sources: true log_level: info ignore_missing: true ================================================ FILE: scripts/package_windows.ps1 ================================================ New-Item -ItemType Directory -Force -Name "dist\tmp" New-Item -ItemType Directory -Force -Name "out" # windows setup # Get-ChildItem -Recurse -File -Path "dist" -Filter "*windows-setup.exe" | Copy-Item -Destination "dist\tmp\hiddify-next-setup.exe" -ErrorAction SilentlyContinue # Compress-Archive -Force -Path "dist\tmp\hiddify-next-setup.exe",".github\help\mac-windows\*.url" -DestinationPath "out\hiddify-windows-x64-setup.zip" Get-ChildItem -Recurse -File -Path "dist" -Filter "*windows-setup.exe" | Copy-Item -Destination "out\Hiddify-Windows-Setup-x64.exe" -ErrorAction SilentlyContinue Get-ChildItem -Recurse -File -Path "dist" -Filter "*windows.msix" | Copy-Item -Destination "out\Hiddify-Windows-Setup-x64.msix" -ErrorAction SilentlyContinue # windows portable xcopy "build\windows\x64\runner\Release" "dist\tmp\hiddify-next" /E/H/C/I/Y xcopy ".github\help\mac-windows\*.url" "dist\tmp\hiddify-next" /E/H/C/I/Y Compress-Archive -Force -Path "dist\tmp\hiddify-next" -DestinationPath "out\Hiddify-Windows-Portable-x64.zip" -ErrorAction SilentlyContinue Remove-Item -Path "$HOME\.pub-cache\git\cache\flutter_circle_flags*" -Force -Recurse -ErrorAction SilentlyContinue echo "Done" ================================================ FILE: snap/gui/app_icon.desktop ================================================ [Desktop Entry] Name=Flutter Linux App Comment=Flutter Linux launcher icon Exec=app_icon Icon=app_icon.png Terminal=false Type=Application Categories=Entertainment; ================================================ FILE: test/core/utils/ip_utils_test.dart ================================================ import 'package:flutter_test/flutter_test.dart'; import 'package:hiddify/core/utils/ip_utils.dart'; void main() { group("obscureIp", () { test("Should obscure parts of ipv4", () { const ipv4 = "1.1.1.1"; final obscured = obscureIp(ipv4); expect(obscured, "1.*.*.1"); }); test("Should obscure parts of full ipv6", () { const ipv6 = "FEDC:BA98:7654:3210:FEDC:BA98:7654:3210"; final obscured = obscureIp(ipv6); expect(obscured, "FEDC:****:****:****:****:****:****:****"); }); test("Should obscure parts of ipv6", () { const ipv6 = "::1"; final obscured = obscureIp(ipv6); expect(obscured, "::*"); }); }); } ================================================ FILE: test/drift/db/generated/schema.dart ================================================ // dart format width=80 // GENERATED CODE, DO NOT EDIT BY HAND. // ignore_for_file: type=lint import 'package:drift/drift.dart'; import 'package:drift/internal/migrations.dart'; import 'schema_v1.dart' as v1; import 'schema_v2.dart' as v2; import 'schema_v3.dart' as v3; import 'schema_v4.dart' as v4; import 'schema_v5.dart' as v5; class GeneratedHelper implements SchemaInstantiationHelper { @override GeneratedDatabase databaseForVersion(QueryExecutor db, int version) { switch (version) { case 1: return v1.DatabaseAtV1(db); case 2: return v2.DatabaseAtV2(db); case 3: return v3.DatabaseAtV3(db); case 4: return v4.DatabaseAtV4(db); case 5: return v5.DatabaseAtV5(db); default: throw MissingSchemaException(version, versions); } } static const versions = const [1, 2, 3, 4, 5]; } ================================================ FILE: test/drift/db/generated/schema_v1.dart ================================================ // dart format width=80 // GENERATED CODE, DO NOT EDIT BY HAND. // ignore_for_file: type=lint import 'package:drift/drift.dart'; class ProfileEntries extends Table with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; ProfileEntries(this.attachedDatabase, [this._alias]); late final GeneratedColumn id = GeneratedColumn( 'id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true, ); late final GeneratedColumn active = GeneratedColumn( 'active', aliasedName, false, type: DriftSqlType.bool, requiredDuringInsert: true, defaultConstraints: GeneratedColumn.constraintIsAlways( 'CHECK ("active" IN (0, 1))', ), ); late final GeneratedColumn name = GeneratedColumn( 'name', aliasedName, false, additionalChecks: GeneratedColumn.checkTextLength(minTextLength: 1), type: DriftSqlType.string, requiredDuringInsert: true, ); late final GeneratedColumn url = GeneratedColumn( 'url', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true, ); late final GeneratedColumn lastUpdate = GeneratedColumn( 'last_update', aliasedName, false, type: DriftSqlType.dateTime, requiredDuringInsert: true, ); late final GeneratedColumn updateInterval = GeneratedColumn( 'update_interval', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false, ); late final GeneratedColumn upload = GeneratedColumn( 'upload', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false, ); late final GeneratedColumn download = GeneratedColumn( 'download', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false, ); late final GeneratedColumn total = GeneratedColumn( 'total', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false, ); late final GeneratedColumn expire = GeneratedColumn( 'expire', aliasedName, true, type: DriftSqlType.dateTime, requiredDuringInsert: false, ); late final GeneratedColumn webPageUrl = GeneratedColumn( 'web_page_url', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false, ); late final GeneratedColumn supportUrl = GeneratedColumn( 'support_url', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false, ); @override List get $columns => [ id, active, name, url, lastUpdate, updateInterval, upload, download, total, expire, webPageUrl, supportUrl, ]; @override String get aliasedName => _alias ?? actualTableName; @override String get actualTableName => $name; static const String $name = 'profile_entries'; @override Set get $primaryKey => {id}; @override ProfileEntriesData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return ProfileEntriesData( id: attachedDatabase.typeMapping.read( DriftSqlType.string, data['${effectivePrefix}id'], )!, active: attachedDatabase.typeMapping.read( DriftSqlType.bool, data['${effectivePrefix}active'], )!, name: attachedDatabase.typeMapping.read( DriftSqlType.string, data['${effectivePrefix}name'], )!, url: attachedDatabase.typeMapping.read( DriftSqlType.string, data['${effectivePrefix}url'], )!, lastUpdate: attachedDatabase.typeMapping.read( DriftSqlType.dateTime, data['${effectivePrefix}last_update'], )!, updateInterval: attachedDatabase.typeMapping.read( DriftSqlType.int, data['${effectivePrefix}update_interval'], ), upload: attachedDatabase.typeMapping.read( DriftSqlType.int, data['${effectivePrefix}upload'], ), download: attachedDatabase.typeMapping.read( DriftSqlType.int, data['${effectivePrefix}download'], ), total: attachedDatabase.typeMapping.read( DriftSqlType.int, data['${effectivePrefix}total'], ), expire: attachedDatabase.typeMapping.read( DriftSqlType.dateTime, data['${effectivePrefix}expire'], ), webPageUrl: attachedDatabase.typeMapping.read( DriftSqlType.string, data['${effectivePrefix}web_page_url'], ), supportUrl: attachedDatabase.typeMapping.read( DriftSqlType.string, data['${effectivePrefix}support_url'], ), ); } @override ProfileEntries createAlias(String alias) { return ProfileEntries(attachedDatabase, alias); } } class ProfileEntriesData extends DataClass implements Insertable { final String id; final bool active; final String name; final String url; final DateTime lastUpdate; final int? updateInterval; final int? upload; final int? download; final int? total; final DateTime? expire; final String? webPageUrl; final String? supportUrl; const ProfileEntriesData({ required this.id, required this.active, required this.name, required this.url, required this.lastUpdate, this.updateInterval, this.upload, this.download, this.total, this.expire, this.webPageUrl, this.supportUrl, }); @override Map toColumns(bool nullToAbsent) { final map = {}; map['id'] = Variable(id); map['active'] = Variable(active); map['name'] = Variable(name); map['url'] = Variable(url); map['last_update'] = Variable(lastUpdate); if (!nullToAbsent || updateInterval != null) { map['update_interval'] = Variable(updateInterval); } if (!nullToAbsent || upload != null) { map['upload'] = Variable(upload); } if (!nullToAbsent || download != null) { map['download'] = Variable(download); } if (!nullToAbsent || total != null) { map['total'] = Variable(total); } if (!nullToAbsent || expire != null) { map['expire'] = Variable(expire); } if (!nullToAbsent || webPageUrl != null) { map['web_page_url'] = Variable(webPageUrl); } if (!nullToAbsent || supportUrl != null) { map['support_url'] = Variable(supportUrl); } return map; } ProfileEntriesCompanion toCompanion(bool nullToAbsent) { return ProfileEntriesCompanion( id: Value(id), active: Value(active), name: Value(name), url: Value(url), lastUpdate: Value(lastUpdate), updateInterval: updateInterval == null && nullToAbsent ? const Value.absent() : Value(updateInterval), upload: upload == null && nullToAbsent ? const Value.absent() : Value(upload), download: download == null && nullToAbsent ? const Value.absent() : Value(download), total: total == null && nullToAbsent ? const Value.absent() : Value(total), expire: expire == null && nullToAbsent ? const Value.absent() : Value(expire), webPageUrl: webPageUrl == null && nullToAbsent ? const Value.absent() : Value(webPageUrl), supportUrl: supportUrl == null && nullToAbsent ? const Value.absent() : Value(supportUrl), ); } factory ProfileEntriesData.fromJson( Map json, { ValueSerializer? serializer, }) { serializer ??= driftRuntimeOptions.defaultSerializer; return ProfileEntriesData( id: serializer.fromJson(json['id']), active: serializer.fromJson(json['active']), name: serializer.fromJson(json['name']), url: serializer.fromJson(json['url']), lastUpdate: serializer.fromJson(json['lastUpdate']), updateInterval: serializer.fromJson(json['updateInterval']), upload: serializer.fromJson(json['upload']), download: serializer.fromJson(json['download']), total: serializer.fromJson(json['total']), expire: serializer.fromJson(json['expire']), webPageUrl: serializer.fromJson(json['webPageUrl']), supportUrl: serializer.fromJson(json['supportUrl']), ); } @override Map toJson({ValueSerializer? serializer}) { serializer ??= driftRuntimeOptions.defaultSerializer; return { 'id': serializer.toJson(id), 'active': serializer.toJson(active), 'name': serializer.toJson(name), 'url': serializer.toJson(url), 'lastUpdate': serializer.toJson(lastUpdate), 'updateInterval': serializer.toJson(updateInterval), 'upload': serializer.toJson(upload), 'download': serializer.toJson(download), 'total': serializer.toJson(total), 'expire': serializer.toJson(expire), 'webPageUrl': serializer.toJson(webPageUrl), 'supportUrl': serializer.toJson(supportUrl), }; } ProfileEntriesData copyWith({ String? id, bool? active, String? name, String? url, DateTime? lastUpdate, Value updateInterval = const Value.absent(), Value upload = const Value.absent(), Value download = const Value.absent(), Value total = const Value.absent(), Value expire = const Value.absent(), Value webPageUrl = const Value.absent(), Value supportUrl = const Value.absent(), }) => ProfileEntriesData( id: id ?? this.id, active: active ?? this.active, name: name ?? this.name, url: url ?? this.url, lastUpdate: lastUpdate ?? this.lastUpdate, updateInterval: updateInterval.present ? updateInterval.value : this.updateInterval, upload: upload.present ? upload.value : this.upload, download: download.present ? download.value : this.download, total: total.present ? total.value : this.total, expire: expire.present ? expire.value : this.expire, webPageUrl: webPageUrl.present ? webPageUrl.value : this.webPageUrl, supportUrl: supportUrl.present ? supportUrl.value : this.supportUrl, ); ProfileEntriesData copyWithCompanion(ProfileEntriesCompanion data) { return ProfileEntriesData( id: data.id.present ? data.id.value : this.id, active: data.active.present ? data.active.value : this.active, name: data.name.present ? data.name.value : this.name, url: data.url.present ? data.url.value : this.url, lastUpdate: data.lastUpdate.present ? data.lastUpdate.value : this.lastUpdate, updateInterval: data.updateInterval.present ? data.updateInterval.value : this.updateInterval, upload: data.upload.present ? data.upload.value : this.upload, download: data.download.present ? data.download.value : this.download, total: data.total.present ? data.total.value : this.total, expire: data.expire.present ? data.expire.value : this.expire, webPageUrl: data.webPageUrl.present ? data.webPageUrl.value : this.webPageUrl, supportUrl: data.supportUrl.present ? data.supportUrl.value : this.supportUrl, ); } @override String toString() { return (StringBuffer('ProfileEntriesData(') ..write('id: $id, ') ..write('active: $active, ') ..write('name: $name, ') ..write('url: $url, ') ..write('lastUpdate: $lastUpdate, ') ..write('updateInterval: $updateInterval, ') ..write('upload: $upload, ') ..write('download: $download, ') ..write('total: $total, ') ..write('expire: $expire, ') ..write('webPageUrl: $webPageUrl, ') ..write('supportUrl: $supportUrl') ..write(')')) .toString(); } @override int get hashCode => Object.hash( id, active, name, url, lastUpdate, updateInterval, upload, download, total, expire, webPageUrl, supportUrl, ); @override bool operator ==(Object other) => identical(this, other) || (other is ProfileEntriesData && other.id == this.id && other.active == this.active && other.name == this.name && other.url == this.url && other.lastUpdate == this.lastUpdate && other.updateInterval == this.updateInterval && other.upload == this.upload && other.download == this.download && other.total == this.total && other.expire == this.expire && other.webPageUrl == this.webPageUrl && other.supportUrl == this.supportUrl); } class ProfileEntriesCompanion extends UpdateCompanion { final Value id; final Value active; final Value name; final Value url; final Value lastUpdate; final Value updateInterval; final Value upload; final Value download; final Value total; final Value expire; final Value webPageUrl; final Value supportUrl; final Value rowid; const ProfileEntriesCompanion({ this.id = const Value.absent(), this.active = const Value.absent(), this.name = const Value.absent(), this.url = const Value.absent(), this.lastUpdate = const Value.absent(), this.updateInterval = const Value.absent(), this.upload = const Value.absent(), this.download = const Value.absent(), this.total = const Value.absent(), this.expire = const Value.absent(), this.webPageUrl = const Value.absent(), this.supportUrl = const Value.absent(), this.rowid = const Value.absent(), }); ProfileEntriesCompanion.insert({ required String id, required bool active, required String name, required String url, required DateTime lastUpdate, this.updateInterval = const Value.absent(), this.upload = const Value.absent(), this.download = const Value.absent(), this.total = const Value.absent(), this.expire = const Value.absent(), this.webPageUrl = const Value.absent(), this.supportUrl = const Value.absent(), this.rowid = const Value.absent(), }) : id = Value(id), active = Value(active), name = Value(name), url = Value(url), lastUpdate = Value(lastUpdate); static Insertable custom({ Expression? id, Expression? active, Expression? name, Expression? url, Expression? lastUpdate, Expression? updateInterval, Expression? upload, Expression? download, Expression? total, Expression? expire, Expression? webPageUrl, Expression? supportUrl, Expression? rowid, }) { return RawValuesInsertable({ if (id != null) 'id': id, if (active != null) 'active': active, if (name != null) 'name': name, if (url != null) 'url': url, if (lastUpdate != null) 'last_update': lastUpdate, if (updateInterval != null) 'update_interval': updateInterval, if (upload != null) 'upload': upload, if (download != null) 'download': download, if (total != null) 'total': total, if (expire != null) 'expire': expire, if (webPageUrl != null) 'web_page_url': webPageUrl, if (supportUrl != null) 'support_url': supportUrl, if (rowid != null) 'rowid': rowid, }); } ProfileEntriesCompanion copyWith({ Value? id, Value? active, Value? name, Value? url, Value? lastUpdate, Value? updateInterval, Value? upload, Value? download, Value? total, Value? expire, Value? webPageUrl, Value? supportUrl, Value? rowid, }) { return ProfileEntriesCompanion( id: id ?? this.id, active: active ?? this.active, name: name ?? this.name, url: url ?? this.url, lastUpdate: lastUpdate ?? this.lastUpdate, updateInterval: updateInterval ?? this.updateInterval, upload: upload ?? this.upload, download: download ?? this.download, total: total ?? this.total, expire: expire ?? this.expire, webPageUrl: webPageUrl ?? this.webPageUrl, supportUrl: supportUrl ?? this.supportUrl, rowid: rowid ?? this.rowid, ); } @override Map toColumns(bool nullToAbsent) { final map = {}; if (id.present) { map['id'] = Variable(id.value); } if (active.present) { map['active'] = Variable(active.value); } if (name.present) { map['name'] = Variable(name.value); } if (url.present) { map['url'] = Variable(url.value); } if (lastUpdate.present) { map['last_update'] = Variable(lastUpdate.value); } if (updateInterval.present) { map['update_interval'] = Variable(updateInterval.value); } if (upload.present) { map['upload'] = Variable(upload.value); } if (download.present) { map['download'] = Variable(download.value); } if (total.present) { map['total'] = Variable(total.value); } if (expire.present) { map['expire'] = Variable(expire.value); } if (webPageUrl.present) { map['web_page_url'] = Variable(webPageUrl.value); } if (supportUrl.present) { map['support_url'] = Variable(supportUrl.value); } if (rowid.present) { map['rowid'] = Variable(rowid.value); } return map; } @override String toString() { return (StringBuffer('ProfileEntriesCompanion(') ..write('id: $id, ') ..write('active: $active, ') ..write('name: $name, ') ..write('url: $url, ') ..write('lastUpdate: $lastUpdate, ') ..write('updateInterval: $updateInterval, ') ..write('upload: $upload, ') ..write('download: $download, ') ..write('total: $total, ') ..write('expire: $expire, ') ..write('webPageUrl: $webPageUrl, ') ..write('supportUrl: $supportUrl, ') ..write('rowid: $rowid') ..write(')')) .toString(); } } class DatabaseAtV1 extends GeneratedDatabase { DatabaseAtV1(QueryExecutor e) : super(e); late final ProfileEntries profileEntries = ProfileEntries(this); @override Iterable> get allTables => allSchemaEntities.whereType>(); @override List get allSchemaEntities => [profileEntries]; @override int get schemaVersion => 1; @override DriftDatabaseOptions get options => const DriftDatabaseOptions(storeDateTimeAsText: true); } ================================================ FILE: test/drift/db/generated/schema_v2.dart ================================================ // dart format width=80 // GENERATED CODE, DO NOT EDIT BY HAND. // ignore_for_file: type=lint import 'package:drift/drift.dart'; class ProfileEntries extends Table with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; ProfileEntries(this.attachedDatabase, [this._alias]); late final GeneratedColumn id = GeneratedColumn( 'id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true, ); late final GeneratedColumn type = GeneratedColumn( 'type', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true, ); late final GeneratedColumn active = GeneratedColumn( 'active', aliasedName, false, type: DriftSqlType.bool, requiredDuringInsert: true, defaultConstraints: GeneratedColumn.constraintIsAlways( 'CHECK ("active" IN (0, 1))', ), ); late final GeneratedColumn name = GeneratedColumn( 'name', aliasedName, false, additionalChecks: GeneratedColumn.checkTextLength(minTextLength: 1), type: DriftSqlType.string, requiredDuringInsert: true, ); late final GeneratedColumn url = GeneratedColumn( 'url', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false, ); late final GeneratedColumn lastUpdate = GeneratedColumn( 'last_update', aliasedName, false, type: DriftSqlType.dateTime, requiredDuringInsert: true, ); late final GeneratedColumn updateInterval = GeneratedColumn( 'update_interval', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false, ); late final GeneratedColumn upload = GeneratedColumn( 'upload', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false, ); late final GeneratedColumn download = GeneratedColumn( 'download', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false, ); late final GeneratedColumn total = GeneratedColumn( 'total', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false, ); late final GeneratedColumn expire = GeneratedColumn( 'expire', aliasedName, true, type: DriftSqlType.dateTime, requiredDuringInsert: false, ); late final GeneratedColumn webPageUrl = GeneratedColumn( 'web_page_url', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false, ); late final GeneratedColumn supportUrl = GeneratedColumn( 'support_url', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false, ); @override List get $columns => [ id, type, active, name, url, lastUpdate, updateInterval, upload, download, total, expire, webPageUrl, supportUrl, ]; @override String get aliasedName => _alias ?? actualTableName; @override String get actualTableName => $name; static const String $name = 'profile_entries'; @override Set get $primaryKey => {id}; @override ProfileEntriesData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return ProfileEntriesData( id: attachedDatabase.typeMapping.read( DriftSqlType.string, data['${effectivePrefix}id'], )!, type: attachedDatabase.typeMapping.read( DriftSqlType.string, data['${effectivePrefix}type'], )!, active: attachedDatabase.typeMapping.read( DriftSqlType.bool, data['${effectivePrefix}active'], )!, name: attachedDatabase.typeMapping.read( DriftSqlType.string, data['${effectivePrefix}name'], )!, url: attachedDatabase.typeMapping.read( DriftSqlType.string, data['${effectivePrefix}url'], ), lastUpdate: attachedDatabase.typeMapping.read( DriftSqlType.dateTime, data['${effectivePrefix}last_update'], )!, updateInterval: attachedDatabase.typeMapping.read( DriftSqlType.int, data['${effectivePrefix}update_interval'], ), upload: attachedDatabase.typeMapping.read( DriftSqlType.int, data['${effectivePrefix}upload'], ), download: attachedDatabase.typeMapping.read( DriftSqlType.int, data['${effectivePrefix}download'], ), total: attachedDatabase.typeMapping.read( DriftSqlType.int, data['${effectivePrefix}total'], ), expire: attachedDatabase.typeMapping.read( DriftSqlType.dateTime, data['${effectivePrefix}expire'], ), webPageUrl: attachedDatabase.typeMapping.read( DriftSqlType.string, data['${effectivePrefix}web_page_url'], ), supportUrl: attachedDatabase.typeMapping.read( DriftSqlType.string, data['${effectivePrefix}support_url'], ), ); } @override ProfileEntries createAlias(String alias) { return ProfileEntries(attachedDatabase, alias); } } class ProfileEntriesData extends DataClass implements Insertable { final String id; final String type; final bool active; final String name; final String? url; final DateTime lastUpdate; final int? updateInterval; final int? upload; final int? download; final int? total; final DateTime? expire; final String? webPageUrl; final String? supportUrl; const ProfileEntriesData({ required this.id, required this.type, required this.active, required this.name, this.url, required this.lastUpdate, this.updateInterval, this.upload, this.download, this.total, this.expire, this.webPageUrl, this.supportUrl, }); @override Map toColumns(bool nullToAbsent) { final map = {}; map['id'] = Variable(id); map['type'] = Variable(type); map['active'] = Variable(active); map['name'] = Variable(name); if (!nullToAbsent || url != null) { map['url'] = Variable(url); } map['last_update'] = Variable(lastUpdate); if (!nullToAbsent || updateInterval != null) { map['update_interval'] = Variable(updateInterval); } if (!nullToAbsent || upload != null) { map['upload'] = Variable(upload); } if (!nullToAbsent || download != null) { map['download'] = Variable(download); } if (!nullToAbsent || total != null) { map['total'] = Variable(total); } if (!nullToAbsent || expire != null) { map['expire'] = Variable(expire); } if (!nullToAbsent || webPageUrl != null) { map['web_page_url'] = Variable(webPageUrl); } if (!nullToAbsent || supportUrl != null) { map['support_url'] = Variable(supportUrl); } return map; } ProfileEntriesCompanion toCompanion(bool nullToAbsent) { return ProfileEntriesCompanion( id: Value(id), type: Value(type), active: Value(active), name: Value(name), url: url == null && nullToAbsent ? const Value.absent() : Value(url), lastUpdate: Value(lastUpdate), updateInterval: updateInterval == null && nullToAbsent ? const Value.absent() : Value(updateInterval), upload: upload == null && nullToAbsent ? const Value.absent() : Value(upload), download: download == null && nullToAbsent ? const Value.absent() : Value(download), total: total == null && nullToAbsent ? const Value.absent() : Value(total), expire: expire == null && nullToAbsent ? const Value.absent() : Value(expire), webPageUrl: webPageUrl == null && nullToAbsent ? const Value.absent() : Value(webPageUrl), supportUrl: supportUrl == null && nullToAbsent ? const Value.absent() : Value(supportUrl), ); } factory ProfileEntriesData.fromJson( Map json, { ValueSerializer? serializer, }) { serializer ??= driftRuntimeOptions.defaultSerializer; return ProfileEntriesData( id: serializer.fromJson(json['id']), type: serializer.fromJson(json['type']), active: serializer.fromJson(json['active']), name: serializer.fromJson(json['name']), url: serializer.fromJson(json['url']), lastUpdate: serializer.fromJson(json['lastUpdate']), updateInterval: serializer.fromJson(json['updateInterval']), upload: serializer.fromJson(json['upload']), download: serializer.fromJson(json['download']), total: serializer.fromJson(json['total']), expire: serializer.fromJson(json['expire']), webPageUrl: serializer.fromJson(json['webPageUrl']), supportUrl: serializer.fromJson(json['supportUrl']), ); } @override Map toJson({ValueSerializer? serializer}) { serializer ??= driftRuntimeOptions.defaultSerializer; return { 'id': serializer.toJson(id), 'type': serializer.toJson(type), 'active': serializer.toJson(active), 'name': serializer.toJson(name), 'url': serializer.toJson(url), 'lastUpdate': serializer.toJson(lastUpdate), 'updateInterval': serializer.toJson(updateInterval), 'upload': serializer.toJson(upload), 'download': serializer.toJson(download), 'total': serializer.toJson(total), 'expire': serializer.toJson(expire), 'webPageUrl': serializer.toJson(webPageUrl), 'supportUrl': serializer.toJson(supportUrl), }; } ProfileEntriesData copyWith({ String? id, String? type, bool? active, String? name, Value url = const Value.absent(), DateTime? lastUpdate, Value updateInterval = const Value.absent(), Value upload = const Value.absent(), Value download = const Value.absent(), Value total = const Value.absent(), Value expire = const Value.absent(), Value webPageUrl = const Value.absent(), Value supportUrl = const Value.absent(), }) => ProfileEntriesData( id: id ?? this.id, type: type ?? this.type, active: active ?? this.active, name: name ?? this.name, url: url.present ? url.value : this.url, lastUpdate: lastUpdate ?? this.lastUpdate, updateInterval: updateInterval.present ? updateInterval.value : this.updateInterval, upload: upload.present ? upload.value : this.upload, download: download.present ? download.value : this.download, total: total.present ? total.value : this.total, expire: expire.present ? expire.value : this.expire, webPageUrl: webPageUrl.present ? webPageUrl.value : this.webPageUrl, supportUrl: supportUrl.present ? supportUrl.value : this.supportUrl, ); ProfileEntriesData copyWithCompanion(ProfileEntriesCompanion data) { return ProfileEntriesData( id: data.id.present ? data.id.value : this.id, type: data.type.present ? data.type.value : this.type, active: data.active.present ? data.active.value : this.active, name: data.name.present ? data.name.value : this.name, url: data.url.present ? data.url.value : this.url, lastUpdate: data.lastUpdate.present ? data.lastUpdate.value : this.lastUpdate, updateInterval: data.updateInterval.present ? data.updateInterval.value : this.updateInterval, upload: data.upload.present ? data.upload.value : this.upload, download: data.download.present ? data.download.value : this.download, total: data.total.present ? data.total.value : this.total, expire: data.expire.present ? data.expire.value : this.expire, webPageUrl: data.webPageUrl.present ? data.webPageUrl.value : this.webPageUrl, supportUrl: data.supportUrl.present ? data.supportUrl.value : this.supportUrl, ); } @override String toString() { return (StringBuffer('ProfileEntriesData(') ..write('id: $id, ') ..write('type: $type, ') ..write('active: $active, ') ..write('name: $name, ') ..write('url: $url, ') ..write('lastUpdate: $lastUpdate, ') ..write('updateInterval: $updateInterval, ') ..write('upload: $upload, ') ..write('download: $download, ') ..write('total: $total, ') ..write('expire: $expire, ') ..write('webPageUrl: $webPageUrl, ') ..write('supportUrl: $supportUrl') ..write(')')) .toString(); } @override int get hashCode => Object.hash( id, type, active, name, url, lastUpdate, updateInterval, upload, download, total, expire, webPageUrl, supportUrl, ); @override bool operator ==(Object other) => identical(this, other) || (other is ProfileEntriesData && other.id == this.id && other.type == this.type && other.active == this.active && other.name == this.name && other.url == this.url && other.lastUpdate == this.lastUpdate && other.updateInterval == this.updateInterval && other.upload == this.upload && other.download == this.download && other.total == this.total && other.expire == this.expire && other.webPageUrl == this.webPageUrl && other.supportUrl == this.supportUrl); } class ProfileEntriesCompanion extends UpdateCompanion { final Value id; final Value type; final Value active; final Value name; final Value url; final Value lastUpdate; final Value updateInterval; final Value upload; final Value download; final Value total; final Value expire; final Value webPageUrl; final Value supportUrl; final Value rowid; const ProfileEntriesCompanion({ this.id = const Value.absent(), this.type = const Value.absent(), this.active = const Value.absent(), this.name = const Value.absent(), this.url = const Value.absent(), this.lastUpdate = const Value.absent(), this.updateInterval = const Value.absent(), this.upload = const Value.absent(), this.download = const Value.absent(), this.total = const Value.absent(), this.expire = const Value.absent(), this.webPageUrl = const Value.absent(), this.supportUrl = const Value.absent(), this.rowid = const Value.absent(), }); ProfileEntriesCompanion.insert({ required String id, required String type, required bool active, required String name, this.url = const Value.absent(), required DateTime lastUpdate, this.updateInterval = const Value.absent(), this.upload = const Value.absent(), this.download = const Value.absent(), this.total = const Value.absent(), this.expire = const Value.absent(), this.webPageUrl = const Value.absent(), this.supportUrl = const Value.absent(), this.rowid = const Value.absent(), }) : id = Value(id), type = Value(type), active = Value(active), name = Value(name), lastUpdate = Value(lastUpdate); static Insertable custom({ Expression? id, Expression? type, Expression? active, Expression? name, Expression? url, Expression? lastUpdate, Expression? updateInterval, Expression? upload, Expression? download, Expression? total, Expression? expire, Expression? webPageUrl, Expression? supportUrl, Expression? rowid, }) { return RawValuesInsertable({ if (id != null) 'id': id, if (type != null) 'type': type, if (active != null) 'active': active, if (name != null) 'name': name, if (url != null) 'url': url, if (lastUpdate != null) 'last_update': lastUpdate, if (updateInterval != null) 'update_interval': updateInterval, if (upload != null) 'upload': upload, if (download != null) 'download': download, if (total != null) 'total': total, if (expire != null) 'expire': expire, if (webPageUrl != null) 'web_page_url': webPageUrl, if (supportUrl != null) 'support_url': supportUrl, if (rowid != null) 'rowid': rowid, }); } ProfileEntriesCompanion copyWith({ Value? id, Value? type, Value? active, Value? name, Value? url, Value? lastUpdate, Value? updateInterval, Value? upload, Value? download, Value? total, Value? expire, Value? webPageUrl, Value? supportUrl, Value? rowid, }) { return ProfileEntriesCompanion( id: id ?? this.id, type: type ?? this.type, active: active ?? this.active, name: name ?? this.name, url: url ?? this.url, lastUpdate: lastUpdate ?? this.lastUpdate, updateInterval: updateInterval ?? this.updateInterval, upload: upload ?? this.upload, download: download ?? this.download, total: total ?? this.total, expire: expire ?? this.expire, webPageUrl: webPageUrl ?? this.webPageUrl, supportUrl: supportUrl ?? this.supportUrl, rowid: rowid ?? this.rowid, ); } @override Map toColumns(bool nullToAbsent) { final map = {}; if (id.present) { map['id'] = Variable(id.value); } if (type.present) { map['type'] = Variable(type.value); } if (active.present) { map['active'] = Variable(active.value); } if (name.present) { map['name'] = Variable(name.value); } if (url.present) { map['url'] = Variable(url.value); } if (lastUpdate.present) { map['last_update'] = Variable(lastUpdate.value); } if (updateInterval.present) { map['update_interval'] = Variable(updateInterval.value); } if (upload.present) { map['upload'] = Variable(upload.value); } if (download.present) { map['download'] = Variable(download.value); } if (total.present) { map['total'] = Variable(total.value); } if (expire.present) { map['expire'] = Variable(expire.value); } if (webPageUrl.present) { map['web_page_url'] = Variable(webPageUrl.value); } if (supportUrl.present) { map['support_url'] = Variable(supportUrl.value); } if (rowid.present) { map['rowid'] = Variable(rowid.value); } return map; } @override String toString() { return (StringBuffer('ProfileEntriesCompanion(') ..write('id: $id, ') ..write('type: $type, ') ..write('active: $active, ') ..write('name: $name, ') ..write('url: $url, ') ..write('lastUpdate: $lastUpdate, ') ..write('updateInterval: $updateInterval, ') ..write('upload: $upload, ') ..write('download: $download, ') ..write('total: $total, ') ..write('expire: $expire, ') ..write('webPageUrl: $webPageUrl, ') ..write('supportUrl: $supportUrl, ') ..write('rowid: $rowid') ..write(')')) .toString(); } } class DatabaseAtV2 extends GeneratedDatabase { DatabaseAtV2(QueryExecutor e) : super(e); late final ProfileEntries profileEntries = ProfileEntries(this); @override Iterable> get allTables => allSchemaEntities.whereType>(); @override List get allSchemaEntities => [profileEntries]; @override int get schemaVersion => 2; @override DriftDatabaseOptions get options => const DriftDatabaseOptions(storeDateTimeAsText: true); } ================================================ FILE: test/drift/db/generated/schema_v3.dart ================================================ // dart format width=80 // GENERATED CODE, DO NOT EDIT BY HAND. // ignore_for_file: type=lint import 'package:drift/drift.dart'; class ProfileEntries extends Table with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; ProfileEntries(this.attachedDatabase, [this._alias]); late final GeneratedColumn id = GeneratedColumn( 'id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true, ); late final GeneratedColumn type = GeneratedColumn( 'type', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true, ); late final GeneratedColumn active = GeneratedColumn( 'active', aliasedName, false, type: DriftSqlType.bool, requiredDuringInsert: true, defaultConstraints: GeneratedColumn.constraintIsAlways( 'CHECK ("active" IN (0, 1))', ), ); late final GeneratedColumn name = GeneratedColumn( 'name', aliasedName, false, additionalChecks: GeneratedColumn.checkTextLength(minTextLength: 1), type: DriftSqlType.string, requiredDuringInsert: true, ); late final GeneratedColumn url = GeneratedColumn( 'url', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false, ); late final GeneratedColumn lastUpdate = GeneratedColumn( 'last_update', aliasedName, false, type: DriftSqlType.dateTime, requiredDuringInsert: true, ); late final GeneratedColumn updateInterval = GeneratedColumn( 'update_interval', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false, ); late final GeneratedColumn upload = GeneratedColumn( 'upload', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false, ); late final GeneratedColumn download = GeneratedColumn( 'download', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false, ); late final GeneratedColumn total = GeneratedColumn( 'total', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false, ); late final GeneratedColumn expire = GeneratedColumn( 'expire', aliasedName, true, type: DriftSqlType.dateTime, requiredDuringInsert: false, ); late final GeneratedColumn webPageUrl = GeneratedColumn( 'web_page_url', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false, ); late final GeneratedColumn supportUrl = GeneratedColumn( 'support_url', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false, ); @override List get $columns => [ id, type, active, name, url, lastUpdate, updateInterval, upload, download, total, expire, webPageUrl, supportUrl, ]; @override String get aliasedName => _alias ?? actualTableName; @override String get actualTableName => $name; static const String $name = 'profile_entries'; @override Set get $primaryKey => {id}; @override ProfileEntriesData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return ProfileEntriesData( id: attachedDatabase.typeMapping.read( DriftSqlType.string, data['${effectivePrefix}id'], )!, type: attachedDatabase.typeMapping.read( DriftSqlType.string, data['${effectivePrefix}type'], )!, active: attachedDatabase.typeMapping.read( DriftSqlType.bool, data['${effectivePrefix}active'], )!, name: attachedDatabase.typeMapping.read( DriftSqlType.string, data['${effectivePrefix}name'], )!, url: attachedDatabase.typeMapping.read( DriftSqlType.string, data['${effectivePrefix}url'], ), lastUpdate: attachedDatabase.typeMapping.read( DriftSqlType.dateTime, data['${effectivePrefix}last_update'], )!, updateInterval: attachedDatabase.typeMapping.read( DriftSqlType.int, data['${effectivePrefix}update_interval'], ), upload: attachedDatabase.typeMapping.read( DriftSqlType.int, data['${effectivePrefix}upload'], ), download: attachedDatabase.typeMapping.read( DriftSqlType.int, data['${effectivePrefix}download'], ), total: attachedDatabase.typeMapping.read( DriftSqlType.int, data['${effectivePrefix}total'], ), expire: attachedDatabase.typeMapping.read( DriftSqlType.dateTime, data['${effectivePrefix}expire'], ), webPageUrl: attachedDatabase.typeMapping.read( DriftSqlType.string, data['${effectivePrefix}web_page_url'], ), supportUrl: attachedDatabase.typeMapping.read( DriftSqlType.string, data['${effectivePrefix}support_url'], ), ); } @override ProfileEntries createAlias(String alias) { return ProfileEntries(attachedDatabase, alias); } } class ProfileEntriesData extends DataClass implements Insertable { final String id; final String type; final bool active; final String name; final String? url; final DateTime lastUpdate; final int? updateInterval; final int? upload; final int? download; final int? total; final DateTime? expire; final String? webPageUrl; final String? supportUrl; const ProfileEntriesData({ required this.id, required this.type, required this.active, required this.name, this.url, required this.lastUpdate, this.updateInterval, this.upload, this.download, this.total, this.expire, this.webPageUrl, this.supportUrl, }); @override Map toColumns(bool nullToAbsent) { final map = {}; map['id'] = Variable(id); map['type'] = Variable(type); map['active'] = Variable(active); map['name'] = Variable(name); if (!nullToAbsent || url != null) { map['url'] = Variable(url); } map['last_update'] = Variable(lastUpdate); if (!nullToAbsent || updateInterval != null) { map['update_interval'] = Variable(updateInterval); } if (!nullToAbsent || upload != null) { map['upload'] = Variable(upload); } if (!nullToAbsent || download != null) { map['download'] = Variable(download); } if (!nullToAbsent || total != null) { map['total'] = Variable(total); } if (!nullToAbsent || expire != null) { map['expire'] = Variable(expire); } if (!nullToAbsent || webPageUrl != null) { map['web_page_url'] = Variable(webPageUrl); } if (!nullToAbsent || supportUrl != null) { map['support_url'] = Variable(supportUrl); } return map; } ProfileEntriesCompanion toCompanion(bool nullToAbsent) { return ProfileEntriesCompanion( id: Value(id), type: Value(type), active: Value(active), name: Value(name), url: url == null && nullToAbsent ? const Value.absent() : Value(url), lastUpdate: Value(lastUpdate), updateInterval: updateInterval == null && nullToAbsent ? const Value.absent() : Value(updateInterval), upload: upload == null && nullToAbsent ? const Value.absent() : Value(upload), download: download == null && nullToAbsent ? const Value.absent() : Value(download), total: total == null && nullToAbsent ? const Value.absent() : Value(total), expire: expire == null && nullToAbsent ? const Value.absent() : Value(expire), webPageUrl: webPageUrl == null && nullToAbsent ? const Value.absent() : Value(webPageUrl), supportUrl: supportUrl == null && nullToAbsent ? const Value.absent() : Value(supportUrl), ); } factory ProfileEntriesData.fromJson( Map json, { ValueSerializer? serializer, }) { serializer ??= driftRuntimeOptions.defaultSerializer; return ProfileEntriesData( id: serializer.fromJson(json['id']), type: serializer.fromJson(json['type']), active: serializer.fromJson(json['active']), name: serializer.fromJson(json['name']), url: serializer.fromJson(json['url']), lastUpdate: serializer.fromJson(json['lastUpdate']), updateInterval: serializer.fromJson(json['updateInterval']), upload: serializer.fromJson(json['upload']), download: serializer.fromJson(json['download']), total: serializer.fromJson(json['total']), expire: serializer.fromJson(json['expire']), webPageUrl: serializer.fromJson(json['webPageUrl']), supportUrl: serializer.fromJson(json['supportUrl']), ); } @override Map toJson({ValueSerializer? serializer}) { serializer ??= driftRuntimeOptions.defaultSerializer; return { 'id': serializer.toJson(id), 'type': serializer.toJson(type), 'active': serializer.toJson(active), 'name': serializer.toJson(name), 'url': serializer.toJson(url), 'lastUpdate': serializer.toJson(lastUpdate), 'updateInterval': serializer.toJson(updateInterval), 'upload': serializer.toJson(upload), 'download': serializer.toJson(download), 'total': serializer.toJson(total), 'expire': serializer.toJson(expire), 'webPageUrl': serializer.toJson(webPageUrl), 'supportUrl': serializer.toJson(supportUrl), }; } ProfileEntriesData copyWith({ String? id, String? type, bool? active, String? name, Value url = const Value.absent(), DateTime? lastUpdate, Value updateInterval = const Value.absent(), Value upload = const Value.absent(), Value download = const Value.absent(), Value total = const Value.absent(), Value expire = const Value.absent(), Value webPageUrl = const Value.absent(), Value supportUrl = const Value.absent(), }) => ProfileEntriesData( id: id ?? this.id, type: type ?? this.type, active: active ?? this.active, name: name ?? this.name, url: url.present ? url.value : this.url, lastUpdate: lastUpdate ?? this.lastUpdate, updateInterval: updateInterval.present ? updateInterval.value : this.updateInterval, upload: upload.present ? upload.value : this.upload, download: download.present ? download.value : this.download, total: total.present ? total.value : this.total, expire: expire.present ? expire.value : this.expire, webPageUrl: webPageUrl.present ? webPageUrl.value : this.webPageUrl, supportUrl: supportUrl.present ? supportUrl.value : this.supportUrl, ); ProfileEntriesData copyWithCompanion(ProfileEntriesCompanion data) { return ProfileEntriesData( id: data.id.present ? data.id.value : this.id, type: data.type.present ? data.type.value : this.type, active: data.active.present ? data.active.value : this.active, name: data.name.present ? data.name.value : this.name, url: data.url.present ? data.url.value : this.url, lastUpdate: data.lastUpdate.present ? data.lastUpdate.value : this.lastUpdate, updateInterval: data.updateInterval.present ? data.updateInterval.value : this.updateInterval, upload: data.upload.present ? data.upload.value : this.upload, download: data.download.present ? data.download.value : this.download, total: data.total.present ? data.total.value : this.total, expire: data.expire.present ? data.expire.value : this.expire, webPageUrl: data.webPageUrl.present ? data.webPageUrl.value : this.webPageUrl, supportUrl: data.supportUrl.present ? data.supportUrl.value : this.supportUrl, ); } @override String toString() { return (StringBuffer('ProfileEntriesData(') ..write('id: $id, ') ..write('type: $type, ') ..write('active: $active, ') ..write('name: $name, ') ..write('url: $url, ') ..write('lastUpdate: $lastUpdate, ') ..write('updateInterval: $updateInterval, ') ..write('upload: $upload, ') ..write('download: $download, ') ..write('total: $total, ') ..write('expire: $expire, ') ..write('webPageUrl: $webPageUrl, ') ..write('supportUrl: $supportUrl') ..write(')')) .toString(); } @override int get hashCode => Object.hash( id, type, active, name, url, lastUpdate, updateInterval, upload, download, total, expire, webPageUrl, supportUrl, ); @override bool operator ==(Object other) => identical(this, other) || (other is ProfileEntriesData && other.id == this.id && other.type == this.type && other.active == this.active && other.name == this.name && other.url == this.url && other.lastUpdate == this.lastUpdate && other.updateInterval == this.updateInterval && other.upload == this.upload && other.download == this.download && other.total == this.total && other.expire == this.expire && other.webPageUrl == this.webPageUrl && other.supportUrl == this.supportUrl); } class ProfileEntriesCompanion extends UpdateCompanion { final Value id; final Value type; final Value active; final Value name; final Value url; final Value lastUpdate; final Value updateInterval; final Value upload; final Value download; final Value total; final Value expire; final Value webPageUrl; final Value supportUrl; final Value rowid; const ProfileEntriesCompanion({ this.id = const Value.absent(), this.type = const Value.absent(), this.active = const Value.absent(), this.name = const Value.absent(), this.url = const Value.absent(), this.lastUpdate = const Value.absent(), this.updateInterval = const Value.absent(), this.upload = const Value.absent(), this.download = const Value.absent(), this.total = const Value.absent(), this.expire = const Value.absent(), this.webPageUrl = const Value.absent(), this.supportUrl = const Value.absent(), this.rowid = const Value.absent(), }); ProfileEntriesCompanion.insert({ required String id, required String type, required bool active, required String name, this.url = const Value.absent(), required DateTime lastUpdate, this.updateInterval = const Value.absent(), this.upload = const Value.absent(), this.download = const Value.absent(), this.total = const Value.absent(), this.expire = const Value.absent(), this.webPageUrl = const Value.absent(), this.supportUrl = const Value.absent(), this.rowid = const Value.absent(), }) : id = Value(id), type = Value(type), active = Value(active), name = Value(name), lastUpdate = Value(lastUpdate); static Insertable custom({ Expression? id, Expression? type, Expression? active, Expression? name, Expression? url, Expression? lastUpdate, Expression? updateInterval, Expression? upload, Expression? download, Expression? total, Expression? expire, Expression? webPageUrl, Expression? supportUrl, Expression? rowid, }) { return RawValuesInsertable({ if (id != null) 'id': id, if (type != null) 'type': type, if (active != null) 'active': active, if (name != null) 'name': name, if (url != null) 'url': url, if (lastUpdate != null) 'last_update': lastUpdate, if (updateInterval != null) 'update_interval': updateInterval, if (upload != null) 'upload': upload, if (download != null) 'download': download, if (total != null) 'total': total, if (expire != null) 'expire': expire, if (webPageUrl != null) 'web_page_url': webPageUrl, if (supportUrl != null) 'support_url': supportUrl, if (rowid != null) 'rowid': rowid, }); } ProfileEntriesCompanion copyWith({ Value? id, Value? type, Value? active, Value? name, Value? url, Value? lastUpdate, Value? updateInterval, Value? upload, Value? download, Value? total, Value? expire, Value? webPageUrl, Value? supportUrl, Value? rowid, }) { return ProfileEntriesCompanion( id: id ?? this.id, type: type ?? this.type, active: active ?? this.active, name: name ?? this.name, url: url ?? this.url, lastUpdate: lastUpdate ?? this.lastUpdate, updateInterval: updateInterval ?? this.updateInterval, upload: upload ?? this.upload, download: download ?? this.download, total: total ?? this.total, expire: expire ?? this.expire, webPageUrl: webPageUrl ?? this.webPageUrl, supportUrl: supportUrl ?? this.supportUrl, rowid: rowid ?? this.rowid, ); } @override Map toColumns(bool nullToAbsent) { final map = {}; if (id.present) { map['id'] = Variable(id.value); } if (type.present) { map['type'] = Variable(type.value); } if (active.present) { map['active'] = Variable(active.value); } if (name.present) { map['name'] = Variable(name.value); } if (url.present) { map['url'] = Variable(url.value); } if (lastUpdate.present) { map['last_update'] = Variable(lastUpdate.value); } if (updateInterval.present) { map['update_interval'] = Variable(updateInterval.value); } if (upload.present) { map['upload'] = Variable(upload.value); } if (download.present) { map['download'] = Variable(download.value); } if (total.present) { map['total'] = Variable(total.value); } if (expire.present) { map['expire'] = Variable(expire.value); } if (webPageUrl.present) { map['web_page_url'] = Variable(webPageUrl.value); } if (supportUrl.present) { map['support_url'] = Variable(supportUrl.value); } if (rowid.present) { map['rowid'] = Variable(rowid.value); } return map; } @override String toString() { return (StringBuffer('ProfileEntriesCompanion(') ..write('id: $id, ') ..write('type: $type, ') ..write('active: $active, ') ..write('name: $name, ') ..write('url: $url, ') ..write('lastUpdate: $lastUpdate, ') ..write('updateInterval: $updateInterval, ') ..write('upload: $upload, ') ..write('download: $download, ') ..write('total: $total, ') ..write('expire: $expire, ') ..write('webPageUrl: $webPageUrl, ') ..write('supportUrl: $supportUrl, ') ..write('rowid: $rowid') ..write(')')) .toString(); } } class GeoAssetEntries extends Table with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; GeoAssetEntries(this.attachedDatabase, [this._alias]); late final GeneratedColumn id = GeneratedColumn( 'id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true, ); late final GeneratedColumn type = GeneratedColumn( 'type', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true, ); late final GeneratedColumn active = GeneratedColumn( 'active', aliasedName, false, type: DriftSqlType.bool, requiredDuringInsert: true, defaultConstraints: GeneratedColumn.constraintIsAlways( 'CHECK ("active" IN (0, 1))', ), ); late final GeneratedColumn name = GeneratedColumn( 'name', aliasedName, false, additionalChecks: GeneratedColumn.checkTextLength(minTextLength: 1), type: DriftSqlType.string, requiredDuringInsert: true, ); late final GeneratedColumn providerName = GeneratedColumn( 'provider_name', aliasedName, false, additionalChecks: GeneratedColumn.checkTextLength(minTextLength: 1), type: DriftSqlType.string, requiredDuringInsert: true, ); late final GeneratedColumn version = GeneratedColumn( 'version', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false, ); late final GeneratedColumn lastCheck = GeneratedColumn( 'last_check', aliasedName, true, type: DriftSqlType.dateTime, requiredDuringInsert: false, ); @override List get $columns => [ id, type, active, name, providerName, version, lastCheck, ]; @override String get aliasedName => _alias ?? actualTableName; @override String get actualTableName => $name; static const String $name = 'geo_asset_entries'; @override Set get $primaryKey => {id}; @override List> get uniqueKeys => [ {name, providerName}, ]; @override GeoAssetEntriesData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return GeoAssetEntriesData( id: attachedDatabase.typeMapping.read( DriftSqlType.string, data['${effectivePrefix}id'], )!, type: attachedDatabase.typeMapping.read( DriftSqlType.string, data['${effectivePrefix}type'], )!, active: attachedDatabase.typeMapping.read( DriftSqlType.bool, data['${effectivePrefix}active'], )!, name: attachedDatabase.typeMapping.read( DriftSqlType.string, data['${effectivePrefix}name'], )!, providerName: attachedDatabase.typeMapping.read( DriftSqlType.string, data['${effectivePrefix}provider_name'], )!, version: attachedDatabase.typeMapping.read( DriftSqlType.string, data['${effectivePrefix}version'], ), lastCheck: attachedDatabase.typeMapping.read( DriftSqlType.dateTime, data['${effectivePrefix}last_check'], ), ); } @override GeoAssetEntries createAlias(String alias) { return GeoAssetEntries(attachedDatabase, alias); } } class GeoAssetEntriesData extends DataClass implements Insertable { final String id; final String type; final bool active; final String name; final String providerName; final String? version; final DateTime? lastCheck; const GeoAssetEntriesData({ required this.id, required this.type, required this.active, required this.name, required this.providerName, this.version, this.lastCheck, }); @override Map toColumns(bool nullToAbsent) { final map = {}; map['id'] = Variable(id); map['type'] = Variable(type); map['active'] = Variable(active); map['name'] = Variable(name); map['provider_name'] = Variable(providerName); if (!nullToAbsent || version != null) { map['version'] = Variable(version); } if (!nullToAbsent || lastCheck != null) { map['last_check'] = Variable(lastCheck); } return map; } GeoAssetEntriesCompanion toCompanion(bool nullToAbsent) { return GeoAssetEntriesCompanion( id: Value(id), type: Value(type), active: Value(active), name: Value(name), providerName: Value(providerName), version: version == null && nullToAbsent ? const Value.absent() : Value(version), lastCheck: lastCheck == null && nullToAbsent ? const Value.absent() : Value(lastCheck), ); } factory GeoAssetEntriesData.fromJson( Map json, { ValueSerializer? serializer, }) { serializer ??= driftRuntimeOptions.defaultSerializer; return GeoAssetEntriesData( id: serializer.fromJson(json['id']), type: serializer.fromJson(json['type']), active: serializer.fromJson(json['active']), name: serializer.fromJson(json['name']), providerName: serializer.fromJson(json['providerName']), version: serializer.fromJson(json['version']), lastCheck: serializer.fromJson(json['lastCheck']), ); } @override Map toJson({ValueSerializer? serializer}) { serializer ??= driftRuntimeOptions.defaultSerializer; return { 'id': serializer.toJson(id), 'type': serializer.toJson(type), 'active': serializer.toJson(active), 'name': serializer.toJson(name), 'providerName': serializer.toJson(providerName), 'version': serializer.toJson(version), 'lastCheck': serializer.toJson(lastCheck), }; } GeoAssetEntriesData copyWith({ String? id, String? type, bool? active, String? name, String? providerName, Value version = const Value.absent(), Value lastCheck = const Value.absent(), }) => GeoAssetEntriesData( id: id ?? this.id, type: type ?? this.type, active: active ?? this.active, name: name ?? this.name, providerName: providerName ?? this.providerName, version: version.present ? version.value : this.version, lastCheck: lastCheck.present ? lastCheck.value : this.lastCheck, ); GeoAssetEntriesData copyWithCompanion(GeoAssetEntriesCompanion data) { return GeoAssetEntriesData( id: data.id.present ? data.id.value : this.id, type: data.type.present ? data.type.value : this.type, active: data.active.present ? data.active.value : this.active, name: data.name.present ? data.name.value : this.name, providerName: data.providerName.present ? data.providerName.value : this.providerName, version: data.version.present ? data.version.value : this.version, lastCheck: data.lastCheck.present ? data.lastCheck.value : this.lastCheck, ); } @override String toString() { return (StringBuffer('GeoAssetEntriesData(') ..write('id: $id, ') ..write('type: $type, ') ..write('active: $active, ') ..write('name: $name, ') ..write('providerName: $providerName, ') ..write('version: $version, ') ..write('lastCheck: $lastCheck') ..write(')')) .toString(); } @override int get hashCode => Object.hash(id, type, active, name, providerName, version, lastCheck); @override bool operator ==(Object other) => identical(this, other) || (other is GeoAssetEntriesData && other.id == this.id && other.type == this.type && other.active == this.active && other.name == this.name && other.providerName == this.providerName && other.version == this.version && other.lastCheck == this.lastCheck); } class GeoAssetEntriesCompanion extends UpdateCompanion { final Value id; final Value type; final Value active; final Value name; final Value providerName; final Value version; final Value lastCheck; final Value rowid; const GeoAssetEntriesCompanion({ this.id = const Value.absent(), this.type = const Value.absent(), this.active = const Value.absent(), this.name = const Value.absent(), this.providerName = const Value.absent(), this.version = const Value.absent(), this.lastCheck = const Value.absent(), this.rowid = const Value.absent(), }); GeoAssetEntriesCompanion.insert({ required String id, required String type, required bool active, required String name, required String providerName, this.version = const Value.absent(), this.lastCheck = const Value.absent(), this.rowid = const Value.absent(), }) : id = Value(id), type = Value(type), active = Value(active), name = Value(name), providerName = Value(providerName); static Insertable custom({ Expression? id, Expression? type, Expression? active, Expression? name, Expression? providerName, Expression? version, Expression? lastCheck, Expression? rowid, }) { return RawValuesInsertable({ if (id != null) 'id': id, if (type != null) 'type': type, if (active != null) 'active': active, if (name != null) 'name': name, if (providerName != null) 'provider_name': providerName, if (version != null) 'version': version, if (lastCheck != null) 'last_check': lastCheck, if (rowid != null) 'rowid': rowid, }); } GeoAssetEntriesCompanion copyWith({ Value? id, Value? type, Value? active, Value? name, Value? providerName, Value? version, Value? lastCheck, Value? rowid, }) { return GeoAssetEntriesCompanion( id: id ?? this.id, type: type ?? this.type, active: active ?? this.active, name: name ?? this.name, providerName: providerName ?? this.providerName, version: version ?? this.version, lastCheck: lastCheck ?? this.lastCheck, rowid: rowid ?? this.rowid, ); } @override Map toColumns(bool nullToAbsent) { final map = {}; if (id.present) { map['id'] = Variable(id.value); } if (type.present) { map['type'] = Variable(type.value); } if (active.present) { map['active'] = Variable(active.value); } if (name.present) { map['name'] = Variable(name.value); } if (providerName.present) { map['provider_name'] = Variable(providerName.value); } if (version.present) { map['version'] = Variable(version.value); } if (lastCheck.present) { map['last_check'] = Variable(lastCheck.value); } if (rowid.present) { map['rowid'] = Variable(rowid.value); } return map; } @override String toString() { return (StringBuffer('GeoAssetEntriesCompanion(') ..write('id: $id, ') ..write('type: $type, ') ..write('active: $active, ') ..write('name: $name, ') ..write('providerName: $providerName, ') ..write('version: $version, ') ..write('lastCheck: $lastCheck, ') ..write('rowid: $rowid') ..write(')')) .toString(); } } class DatabaseAtV3 extends GeneratedDatabase { DatabaseAtV3(QueryExecutor e) : super(e); late final ProfileEntries profileEntries = ProfileEntries(this); late final GeoAssetEntries geoAssetEntries = GeoAssetEntries(this); @override Iterable> get allTables => allSchemaEntities.whereType>(); @override List get allSchemaEntities => [ profileEntries, geoAssetEntries, ]; @override int get schemaVersion => 3; @override DriftDatabaseOptions get options => const DriftDatabaseOptions(storeDateTimeAsText: true); } ================================================ FILE: test/drift/db/generated/schema_v4.dart ================================================ // dart format width=80 // GENERATED CODE, DO NOT EDIT BY HAND. // ignore_for_file: type=lint import 'package:drift/drift.dart'; class ProfileEntries extends Table with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; ProfileEntries(this.attachedDatabase, [this._alias]); late final GeneratedColumn id = GeneratedColumn( 'id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true, ); late final GeneratedColumn type = GeneratedColumn( 'type', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true, ); late final GeneratedColumn active = GeneratedColumn( 'active', aliasedName, false, type: DriftSqlType.bool, requiredDuringInsert: true, defaultConstraints: GeneratedColumn.constraintIsAlways( 'CHECK ("active" IN (0, 1))', ), ); late final GeneratedColumn name = GeneratedColumn( 'name', aliasedName, false, additionalChecks: GeneratedColumn.checkTextLength(minTextLength: 1), type: DriftSqlType.string, requiredDuringInsert: true, ); late final GeneratedColumn url = GeneratedColumn( 'url', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false, ); late final GeneratedColumn lastUpdate = GeneratedColumn( 'last_update', aliasedName, false, type: DriftSqlType.dateTime, requiredDuringInsert: true, ); late final GeneratedColumn updateInterval = GeneratedColumn( 'update_interval', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false, ); late final GeneratedColumn upload = GeneratedColumn( 'upload', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false, ); late final GeneratedColumn download = GeneratedColumn( 'download', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false, ); late final GeneratedColumn total = GeneratedColumn( 'total', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false, ); late final GeneratedColumn expire = GeneratedColumn( 'expire', aliasedName, true, type: DriftSqlType.dateTime, requiredDuringInsert: false, ); late final GeneratedColumn webPageUrl = GeneratedColumn( 'web_page_url', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false, ); late final GeneratedColumn supportUrl = GeneratedColumn( 'support_url', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false, ); late final GeneratedColumn testUrl = GeneratedColumn( 'test_url', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false, ); @override List get $columns => [ id, type, active, name, url, lastUpdate, updateInterval, upload, download, total, expire, webPageUrl, supportUrl, testUrl, ]; @override String get aliasedName => _alias ?? actualTableName; @override String get actualTableName => $name; static const String $name = 'profile_entries'; @override Set get $primaryKey => {id}; @override ProfileEntriesData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return ProfileEntriesData( id: attachedDatabase.typeMapping.read( DriftSqlType.string, data['${effectivePrefix}id'], )!, type: attachedDatabase.typeMapping.read( DriftSqlType.string, data['${effectivePrefix}type'], )!, active: attachedDatabase.typeMapping.read( DriftSqlType.bool, data['${effectivePrefix}active'], )!, name: attachedDatabase.typeMapping.read( DriftSqlType.string, data['${effectivePrefix}name'], )!, url: attachedDatabase.typeMapping.read( DriftSqlType.string, data['${effectivePrefix}url'], ), lastUpdate: attachedDatabase.typeMapping.read( DriftSqlType.dateTime, data['${effectivePrefix}last_update'], )!, updateInterval: attachedDatabase.typeMapping.read( DriftSqlType.int, data['${effectivePrefix}update_interval'], ), upload: attachedDatabase.typeMapping.read( DriftSqlType.int, data['${effectivePrefix}upload'], ), download: attachedDatabase.typeMapping.read( DriftSqlType.int, data['${effectivePrefix}download'], ), total: attachedDatabase.typeMapping.read( DriftSqlType.int, data['${effectivePrefix}total'], ), expire: attachedDatabase.typeMapping.read( DriftSqlType.dateTime, data['${effectivePrefix}expire'], ), webPageUrl: attachedDatabase.typeMapping.read( DriftSqlType.string, data['${effectivePrefix}web_page_url'], ), supportUrl: attachedDatabase.typeMapping.read( DriftSqlType.string, data['${effectivePrefix}support_url'], ), testUrl: attachedDatabase.typeMapping.read( DriftSqlType.string, data['${effectivePrefix}test_url'], ), ); } @override ProfileEntries createAlias(String alias) { return ProfileEntries(attachedDatabase, alias); } } class ProfileEntriesData extends DataClass implements Insertable { final String id; final String type; final bool active; final String name; final String? url; final DateTime lastUpdate; final int? updateInterval; final int? upload; final int? download; final int? total; final DateTime? expire; final String? webPageUrl; final String? supportUrl; final String? testUrl; const ProfileEntriesData({ required this.id, required this.type, required this.active, required this.name, this.url, required this.lastUpdate, this.updateInterval, this.upload, this.download, this.total, this.expire, this.webPageUrl, this.supportUrl, this.testUrl, }); @override Map toColumns(bool nullToAbsent) { final map = {}; map['id'] = Variable(id); map['type'] = Variable(type); map['active'] = Variable(active); map['name'] = Variable(name); if (!nullToAbsent || url != null) { map['url'] = Variable(url); } map['last_update'] = Variable(lastUpdate); if (!nullToAbsent || updateInterval != null) { map['update_interval'] = Variable(updateInterval); } if (!nullToAbsent || upload != null) { map['upload'] = Variable(upload); } if (!nullToAbsent || download != null) { map['download'] = Variable(download); } if (!nullToAbsent || total != null) { map['total'] = Variable(total); } if (!nullToAbsent || expire != null) { map['expire'] = Variable(expire); } if (!nullToAbsent || webPageUrl != null) { map['web_page_url'] = Variable(webPageUrl); } if (!nullToAbsent || supportUrl != null) { map['support_url'] = Variable(supportUrl); } if (!nullToAbsent || testUrl != null) { map['test_url'] = Variable(testUrl); } return map; } ProfileEntriesCompanion toCompanion(bool nullToAbsent) { return ProfileEntriesCompanion( id: Value(id), type: Value(type), active: Value(active), name: Value(name), url: url == null && nullToAbsent ? const Value.absent() : Value(url), lastUpdate: Value(lastUpdate), updateInterval: updateInterval == null && nullToAbsent ? const Value.absent() : Value(updateInterval), upload: upload == null && nullToAbsent ? const Value.absent() : Value(upload), download: download == null && nullToAbsent ? const Value.absent() : Value(download), total: total == null && nullToAbsent ? const Value.absent() : Value(total), expire: expire == null && nullToAbsent ? const Value.absent() : Value(expire), webPageUrl: webPageUrl == null && nullToAbsent ? const Value.absent() : Value(webPageUrl), supportUrl: supportUrl == null && nullToAbsent ? const Value.absent() : Value(supportUrl), testUrl: testUrl == null && nullToAbsent ? const Value.absent() : Value(testUrl), ); } factory ProfileEntriesData.fromJson( Map json, { ValueSerializer? serializer, }) { serializer ??= driftRuntimeOptions.defaultSerializer; return ProfileEntriesData( id: serializer.fromJson(json['id']), type: serializer.fromJson(json['type']), active: serializer.fromJson(json['active']), name: serializer.fromJson(json['name']), url: serializer.fromJson(json['url']), lastUpdate: serializer.fromJson(json['lastUpdate']), updateInterval: serializer.fromJson(json['updateInterval']), upload: serializer.fromJson(json['upload']), download: serializer.fromJson(json['download']), total: serializer.fromJson(json['total']), expire: serializer.fromJson(json['expire']), webPageUrl: serializer.fromJson(json['webPageUrl']), supportUrl: serializer.fromJson(json['supportUrl']), testUrl: serializer.fromJson(json['testUrl']), ); } @override Map toJson({ValueSerializer? serializer}) { serializer ??= driftRuntimeOptions.defaultSerializer; return { 'id': serializer.toJson(id), 'type': serializer.toJson(type), 'active': serializer.toJson(active), 'name': serializer.toJson(name), 'url': serializer.toJson(url), 'lastUpdate': serializer.toJson(lastUpdate), 'updateInterval': serializer.toJson(updateInterval), 'upload': serializer.toJson(upload), 'download': serializer.toJson(download), 'total': serializer.toJson(total), 'expire': serializer.toJson(expire), 'webPageUrl': serializer.toJson(webPageUrl), 'supportUrl': serializer.toJson(supportUrl), 'testUrl': serializer.toJson(testUrl), }; } ProfileEntriesData copyWith({ String? id, String? type, bool? active, String? name, Value url = const Value.absent(), DateTime? lastUpdate, Value updateInterval = const Value.absent(), Value upload = const Value.absent(), Value download = const Value.absent(), Value total = const Value.absent(), Value expire = const Value.absent(), Value webPageUrl = const Value.absent(), Value supportUrl = const Value.absent(), Value testUrl = const Value.absent(), }) => ProfileEntriesData( id: id ?? this.id, type: type ?? this.type, active: active ?? this.active, name: name ?? this.name, url: url.present ? url.value : this.url, lastUpdate: lastUpdate ?? this.lastUpdate, updateInterval: updateInterval.present ? updateInterval.value : this.updateInterval, upload: upload.present ? upload.value : this.upload, download: download.present ? download.value : this.download, total: total.present ? total.value : this.total, expire: expire.present ? expire.value : this.expire, webPageUrl: webPageUrl.present ? webPageUrl.value : this.webPageUrl, supportUrl: supportUrl.present ? supportUrl.value : this.supportUrl, testUrl: testUrl.present ? testUrl.value : this.testUrl, ); ProfileEntriesData copyWithCompanion(ProfileEntriesCompanion data) { return ProfileEntriesData( id: data.id.present ? data.id.value : this.id, type: data.type.present ? data.type.value : this.type, active: data.active.present ? data.active.value : this.active, name: data.name.present ? data.name.value : this.name, url: data.url.present ? data.url.value : this.url, lastUpdate: data.lastUpdate.present ? data.lastUpdate.value : this.lastUpdate, updateInterval: data.updateInterval.present ? data.updateInterval.value : this.updateInterval, upload: data.upload.present ? data.upload.value : this.upload, download: data.download.present ? data.download.value : this.download, total: data.total.present ? data.total.value : this.total, expire: data.expire.present ? data.expire.value : this.expire, webPageUrl: data.webPageUrl.present ? data.webPageUrl.value : this.webPageUrl, supportUrl: data.supportUrl.present ? data.supportUrl.value : this.supportUrl, testUrl: data.testUrl.present ? data.testUrl.value : this.testUrl, ); } @override String toString() { return (StringBuffer('ProfileEntriesData(') ..write('id: $id, ') ..write('type: $type, ') ..write('active: $active, ') ..write('name: $name, ') ..write('url: $url, ') ..write('lastUpdate: $lastUpdate, ') ..write('updateInterval: $updateInterval, ') ..write('upload: $upload, ') ..write('download: $download, ') ..write('total: $total, ') ..write('expire: $expire, ') ..write('webPageUrl: $webPageUrl, ') ..write('supportUrl: $supportUrl, ') ..write('testUrl: $testUrl') ..write(')')) .toString(); } @override int get hashCode => Object.hash( id, type, active, name, url, lastUpdate, updateInterval, upload, download, total, expire, webPageUrl, supportUrl, testUrl, ); @override bool operator ==(Object other) => identical(this, other) || (other is ProfileEntriesData && other.id == this.id && other.type == this.type && other.active == this.active && other.name == this.name && other.url == this.url && other.lastUpdate == this.lastUpdate && other.updateInterval == this.updateInterval && other.upload == this.upload && other.download == this.download && other.total == this.total && other.expire == this.expire && other.webPageUrl == this.webPageUrl && other.supportUrl == this.supportUrl && other.testUrl == this.testUrl); } class ProfileEntriesCompanion extends UpdateCompanion { final Value id; final Value type; final Value active; final Value name; final Value url; final Value lastUpdate; final Value updateInterval; final Value upload; final Value download; final Value total; final Value expire; final Value webPageUrl; final Value supportUrl; final Value testUrl; final Value rowid; const ProfileEntriesCompanion({ this.id = const Value.absent(), this.type = const Value.absent(), this.active = const Value.absent(), this.name = const Value.absent(), this.url = const Value.absent(), this.lastUpdate = const Value.absent(), this.updateInterval = const Value.absent(), this.upload = const Value.absent(), this.download = const Value.absent(), this.total = const Value.absent(), this.expire = const Value.absent(), this.webPageUrl = const Value.absent(), this.supportUrl = const Value.absent(), this.testUrl = const Value.absent(), this.rowid = const Value.absent(), }); ProfileEntriesCompanion.insert({ required String id, required String type, required bool active, required String name, this.url = const Value.absent(), required DateTime lastUpdate, this.updateInterval = const Value.absent(), this.upload = const Value.absent(), this.download = const Value.absent(), this.total = const Value.absent(), this.expire = const Value.absent(), this.webPageUrl = const Value.absent(), this.supportUrl = const Value.absent(), this.testUrl = const Value.absent(), this.rowid = const Value.absent(), }) : id = Value(id), type = Value(type), active = Value(active), name = Value(name), lastUpdate = Value(lastUpdate); static Insertable custom({ Expression? id, Expression? type, Expression? active, Expression? name, Expression? url, Expression? lastUpdate, Expression? updateInterval, Expression? upload, Expression? download, Expression? total, Expression? expire, Expression? webPageUrl, Expression? supportUrl, Expression? testUrl, Expression? rowid, }) { return RawValuesInsertable({ if (id != null) 'id': id, if (type != null) 'type': type, if (active != null) 'active': active, if (name != null) 'name': name, if (url != null) 'url': url, if (lastUpdate != null) 'last_update': lastUpdate, if (updateInterval != null) 'update_interval': updateInterval, if (upload != null) 'upload': upload, if (download != null) 'download': download, if (total != null) 'total': total, if (expire != null) 'expire': expire, if (webPageUrl != null) 'web_page_url': webPageUrl, if (supportUrl != null) 'support_url': supportUrl, if (testUrl != null) 'test_url': testUrl, if (rowid != null) 'rowid': rowid, }); } ProfileEntriesCompanion copyWith({ Value? id, Value? type, Value? active, Value? name, Value? url, Value? lastUpdate, Value? updateInterval, Value? upload, Value? download, Value? total, Value? expire, Value? webPageUrl, Value? supportUrl, Value? testUrl, Value? rowid, }) { return ProfileEntriesCompanion( id: id ?? this.id, type: type ?? this.type, active: active ?? this.active, name: name ?? this.name, url: url ?? this.url, lastUpdate: lastUpdate ?? this.lastUpdate, updateInterval: updateInterval ?? this.updateInterval, upload: upload ?? this.upload, download: download ?? this.download, total: total ?? this.total, expire: expire ?? this.expire, webPageUrl: webPageUrl ?? this.webPageUrl, supportUrl: supportUrl ?? this.supportUrl, testUrl: testUrl ?? this.testUrl, rowid: rowid ?? this.rowid, ); } @override Map toColumns(bool nullToAbsent) { final map = {}; if (id.present) { map['id'] = Variable(id.value); } if (type.present) { map['type'] = Variable(type.value); } if (active.present) { map['active'] = Variable(active.value); } if (name.present) { map['name'] = Variable(name.value); } if (url.present) { map['url'] = Variable(url.value); } if (lastUpdate.present) { map['last_update'] = Variable(lastUpdate.value); } if (updateInterval.present) { map['update_interval'] = Variable(updateInterval.value); } if (upload.present) { map['upload'] = Variable(upload.value); } if (download.present) { map['download'] = Variable(download.value); } if (total.present) { map['total'] = Variable(total.value); } if (expire.present) { map['expire'] = Variable(expire.value); } if (webPageUrl.present) { map['web_page_url'] = Variable(webPageUrl.value); } if (supportUrl.present) { map['support_url'] = Variable(supportUrl.value); } if (testUrl.present) { map['test_url'] = Variable(testUrl.value); } if (rowid.present) { map['rowid'] = Variable(rowid.value); } return map; } @override String toString() { return (StringBuffer('ProfileEntriesCompanion(') ..write('id: $id, ') ..write('type: $type, ') ..write('active: $active, ') ..write('name: $name, ') ..write('url: $url, ') ..write('lastUpdate: $lastUpdate, ') ..write('updateInterval: $updateInterval, ') ..write('upload: $upload, ') ..write('download: $download, ') ..write('total: $total, ') ..write('expire: $expire, ') ..write('webPageUrl: $webPageUrl, ') ..write('supportUrl: $supportUrl, ') ..write('testUrl: $testUrl, ') ..write('rowid: $rowid') ..write(')')) .toString(); } } class GeoAssetEntries extends Table with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; GeoAssetEntries(this.attachedDatabase, [this._alias]); late final GeneratedColumn id = GeneratedColumn( 'id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true, ); late final GeneratedColumn type = GeneratedColumn( 'type', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true, ); late final GeneratedColumn active = GeneratedColumn( 'active', aliasedName, false, type: DriftSqlType.bool, requiredDuringInsert: true, defaultConstraints: GeneratedColumn.constraintIsAlways( 'CHECK ("active" IN (0, 1))', ), ); late final GeneratedColumn name = GeneratedColumn( 'name', aliasedName, false, additionalChecks: GeneratedColumn.checkTextLength(minTextLength: 1), type: DriftSqlType.string, requiredDuringInsert: true, ); late final GeneratedColumn providerName = GeneratedColumn( 'provider_name', aliasedName, false, additionalChecks: GeneratedColumn.checkTextLength(minTextLength: 1), type: DriftSqlType.string, requiredDuringInsert: true, ); late final GeneratedColumn version = GeneratedColumn( 'version', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false, ); late final GeneratedColumn lastCheck = GeneratedColumn( 'last_check', aliasedName, true, type: DriftSqlType.dateTime, requiredDuringInsert: false, ); @override List get $columns => [ id, type, active, name, providerName, version, lastCheck, ]; @override String get aliasedName => _alias ?? actualTableName; @override String get actualTableName => $name; static const String $name = 'geo_asset_entries'; @override Set get $primaryKey => {id}; @override List> get uniqueKeys => [ {name, providerName}, ]; @override GeoAssetEntriesData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return GeoAssetEntriesData( id: attachedDatabase.typeMapping.read( DriftSqlType.string, data['${effectivePrefix}id'], )!, type: attachedDatabase.typeMapping.read( DriftSqlType.string, data['${effectivePrefix}type'], )!, active: attachedDatabase.typeMapping.read( DriftSqlType.bool, data['${effectivePrefix}active'], )!, name: attachedDatabase.typeMapping.read( DriftSqlType.string, data['${effectivePrefix}name'], )!, providerName: attachedDatabase.typeMapping.read( DriftSqlType.string, data['${effectivePrefix}provider_name'], )!, version: attachedDatabase.typeMapping.read( DriftSqlType.string, data['${effectivePrefix}version'], ), lastCheck: attachedDatabase.typeMapping.read( DriftSqlType.dateTime, data['${effectivePrefix}last_check'], ), ); } @override GeoAssetEntries createAlias(String alias) { return GeoAssetEntries(attachedDatabase, alias); } } class GeoAssetEntriesData extends DataClass implements Insertable { final String id; final String type; final bool active; final String name; final String providerName; final String? version; final DateTime? lastCheck; const GeoAssetEntriesData({ required this.id, required this.type, required this.active, required this.name, required this.providerName, this.version, this.lastCheck, }); @override Map toColumns(bool nullToAbsent) { final map = {}; map['id'] = Variable(id); map['type'] = Variable(type); map['active'] = Variable(active); map['name'] = Variable(name); map['provider_name'] = Variable(providerName); if (!nullToAbsent || version != null) { map['version'] = Variable(version); } if (!nullToAbsent || lastCheck != null) { map['last_check'] = Variable(lastCheck); } return map; } GeoAssetEntriesCompanion toCompanion(bool nullToAbsent) { return GeoAssetEntriesCompanion( id: Value(id), type: Value(type), active: Value(active), name: Value(name), providerName: Value(providerName), version: version == null && nullToAbsent ? const Value.absent() : Value(version), lastCheck: lastCheck == null && nullToAbsent ? const Value.absent() : Value(lastCheck), ); } factory GeoAssetEntriesData.fromJson( Map json, { ValueSerializer? serializer, }) { serializer ??= driftRuntimeOptions.defaultSerializer; return GeoAssetEntriesData( id: serializer.fromJson(json['id']), type: serializer.fromJson(json['type']), active: serializer.fromJson(json['active']), name: serializer.fromJson(json['name']), providerName: serializer.fromJson(json['providerName']), version: serializer.fromJson(json['version']), lastCheck: serializer.fromJson(json['lastCheck']), ); } @override Map toJson({ValueSerializer? serializer}) { serializer ??= driftRuntimeOptions.defaultSerializer; return { 'id': serializer.toJson(id), 'type': serializer.toJson(type), 'active': serializer.toJson(active), 'name': serializer.toJson(name), 'providerName': serializer.toJson(providerName), 'version': serializer.toJson(version), 'lastCheck': serializer.toJson(lastCheck), }; } GeoAssetEntriesData copyWith({ String? id, String? type, bool? active, String? name, String? providerName, Value version = const Value.absent(), Value lastCheck = const Value.absent(), }) => GeoAssetEntriesData( id: id ?? this.id, type: type ?? this.type, active: active ?? this.active, name: name ?? this.name, providerName: providerName ?? this.providerName, version: version.present ? version.value : this.version, lastCheck: lastCheck.present ? lastCheck.value : this.lastCheck, ); GeoAssetEntriesData copyWithCompanion(GeoAssetEntriesCompanion data) { return GeoAssetEntriesData( id: data.id.present ? data.id.value : this.id, type: data.type.present ? data.type.value : this.type, active: data.active.present ? data.active.value : this.active, name: data.name.present ? data.name.value : this.name, providerName: data.providerName.present ? data.providerName.value : this.providerName, version: data.version.present ? data.version.value : this.version, lastCheck: data.lastCheck.present ? data.lastCheck.value : this.lastCheck, ); } @override String toString() { return (StringBuffer('GeoAssetEntriesData(') ..write('id: $id, ') ..write('type: $type, ') ..write('active: $active, ') ..write('name: $name, ') ..write('providerName: $providerName, ') ..write('version: $version, ') ..write('lastCheck: $lastCheck') ..write(')')) .toString(); } @override int get hashCode => Object.hash(id, type, active, name, providerName, version, lastCheck); @override bool operator ==(Object other) => identical(this, other) || (other is GeoAssetEntriesData && other.id == this.id && other.type == this.type && other.active == this.active && other.name == this.name && other.providerName == this.providerName && other.version == this.version && other.lastCheck == this.lastCheck); } class GeoAssetEntriesCompanion extends UpdateCompanion { final Value id; final Value type; final Value active; final Value name; final Value providerName; final Value version; final Value lastCheck; final Value rowid; const GeoAssetEntriesCompanion({ this.id = const Value.absent(), this.type = const Value.absent(), this.active = const Value.absent(), this.name = const Value.absent(), this.providerName = const Value.absent(), this.version = const Value.absent(), this.lastCheck = const Value.absent(), this.rowid = const Value.absent(), }); GeoAssetEntriesCompanion.insert({ required String id, required String type, required bool active, required String name, required String providerName, this.version = const Value.absent(), this.lastCheck = const Value.absent(), this.rowid = const Value.absent(), }) : id = Value(id), type = Value(type), active = Value(active), name = Value(name), providerName = Value(providerName); static Insertable custom({ Expression? id, Expression? type, Expression? active, Expression? name, Expression? providerName, Expression? version, Expression? lastCheck, Expression? rowid, }) { return RawValuesInsertable({ if (id != null) 'id': id, if (type != null) 'type': type, if (active != null) 'active': active, if (name != null) 'name': name, if (providerName != null) 'provider_name': providerName, if (version != null) 'version': version, if (lastCheck != null) 'last_check': lastCheck, if (rowid != null) 'rowid': rowid, }); } GeoAssetEntriesCompanion copyWith({ Value? id, Value? type, Value? active, Value? name, Value? providerName, Value? version, Value? lastCheck, Value? rowid, }) { return GeoAssetEntriesCompanion( id: id ?? this.id, type: type ?? this.type, active: active ?? this.active, name: name ?? this.name, providerName: providerName ?? this.providerName, version: version ?? this.version, lastCheck: lastCheck ?? this.lastCheck, rowid: rowid ?? this.rowid, ); } @override Map toColumns(bool nullToAbsent) { final map = {}; if (id.present) { map['id'] = Variable(id.value); } if (type.present) { map['type'] = Variable(type.value); } if (active.present) { map['active'] = Variable(active.value); } if (name.present) { map['name'] = Variable(name.value); } if (providerName.present) { map['provider_name'] = Variable(providerName.value); } if (version.present) { map['version'] = Variable(version.value); } if (lastCheck.present) { map['last_check'] = Variable(lastCheck.value); } if (rowid.present) { map['rowid'] = Variable(rowid.value); } return map; } @override String toString() { return (StringBuffer('GeoAssetEntriesCompanion(') ..write('id: $id, ') ..write('type: $type, ') ..write('active: $active, ') ..write('name: $name, ') ..write('providerName: $providerName, ') ..write('version: $version, ') ..write('lastCheck: $lastCheck, ') ..write('rowid: $rowid') ..write(')')) .toString(); } } class DatabaseAtV4 extends GeneratedDatabase { DatabaseAtV4(QueryExecutor e) : super(e); late final ProfileEntries profileEntries = ProfileEntries(this); late final GeoAssetEntries geoAssetEntries = GeoAssetEntries(this); @override Iterable> get allTables => allSchemaEntities.whereType>(); @override List get allSchemaEntities => [ profileEntries, geoAssetEntries, ]; @override int get schemaVersion => 4; @override DriftDatabaseOptions get options => const DriftDatabaseOptions(storeDateTimeAsText: true); } ================================================ FILE: test/drift/db/generated/schema_v5.dart ================================================ // dart format width=80 // GENERATED CODE, DO NOT EDIT BY HAND. // ignore_for_file: type=lint import 'package:drift/drift.dart'; class ProfileEntries extends Table with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; ProfileEntries(this.attachedDatabase, [this._alias]); late final GeneratedColumn id = GeneratedColumn( 'id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true, ); late final GeneratedColumn type = GeneratedColumn( 'type', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true, ); late final GeneratedColumn active = GeneratedColumn( 'active', aliasedName, false, type: DriftSqlType.bool, requiredDuringInsert: true, defaultConstraints: GeneratedColumn.constraintIsAlways( 'CHECK ("active" IN (0, 1))', ), ); late final GeneratedColumn name = GeneratedColumn( 'name', aliasedName, false, additionalChecks: GeneratedColumn.checkTextLength(minTextLength: 1), type: DriftSqlType.string, requiredDuringInsert: true, ); late final GeneratedColumn url = GeneratedColumn( 'url', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false, ); late final GeneratedColumn lastUpdate = GeneratedColumn( 'last_update', aliasedName, false, type: DriftSqlType.dateTime, requiredDuringInsert: true, ); late final GeneratedColumn updateInterval = GeneratedColumn( 'update_interval', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false, ); late final GeneratedColumn upload = GeneratedColumn( 'upload', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false, ); late final GeneratedColumn download = GeneratedColumn( 'download', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false, ); late final GeneratedColumn total = GeneratedColumn( 'total', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false, ); late final GeneratedColumn expire = GeneratedColumn( 'expire', aliasedName, true, type: DriftSqlType.dateTime, requiredDuringInsert: false, ); late final GeneratedColumn webPageUrl = GeneratedColumn( 'web_page_url', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false, ); late final GeneratedColumn supportUrl = GeneratedColumn( 'support_url', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false, ); late final GeneratedColumn populatedHeaders = GeneratedColumn( 'populated_headers', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false, ); late final GeneratedColumn profileOverride = GeneratedColumn( 'profile_override', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false, ); late final GeneratedColumn userOverride = GeneratedColumn( 'user_override', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false, ); @override List get $columns => [ id, type, active, name, url, lastUpdate, updateInterval, upload, download, total, expire, webPageUrl, supportUrl, populatedHeaders, profileOverride, userOverride, ]; @override String get aliasedName => _alias ?? actualTableName; @override String get actualTableName => $name; static const String $name = 'profile_entries'; @override Set get $primaryKey => {id}; @override ProfileEntriesData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return ProfileEntriesData( id: attachedDatabase.typeMapping.read( DriftSqlType.string, data['${effectivePrefix}id'], )!, type: attachedDatabase.typeMapping.read( DriftSqlType.string, data['${effectivePrefix}type'], )!, active: attachedDatabase.typeMapping.read( DriftSqlType.bool, data['${effectivePrefix}active'], )!, name: attachedDatabase.typeMapping.read( DriftSqlType.string, data['${effectivePrefix}name'], )!, url: attachedDatabase.typeMapping.read( DriftSqlType.string, data['${effectivePrefix}url'], ), lastUpdate: attachedDatabase.typeMapping.read( DriftSqlType.dateTime, data['${effectivePrefix}last_update'], )!, updateInterval: attachedDatabase.typeMapping.read( DriftSqlType.int, data['${effectivePrefix}update_interval'], ), upload: attachedDatabase.typeMapping.read( DriftSqlType.int, data['${effectivePrefix}upload'], ), download: attachedDatabase.typeMapping.read( DriftSqlType.int, data['${effectivePrefix}download'], ), total: attachedDatabase.typeMapping.read( DriftSqlType.int, data['${effectivePrefix}total'], ), expire: attachedDatabase.typeMapping.read( DriftSqlType.dateTime, data['${effectivePrefix}expire'], ), webPageUrl: attachedDatabase.typeMapping.read( DriftSqlType.string, data['${effectivePrefix}web_page_url'], ), supportUrl: attachedDatabase.typeMapping.read( DriftSqlType.string, data['${effectivePrefix}support_url'], ), populatedHeaders: attachedDatabase.typeMapping.read( DriftSqlType.string, data['${effectivePrefix}populated_headers'], ), profileOverride: attachedDatabase.typeMapping.read( DriftSqlType.string, data['${effectivePrefix}profile_override'], ), userOverride: attachedDatabase.typeMapping.read( DriftSqlType.string, data['${effectivePrefix}user_override'], ), ); } @override ProfileEntries createAlias(String alias) { return ProfileEntries(attachedDatabase, alias); } } class ProfileEntriesData extends DataClass implements Insertable { final String id; final String type; final bool active; final String name; final String? url; final DateTime lastUpdate; final int? updateInterval; final int? upload; final int? download; final int? total; final DateTime? expire; final String? webPageUrl; final String? supportUrl; final String? populatedHeaders; final String? profileOverride; final String? userOverride; const ProfileEntriesData({ required this.id, required this.type, required this.active, required this.name, this.url, required this.lastUpdate, this.updateInterval, this.upload, this.download, this.total, this.expire, this.webPageUrl, this.supportUrl, this.populatedHeaders, this.profileOverride, this.userOverride, }); @override Map toColumns(bool nullToAbsent) { final map = {}; map['id'] = Variable(id); map['type'] = Variable(type); map['active'] = Variable(active); map['name'] = Variable(name); if (!nullToAbsent || url != null) { map['url'] = Variable(url); } map['last_update'] = Variable(lastUpdate); if (!nullToAbsent || updateInterval != null) { map['update_interval'] = Variable(updateInterval); } if (!nullToAbsent || upload != null) { map['upload'] = Variable(upload); } if (!nullToAbsent || download != null) { map['download'] = Variable(download); } if (!nullToAbsent || total != null) { map['total'] = Variable(total); } if (!nullToAbsent || expire != null) { map['expire'] = Variable(expire); } if (!nullToAbsent || webPageUrl != null) { map['web_page_url'] = Variable(webPageUrl); } if (!nullToAbsent || supportUrl != null) { map['support_url'] = Variable(supportUrl); } if (!nullToAbsent || populatedHeaders != null) { map['populated_headers'] = Variable(populatedHeaders); } if (!nullToAbsent || profileOverride != null) { map['profile_override'] = Variable(profileOverride); } if (!nullToAbsent || userOverride != null) { map['user_override'] = Variable(userOverride); } return map; } ProfileEntriesCompanion toCompanion(bool nullToAbsent) { return ProfileEntriesCompanion( id: Value(id), type: Value(type), active: Value(active), name: Value(name), url: url == null && nullToAbsent ? const Value.absent() : Value(url), lastUpdate: Value(lastUpdate), updateInterval: updateInterval == null && nullToAbsent ? const Value.absent() : Value(updateInterval), upload: upload == null && nullToAbsent ? const Value.absent() : Value(upload), download: download == null && nullToAbsent ? const Value.absent() : Value(download), total: total == null && nullToAbsent ? const Value.absent() : Value(total), expire: expire == null && nullToAbsent ? const Value.absent() : Value(expire), webPageUrl: webPageUrl == null && nullToAbsent ? const Value.absent() : Value(webPageUrl), supportUrl: supportUrl == null && nullToAbsent ? const Value.absent() : Value(supportUrl), populatedHeaders: populatedHeaders == null && nullToAbsent ? const Value.absent() : Value(populatedHeaders), profileOverride: profileOverride == null && nullToAbsent ? const Value.absent() : Value(profileOverride), userOverride: userOverride == null && nullToAbsent ? const Value.absent() : Value(userOverride), ); } factory ProfileEntriesData.fromJson( Map json, { ValueSerializer? serializer, }) { serializer ??= driftRuntimeOptions.defaultSerializer; return ProfileEntriesData( id: serializer.fromJson(json['id']), type: serializer.fromJson(json['type']), active: serializer.fromJson(json['active']), name: serializer.fromJson(json['name']), url: serializer.fromJson(json['url']), lastUpdate: serializer.fromJson(json['lastUpdate']), updateInterval: serializer.fromJson(json['updateInterval']), upload: serializer.fromJson(json['upload']), download: serializer.fromJson(json['download']), total: serializer.fromJson(json['total']), expire: serializer.fromJson(json['expire']), webPageUrl: serializer.fromJson(json['webPageUrl']), supportUrl: serializer.fromJson(json['supportUrl']), populatedHeaders: serializer.fromJson(json['populatedHeaders']), profileOverride: serializer.fromJson(json['profileOverride']), userOverride: serializer.fromJson(json['userOverride']), ); } @override Map toJson({ValueSerializer? serializer}) { serializer ??= driftRuntimeOptions.defaultSerializer; return { 'id': serializer.toJson(id), 'type': serializer.toJson(type), 'active': serializer.toJson(active), 'name': serializer.toJson(name), 'url': serializer.toJson(url), 'lastUpdate': serializer.toJson(lastUpdate), 'updateInterval': serializer.toJson(updateInterval), 'upload': serializer.toJson(upload), 'download': serializer.toJson(download), 'total': serializer.toJson(total), 'expire': serializer.toJson(expire), 'webPageUrl': serializer.toJson(webPageUrl), 'supportUrl': serializer.toJson(supportUrl), 'populatedHeaders': serializer.toJson(populatedHeaders), 'profileOverride': serializer.toJson(profileOverride), 'userOverride': serializer.toJson(userOverride), }; } ProfileEntriesData copyWith({ String? id, String? type, bool? active, String? name, Value url = const Value.absent(), DateTime? lastUpdate, Value updateInterval = const Value.absent(), Value upload = const Value.absent(), Value download = const Value.absent(), Value total = const Value.absent(), Value expire = const Value.absent(), Value webPageUrl = const Value.absent(), Value supportUrl = const Value.absent(), Value populatedHeaders = const Value.absent(), Value profileOverride = const Value.absent(), Value userOverride = const Value.absent(), }) => ProfileEntriesData( id: id ?? this.id, type: type ?? this.type, active: active ?? this.active, name: name ?? this.name, url: url.present ? url.value : this.url, lastUpdate: lastUpdate ?? this.lastUpdate, updateInterval: updateInterval.present ? updateInterval.value : this.updateInterval, upload: upload.present ? upload.value : this.upload, download: download.present ? download.value : this.download, total: total.present ? total.value : this.total, expire: expire.present ? expire.value : this.expire, webPageUrl: webPageUrl.present ? webPageUrl.value : this.webPageUrl, supportUrl: supportUrl.present ? supportUrl.value : this.supportUrl, populatedHeaders: populatedHeaders.present ? populatedHeaders.value : this.populatedHeaders, profileOverride: profileOverride.present ? profileOverride.value : this.profileOverride, userOverride: userOverride.present ? userOverride.value : this.userOverride, ); ProfileEntriesData copyWithCompanion(ProfileEntriesCompanion data) { return ProfileEntriesData( id: data.id.present ? data.id.value : this.id, type: data.type.present ? data.type.value : this.type, active: data.active.present ? data.active.value : this.active, name: data.name.present ? data.name.value : this.name, url: data.url.present ? data.url.value : this.url, lastUpdate: data.lastUpdate.present ? data.lastUpdate.value : this.lastUpdate, updateInterval: data.updateInterval.present ? data.updateInterval.value : this.updateInterval, upload: data.upload.present ? data.upload.value : this.upload, download: data.download.present ? data.download.value : this.download, total: data.total.present ? data.total.value : this.total, expire: data.expire.present ? data.expire.value : this.expire, webPageUrl: data.webPageUrl.present ? data.webPageUrl.value : this.webPageUrl, supportUrl: data.supportUrl.present ? data.supportUrl.value : this.supportUrl, populatedHeaders: data.populatedHeaders.present ? data.populatedHeaders.value : this.populatedHeaders, profileOverride: data.profileOverride.present ? data.profileOverride.value : this.profileOverride, userOverride: data.userOverride.present ? data.userOverride.value : this.userOverride, ); } @override String toString() { return (StringBuffer('ProfileEntriesData(') ..write('id: $id, ') ..write('type: $type, ') ..write('active: $active, ') ..write('name: $name, ') ..write('url: $url, ') ..write('lastUpdate: $lastUpdate, ') ..write('updateInterval: $updateInterval, ') ..write('upload: $upload, ') ..write('download: $download, ') ..write('total: $total, ') ..write('expire: $expire, ') ..write('webPageUrl: $webPageUrl, ') ..write('supportUrl: $supportUrl, ') ..write('populatedHeaders: $populatedHeaders, ') ..write('profileOverride: $profileOverride, ') ..write('userOverride: $userOverride') ..write(')')) .toString(); } @override int get hashCode => Object.hash( id, type, active, name, url, lastUpdate, updateInterval, upload, download, total, expire, webPageUrl, supportUrl, populatedHeaders, profileOverride, userOverride, ); @override bool operator ==(Object other) => identical(this, other) || (other is ProfileEntriesData && other.id == this.id && other.type == this.type && other.active == this.active && other.name == this.name && other.url == this.url && other.lastUpdate == this.lastUpdate && other.updateInterval == this.updateInterval && other.upload == this.upload && other.download == this.download && other.total == this.total && other.expire == this.expire && other.webPageUrl == this.webPageUrl && other.supportUrl == this.supportUrl && other.populatedHeaders == this.populatedHeaders && other.profileOverride == this.profileOverride && other.userOverride == this.userOverride); } class ProfileEntriesCompanion extends UpdateCompanion { final Value id; final Value type; final Value active; final Value name; final Value url; final Value lastUpdate; final Value updateInterval; final Value upload; final Value download; final Value total; final Value expire; final Value webPageUrl; final Value supportUrl; final Value populatedHeaders; final Value profileOverride; final Value userOverride; final Value rowid; const ProfileEntriesCompanion({ this.id = const Value.absent(), this.type = const Value.absent(), this.active = const Value.absent(), this.name = const Value.absent(), this.url = const Value.absent(), this.lastUpdate = const Value.absent(), this.updateInterval = const Value.absent(), this.upload = const Value.absent(), this.download = const Value.absent(), this.total = const Value.absent(), this.expire = const Value.absent(), this.webPageUrl = const Value.absent(), this.supportUrl = const Value.absent(), this.populatedHeaders = const Value.absent(), this.profileOverride = const Value.absent(), this.userOverride = const Value.absent(), this.rowid = const Value.absent(), }); ProfileEntriesCompanion.insert({ required String id, required String type, required bool active, required String name, this.url = const Value.absent(), required DateTime lastUpdate, this.updateInterval = const Value.absent(), this.upload = const Value.absent(), this.download = const Value.absent(), this.total = const Value.absent(), this.expire = const Value.absent(), this.webPageUrl = const Value.absent(), this.supportUrl = const Value.absent(), this.populatedHeaders = const Value.absent(), this.profileOverride = const Value.absent(), this.userOverride = const Value.absent(), this.rowid = const Value.absent(), }) : id = Value(id), type = Value(type), active = Value(active), name = Value(name), lastUpdate = Value(lastUpdate); static Insertable custom({ Expression? id, Expression? type, Expression? active, Expression? name, Expression? url, Expression? lastUpdate, Expression? updateInterval, Expression? upload, Expression? download, Expression? total, Expression? expire, Expression? webPageUrl, Expression? supportUrl, Expression? populatedHeaders, Expression? profileOverride, Expression? userOverride, Expression? rowid, }) { return RawValuesInsertable({ if (id != null) 'id': id, if (type != null) 'type': type, if (active != null) 'active': active, if (name != null) 'name': name, if (url != null) 'url': url, if (lastUpdate != null) 'last_update': lastUpdate, if (updateInterval != null) 'update_interval': updateInterval, if (upload != null) 'upload': upload, if (download != null) 'download': download, if (total != null) 'total': total, if (expire != null) 'expire': expire, if (webPageUrl != null) 'web_page_url': webPageUrl, if (supportUrl != null) 'support_url': supportUrl, if (populatedHeaders != null) 'populated_headers': populatedHeaders, if (profileOverride != null) 'profile_override': profileOverride, if (userOverride != null) 'user_override': userOverride, if (rowid != null) 'rowid': rowid, }); } ProfileEntriesCompanion copyWith({ Value? id, Value? type, Value? active, Value? name, Value? url, Value? lastUpdate, Value? updateInterval, Value? upload, Value? download, Value? total, Value? expire, Value? webPageUrl, Value? supportUrl, Value? populatedHeaders, Value? profileOverride, Value? userOverride, Value? rowid, }) { return ProfileEntriesCompanion( id: id ?? this.id, type: type ?? this.type, active: active ?? this.active, name: name ?? this.name, url: url ?? this.url, lastUpdate: lastUpdate ?? this.lastUpdate, updateInterval: updateInterval ?? this.updateInterval, upload: upload ?? this.upload, download: download ?? this.download, total: total ?? this.total, expire: expire ?? this.expire, webPageUrl: webPageUrl ?? this.webPageUrl, supportUrl: supportUrl ?? this.supportUrl, populatedHeaders: populatedHeaders ?? this.populatedHeaders, profileOverride: profileOverride ?? this.profileOverride, userOverride: userOverride ?? this.userOverride, rowid: rowid ?? this.rowid, ); } @override Map toColumns(bool nullToAbsent) { final map = {}; if (id.present) { map['id'] = Variable(id.value); } if (type.present) { map['type'] = Variable(type.value); } if (active.present) { map['active'] = Variable(active.value); } if (name.present) { map['name'] = Variable(name.value); } if (url.present) { map['url'] = Variable(url.value); } if (lastUpdate.present) { map['last_update'] = Variable(lastUpdate.value); } if (updateInterval.present) { map['update_interval'] = Variable(updateInterval.value); } if (upload.present) { map['upload'] = Variable(upload.value); } if (download.present) { map['download'] = Variable(download.value); } if (total.present) { map['total'] = Variable(total.value); } if (expire.present) { map['expire'] = Variable(expire.value); } if (webPageUrl.present) { map['web_page_url'] = Variable(webPageUrl.value); } if (supportUrl.present) { map['support_url'] = Variable(supportUrl.value); } if (populatedHeaders.present) { map['populated_headers'] = Variable(populatedHeaders.value); } if (profileOverride.present) { map['profile_override'] = Variable(profileOverride.value); } if (userOverride.present) { map['user_override'] = Variable(userOverride.value); } if (rowid.present) { map['rowid'] = Variable(rowid.value); } return map; } @override String toString() { return (StringBuffer('ProfileEntriesCompanion(') ..write('id: $id, ') ..write('type: $type, ') ..write('active: $active, ') ..write('name: $name, ') ..write('url: $url, ') ..write('lastUpdate: $lastUpdate, ') ..write('updateInterval: $updateInterval, ') ..write('upload: $upload, ') ..write('download: $download, ') ..write('total: $total, ') ..write('expire: $expire, ') ..write('webPageUrl: $webPageUrl, ') ..write('supportUrl: $supportUrl, ') ..write('populatedHeaders: $populatedHeaders, ') ..write('profileOverride: $profileOverride, ') ..write('userOverride: $userOverride, ') ..write('rowid: $rowid') ..write(')')) .toString(); } } class AppProxyEntries extends Table with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; AppProxyEntries(this.attachedDatabase, [this._alias]); late final GeneratedColumn mode = GeneratedColumn( 'mode', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true, ); late final GeneratedColumn pkgName = GeneratedColumn( 'pkg_name', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true, ); late final GeneratedColumn flags = GeneratedColumn( 'flags', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: false, defaultValue: const CustomExpression('0'), ); @override List get $columns => [mode, pkgName, flags]; @override String get aliasedName => _alias ?? actualTableName; @override String get actualTableName => $name; static const String $name = 'app_proxy_entries'; @override Set get $primaryKey => {mode, pkgName}; @override AppProxyEntriesData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return AppProxyEntriesData( mode: attachedDatabase.typeMapping.read( DriftSqlType.string, data['${effectivePrefix}mode'], )!, pkgName: attachedDatabase.typeMapping.read( DriftSqlType.string, data['${effectivePrefix}pkg_name'], )!, flags: attachedDatabase.typeMapping.read( DriftSqlType.int, data['${effectivePrefix}flags'], )!, ); } @override AppProxyEntries createAlias(String alias) { return AppProxyEntries(attachedDatabase, alias); } } class AppProxyEntriesData extends DataClass implements Insertable { final String mode; final String pkgName; final int flags; const AppProxyEntriesData({ required this.mode, required this.pkgName, required this.flags, }); @override Map toColumns(bool nullToAbsent) { final map = {}; map['mode'] = Variable(mode); map['pkg_name'] = Variable(pkgName); map['flags'] = Variable(flags); return map; } AppProxyEntriesCompanion toCompanion(bool nullToAbsent) { return AppProxyEntriesCompanion( mode: Value(mode), pkgName: Value(pkgName), flags: Value(flags), ); } factory AppProxyEntriesData.fromJson( Map json, { ValueSerializer? serializer, }) { serializer ??= driftRuntimeOptions.defaultSerializer; return AppProxyEntriesData( mode: serializer.fromJson(json['mode']), pkgName: serializer.fromJson(json['pkgName']), flags: serializer.fromJson(json['flags']), ); } @override Map toJson({ValueSerializer? serializer}) { serializer ??= driftRuntimeOptions.defaultSerializer; return { 'mode': serializer.toJson(mode), 'pkgName': serializer.toJson(pkgName), 'flags': serializer.toJson(flags), }; } AppProxyEntriesData copyWith({String? mode, String? pkgName, int? flags}) => AppProxyEntriesData( mode: mode ?? this.mode, pkgName: pkgName ?? this.pkgName, flags: flags ?? this.flags, ); AppProxyEntriesData copyWithCompanion(AppProxyEntriesCompanion data) { return AppProxyEntriesData( mode: data.mode.present ? data.mode.value : this.mode, pkgName: data.pkgName.present ? data.pkgName.value : this.pkgName, flags: data.flags.present ? data.flags.value : this.flags, ); } @override String toString() { return (StringBuffer('AppProxyEntriesData(') ..write('mode: $mode, ') ..write('pkgName: $pkgName, ') ..write('flags: $flags') ..write(')')) .toString(); } @override int get hashCode => Object.hash(mode, pkgName, flags); @override bool operator ==(Object other) => identical(this, other) || (other is AppProxyEntriesData && other.mode == this.mode && other.pkgName == this.pkgName && other.flags == this.flags); } class AppProxyEntriesCompanion extends UpdateCompanion { final Value mode; final Value pkgName; final Value flags; final Value rowid; const AppProxyEntriesCompanion({ this.mode = const Value.absent(), this.pkgName = const Value.absent(), this.flags = const Value.absent(), this.rowid = const Value.absent(), }); AppProxyEntriesCompanion.insert({ required String mode, required String pkgName, this.flags = const Value.absent(), this.rowid = const Value.absent(), }) : mode = Value(mode), pkgName = Value(pkgName); static Insertable custom({ Expression? mode, Expression? pkgName, Expression? flags, Expression? rowid, }) { return RawValuesInsertable({ if (mode != null) 'mode': mode, if (pkgName != null) 'pkg_name': pkgName, if (flags != null) 'flags': flags, if (rowid != null) 'rowid': rowid, }); } AppProxyEntriesCompanion copyWith({ Value? mode, Value? pkgName, Value? flags, Value? rowid, }) { return AppProxyEntriesCompanion( mode: mode ?? this.mode, pkgName: pkgName ?? this.pkgName, flags: flags ?? this.flags, rowid: rowid ?? this.rowid, ); } @override Map toColumns(bool nullToAbsent) { final map = {}; if (mode.present) { map['mode'] = Variable(mode.value); } if (pkgName.present) { map['pkg_name'] = Variable(pkgName.value); } if (flags.present) { map['flags'] = Variable(flags.value); } if (rowid.present) { map['rowid'] = Variable(rowid.value); } return map; } @override String toString() { return (StringBuffer('AppProxyEntriesCompanion(') ..write('mode: $mode, ') ..write('pkgName: $pkgName, ') ..write('flags: $flags, ') ..write('rowid: $rowid') ..write(')')) .toString(); } } class DatabaseAtV5 extends GeneratedDatabase { DatabaseAtV5(QueryExecutor e) : super(e); late final ProfileEntries profileEntries = ProfileEntries(this); late final AppProxyEntries appProxyEntries = AppProxyEntries(this); @override Iterable> get allTables => allSchemaEntities.whereType>(); @override List get allSchemaEntities => [ profileEntries, appProxyEntries, ]; @override int get schemaVersion => 5; @override DriftDatabaseOptions get options => const DriftDatabaseOptions(storeDateTimeAsText: true); } ================================================ FILE: test/drift/db/migration_test.dart ================================================ // dart format width=80 // ignore_for_file: unused_local_variable, unused_import import 'package:drift/drift.dart'; import 'package:drift_dev/api/migrations_native.dart'; import 'package:hiddify/core/db/db.dart'; import 'package:flutter_test/flutter_test.dart'; import 'generated/schema.dart'; import 'generated/schema_v1.dart' as v1; import 'generated/schema_v2.dart' as v2; import 'generated/schema_v3.dart' as v3; import 'generated/schema_v4.dart' as v4; void main() { driftRuntimeOptions.dontWarnAboutMultipleDatabases = true; late SchemaVerifier verifier; setUpAll(() { verifier = SchemaVerifier(GeneratedHelper()); }); group('simple database migrations', () { // These simple tests verify all possible schema updates with a simple (no // data) migration. This is a quick way to ensure that written database // migrations properly alter the schema. const versions = GeneratedHelper.versions; for (final (i, fromVersion) in versions.indexed) { group('from $fromVersion', () { for (final toVersion in versions.skip(i + 1)) { test('to $toVersion', () async { final schema = await verifier.schemaAt(fromVersion); final db = Db(schema.newConnection()); await verifier.migrateAndValidate(db, toVersion); await db.close(); }); } }); } }); // The following template shows how to write tests ensuring your migrations // preserve existing data. // Testing this can be useful for migrations that change existing columns // (e.g. by alterating their type or constraints). Migrations that only add // tables or columns typically don't need these advanced tests. For more // information, see https://drift.simonbinder.eu/migrations/tests/#verifying-data-integrity // TODO: This generated template shows how these tests could be written. Adopt // it to your own needs when testing migrations with data integrity. test('migration from v1 to v2 does not corrupt data', () async { // Add data to insert into the old database, and the expected rows after the // migration. // TODO: Fill these lists final oldProfileEntriesData = []; final expectedNewProfileEntriesData = []; await verifier.testWithDataIntegrity( oldVersion: 1, newVersion: 2, createOld: v1.DatabaseAtV1.new, createNew: v2.DatabaseAtV2.new, openTestedDatabase: Db.new, createItems: (batch, oldDb) { batch.insertAll(oldDb.profileEntries, oldProfileEntriesData); }, validateItems: (newDb) async { expect( expectedNewProfileEntriesData, await newDb.select(newDb.profileEntries).get(), ); }, ); }); group('_columnExists-backed migrations', () { test('migration from v3 to v4 adds test_url when missing', () async { final schema = await verifier.schemaAt(3); addTearDown(() => schema.rawDatabase.dispose()); final oldDb = v3.DatabaseAtV3(schema.newConnection()); final oldColumns = await oldDb .customSelect('PRAGMA table_info(profile_entries);') .get(); expect( oldColumns.where((row) => row.data['name'] == 'test_url'), isEmpty, ); await oldDb.close(); final migratedDb = Db(schema.newConnection()); await verifier.migrateAndValidate(migratedDb, 4); await migratedDb.close(); final newDb = v4.DatabaseAtV4(schema.newConnection()); final newColumns = await newDb .customSelect('PRAGMA table_info(profile_entries);') .get(); expect( newColumns.where((row) => row.data['name'] == 'test_url'), hasLength(1), ); await newDb.close(); }); test( 'migration from v3 to v4 skips adding test_url when it already exists', () async { final schema = await verifier.schemaAt(3); addTearDown(() => schema.rawDatabase.dispose()); schema.rawDatabase.execute( 'ALTER TABLE profile_entries ADD COLUMN test_url TEXT NULL;', ); final migratedDb = Db(schema.newConnection()); await verifier.migrateAndValidate(migratedDb, 4); await migratedDb.close(); final newDb = v4.DatabaseAtV4(schema.newConnection()); final newColumns = await newDb .customSelect('PRAGMA table_info(profile_entries);') .get(); expect( newColumns.where((row) => row.data['name'] == 'test_url'), hasLength(1), ); await newDb.close(); }, ); }); } ================================================ FILE: test/features/profile/data/profile_parser_test.dart ================================================ import 'package:flutter_test/flutter_test.dart'; import 'package:hiddify/features/profile/data/profile_parser.dart'; import 'package:hiddify/features/profile/model/profile_entity.dart'; import 'package:uuid/uuid.dart'; void main() { const validBaseUrl = "https://example.com/configurations/user1/filename.yaml"; const validExtendedUrl = "https://example.com/configurations/user1/filename.yaml?test#b"; const validSupportUrl = "https://example.com/support"; group("parse", () { test("Should use filename in url with no headers and fragment", () { final profile = ProfileParser.parse( tempFilePath: '', profile: ProfileEntity.remote( id: const Uuid().v4(), active: true, name: '', url: validBaseUrl, lastUpdate: DateTime.now(), ), ); expect(profile.isRight(), true); profile.match((l) {}, (r) { expect(r is RemoteProfileEntity, true); r.map( remote: (rp) { expect(rp.name, equals("filename")); expect(rp.url, equals(validBaseUrl)); expect(rp.options, isNull); expect(rp.subInfo, isNull); }, local: (lp) {}, ); }); }); test("Should use fragment in url with no headers", () { final profile = ProfileParser.parse( tempFilePath: '', profile: ProfileEntity.remote( id: const Uuid().v4(), active: true, name: '', url: validExtendedUrl, lastUpdate: DateTime.now(), ), ); expect(profile.isRight(), true); profile.match((l) {}, (r) { expect(r is RemoteProfileEntity, true); r.map( remote: (rp) { expect(rp.name, equals("b")); expect(rp.url, equals(validExtendedUrl)); expect(rp.options, isNull); expect(rp.subInfo, isNull); }, local: (lp) {}, ); }); }); test("Should use base64 title in headers", () { final headers = >{ "profile-title": ["base64:ZXhhbXBsZVRpdGxl"], "profile-update-interval": ["1"], "connection-test-url": [validBaseUrl], "remote-dns-address": [validBaseUrl], "subscription-userinfo": ["upload=0;download=1024;total=10240.5;expire=1704054600.55"], "profile-web-page-url": [validBaseUrl], "support-url": [validSupportUrl], }; // This fix occurs in the _downloadProfile method within ProfileParser, and the fixed headers are passed to populateHeaders final fixedHeaders = headers.map((key, value) { if (value.length == 1) return MapEntry(key, value.first); return MapEntry(key, value); }); final allHeaders = ProfileParser.populateHeaders(content: '', remoteHeaders: fixedHeaders); expect(allHeaders.isRight(), true); allHeaders.match((l) {}, (r) { final profile = ProfileParser.parse( tempFilePath: '', profile: ProfileEntity.remote( id: const Uuid().v4(), active: true, name: '', url: validExtendedUrl, lastUpdate: DateTime.now(), populatedHeaders: r, ), ); expect(profile.isRight(), true); profile.match((l) {}, (r) { expect(r is RemoteProfileEntity, true); r.map( remote: (rp) { expect(rp.name, equals("exampleTitle")); expect(rp.url, equals(validExtendedUrl)); expect(rp.options, equals(const ProfileOptions(updateInterval: Duration(hours: 1)))); expect( rp.subInfo, equals( SubscriptionInfo( upload: 0, download: 1024, total: 10240, expire: DateTime.fromMillisecondsSinceEpoch(1704054600 * 1000), webPageUrl: validBaseUrl, supportUrl: validSupportUrl, ), ), ); }, local: (lp) {}, ); }); }); }); test("Should use infinite when given 0 for subscription properties", () { final headers = >{ "profile-title": ["title"], "profile-update-interval": ["1"], "subscription-userinfo": ["upload=0;download=1024;total=0;expire=0"], "profile-web-page-url": [validBaseUrl], "support-url": [validSupportUrl], }; // This fix occurs in the _downloadProfile method within ProfileParser, and the fixed headers are passed to populateHeaders final fixedHeaders = headers.map((key, value) { if (value.length == 1) return MapEntry(key, value.first); return MapEntry(key, value); }); final allHeaders = ProfileParser.populateHeaders(content: '', remoteHeaders: fixedHeaders); expect(allHeaders.isRight(), true); allHeaders.match((l) {}, (r) { final profile = ProfileParser.parse( tempFilePath: '', profile: RemoteProfileEntity( id: const Uuid().v4(), active: true, name: '', url: validBaseUrl, lastUpdate: DateTime.now(), populatedHeaders: r, ), ); expect(profile.isRight(), true); profile.match((l) {}, (r) { expect(r is RemoteProfileEntity, true); r.map( remote: (rp) { expect(rp.subInfo, isNotNull); expect(rp.subInfo!.total, equals(ProfileParser.infiniteTrafficThreshold + 1)); expect( rp.subInfo!.expire, equals(DateTime.fromMillisecondsSinceEpoch(ProfileParser.infiniteTimeThreshold * 1000)), ); }, local: (lp) {}, ); }); }); }); }); } ================================================ FILE: test.configs/README.md ================================================ # Add Warp Config ## Step 1 مرحله You need to install the Hiddify application from [our site](https://app.hiddify.com) or [GitHub](https://github.com/hiddify/hiddify-app/tree/main?tab=readme-ov-file#%EF%B8%8F-get-it-on-stores). شما باید ابتدا نرم‌افزار هیدیفای را از [سایت ما](https://app.hiddify.com) یا [گیت‌هاب](https://github.com/hiddify/hiddify-app/tree/main?tab=readme-ov-file#%EF%B8%8F-get-it-on-stores) دانلود کنید. ## Step 2-1: Easy Way Easy Way but requires another VPN for first-time use [Click to Import Free Warp Config](https://app.hiddify.com/warp) راه ساده، اما نیاز به یک فیلترشکن دیگر برای اولین بار دارد. [کلیک برای اتصال به کانفیگ رایگان](https://app.hiddify.com/warp) ## Step 2-2: A More Advanced Way If the previous step does not work, copy the following link and paste it into your Hiddify app. اگر روش قبلی کار نکرد، لینک زیر را کپی کنید و مطابق تصویر به نرم‌افزار هیدیفای منتقل کنید. ``` https://raw.githubusercontent.com/hiddify/hiddify-app/refs/heads/main/test.configs/warp ``` ![image](https://github.com/user-attachments/assets/2ad5a5d1-4c75-4b2d-b8b3-52a9f1e3e613) ## Video [مشاهده فیلم آموزش See Guide Video](https://www.youtube.com/watch?v=Xv_7TDo80k4) ================================================ FILE: test.configs/ainita ================================================ #profile-title: base64:QWluaXRhLm5ldA== #profile-update-interval: 24 #subscription-userinfo: upload=0; download=0; total=10737418240000000; expire=2546249531 #support-url: https://t.me/ainita #profile-web-page-url: https://ainita.net ssconf://ainita.s3.eu-north-1.amazonaws.com/AinitaServer-1.csv ssconf://ainita.s3.eu-north-1.amazonaws.com/AinitaServer-2.csv ssconf://ainita.s3.eu-north-1.amazonaws.com/AinitaServer-3.csv ssconf://ainita.s3.eu-north-1.amazonaws.com/AinitaServer-4.csv ================================================ FILE: test.configs/dnstt/dnstt_raw_config.json ================================================ { "outbounds": [ { "type": "dnstt", "tag": "§hide§ § 0", "publicKey": "publickey", "domain": "yourdomain", "resolvers": [ "8.8.8.8:53", "8.8.4.4:53", //add more dns server ], "tunnel_per_resolver": 40, "udp_over_tcp": true }, { "type": "socks", "tag": "select", "detour": "§hide§ § 0", "server": "", "server_port": 0 }, { "type": "direct", "tag": "direct §hide§" }, { "type": "direct", "tag": "direct-fragment §hide§", "tls_fragment": { "enabled": true, "size": "10-30", "sleep": "2-8" } } ], "inbounds": [ { "type": "mixed", "tag": "mixed-in127.0.0.1", "listen": "127.0.0.1", "listen_port": 12334 }, { "type": "tproxy", "tag": "tproxy-in127.0.0.1", "listen": "127.0.0.1", "listen_port": 12335 }, { "type": "redirect", "tag": "redirect-in127.0.0.1", "listen": "127.0.0.1", "listen_port": 12336 }, { "type": "direct", "tag": "dns-in127.0.0.1", "listen": "127.0.0.1", "listen_port": 12337 } ], "log": { "level": "info", "output": "data/box.log" }, "dns": { "servers": [ { "type": "hosts", "tag": "dns-static" }, { "type": "tcp", "tag": "dns-remote", "detour": "select", "server": "1.1.1.1" }, { "type": "https", "tag": "dns-remote-fallback", "detour": "select", "server": "8.8.8.8", "tls": { "enabled": true } }, { "type": "https", "tag": "dns-trick-direct", "detour": "direct-fragment §hide§", "domain_resolver": { "server": "dns-direct", "strategy": "prefer_ipv4" }, "server": "dns.cloudflare.com", "tls": { "enabled": true, "fragment": true, "fragment_fallback_delay": "300ms", "record_fragment": true } }, { "type": "udp", "tag": "dns-direct", "connect_timeout": "5s", "disable_tcp_keep_alive": true, "server": "1.1.1.1" }, { "type": "local", "tag": "dns-local", "prefer_go": true }, { "type": "tcp", "tag": "dns-remote-no-warp", "detour": "direct-fragment §hide§", "server": "1.1.1.1" }, { "type": "fakeip", "tag": "dns-fake", "inet4_range": "198.18.0.0/15", "inet6_range": "fc00::/18" } ], "rules": [ { "domain": "api.cloudflareclient.com", "server": "dns-remote-no-warp", "rewrite_ttl": 86400 }, { "domain": [ "captive.apple.com", "cp.cloudflare.com", "google.com", "api.cloudflareclient.com", "www.gstatic.com" ], "server": "dns-direct", "rewrite_ttl": 86400 }, { "domain_suffix": ".ir", "server": "dns-direct", "rewrite_ttl": 86400 }, { "rule_set": "geosite-ir", "server": "dns-direct", "rewrite_ttl": 86400 }, { "query_type": [ "A", "AAAA" ], "server": "dns-fake", "disable_cache": true, "rewrite_ttl": 86400 }, { "server": "dns-remote", "rewrite_ttl": 86400 } ], "final": "dns-remote", "disable_expire": true, "independent_cache": true }, "route": { "rules": [ { "action": "sniff" }, { "protocol": "dns", "action": "hijack-dns" }, { "ip_cidr": [ "10.10.34.0/24", "2001:4188:2:600:10:10:34:0/120" ], "outbound": "select" }, { "domain_suffix": ".ir", "outbound": "direct §hide§" }, { "rule_set": [ "geoip-ir", "geosite-ir" ], "outbound": "direct §hide§" } ], "rule_set": [ { "type": "remote", "tag": "geoip-ir", "url": "https://raw.githubusercontent.com/hiddify/hiddify-geo/rule-set/country/geoip-ir.srs", "download_detour": "select", "update_interval": "120h0m0s" }, { "type": "remote", "tag": "geosite-ir", "url": "https://raw.githubusercontent.com/hiddify/hiddify-geo/rule-set/country/geosite-ir.srs", "download_detour": "select", "update_interval": "120h0m0s" } ], "final": "select", "default_domain_resolver": "dns-direct" } } ================================================ FILE: test.configs/dnstt/readme.md ================================================ # for app: ``` socks://#name -> dnstt://?tunnel_per_resolver=4&resolver=8.8.8.8:53&resolver=8.8.4.4:53&domain=dnstt.hiddify.com&publicKey=xxxx ``` or ``` { "outbounds": [ { "type": "socks", "tag": "socks", "version": "5", "detour": "dnstt1§hide§" }, { "type": "dnstt", "tag": "dnstt1§hide§", "publicKey": "xxxx", "domain": "dnstt.hiddify.com", "tunnel_per_resolver": 4, "resolvers": ["8.8.8.8:53", "8.8.4.4:53"] } ] } ``` # For cli or router or relay server: download hiddify core from: https://github.com/hiddify/hiddify-core/releases/ then you can run dnstt in router or relay server via ``` hiddify-core srun -c config.json ``` see: dnstt_raw_config.json ================================================ FILE: test.configs/fragment ================================================ #profile-title: base64:8J+UpSBGcmFnbWVudCDwn5Sl #profile-update-interval: 24 #subscription-userinfo: upload=0; download=0; total=10737418240000000; expire=2546249531 #support-url: https://t.me/hiddify #profile-web-page-url: https://hiddify.com #connection-test-url: https://instagram.com #remote-dns-address: https://sky.rethinkdns.com/dns-query direct://?fragment=tlshello,10-100,100-200#Fragment1 direct://?fragment=tlshello,1-2,1-3#Fragment2 xdirect://?fragment=tlshello,10-100,100-200#XFragment1 xdirect://?fragment=tlshello,1-2,1-3#XFragment2 ================================================ FILE: test.configs/free_configs ================================================ { "profiles":[ { "region": ["ir", "cn", "other"], "title": {"en": "Warp & Psiphon", "fa": "وارپ و سایفون"}, "sublink": "https://raw.githubusercontent.com/hiddify/hiddify-app/refs/heads/main/test.configs/warp", "tags": { "en": ["Warp"], "fa": ["وارپ"] }, "consent": { "en": "Cloudflare WARP is a Free WireGuard VPN Provider. By enabling this option you are agreeing to the Cloudflare WARP's [Term of service](https://www.cloudflare.com/application/terms/) and [Privacy Policy](https://www.cloudflare.com/application/privacypolicy/).", "fa": "وارپ کلودفلر یک ارائه دهنده رایگان وایرگارد است. با فعال کردن این گزینه، با [شرایط خدمات] (https://www.cloudflare.com/application/terms/) و [خط‌مشی رازداری] (https://www.cloudflare.com/application/) کلودفلر موافقت می‌کنید." } }, { "region": ["ir"], "title": {"en": "Anonymous 𝕏", "fa": "انونیموس 𝕏"}, "sublink": "https://raw.githubusercontent.com/4n0nymou3/multi-proxy-config-fetcher/refs/heads/main/configs/proxy_configs.txt", "tags": { "en": ["mix"], "fa": ["عمومی"] }, "consent": { "en": "This profile collects public and published configurations with the consent of their owners, and tests and verifies them to help the free flow of information.\nTo enhance security, Hidify applies a Warp-based security layer to these configurations to prevent the risk of unauthorized access to information.\nThere is no guarantee that this configuration will function properly and it is recommended to use it only in emergency situations.", "fa": "این پروفایل کانفیگ‌های عمومی و منتشر شده را با رضایت صاحبان آن‌ها جمع آوری می‌کند و تست و بررسی می‌کند تا به گردش آزاد اطلاعات کمک کند.\nبرای بالا بردن امنیت، هیدیفای یک لایه امنیتی مبتنی بر وارپ بر روی این کانفیگها اعمال میکند تا از خطر دسترسی غیرمجاز به اطلاعات جلوگیری کند.\nهیچ ضمانتی برای عملکرد مناسب این کانفیگ وجود ندارد و پیشنهاد میشود صرفا در مواقع اضطراری از آن استفاده کنید" } }, { "region": ["ir"], "title": {"en": "Mahsa", "fa": "مهسا"}, "sublink": "https://raw.githubusercontent.com/hiddify/hiddify-app/refs/heads/main/test.configs/mahsa", "tags": { "en": ["mix"], "fa": ["عمومی"] }, "consent": { "en": "This profile collects public and published configurations with the consent of their owners, and tests and verifies them to help the free flow of information.\nTo enhance security, Hidify applies a Warp-based security layer to these configurations to prevent the risk of unauthorized access to information.\nThere is no guarantee that this configuration will function properly and it is recommended to use it only in emergency situations.", "fa": "این پروفایل کانفیگ‌های عمومی و منتشر شده را با رضایت صاحبان آن‌ها جمع آوری می‌کند و تست و بررسی می‌کند تا به گردش آزاد اطلاعات کمک کند.\nبرای بالا بردن امنیت، هیدیفای یک لایه امنیتی مبتنی بر وارپ بر روی این کانفیگها اعمال میکند تا از خطر دسترسی غیرمجاز به اطلاعات جلوگیری کند.\nهیچ ضمانتی برای عملکرد مناسب این کانفیگ وجود ندارد و پیشنهاد میشود صرفا در مواقع اضطراری از آن استفاده کنید" } } ] } ================================================ FILE: test.configs/mahsa ================================================ #profile-title: Mahsa #profile-update-interval: 24 #subscription-userinfo: upload=0; download=0; total=10737418240000000; expire=2546249531 #support-url: https://github.com/mahsanet/MahsaFreeConfig #profile-web-page-url: https://github.com/mahsanet/MahsaFreeConfig https://raw.githubusercontent.com/mahsanet/MahsaFreeConfig/refs/heads/main/mci/sub_1.txt https://raw.githubusercontent.com/mahsanet/MahsaFreeConfig/refs/heads/main/mci/sub_2.txt https://raw.githubusercontent.com/mahsanet/MahsaFreeConfig/refs/heads/main/mci/sub_3.txt https://raw.githubusercontent.com/mahsanet/MahsaFreeConfig/refs/heads/main/mci/sub_4.txt https://raw.githubusercontent.com/mahsanet/MahsaFreeConfig/refs/heads/main/mtn/sub_1.txt https://raw.githubusercontent.com/mahsanet/MahsaFreeConfig/refs/heads/main/mtn/sub_2.txt https://raw.githubusercontent.com/mahsanet/MahsaFreeConfig/refs/heads/main/mtn/sub_3.txt https://raw.githubusercontent.com/mahsanet/MahsaFreeConfig/refs/heads/main/mtn/sub_4.txt ================================================ FILE: test.configs/super_fragment ================================================ #profile-title: base64:8J+UpSBTdXBlckZyYWdtZW50IPCflKU= #profile-update-interval: 24 #subscription-userinfo: upload=0; download=0; total=10737418240000000; expire=2546249531 #support-url: https://t.me/hiddify #profile-web-page-url: https://hiddify.com #connection-test-url: https://instagram.com #remote-dns-address: https://dns.cloudflare.com/dns-query { "outbounds": [ { "type": "xray", "tag": "XSuperFragment", "xconfig": { "dns": { "disableFallback": true, "hosts": { "dns.cloudflare.com": "cloudflare.com" }, "servers": [ "https://dns.cloudflare.com/dns-query", {"address": "1.1.1.1", "domains": ["full:cloudflare.com"]}, {"address": "223.5.5.5", "domains": ["full:cloudflare.com"]} ], "tag": "dns-query" }, "outbounds": [ { "domainStrategy": "AsIs", "protocol": "freedom", "streamSettings": { "sockopt": { "tcpKeepAliveIdle": 100, "tcpNoDelay": true } }, "tag": "XFragment1" }, { "protocol": "blackhole", "tag": "block" }, { "protocol": "freedom", "settings": { "domainStrategy": "ForceIP" }, "tag": "direct" }, { "protocol": "dns", "streamSettings": { "sockopt": { "dialerProxy": "chain1-fragment" } }, "tag": "dns-out" }, { "protocol": "freedom", "settings": { "fragment": { "interval": "0", "length": "6", "packets": "tlshello" } }, "streamSettings": { "sockopt": { "dialerProxy": "chain1-fragment" } }, "tag": "super-fragment" }, { "protocol": "freedom", "settings": { "fragment": { "interval": "1", "length": "517", "packets": "1-3" } }, "streamSettings": { "sockopt": { "dialerProxy": "chain2-fragment" } }, "tag": "chain1-fragment" }, { "protocol": "freedom", "settings": { "domainStrategy": "ForceIP", "fragment": { "interval": "2", "length": "1", "packets": "1-1" } }, "streamSettings": { "sockopt": { "tcpNoDelay": true } }, "tag": "chain2-fragment" }, { "tag": "udp-noisesv4", "protocol": "freedom", "settings": { "domainStrategy": "ForceIPv4", "noises": [ {"type": "rand", "packet": "1250", "delay": "10"}, {"type": "rand", "packet": "1250", "delay": "10"}, {"type": "rand", "packet": "1250", "delay": "10"}, {"type": "rand", "packet": "1250", "delay": "10"}, {"type": "rand", "packet": "1250", "delay": "10"}, {"type": "rand", "packet": "1250", "delay": "10"}, {"type": "rand", "packet": "1250", "delay": "10"}, {"type": "rand", "packet": "1250", "delay": "10"}, {"type": "rand", "packet": "1250", "delay": "10"}, {"type": "rand", "packet": "1250", "delay": "10"}, {"type": "rand", "packet": "1250", "delay": "10"}, {"type": "rand", "packet": "1250", "delay": "10"}, {"type": "rand", "packet": "1250", "delay": "10"}, {"type": "rand", "packet": "1250", "delay": "10"}, {"type": "rand", "packet": "1250", "delay": "10"}, {"type": "rand", "packet": "1250", "delay": "10"}, {"type": "rand", "packet": "1250", "delay": "10"}, {"type": "rand", "packet": "1250", "delay": "10"}, {"type": "rand", "packet": "1250", "delay": "10"}, {"type": "rand", "packet": "1250", "delay": "10"}, {"type": "rand", "packet": "1250", "delay": "10"}, {"type": "rand", "packet": "1250", "delay": "10"}, {"type": "rand", "packet": "1250", "delay": "10"}, {"type": "rand", "packet": "1250", "delay": "10"} ] } }, { "tag": "udp-noisesv6", "protocol": "freedom", "settings": { "domainStrategy": "ForceIPv6", "noises": [ {"type": "rand", "packet": "1230", "delay": "10"}, {"type": "rand", "packet": "1230", "delay": "10"}, {"type": "rand", "packet": "1230", "delay": "10"}, {"type": "rand", "packet": "1230", "delay": "10"}, {"type": "rand", "packet": "1230", "delay": "10"}, {"type": "rand", "packet": "1230", "delay": "10"}, {"type": "rand", "packet": "1230", "delay": "10"}, {"type": "rand", "packet": "1230", "delay": "10"}, {"type": "rand", "packet": "1230", "delay": "10"}, {"type": "rand", "packet": "1230", "delay": "10"}, {"type": "rand", "packet": "1230", "delay": "10"}, {"type": "rand", "packet": "1230", "delay": "10"}, {"type": "rand", "packet": "1230", "delay": "10"}, {"type": "rand", "packet": "1230", "delay": "10"}, {"type": "rand", "packet": "1230", "delay": "10"}, {"type": "rand", "packet": "1230", "delay": "10"}, {"type": "rand", "packet": "1230", "delay": "10"}, {"type": "rand", "packet": "1230", "delay": "10"}, {"type": "rand", "packet": "1230", "delay": "10"}, {"type": "rand", "packet": "1230", "delay": "10"}, {"type": "rand", "packet": "1230", "delay": "10"}, {"type": "rand", "packet": "1230", "delay": "10"}, {"type": "rand", "packet": "1230", "delay": "10"}, {"type": "rand", "packet": "1230", "delay": "10"} ] } } ], "routing": { "domainStrategy": "IPOnDemand", "rules": [ { "inboundTag": ["dns-query"], "network": "tcp", "outboundTag": "super-fragment" }, { "ip": ["10.10.34.0/24","2001:4188:2:600:10:10::/64"], "outboundTag": "block" }, { "network": "udp", "port": 443, "ip": ["0.0.0.0/0"], "outboundTag": "udp-noisesv4" }, { "network": "udp", "port": 443, "ip": ["::/0"], "outboundTag": "udp-noisesv6" }, { "network": "udp", "outboundTag": "direct" }, { "network": "tcp", "outboundTag": "super-fragment" } ] } }, "xdebug": false }, { "type": "xray", "tag": "XFragment", "xconfig": { "dns": { "disableFallback": true, "hosts": { "dns.cloudflare.com": "cloudflare.com" }, "servers": [ "https://dns.cloudflare.com/dns-query", {"address": "1.1.1.1", "domains": ["full:cloudflare.com"]}, {"address": "223.5.5.5", "domains": ["full:cloudflare.com"]} ], "tag": "dns-query" }, "outbounds": [ { "domainStrategy": "AsIs", "protocol": "freedom", "streamSettings": { "sockopt": { "tcpKeepAliveIdle": 100, "tcpNoDelay": true } }, "tag": "XFragment" }, { "protocol": "blackhole", "tag": "block" }, { "protocol": "freedom", "settings": { "domainStrategy": "ForceIP" }, "tag": "direct" }, { "protocol": "dns", "streamSettings": { "sockopt": { "dialerProxy": "chain1-fragment" } }, "tag": "dns-out" }, { "protocol": "freedom", "settings": { "fragment": { "interval": "0", "length": "6", "packets": "tlshello" } }, "streamSettings": { "sockopt": { "dialerProxy": "chain1-fragment" } }, "tag": "super-fragment" }, { "protocol": "freedom", "settings": { "fragment": { "interval": "1", "length": "517", "packets": "1-3" } }, "streamSettings": { "sockopt": { "dialerProxy": "chain2-fragment" } }, "tag": "chain1-fragment" }, { "protocol": "freedom", "settings": { "domainStrategy": "ForceIP", "fragment": { "interval": "2", "length": "1", "packets": "1-1" } }, "streamSettings": { "sockopt": { "tcpNoDelay": true } }, "tag": "chain2-fragment" }, { "tag": "udp-noisesv4", "protocol": "freedom", "settings": { "domainStrategy": "ForceIPv4", "noises": [ {"type": "rand", "packet": "1250", "delay": "10"}, {"type": "rand", "packet": "1250", "delay": "10"}, {"type": "rand", "packet": "1250", "delay": "10"}, {"type": "rand", "packet": "1250", "delay": "10"}, {"type": "rand", "packet": "1250", "delay": "10"}, {"type": "rand", "packet": "1250", "delay": "10"}, {"type": "rand", "packet": "1250", "delay": "10"}, {"type": "rand", "packet": "1250", "delay": "10"}, {"type": "rand", "packet": "1250", "delay": "10"}, {"type": "rand", "packet": "1250", "delay": "10"}, {"type": "rand", "packet": "1250", "delay": "10"}, {"type": "rand", "packet": "1250", "delay": "10"}, {"type": "rand", "packet": "1250", "delay": "10"}, {"type": "rand", "packet": "1250", "delay": "10"}, {"type": "rand", "packet": "1250", "delay": "10"}, {"type": "rand", "packet": "1250", "delay": "10"}, {"type": "rand", "packet": "1250", "delay": "10"}, {"type": "rand", "packet": "1250", "delay": "10"}, {"type": "rand", "packet": "1250", "delay": "10"}, {"type": "rand", "packet": "1250", "delay": "10"}, {"type": "rand", "packet": "1250", "delay": "10"}, {"type": "rand", "packet": "1250", "delay": "10"}, {"type": "rand", "packet": "1250", "delay": "10"}, {"type": "rand", "packet": "1250", "delay": "10"} ] } }, { "tag": "udp-noisesv6", "protocol": "freedom", "settings": { "domainStrategy": "ForceIPv6", "noises": [ {"type": "rand", "packet": "1230", "delay": "10"}, {"type": "rand", "packet": "1230", "delay": "10"}, {"type": "rand", "packet": "1230", "delay": "10"}, {"type": "rand", "packet": "1230", "delay": "10"}, {"type": "rand", "packet": "1230", "delay": "10"}, {"type": "rand", "packet": "1230", "delay": "10"}, {"type": "rand", "packet": "1230", "delay": "10"}, {"type": "rand", "packet": "1230", "delay": "10"}, {"type": "rand", "packet": "1230", "delay": "10"}, {"type": "rand", "packet": "1230", "delay": "10"}, {"type": "rand", "packet": "1230", "delay": "10"}, {"type": "rand", "packet": "1230", "delay": "10"}, {"type": "rand", "packet": "1230", "delay": "10"}, {"type": "rand", "packet": "1230", "delay": "10"}, {"type": "rand", "packet": "1230", "delay": "10"}, {"type": "rand", "packet": "1230", "delay": "10"}, {"type": "rand", "packet": "1230", "delay": "10"}, {"type": "rand", "packet": "1230", "delay": "10"}, {"type": "rand", "packet": "1230", "delay": "10"}, {"type": "rand", "packet": "1230", "delay": "10"}, {"type": "rand", "packet": "1230", "delay": "10"}, {"type": "rand", "packet": "1230", "delay": "10"}, {"type": "rand", "packet": "1230", "delay": "10"}, {"type": "rand", "packet": "1230", "delay": "10"} ] } } ], "routing": { "domainStrategy": "IPOnDemand", "rules": [ { "inboundTag": ["dns-query"], "network": "tcp", "outboundTag": "chain1-fragment" }, { "ip": ["10.10.34.0/24","2001:4188:2:600:10:10::/64"], "outboundTag": "block" }, { "network": "udp", "port": 443, "ip": ["0.0.0.0/0"], "outboundTag": "udp-noisesv4" }, { "network": "udp", "port": 443, "ip": ["::/0"], "outboundTag": "udp-noisesv6" }, { "network": "udp", "outboundTag": "direct" }, { "network": "tcp", "outboundTag": "chain1-fragment" } ] } }, "xdebug": false }, { "type": "direct", "tag": "Fragment1 § 0", "tls_fragment": { "enabled": true, "size": "1-2", "sleep": "1-4" } }, { "type": "direct", "tag": "Fragment2 § 0", "tls_fragment": { "enabled": true, "size": "10-100", "sleep": "100-200" } } ] } ================================================ FILE: test.configs/warp ================================================ //profile-title: base64:8J+UpVBzaXBob24gJiBXQVJQIPCflKU= //profile-update-interval: 24 //subscription-userinfo: upload=0; download=0; total=10737418240000000; expire=2546249531 //support-url: https://t.me/hiddify //profile-web-page-url: https://hiddify.com psiphon://auto/ #psiphon://auto/?region=...&remote_server_list_url=...&remote_server_list_download_filename=...&remote_server_list_signature_public_key=... warp://A1@188.114.97.170:894#warp_in_warp -> warp://A2@188.114.97.170:894?ifp=40-80&ifps=40-100&ifpd=4-8&ifpm=m4#m4 warp://B1@auto#WarpInWarp✅ -> warp://B2@auto?ifpm=m4#LocalIP #warp://p1@ip1?ifp=1-3#WarpInWarp✅ -> warp://p2@ip2ifp=1-3#Warp🇮🇷IP #warp://auto?ifp=10-20&ifps=40-100&ifpd=10-20#Warp_10-20_40-100_10-20 #warp://auto?ifp=10-20&ifps=40-100&ifpd=30-200#Warp_10-20_40-100_30-50 #warp://auto?ifp=10-20&ifps=40-100&ifpd=300-500#Warp_10-20_40-100_300-500 #warp://auto?ifp=5-10&ifps=40-100&ifpd=10-20#Warp_5-10_40-100_10-20 #warp://auto?ifp=5-10&ifps=40-100&ifpd=30-50#Warp_5-10_40-100_30-50 #warp://auto?ifp=5-10&ifps=40-100&ifpd=300-500#Warp_5-10_40-100_300-500 ================================================ FILE: test.configs/warp2 ================================================ //profile-title: base64:8J+UpSBXQVJQIPCflKU= //profile-update-interval: 24 //subscription-userinfo: upload=0; download=0; total=10737418240000000; expire=2546249531 //support-url: https://t.me/hiddify //profile-web-page-url: https://hiddify.com #attention! do not use p1 and p2 in more than two configs otherwise confilicts may occure #ifpm is about mode we have currently 6 modes. warp://p1@auto/?ifp=1-3&ifpm=m1#m1 warp://p2@auto/?ifp=1-3&ifpm=m2#m2 warp://p1@auto/?ifp=1-3&ifpm=m3#m3 warp://p2@auto/?ifp=1-3&ifpm=m4#m4 warp://p1@auto/?ifp=1-3&ifpm=m5#m5 warp://p2@auto/?ifp=1-3&ifpm=m6#m6 #For g and h mode, you can use hex bytes between each _, with each part ranging from 00 to FF. You can also select more hex bytes to randomly choose between them. #In the next line, this means that the fake packet uses 0x30 as the first byte in the noise. warp://p2@auto/?ifp=1-3&ifpm=h_30#h_30 #In the following line, g indicates that the fake packet randomly uses one of the values 0x10, 0x20, or 0x30 as the initial byte in the noise after the WireGuard reserved key. This should not be effective. warp://p1@auto/?ifp=1-3&ifpm=g_10_20_30#g_10_20_30 ================================================ FILE: web/drift_worker.js ================================================ (function dartProgram(){function copyProperties(a,b){var s=Object.keys(a) for(var r=0;r=0)return true if(typeof version=="function"&&version.length==0){var q=version() if(/^\d+\.\d+\.\d+\.\d+$/.test(q))return true}}catch(p){}return false}() function inherit(a,b){a.prototype.constructor=a a.prototype["$i"+a.name]=a if(b!=null){if(z){Object.setPrototypeOf(a.prototype,b.prototype) return}var s=Object.create(b.prototype) copyProperties(a.prototype,s) a.prototype=s}}function inheritMany(a,b){for(var s=0;s4294967295)throw A.a(A.a4(a,0,4294967295,"length",null)) return J.uc(new Array(a),b)}, pW(a,b){if(a<0)throw A.a(A.K("Length must be a non-negative integer: "+a,null)) return A.d(new Array(a),b.h("w<0>"))}, pU(a,b){if(a<0)throw A.a(A.K("Length must be a non-negative integer: "+a,null)) return A.d(new Array(a),b.h("w<0>"))}, uc(a,b){return J.k8(A.d(a,b.h("w<0>")))}, k8(a){a.fixed$length=Array return a}, ud(a,b){return J.tB(a,b)}, pX(a){if(a<256)switch(a){case 9:case 10:case 11:case 12:case 13:case 32:case 133:case 160:return!0 default:return!1}switch(a){case 5760:case 8192:case 8193:case 8194:case 8195:case 8196:case 8197:case 8198:case 8199:case 8200:case 8201:case 8202:case 8232:case 8233:case 8239:case 8287:case 12288:case 65279:return!0 default:return!1}}, ue(a,b){var s,r for(s=a.length;b0;b=s){s=b-1 r=a.charCodeAt(s) if(r!==32&&r!==13&&!J.pX(r))break}return b}, cg(a){if(typeof a=="number"){if(Math.floor(a)==a)return J.ei.prototype return J.h9.prototype}if(typeof a=="string")return J.bV.prototype if(a==null)return J.ej.prototype if(typeof a=="boolean")return J.h8.prototype if(Array.isArray(a))return J.w.prototype if(typeof a!="object"){if(typeof a=="function")return J.bW.prototype if(typeof a=="symbol")return J.el.prototype if(typeof a=="bigint")return J.aY.prototype return a}if(a instanceof A.e)return a return J.ph(a)}, V(a){if(typeof a=="string")return J.bV.prototype if(a==null)return a if(Array.isArray(a))return J.w.prototype if(typeof a!="object"){if(typeof a=="function")return J.bW.prototype if(typeof a=="symbol")return J.el.prototype if(typeof a=="bigint")return J.aY.prototype return a}if(a instanceof A.e)return a return J.ph(a)}, aM(a){if(a==null)return a if(Array.isArray(a))return J.w.prototype if(typeof a!="object"){if(typeof a=="function")return J.bW.prototype if(typeof a=="symbol")return J.el.prototype if(typeof a=="bigint")return J.aY.prototype return a}if(a instanceof A.e)return a return J.ph(a)}, x3(a){if(typeof a=="number")return J.cZ.prototype if(typeof a=="string")return J.bV.prototype if(a==null)return a if(!(a instanceof A.e))return J.cy.prototype return a}, fs(a){if(typeof a=="string")return J.bV.prototype if(a==null)return a if(!(a instanceof A.e))return J.cy.prototype return a}, X(a,b){if(a==null)return b==null if(typeof a!="object")return b!=null&&a===b return J.cg(a).O(a,b)}, aO(a,b){if(typeof b==="number")if(Array.isArray(a)||typeof a=="string"||A.rE(a,a[v.dispatchPropertyName]))if(b>>>0===b&&b>>0===b&&b").b(a))return new A.eS(a,b.h("@<0>").H(c).h("eS<1,2>")) return new A.ck(a,b.h("@<0>").H(c).h("ck<1,2>"))}, ug(a){return new A.bX("Field '"+a+"' has not been initialized.")}, o5(a){var s,r=a^48 if(r<=9)return r s=a|32 if(97<=s&&s<=102)return s-87 return-1}, c6(a,b){a=a+b&536870911 a=a+((a&524287)<<10)&536870911 return a^a>>>6}, oI(a){a=a+((a&67108863)<<3)&536870911 a^=a>>>11 return a+((a&16383)<<15)&536870911}, aD(a,b,c){return a}, pk(a){var s,r for(s=$.cO.length,r=0;rc)A.y(A.a4(b,0,c,"start",null))}return new A.cw(a,b,c,d.h("cw<0>"))}, en(a,b,c,d){if(t.Q.b(a))return new A.cp(a,b,c.h("@<0>").H(d).h("cp<1,2>")) return new A.az(a,b,c.h("@<0>").H(d).h("az<1,2>"))}, oJ(a,b,c){var s="takeCount" A.bR(b,s) A.ab(b,s) if(t.Q.b(a))return new A.eb(a,b,c.h("eb<0>")) return new A.cx(a,b,c.h("cx<0>"))}, qf(a,b,c){var s="count" if(t.Q.b(a)){A.bR(b,s) A.ab(b,s) return new A.cV(a,b,c.h("cV<0>"))}A.bR(b,s) A.ab(b,s) return new A.bB(a,b,c.h("bB<0>"))}, ua(a,b,c){return new A.co(a,b,c.h("co<0>"))}, ak(){return new A.b2("No element")}, pT(){return new A.b2("Too few elements")}, ca:function ca(){}, fJ:function fJ(a,b){this.a=a this.$ti=b}, ck:function ck(a,b){this.a=a this.$ti=b}, eS:function eS(a,b){this.a=a this.$ti=b}, eN:function eN(){}, ah:function ah(a,b){this.a=a this.$ti=b}, bX:function bX(a){this.a=a}, e6:function e6(a){this.a=a}, oc:function oc(){}, kB:function kB(){}, v:function v(){}, P:function P(){}, cw:function cw(a,b,c,d){var _=this _.a=a _.b=b _.c=c _.$ti=d}, aZ:function aZ(a,b,c){var _=this _.a=a _.b=b _.c=0 _.d=null _.$ti=c}, az:function az(a,b,c){this.a=a this.b=b this.$ti=c}, cp:function cp(a,b,c){this.a=a this.b=b this.$ti=c}, b0:function b0(a,b,c){var _=this _.a=null _.b=a _.c=b _.$ti=c}, D:function D(a,b,c){this.a=a this.b=b this.$ti=c}, aT:function aT(a,b,c){this.a=a this.b=b this.$ti=c}, eH:function eH(a,b){this.a=a this.b=b}, ed:function ed(a,b,c){this.a=a this.b=b this.$ti=c}, fY:function fY(a,b,c,d){var _=this _.a=a _.b=b _.c=c _.d=null _.$ti=d}, cx:function cx(a,b,c){this.a=a this.b=b this.$ti=c}, eb:function eb(a,b,c){this.a=a this.b=b this.$ti=c}, hF:function hF(a,b,c){this.a=a this.b=b this.$ti=c}, bB:function bB(a,b,c){this.a=a this.b=b this.$ti=c}, cV:function cV(a,b,c){this.a=a this.b=b this.$ti=c}, hy:function hy(a,b){this.a=a this.b=b}, ex:function ex(a,b,c){this.a=a this.b=b this.$ti=c}, hz:function hz(a,b){this.a=a this.b=b this.c=!1}, cq:function cq(a){this.$ti=a}, fV:function fV(){}, eI:function eI(a,b){this.a=a this.$ti=b}, hY:function hY(a,b){this.a=a this.$ti=b}, bt:function bt(a,b,c){this.a=a this.b=b this.$ti=c}, co:function co(a,b,c){this.a=a this.b=b this.$ti=c}, eg:function eg(a,b){this.a=a this.b=b this.c=-1}, ee:function ee(){}, hJ:function hJ(){}, dh:function dh(){}, ew:function ew(a,b){this.a=a this.$ti=b}, hE:function hE(a){this.a=a}, fn:function fn(){}, rO(a){var s=v.mangledGlobalNames[a] if(s!=null)return s return"minified:"+a}, rE(a,b){var s if(b!=null){s=b.x if(s!=null)return s}return t.aU.b(a)}, u(a){var s if(typeof a=="string")return a if(typeof a=="number"){if(a!==0)return""+a}else if(!0===a)return"true" else if(!1===a)return"false" else if(a==null)return"null" s=J.aU(a) return s}, ev(a){var s,r=$.q3 if(r==null)r=$.q3=Symbol("identityHashCode") s=a[r] if(s==null){s=Math.random()*0x3fffffff|0 a[r]=s}return s}, q4(a,b){var s,r,q,p,o,n=null,m=/^\s*[+-]?((0x[a-f0-9]+)|(\d+)|([a-z0-9]+))\s*$/i.exec(a) if(m==null)return n s=m[3] if(b==null){if(s!=null)return parseInt(a,10) if(m[2]!=null)return parseInt(a,16) return n}if(b<2||b>36)throw A.a(A.a4(b,2,36,"radix",n)) if(b===10&&s!=null)return parseInt(a,10) if(b<10||s==null){r=b<=10?47+b:86+b q=m[1] for(p=q.length,o=0;or)return n}return parseInt(a,b)}, kp(a){return A.uo(a)}, uo(a){var s,r,q,p if(a instanceof A.e)return A.aK(A.aE(a),null) s=J.cg(a) if(s===B.aG||s===B.aJ||t.ak.b(a)){r=B.a1(a) if(r!=="Object"&&r!=="")return r q=a.constructor if(typeof q=="function"){p=q.name if(typeof p=="string"&&p!=="Object"&&p!=="")return p}}return A.aK(A.aE(a),null)}, q5(a){if(a==null||typeof a=="number"||A.bM(a))return J.aU(a) if(typeof a=="string")return JSON.stringify(a) if(a instanceof A.cl)return a.j(0) if(a instanceof A.f6)return a.fP(!0) return"Instance of '"+A.kp(a)+"'"}, up(){if(!!self.location)return self.location.href return null}, q2(a){var s,r,q,p,o=a.length if(o<=500)return String.fromCharCode.apply(null,a) for(s="",r=0;r65535)return A.uy(a)}return A.q2(a)}, uz(a,b,c){var s,r,q,p if(c<=500&&b===0&&c===a.length)return String.fromCharCode.apply(null,a) for(s=b,r="";s>>0,s&1023|56320)}}throw A.a(A.a4(a,0,1114111,null,null))}, aS(a){if(a.date===void 0)a.date=new Date(a.a) return a.date}, ux(a){return a.c?A.aS(a).getUTCFullYear()+0:A.aS(a).getFullYear()+0}, uv(a){return a.c?A.aS(a).getUTCMonth()+1:A.aS(a).getMonth()+1}, ur(a){return a.c?A.aS(a).getUTCDate()+0:A.aS(a).getDate()+0}, us(a){return a.c?A.aS(a).getUTCHours()+0:A.aS(a).getHours()+0}, uu(a){return a.c?A.aS(a).getUTCMinutes()+0:A.aS(a).getMinutes()+0}, uw(a){return a.c?A.aS(a).getUTCSeconds()+0:A.aS(a).getSeconds()+0}, ut(a){return a.c?A.aS(a).getUTCMilliseconds()+0:A.aS(a).getMilliseconds()+0}, uq(a){var s=a.$thrownJsError if(s==null)return null return A.R(s)}, dV(a,b){var s,r="index" if(!A.bn(b))return new A.aV(!0,b,r,null) s=J.ae(a) if(b<0||b>=s)return A.h4(b,s,a,null,r) return A.kt(b,r)}, wY(a,b,c){if(a>c)return A.a4(a,0,c,"start",null) if(b!=null)if(bc)return A.a4(b,a,c,"end",null) return new A.aV(!0,b,"end",null)}, dS(a){return new A.aV(!0,a,null,null)}, a(a){return A.rC(new Error(),a)}, rC(a,b){var s if(b==null)b=new A.bD() a.dartException=b s=A.xC if("defineProperty" in Object){Object.defineProperty(a,"message",{get:s}) a.name=""}else a.toString=s return a}, xC(){return J.aU(this.dartException)}, y(a){throw A.a(a)}, oi(a,b){throw A.rC(b,a)}, W(a){throw A.a(A.ax(a))}, bE(a){var s,r,q,p,o,n a=A.rM(a.replace(String({}),"$receiver$")) s=a.match(/\\\$[a-zA-Z]+\\\$/g) if(s==null)s=A.d([],t.s) r=s.indexOf("\\$arguments\\$") q=s.indexOf("\\$argumentsExpr\\$") p=s.indexOf("\\$expr\\$") o=s.indexOf("\\$method\\$") n=s.indexOf("\\$receiver\\$") return new A.lc(a.replace(new RegExp("\\\\\\$arguments\\\\\\$","g"),"((?:x|[^x])*)").replace(new RegExp("\\\\\\$argumentsExpr\\\\\\$","g"),"((?:x|[^x])*)").replace(new RegExp("\\\\\\$expr\\\\\\$","g"),"((?:x|[^x])*)").replace(new RegExp("\\\\\\$method\\\\\\$","g"),"((?:x|[^x])*)").replace(new RegExp("\\\\\\$receiver\\\\\\$","g"),"((?:x|[^x])*)"),r,q,p,o,n)}, ld(a){return function($expr$){var $argumentsExpr$="$arguments$" try{$expr$.$method$($argumentsExpr$)}catch(s){return s.message}}(a)}, qo(a){return function($expr$){try{$expr$.$method$}catch(s){return s.message}}(a)}, oB(a,b){var s=b==null,r=s?null:b.method return new A.hb(a,r,s?null:b.receiver)}, E(a){if(a==null)return new A.hp(a) if(a instanceof A.ec)return A.ch(a,a.a) if(typeof a!=="object")return a if("dartException" in a)return A.ch(a,a.dartException) return A.wv(a)}, ch(a,b){if(t.w.b(b))if(b.$thrownJsError==null)b.$thrownJsError=a return b}, wv(a){var s,r,q,p,o,n,m,l,k,j,i,h,g if(!("message" in a))return a s=a.message if("number" in a&&typeof a.number=="number"){r=a.number q=r&65535 if((B.b.P(r,16)&8191)===10)switch(q){case 438:return A.ch(a,A.oB(A.u(s)+" (Error "+q+")",null)) case 445:case 5007:A.u(s) return A.ch(a,new A.er())}}if(a instanceof TypeError){p=$.rU() o=$.rV() n=$.rW() m=$.rX() l=$.t_() k=$.t0() j=$.rZ() $.rY() i=$.t2() h=$.t1() g=p.ar(s) if(g!=null)return A.ch(a,A.oB(s,g)) else{g=o.ar(s) if(g!=null){g.method="call" return A.ch(a,A.oB(s,g))}else if(n.ar(s)!=null||m.ar(s)!=null||l.ar(s)!=null||k.ar(s)!=null||j.ar(s)!=null||m.ar(s)!=null||i.ar(s)!=null||h.ar(s)!=null)return A.ch(a,new A.er())}return A.ch(a,new A.hI(typeof s=="string"?s:""))}if(a instanceof RangeError){if(typeof s=="string"&&s.indexOf("call stack")!==-1)return new A.eA() s=function(b){try{return String(b)}catch(f){}return null}(a) return A.ch(a,new A.aV(!1,null,null,typeof s=="string"?s.replace(/^RangeError:\s*/,""):s))}if(typeof InternalError=="function"&&a instanceof InternalError)if(typeof s=="string"&&s==="too much recursion")return new A.eA() return a}, R(a){var s if(a instanceof A.ec)return a.b if(a==null)return new A.fa(a) s=a.$cachedTrace if(s!=null)return s s=new A.fa(a) if(typeof a==="object")a.$cachedTrace=s return s}, pm(a){if(a==null)return J.aw(a) if(typeof a=="object")return A.ev(a) return J.aw(a)}, x_(a,b){var s,r,q,p=a.length for(s=0;s=0 else if(b instanceof A.cs){s=B.a.K(a,c) return b.b.test(s)}else return!J.oo(b,B.a.K(a,c)).gF(0)}, pg(a){if(a.indexOf("$",0)>=0)return a.replace(/\$/g,"$$$$") return a}, xy(a,b,c,d){var s=b.fe(a,d) if(s==null)return a return A.po(a,s.b.index,s.gby(),c)}, rM(a){if(/[[\]{}()*+?.\\^$|]/.test(a))return a.replace(/[[\]{}()*+?.\\^$|]/g,"\\$&") return a}, bc(a,b,c){var s if(typeof b=="string")return A.xx(a,b,c) if(b instanceof A.cs){s=b.gfq() s.lastIndex=0 return a.replace(s,A.pg(c))}return A.xw(a,b,c)}, xw(a,b,c){var s,r,q,p for(s=J.oo(b,a),s=s.gt(s),r=0,q="";s.k();){p=s.gm() q=q+a.substring(r,p.gcq())+c r=p.gby()}s=q+a.substring(r) return s.charCodeAt(0)==0?s:s}, xx(a,b,c){var s,r,q if(b===""){if(a==="")return c s=a.length r=""+c for(q=0;q=0)return a.split(b).join(c) return a.replace(new RegExp(A.rM(b),"g"),A.pg(c))}, xz(a,b,c,d){var s,r,q,p if(typeof b=="string"){s=a.indexOf(b,d) if(s<0)return a return A.po(a,s,s+b.length,c)}if(b instanceof A.cs)return d===0?a.replace(b.b,A.pg(c)):A.xy(a,b,c,d) r=J.tz(b,a,d) q=r.gt(r) if(!q.k())return a p=q.gm() return B.a.aO(a,p.gcq(),p.gby(),c)}, po(a,b,c,d){return a.substring(0,b)+d+a.substring(c)}, ap:function ap(a,b){this.a=a this.b=b}, cH:function cH(a,b){this.a=a this.b=b}, e7:function e7(){}, e8:function e8(a,b,c){this.a=a this.b=b this.$ti=c}, cG:function cG(a,b){this.a=a this.$ti=b}, il:function il(a,b,c){var _=this _.a=a _.b=b _.c=0 _.d=null _.$ti=c}, k3:function k3(){}, eh:function eh(a,b){this.a=a this.$ti=b}, lc:function lc(a,b,c,d,e,f){var _=this _.a=a _.b=b _.c=c _.d=d _.e=e _.f=f}, er:function er(){}, hb:function hb(a,b,c){this.a=a this.b=b this.c=c}, hI:function hI(a){this.a=a}, hp:function hp(a){this.a=a}, ec:function ec(a,b){this.a=a this.b=b}, fa:function fa(a){this.a=a this.b=null}, cl:function cl(){}, j9:function j9(){}, ja:function ja(){}, l2:function l2(){}, kT:function kT(){}, e2:function e2(a,b){this.a=a this.b=b}, i8:function i8(a){this.a=a}, hv:function hv(a){this.a=a}, bu:function bu(a){var _=this _.a=0 _.f=_.e=_.d=_.c=_.b=null _.r=0 _.$ti=a}, kb:function kb(a){this.a=a}, ka:function ka(a){this.a=a}, ke:function ke(a,b){var _=this _.a=a _.b=b _.d=_.c=null}, b8:function b8(a,b){this.a=a this.$ti=b}, he:function he(a,b){var _=this _.a=a _.b=b _.d=_.c=null}, o6:function o6(a){this.a=a}, o7:function o7(a){this.a=a}, o8:function o8(a){this.a=a}, f6:function f6(){}, is:function is(){}, cs:function cs(a,b){var _=this _.a=a _.b=b _.d=_.c=null}, dz:function dz(a){this.b=a}, hZ:function hZ(a,b,c){this.a=a this.b=b this.c=c}, lO:function lO(a,b,c){var _=this _.a=a _.b=b _.c=c _.d=null}, dg:function dg(a,b){this.a=a this.c=b}, iA:function iA(a,b,c){this.a=a this.b=b this.c=c}, ns:function ns(a,b,c){var _=this _.a=a _.b=b _.c=c _.d=null}, xB(a){A.oi(new A.bX("Field '"+a+"' has been assigned during initialization."),new Error())}, G(){A.oi(new A.bX("Field '' has not been initialized."),new Error())}, pq(){A.oi(new A.bX("Field '' has already been initialized."),new Error())}, oj(){A.oi(new A.bX("Field '' has been assigned during initialization."),new Error())}, m4(a){var s=new A.m3(a) return s.b=s}, m3:function m3(a){this.a=a this.b=null}, vN(a){return a}, p5(a,b,c){}, iJ(a){var s,r,q if(t.aP.b(a))return a s=J.V(a) r=A.b_(s.gl(a),null,!1,t.z) for(q=0;q>>0!==a||a>=c)throw A.a(A.dV(b,a))}, ce(a,b,c){var s if(!(a>>>0!==a))s=b>>>0!==b||a>b||b>c else s=!0 if(s)throw A.a(A.wY(a,b,c)) return b}, d_:function d_(){}, ep:function ep(){}, d0:function d0(){}, d2:function d2(){}, c_:function c_(){}, aR:function aR(){}, hg:function hg(){}, hh:function hh(){}, hi:function hi(){}, d1:function d1(){}, hj:function hj(){}, hk:function hk(){}, hl:function hl(){}, eq:function eq(){}, bx:function bx(){}, f1:function f1(){}, f2:function f2(){}, f3:function f3(){}, f4:function f4(){}, qc(a,b){var s=b.c return s==null?b.c=A.p0(a,b.x,!0):s}, oE(a,b){var s=b.c return s==null?b.c=A.fg(a,"C",[b.x]):s}, qd(a){var s=a.w if(s===6||s===7||s===8)return A.qd(a.x) return s===12||s===13}, uB(a){return a.as}, aq(a){return A.iG(v.typeUniverse,a,!1)}, xb(a,b){var s,r,q,p,o if(a==null)return null s=b.y r=a.Q if(r==null)r=a.Q=new Map() q=b.as p=r.get(q) if(p!=null)return p o=A.bN(v.typeUniverse,a.x,s,0) r.set(q,o) return o}, bN(a1,a2,a3,a4){var s,r,q,p,o,n,m,l,k,j,i,h,g,f,e,d,c,b,a,a0=a2.w switch(a0){case 5:case 1:case 2:case 3:case 4:return a2 case 6:s=a2.x r=A.bN(a1,s,a3,a4) if(r===s)return a2 return A.qS(a1,r,!0) case 7:s=a2.x r=A.bN(a1,s,a3,a4) if(r===s)return a2 return A.p0(a1,r,!0) case 8:s=a2.x r=A.bN(a1,s,a3,a4) if(r===s)return a2 return A.qQ(a1,r,!0) case 9:q=a2.y p=A.dP(a1,q,a3,a4) if(p===q)return a2 return A.fg(a1,a2.x,p) case 10:o=a2.x n=A.bN(a1,o,a3,a4) m=a2.y l=A.dP(a1,m,a3,a4) if(n===o&&l===m)return a2 return A.oZ(a1,n,l) case 11:k=a2.x j=a2.y i=A.dP(a1,j,a3,a4) if(i===j)return a2 return A.qR(a1,k,i) case 12:h=a2.x g=A.bN(a1,h,a3,a4) f=a2.y e=A.ws(a1,f,a3,a4) if(g===h&&e===f)return a2 return A.qP(a1,g,e) case 13:d=a2.y a4+=d.length c=A.dP(a1,d,a3,a4) o=a2.x n=A.bN(a1,o,a3,a4) if(c===d&&n===o)return a2 return A.p_(a1,n,c,!0) case 14:b=a2.x if(b") for(r=1;r=0)p+=" "+r[q];++q}return p+"})"}, rf(a3,a4,a5){var s,r,q,p,o,n,m,l,k,j,i,h,g,f,e,d,c,b,a,a0,a1=", ",a2=null if(a5!=null){s=a5.length if(a4==null)a4=A.d([],t.s) else a2=a4.length r=a4.length for(q=s;q>0;--q)a4.push("T"+(r+q)) for(p=t.X,o=t._,n="<",m="",q=0;q0){a+=a0+"[" for(a0="",q=0;q0){a+=a0+"{" for(a0="",q=0;q "+b}, aK(a,b){var s,r,q,p,o,n,m=a.w if(m===5)return"erased" if(m===2)return"dynamic" if(m===3)return"void" if(m===1)return"Never" if(m===4)return"any" if(m===6)return A.aK(a.x,b) if(m===7){s=a.x r=A.aK(s,b) q=s.w return(q===12||q===13?"("+r+")":r)+"?"}if(m===8)return"FutureOr<"+A.aK(a.x,b)+">" if(m===9){p=A.wu(a.x) o=a.y return o.length>0?p+("<"+A.ro(o,b)+">"):p}if(m===11)return A.wg(a,b) if(m===12)return A.rf(a,b,null) if(m===13)return A.rf(a.x,b,a.y) if(m===14){n=a.x return b[b.length-1-n]}return"?"}, wu(a){var s=v.mangledGlobalNames[a] if(s!=null)return s return"minified:"+a}, vm(a,b){var s=a.tR[b] for(;typeof s=="string";)s=a.tR[s] return s}, vl(a,b){var s,r,q,p,o,n=a.eT,m=n[b] if(m==null)return A.iG(a,b,!1) else if(typeof m=="number"){s=m r=A.fh(a,5,"#") q=A.nG(s) for(p=0;p0)p+="<"+A.ff(c)+">" s=a.eC.get(p) if(s!=null)return s r=new A.b1(null,null) r.w=9 r.x=b r.y=c if(c.length>0)r.c=c[0] r.as=p q=A.bI(a,r) a.eC.set(p,q) return q}, oZ(a,b,c){var s,r,q,p,o,n if(b.w===10){s=b.x r=b.y.concat(c)}else{r=c s=b}q=s.as+(";<"+A.ff(r)+">") p=a.eC.get(q) if(p!=null)return p o=new A.b1(null,null) o.w=10 o.x=s o.y=r o.as=q n=A.bI(a,o) a.eC.set(q,n) return n}, qR(a,b,c){var s,r,q="+"+(b+"("+A.ff(c)+")"),p=a.eC.get(q) if(p!=null)return p s=new A.b1(null,null) s.w=11 s.x=b s.y=c s.as=q r=A.bI(a,s) a.eC.set(q,r) return r}, qP(a,b,c){var s,r,q,p,o,n=b.as,m=c.a,l=m.length,k=c.b,j=k.length,i=c.c,h=i.length,g="("+A.ff(m) if(j>0){s=l>0?",":"" g+=s+"["+A.ff(k)+"]"}if(h>0){s=l>0?",":"" g+=s+"{"+A.vd(i)+"}"}r=n+(g+")") q=a.eC.get(r) if(q!=null)return q p=new A.b1(null,null) p.w=12 p.x=b p.y=c p.as=r o=A.bI(a,p) a.eC.set(r,o) return o}, p_(a,b,c,d){var s,r=b.as+("<"+A.ff(c)+">"),q=a.eC.get(r) if(q!=null)return q s=A.vf(a,b,c,r,d) a.eC.set(r,s) return s}, vf(a,b,c,d,e){var s,r,q,p,o,n,m,l if(e){s=c.length r=A.nG(s) for(q=0,p=0;p0){n=A.bN(a,b,r,0) m=A.dP(a,c,r,0) return A.p_(a,n,m,c!==m)}}l=new A.b1(null,null) l.w=13 l.x=b l.y=c l.as=d return A.bI(a,l)}, qJ(a,b,c,d){return{u:a,e:b,r:c,s:[],p:0,n:d}}, qL(a){var s,r,q,p,o,n,m,l=a.r,k=a.s for(s=l.length,r=0;r=48&&q<=57)r=A.v4(r+1,q,l,k) else if((((q|32)>>>0)-97&65535)<26||q===95||q===36||q===124)r=A.qK(a,r,l,k,!1) else if(q===46)r=A.qK(a,r,l,k,!0) else{++r switch(q){case 44:break case 58:k.push(!1) break case 33:k.push(!0) break case 59:k.push(A.cd(a.u,a.e,k.pop())) break case 94:k.push(A.vi(a.u,k.pop())) break case 35:k.push(A.fh(a.u,5,"#")) break case 64:k.push(A.fh(a.u,2,"@")) break case 126:k.push(A.fh(a.u,3,"~")) break case 60:k.push(a.p) a.p=k.length break case 62:A.v6(a,k) break case 38:A.v5(a,k) break case 42:p=a.u k.push(A.qS(p,A.cd(p,a.e,k.pop()),a.n)) break case 63:p=a.u k.push(A.p0(p,A.cd(p,a.e,k.pop()),a.n)) break case 47:p=a.u k.push(A.qQ(p,A.cd(p,a.e,k.pop()),a.n)) break case 40:k.push(-3) k.push(a.p) a.p=k.length break case 41:A.v3(a,k) break case 91:k.push(a.p) a.p=k.length break case 93:o=k.splice(a.p) A.qM(a.u,a.e,o) a.p=k.pop() k.push(o) k.push(-1) break case 123:k.push(a.p) a.p=k.length break case 125:o=k.splice(a.p) A.v8(a.u,a.e,o) a.p=k.pop() k.push(o) k.push(-2) break case 43:n=l.indexOf("(",r) k.push(l.substring(r,n)) k.push(-4) k.push(a.p) a.p=k.length r=n+1 break default:throw"Bad character "+q}}}m=k.pop() return A.cd(a.u,a.e,m)}, v4(a,b,c,d){var s,r,q=b-48 for(s=c.length;a=48&&r<=57))break q=q*10+(r-48)}d.push(q) return a}, qK(a,b,c,d,e){var s,r,q,p,o,n,m=b+1 for(s=c.length;m>>0)-97&65535)<26||r===95||r===36||r===124))q=r>=48&&r<=57 else q=!0 if(!q)break}}p=c.substring(b,m) if(e){s=a.u o=a.e if(o.w===10)o=o.x n=A.vm(s,o.x)[p] if(n==null)A.y('No "'+p+'" in "'+A.uB(o)+'"') d.push(A.fi(s,o,n))}else d.push(p) return m}, v6(a,b){var s,r=a.u,q=A.qI(a,b),p=b.pop() if(typeof p=="string")b.push(A.fg(r,p,q)) else{s=A.cd(r,a.e,p) switch(s.w){case 12:b.push(A.p_(r,s,q,a.n)) break default:b.push(A.oZ(r,s,q)) break}}}, v3(a,b){var s,r,q,p=a.u,o=b.pop(),n=null,m=null if(typeof o=="number")switch(o){case-1:n=b.pop() break case-2:m=b.pop() break default:b.push(o) break}else b.push(o) s=A.qI(a,b) o=b.pop() switch(o){case-3:o=b.pop() if(n==null)n=p.sEA if(m==null)m=p.sEA r=A.cd(p,a.e,o) q=new A.ig() q.a=s q.b=n q.c=m b.push(A.qP(p,r,q)) return case-4:b.push(A.qR(p,b.pop(),s)) return default:throw A.a(A.e_("Unexpected state under `()`: "+A.u(o)))}}, v5(a,b){var s=b.pop() if(0===s){b.push(A.fh(a.u,1,"0&")) return}if(1===s){b.push(A.fh(a.u,4,"1&")) return}throw A.a(A.e_("Unexpected extended operation "+A.u(s)))}, qI(a,b){var s=b.splice(a.p) A.qM(a.u,a.e,s) a.p=b.pop() return s}, cd(a,b,c){if(typeof c=="string")return A.fg(a,c,a.sEA) else if(typeof c=="number"){b.toString return A.v7(a,b,c)}else return c}, qM(a,b,c){var s,r=c.length for(s=0;sn)return!1 m=n-o l=s.b k=r.b j=l.length i=k.length if(o+j=d)return!1 a1=f[b] b+=3 if(a00?new Array(q):v.typeUniverse.sEA for(o=0;o0?new Array(a):v.typeUniverse.sEA}, b1:function b1(a,b){var _=this _.a=a _.b=b _.r=_.f=_.d=_.c=null _.w=0 _.as=_.Q=_.z=_.y=_.x=null}, ig:function ig(){this.c=this.b=this.a=null}, ny:function ny(a){this.a=a}, ib:function ib(){}, fe:function fe(a){this.a=a}, uQ(){var s,r,q={} if(self.scheduleImmediate!=null)return A.wy() if(self.MutationObserver!=null&&self.document!=null){s=self.document.createElement("div") r=self.document.createElement("span") q.a=null new self.MutationObserver(A.cf(new A.lQ(q),1)).observe(s,{childList:true}) return new A.lP(q,s,r)}else if(self.setImmediate!=null)return A.wz() return A.wA()}, uR(a){self.scheduleImmediate(A.cf(new A.lR(a),0))}, uS(a){self.setImmediate(A.cf(new A.lS(a),0))}, uT(a){A.oK(B.z,a)}, oK(a,b){var s=B.b.I(a.a,1000) return A.va(s<0?0:s,b)}, va(a,b){var s=new A.iD() s.hR(a,b) return s}, vb(a,b){var s=new A.iD() s.hS(a,b) return s}, o(a){return new A.i_(new A.k($.i,a.h("k<0>")),a.h("i_<0>"))}, n(a,b){a.$2(0,null) b.b=!0 return b.a}, c(a,b){A.vE(a,b)}, m(a,b){b.L(a)}, l(a,b){b.bx(A.E(a),A.R(a))}, vE(a,b){var s,r,q=new A.nI(b),p=new A.nJ(b) if(a instanceof A.k)a.fN(q,p,t.z) else{s=t.z if(a instanceof A.k)a.bG(q,p,s) else{r=new A.k($.i,t.eI) r.a=8 r.c=a r.fN(q,p,s)}}}, p(a){var s=function(b,c){return function(d,e){while(true){try{b(d,e) break}catch(r){e=r d=c}}}}(a,1) return $.i.d5(new A.nZ(s),t.H,t.S,t.z)}, qO(a,b,c){return 0}, iV(a,b){var s=A.aD(a,"error",t.K) return new A.cS(s,b==null?A.fD(a):b)}, fD(a){var s if(t.w.b(a)){s=a.gbJ() if(s!=null)return s}return B.bu}, u8(a,b){var s=new A.k($.i,b.h("k<0>")) A.qi(B.z,new A.jX(a,s)) return s}, jW(a,b){var s,r,q,p,o,n,m=null try{m=a.$0()}catch(o){s=A.E(o) r=A.R(o) n=$.i q=new A.k(n,b.h("k<0>")) p=n.aL(s,r) if(p!=null)q.aE(p.a,p.b) else q.aE(s,r) return q}return b.h("C<0>").b(m)?m:A.eX(m,b)}, aX(a,b){var s=a==null?b.a(a):a,r=new A.k($.i,b.h("k<0>")) r.b1(s) return r}, pQ(a,b,c){var s,r A.aD(a,"error",t.K) s=$.i if(s!==B.d){r=s.aL(a,b) if(r!=null){a=r.a b=r.b}}if(b==null)b=A.fD(a) s=new A.k($.i,c.h("k<0>")) s.aE(a,b) return s}, pP(a,b){var s,r=!b.b(null) if(r)throw A.a(A.ag(null,"computation","The type parameter is not nullable")) s=new A.k($.i,b.h("k<0>")) A.qi(a,new A.jV(null,s,b)) return s}, ou(a,b){var s,r,q,p,o,n,m,l,k={},j=null,i=!1,h=new A.k($.i,b.h("k>")) k.a=null k.b=0 k.c=k.d=null s=new A.jZ(k,j,i,h) try{for(n=J.M(a),m=t.P;n.k();){r=n.gm() q=k.b r.bG(new A.jY(k,q,h,b,j,i),s,m);++k.b}n=k.b if(n===0){n=h n.bq(A.d([],b.h("w<0>"))) return n}k.a=A.b_(n,null,!1,b.h("0?"))}catch(l){p=A.E(l) o=A.R(l) if(k.b===0||i)return A.pQ(p,o,b.h("q<0>")) else{k.d=p k.c=o}}return h}, p6(a,b,c){var s=$.i.aL(b,c) if(s!=null){b=s.a c=s.b}else if(c==null)c=A.fD(b) a.W(b,c)}, v0(a,b,c){var s=new A.k(b,c.h("k<0>")) s.a=8 s.c=a return s}, eX(a,b){var s=new A.k($.i,b.h("k<0>")) s.a=8 s.c=a return s}, oV(a,b){var s,r for(;s=a.a,(s&4)!==0;)a=a.c if(a===b){b.aE(new A.aV(!0,a,null,"Cannot complete a future with itself"),A.oG()) return}s|=b.a&1 a.a=s if((s&24)!==0){r=b.cE() b.cu(a) A.du(b,r)}else{r=b.c b.fH(a) a.dY(r)}}, v1(a,b){var s,r,q={},p=q.a=a for(;s=p.a,(s&4)!==0;){p=p.c q.a=p}if(p===b){b.aE(new A.aV(!0,p,null,"Cannot complete a future with itself"),A.oG()) return}if((s&24)===0){r=b.c b.fH(p) q.a.dY(r) return}if((s&16)===0&&b.c==null){b.cu(p) return}b.a^=2 b.b.b_(new A.mm(q,b))}, du(a,b){var s,r,q,p,o,n,m,l,k,j,i,h,g={},f=g.a=a for(;!0;){s={} r=f.a q=(r&16)===0 p=!q if(b==null){if(p&&(r&1)===0){r=f.c f.b.c4(r.a,r.b)}return}s.a=b o=b.a for(f=b;o!=null;f=o,o=n){f.a=null A.du(g.a,f) s.a=o n=o.a}r=g.a m=r.c s.b=p s.c=m if(q){l=f.c l=(l&1)!==0||(l&15)===8}else l=!0 if(l){k=f.b.b if(p){f=r.b f=!(f===k||f.gbb()===k.gbb())}else f=!1 if(f){f=g.a r=f.c f.b.c4(r.a,r.b) return}j=$.i if(j!==k)$.i=k else j=null f=s.a.c if((f&15)===8)new A.mt(s,g,p).$0() else if(q){if((f&1)!==0)new A.ms(s,m).$0()}else if((f&2)!==0)new A.mr(g,s).$0() if(j!=null)$.i=j f=s.c if(f instanceof A.k){r=s.a.$ti r=r.h("C<2>").b(f)||!r.y[1].b(f)}else r=!1 if(r){i=s.a.b if((f.a&24)!==0){h=i.c i.c=null b=i.cF(h) i.a=f.a&30|i.a&1 i.c=f.c g.a=f continue}else A.oV(f,i) return}}i=s.a.b h=i.c i.c=null b=i.cF(h) f=s.b r=s.c if(!f){i.a=8 i.c=r}else{i.a=i.a&1|16 i.c=r}g.a=i f=i}}, wi(a,b){if(t.b.b(a))return b.d5(a,t.z,t.K,t.l) if(t.bI.b(a))return b.be(a,t.z,t.K) throw A.a(A.ag(a,"onError",u.c))}, wa(){var s,r for(s=$.dO;s!=null;s=$.dO){$.fp=null r=s.b $.dO=r if(r==null)$.fo=null s.a.$0()}}, wr(){$.p9=!0 try{A.wa()}finally{$.fp=null $.p9=!1 if($.dO!=null)$.pt().$1(A.rw())}}, rq(a){var s=new A.i0(a),r=$.fo if(r==null){$.dO=$.fo=s if(!$.p9)$.pt().$1(A.rw())}else $.fo=r.b=s}, wq(a){var s,r,q,p=$.dO if(p==null){A.rq(a) $.fp=$.fo return}s=new A.i0(a) r=$.fp if(r==null){s.b=p $.dO=$.fp=s}else{q=r.b s.b=q $.fp=r.b=s if(q==null)$.fo=s}}, oh(a){var s,r=null,q=$.i if(B.d===q){A.nW(r,r,B.d,a) return}if(B.d===q.ge1().a)s=B.d.gbb()===q.gbb() else s=!1 if(s){A.nW(r,r,q,q.au(a,t.H)) return}s=$.i s.b_(s.cQ(a))}, xS(a){return new A.dG(A.aD(a,"stream",t.K))}, eD(a,b,c,d){var s=null return c?new A.dK(b,s,s,a,d.h("dK<0>")):new A.dn(b,s,s,a,d.h("dn<0>"))}, iM(a){var s,r,q if(a==null)return try{a.$0()}catch(q){s=A.E(q) r=A.R(q) $.i.c4(s,r)}}, v_(a,b,c,d,e,f){var s=$.i,r=e?1:0,q=c!=null?32:0,p=A.i5(s,b,f),o=A.i6(s,c),n=d==null?A.rv():d return new A.cb(a,p,o,s.au(n,t.H),s,r|q,f.h("cb<0>"))}, i5(a,b,c){var s=b==null?A.wB():b return a.be(s,t.H,c)}, i6(a,b){if(b==null)b=A.wC() if(t.da.b(b))return a.d5(b,t.z,t.K,t.l) if(t.d5.b(b))return a.be(b,t.z,t.K) throw A.a(A.K("handleError callback must take either an Object (the error), or both an Object (the error) and a StackTrace.",null))}, wb(a){}, wd(a,b){$.i.c4(a,b)}, wc(){}, wo(a,b,c){var s,r,q,p,o,n try{b.$1(a.$0())}catch(n){s=A.E(n) r=A.R(n) q=$.i.aL(s,r) if(q==null)c.$2(s,r) else{p=q.a o=q.b c.$2(p,o)}}}, vK(a,b,c,d){var s=a.J(),r=$.ci() if(s!==r)s.ai(new A.nL(b,c,d)) else b.W(c,d)}, vL(a,b){return new A.nK(a,b)}, r8(a,b,c){var s=a.J(),r=$.ci() if(s!==r)s.ai(new A.nM(b,c)) else b.b2(c)}, v9(a,b,c){return new A.dE(new A.nr(null,null,a,c,b),b.h("@<0>").H(c).h("dE<1,2>"))}, qi(a,b){var s=$.i if(s===B.d)return s.ei(a,b) return s.ei(a,s.cQ(b))}, wm(a,b,c,d,e){A.fq(d,e)}, fq(a,b){A.wq(new A.nS(a,b))}, nT(a,b,c,d){var s,r=$.i if(r===c)return d.$0() $.i=c s=r try{r=d.$0() return r}finally{$.i=s}}, nV(a,b,c,d,e){var s,r=$.i if(r===c)return d.$1(e) $.i=c s=r try{r=d.$1(e) return r}finally{$.i=s}}, nU(a,b,c,d,e,f){var s,r=$.i if(r===c)return d.$2(e,f) $.i=c s=r try{r=d.$2(e,f) return r}finally{$.i=s}}, rm(a,b,c,d){return d}, rn(a,b,c,d){return d}, rl(a,b,c,d){return d}, wl(a,b,c,d,e){return null}, nW(a,b,c,d){var s,r if(B.d!==c){s=B.d.gbb() r=c.gbb() d=s!==r?c.cQ(d):c.ef(d,t.H)}A.rq(d)}, wk(a,b,c,d,e){return A.oK(d,B.d!==c?c.ef(e,t.H):e)}, wj(a,b,c,d,e){var s if(B.d!==c)e=c.fU(e,t.H,t.aF) s=B.b.I(d.a,1000) return A.vb(s<0?0:s,e)}, wn(a,b,c,d){A.pn(d)}, wf(a){$.i.hf(a)}, rk(a,b,c,d,e){var s,r,q $.rK=A.wD() if(d==null)d=B.bI if(e==null)s=c.gfm() else{r=t.X s=A.u9(e,r,r)}r=new A.i7(c.gfE(),c.gfG(),c.gfF(),c.gfA(),c.gfB(),c.gfz(),c.gfd(),c.ge1(),c.gfa(),c.gf9(),c.gft(),c.gfg(),c.gdS(),c,s) q=d.a if(q!=null)r.as=new A.au(r,q) return r}, xs(a,b,c){A.aD(a,"body",c.h("0()")) return A.wp(a,b,null,c)}, wp(a,b,c,d){return $.i.h4(c,b).bg(a,d)}, lQ:function lQ(a){this.a=a}, lP:function lP(a,b,c){this.a=a this.b=b this.c=c}, lR:function lR(a){this.a=a}, lS:function lS(a){this.a=a}, iD:function iD(){this.c=0}, nx:function nx(a,b){this.a=a this.b=b}, nw:function nw(a,b,c,d){var _=this _.a=a _.b=b _.c=c _.d=d}, i_:function i_(a,b){this.a=a this.b=!1 this.$ti=b}, nI:function nI(a){this.a=a}, nJ:function nJ(a){this.a=a}, nZ:function nZ(a){this.a=a}, iB:function iB(a){var _=this _.a=a _.e=_.d=_.c=_.b=null}, dJ:function dJ(a,b){this.a=a this.$ti=b}, cS:function cS(a,b){this.a=a this.b=b}, eM:function eM(a,b){this.a=a this.$ti=b}, cB:function cB(a,b,c,d,e,f,g){var _=this _.ay=0 _.CW=_.ch=null _.w=a _.a=b _.b=c _.c=d _.d=e _.e=f _.r=_.f=null _.$ti=g}, cA:function cA(){}, fd:function fd(a,b,c){var _=this _.a=a _.b=b _.c=0 _.r=_.f=_.e=_.d=null _.$ti=c}, nt:function nt(a,b){this.a=a this.b=b}, nv:function nv(a,b,c){this.a=a this.b=b this.c=c}, nu:function nu(a){this.a=a}, jX:function jX(a,b){this.a=a this.b=b}, jV:function jV(a,b,c){this.a=a this.b=b this.c=c}, jZ:function jZ(a,b,c,d){var _=this _.a=a _.b=b _.c=c _.d=d}, jY:function jY(a,b,c,d,e,f){var _=this _.a=a _.b=b _.c=c _.d=d _.e=e _.f=f}, dp:function dp(){}, a2:function a2(a,b){this.a=a this.$ti=b}, a8:function a8(a,b){this.a=a this.$ti=b}, cc:function cc(a,b,c,d,e){var _=this _.a=null _.b=a _.c=b _.d=c _.e=d _.$ti=e}, k:function k(a,b){var _=this _.a=0 _.b=a _.c=null _.$ti=b}, mj:function mj(a,b){this.a=a this.b=b}, mq:function mq(a,b){this.a=a this.b=b}, mn:function mn(a){this.a=a}, mo:function mo(a){this.a=a}, mp:function mp(a,b,c){this.a=a this.b=b this.c=c}, mm:function mm(a,b){this.a=a this.b=b}, ml:function ml(a,b){this.a=a this.b=b}, mk:function mk(a,b,c){this.a=a this.b=b this.c=c}, mt:function mt(a,b,c){this.a=a this.b=b this.c=c}, mu:function mu(a){this.a=a}, ms:function ms(a,b){this.a=a this.b=b}, mr:function mr(a,b){this.a=a this.b=b}, i0:function i0(a){this.a=a this.b=null}, Y:function Y(){}, l_:function l_(a,b){this.a=a this.b=b}, l0:function l0(a,b){this.a=a this.b=b}, kY:function kY(a){this.a=a}, kZ:function kZ(a,b,c){this.a=a this.b=b this.c=c}, kW:function kW(a,b){this.a=a this.b=b}, kX:function kX(a,b,c,d){var _=this _.a=a _.b=b _.c=c _.d=d}, kU:function kU(a,b){this.a=a this.b=b}, kV:function kV(a,b,c){this.a=a this.b=b this.c=c}, hD:function hD(){}, cI:function cI(){}, nq:function nq(a){this.a=a}, np:function np(a){this.a=a}, iC:function iC(){}, i1:function i1(){}, dn:function dn(a,b,c,d,e){var _=this _.a=null _.b=0 _.c=null _.d=a _.e=b _.f=c _.r=d _.$ti=e}, dK:function dK(a,b,c,d,e){var _=this _.a=null _.b=0 _.c=null _.d=a _.e=b _.f=c _.r=d _.$ti=e}, an:function an(a,b){this.a=a this.$ti=b}, cb:function cb(a,b,c,d,e,f,g){var _=this _.w=a _.a=b _.b=c _.c=d _.d=e _.e=f _.r=_.f=null _.$ti=g}, dH:function dH(a){this.a=a}, af:function af(){}, m2:function m2(a,b,c){this.a=a this.b=b this.c=c}, m1:function m1(a){this.a=a}, dF:function dF(){}, ia:function ia(){}, dq:function dq(a){this.b=a this.a=null}, eQ:function eQ(a,b){this.b=a this.c=b this.a=null}, mc:function mc(){}, f5:function f5(){this.a=0 this.c=this.b=null}, nf:function nf(a,b){this.a=a this.b=b}, eR:function eR(a){this.a=1 this.b=a this.c=null}, dG:function dG(a){this.a=null this.b=a this.c=!1}, nL:function nL(a,b,c){this.a=a this.b=b this.c=c}, nK:function nK(a,b){this.a=a this.b=b}, nM:function nM(a,b){this.a=a this.b=b}, eW:function eW(){}, ds:function ds(a,b,c,d,e,f,g){var _=this _.w=a _.x=null _.a=b _.b=c _.c=d _.d=e _.e=f _.r=_.f=null _.$ti=g}, f0:function f0(a,b,c){this.b=a this.a=b this.$ti=c}, eT:function eT(a){this.a=a}, dD:function dD(a,b,c,d,e,f){var _=this _.w=$ _.x=null _.a=a _.b=b _.c=c _.d=d _.e=e _.r=_.f=null _.$ti=f}, fc:function fc(){}, eL:function eL(a,b,c){this.a=a this.b=b this.$ti=c}, dv:function dv(a,b,c,d,e){var _=this _.a=a _.b=b _.c=c _.d=d _.$ti=e}, dE:function dE(a,b){this.a=a this.$ti=b}, nr:function nr(a,b,c,d,e){var _=this _.a=a _.b=b _.c=c _.d=d _.e=e}, au:function au(a,b){this.a=a this.b=b}, iI:function iI(a,b,c,d,e,f,g,h,i,j,k,l,m){var _=this _.a=a _.b=b _.c=c _.d=d _.e=e _.f=f _.r=g _.w=h _.x=i _.y=j _.z=k _.Q=l _.as=m}, dM:function dM(a){this.a=a}, iH:function iH(){}, i7:function i7(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o){var _=this _.a=a _.b=b _.c=c _.d=d _.e=e _.f=f _.r=g _.w=h _.x=i _.y=j _.z=k _.Q=l _.as=m _.at=null _.ax=n _.ay=o}, m9:function m9(a,b,c){this.a=a this.b=b this.c=c}, mb:function mb(a,b,c,d){var _=this _.a=a _.b=b _.c=c _.d=d}, m8:function m8(a,b){this.a=a this.b=b}, ma:function ma(a,b,c){this.a=a this.b=b this.c=c}, nS:function nS(a,b){this.a=a this.b=b}, iw:function iw(){}, nk:function nk(a,b,c){this.a=a this.b=b this.c=c}, nm:function nm(a,b,c,d){var _=this _.a=a _.b=b _.c=c _.d=d}, nj:function nj(a,b){this.a=a this.b=b}, nl:function nl(a,b,c){this.a=a this.b=b this.c=c}, pS(a,b){return new A.cE(a.h("@<0>").H(b).h("cE<1,2>"))}, qH(a,b){var s=a[b] return s===a?null:s}, oX(a,b,c){if(c==null)a[b]=a else a[b]=c}, oW(){var s=Object.create(null) A.oX(s,"",s) delete s[""] return s}, uh(a,b){return new A.bu(a.h("@<0>").H(b).h("bu<1,2>"))}, kf(a,b,c){return A.x_(a,new A.bu(b.h("@<0>").H(c).h("bu<1,2>")))}, a3(a,b){return new A.bu(a.h("@<0>").H(b).h("bu<1,2>"))}, oC(a){return new A.eZ(a.h("eZ<0>"))}, oY(){var s=Object.create(null) s[""]=s delete s[""] return s}, im(a,b,c){var s=new A.dy(a,b,c.h("dy<0>")) s.c=a.e return s}, u9(a,b,c){var s=A.pS(b,c) a.aa(0,new A.k1(s,b,c)) return s}, oD(a){var s,r={} if(A.pk(a))return"{...}" s=new A.av("") try{$.cO.push(a) s.a+="{" r.a=!0 a.aa(0,new A.kj(r,s)) s.a+="}"}finally{$.cO.pop()}r=s.a return r.charCodeAt(0)==0?r:r}, cE:function cE(a){var _=this _.a=0 _.e=_.d=_.c=_.b=null _.$ti=a}, mv:function mv(a){this.a=a}, dw:function dw(a){var _=this _.a=0 _.e=_.d=_.c=_.b=null _.$ti=a}, cF:function cF(a,b){this.a=a this.$ti=b}, ih:function ih(a,b,c){var _=this _.a=a _.b=b _.c=0 _.d=null _.$ti=c}, eZ:function eZ(a){var _=this _.a=0 _.f=_.e=_.d=_.c=_.b=null _.r=0 _.$ti=a}, ne:function ne(a){this.a=a this.c=this.b=null}, dy:function dy(a,b,c){var _=this _.a=a _.b=b _.d=_.c=null _.$ti=c}, k1:function k1(a,b,c){this.a=a this.b=b this.c=c}, em:function em(a){var _=this _.b=_.a=0 _.c=null _.$ti=a}, io:function io(a,b,c,d){var _=this _.a=a _.b=b _.c=null _.d=c _.e=!1 _.$ti=d}, aF:function aF(){}, z:function z(){}, T:function T(){}, ki:function ki(a){this.a=a}, kj:function kj(a,b){this.a=a this.b=b}, f_:function f_(a,b){this.a=a this.$ti=b}, ip:function ip(a,b,c){var _=this _.a=a _.b=b _.c=null _.$ti=c}, dd:function dd(){}, f8:function f8(){}, vz(a,b,c){var s,r,q,p,o=c-b if(o<=4096)s=$.tc() else s=new Uint8Array(o) for(r=J.V(a),q=0;q=16)return null r=r*16+o}n=h-1 i[h]=r for(;s=16)return null r=r*16+o}m=n-1 i[n]=r}if(j===1&&i[0]===0)return $.b7() l=A.aJ(j,i) return new A.a6(l===0?!1:c,i,l)}, qF(a,b){var s,r,q,p,o if(a==="")return null s=$.t5().a9(a) if(s==null)return null r=s.b q=r[1]==="-" p=r[4] o=r[3] if(p!=null)return A.uX(p,q) if(o!=null)return A.uY(o,2,q) return null}, aJ(a,b){while(!0){if(!(a>0&&b[a-1]===0))break;--a}return a}, oS(a,b,c,d){var s,r=new Uint16Array(d),q=c-b for(s=0;s>>0)+(o>>>4)-1075 m=new Uint16Array(4) m[0]=(r[1]<<8>>>0)+r[0] m[1]=(r[3]<<8>>>0)+r[2] m[2]=(r[5]<<8>>>0)+r[4] m[3]=o&15|16 l=new A.a6(!1,m,4) if(n<0)k=l.bl(0,-n) else k=n>0?l.b0(0,n):l if(s)return k.aB(0) return k}, oT(a,b,c,d){var s if(b===0)return 0 if(c===0&&d===a)return b for(s=b-1;s>=0;--s)d[s+c]=a[s] for(s=c-1;s>=0;--s)d[s]=0 return b+c}, qD(a,b,c,d){var s,r,q,p=B.b.I(c,16),o=B.b.aA(c,16),n=16-o,m=B.b.b0(1,n)-1 for(s=b-1,r=0;s>=0;--s){q=a[s] d[s+p+1]=(B.b.bl(q,n)|r)>>>0 r=B.b.b0((q&m)>>>0,o)}d[p]=r}, qy(a,b,c,d){var s,r,q,p=B.b.I(c,16) if(B.b.aA(c,16)===0)return A.oT(a,b,p,d) s=b+p+1 A.qD(a,b,c,d) for(r=p;--r,r>=0;)d[r]=0 q=s-1 return d[q]===0?q:s}, uZ(a,b,c,d){var s,r,q=B.b.I(c,16),p=B.b.aA(c,16),o=16-p,n=B.b.b0(1,p)-1,m=B.b.bl(a[q],p),l=b-q-1 for(s=0;s>>0,o)|m)>>>0 m=B.b.bl(r,p)}d[l]=m}, lZ(a,b,c,d){var s,r=b-d if(r===0)for(s=b-1;s>=0;--s){r=a[s]-c[s] if(r!==0)return r}return r}, uV(a,b,c,d,e){var s,r for(s=0,r=0;r=0;e=p,c=r){r=c+1 q=a*b[c]+d[e]+s p=e+1 d[e]=q&65535 s=B.b.I(q,65536)}for(;s!==0;e=p){o=d[e]+s p=e+1 d[e]=o&65535 s=B.b.I(o,65536)}}, uW(a,b,c){var s,r=b[c] if(r===a)return 65535 s=B.b.eX((r<<16|b[c-1])>>>0,a) if(s>65535)return 65535 return s}, u_(a){throw A.a(A.ag(a,"object","Expandos are not allowed on strings, numbers, bools, records or null"))}, aN(a,b){var s=A.q4(a,b) if(s!=null)return s throw A.a(A.ai(a,null,null))}, tZ(a,b){a=A.a(a) a.stack=b.j(0) throw a throw A.a("unreachable")}, b_(a,b,c,d){var s,r=c?J.pW(a,d):J.pV(a,d) if(a!==0&&b!=null)for(s=0;s")) for(s=J.M(a);s.k();)r.push(s.gm()) if(b)return r return J.k8(r)}, ay(a,b,c){var s if(b)return A.pY(a,c) s=J.k8(A.pY(a,c)) return s}, pY(a,b){var s,r if(Array.isArray(a))return A.d(a.slice(0),b.h("w<0>")) s=A.d([],b.h("w<0>")) for(r=J.M(a);r.k();)s.push(r.gm()) return s}, aG(a,b){var s=A.pZ(a,!1,b) s.fixed$length=Array s.immutable$list=Array return s}, qh(a,b,c){var s,r,q,p,o A.ab(b,"start") s=c==null r=!s if(r){q=c-b if(q<0)throw A.a(A.a4(c,b,null,"end",null)) if(q===0)return""}if(Array.isArray(a)){p=a o=p.length if(s)c=o return A.q6(b>0||c0)a=J.dZ(a,b) return A.q6(A.ay(a,!0,t.S))}, qg(a){return A.aA(a)}, uD(a,b,c){var s=a.length if(b>=s)return"" return A.uz(a,b,c==null||c>s?s:c)}, J(a,b,c,d,e){return new A.cs(a,A.oz(a,d,b,e,c,!1))}, oH(a,b,c){var s=J.M(b) if(!s.k())return a if(c.length===0){do a+=A.u(s.gm()) while(s.k())}else{a+=A.u(s.gm()) for(;s.k();)a=a+c+A.u(s.gm())}return a}, eF(){var s,r,q=A.up() if(q==null)throw A.a(A.H("'Uri.base' is not supported")) s=$.qt if(s!=null&&q===$.qs)return s r=A.bm(q) $.qt=r $.qs=q return r}, vx(a,b,c,d){var s,r,q,p,o,n="0123456789ABCDEF" if(c===B.j){s=$.t9() s=s.b.test(b)}else s=!1 if(s)return b r=B.i.a5(b) for(s=r.length,q=0,p="";q>>4]&1<<(o&15))!==0)p+=A.aA(o) else p=d&&o===32?p+"+":p+"%"+n[o>>>4&15]+n[o&15]}return p.charCodeAt(0)==0?p:p}, oG(){return A.R(new Error())}, tV(a){var s=Math.abs(a),r=a<0?"-":"" if(s>=1000)return""+a if(s>=100)return r+"0"+s if(s>=10)return r+"00"+s return r+"000"+s}, pI(a){if(a>=100)return""+a if(a>=10)return"0"+a return"00"+a}, fO(a){if(a>=10)return""+a return"0"+a}, pJ(a,b){return new A.bp(a+1000*b)}, or(a,b){var s,r,q for(s=a.length,r=0;rc)throw A.a(A.a4(a,b,c,d,null)) return a}, b9(a,b,c){if(0>a||a>c)throw A.a(A.a4(a,0,c,"start",null)) if(b!=null){if(a>b||b>c)throw A.a(A.a4(b,a,c,"end",null)) return b}return c}, ab(a,b){if(a<0)throw A.a(A.a4(a,0,null,b,null)) return a}, h4(a,b,c,d,e){return new A.h3(b,!0,a,e,"Index out of range")}, H(a){return new A.hL(a)}, qp(a){return new A.hH(a)}, B(a){return new A.b2(a)}, ax(a){return new A.fK(a)}, jL(a){return new A.id(a)}, ai(a,b,c){return new A.bs(a,b,c)}, ub(a,b,c){var s,r if(A.pk(a)){if(b==="("&&c===")")return"(...)" return b+"..."+c}s=A.d([],t.s) $.cO.push(a) try{A.w9(a,s)}finally{$.cO.pop()}r=A.oH(b,s,", ")+c return r.charCodeAt(0)==0?r:r}, ox(a,b,c){var s,r if(A.pk(a))return b+"..."+c s=new A.av(b) $.cO.push(a) try{r=s r.a=A.oH(r.a,a,", ")}finally{$.cO.pop()}s.a+=c r=s.a return r.charCodeAt(0)==0?r:r}, w9(a,b){var s,r,q,p,o,n,m,l=a.gt(a),k=0,j=0 while(!0){if(!(k<80||j<3))break if(!l.k())return s=A.u(l.gm()) b.push(s) k+=s.length+2;++j}if(!l.k()){if(j<=5)return r=b.pop() q=b.pop()}else{p=l.gm();++j if(!l.k()){if(j<=4){b.push(A.u(p)) return}r=A.u(p) q=b.pop() k+=r.length+2}else{o=l.gm();++j for(;l.k();p=o,o=n){n=l.gm();++j if(j>100){while(!0){if(!(k>75&&j>3))break k-=b.pop().length+2;--j}b.push("...") return}}q=A.u(p) r=A.u(o) k+=r.length+q.length+4}}if(j>b.length+2){k+=5 m="..."}else m=null while(!0){if(!(k>80&&b.length>3))break k-=b.pop().length+2 if(m==null){k+=5 m="..."}}if(m!=null)b.push(m) b.push(q) b.push(r)}, es(a,b,c,d){var s if(B.f===c){s=J.aw(a) b=J.aw(b) return A.oI(A.c6(A.c6($.om(),s),b))}if(B.f===d){s=J.aw(a) b=J.aw(b) c=J.aw(c) return A.oI(A.c6(A.c6(A.c6($.om(),s),b),c))}s=J.aw(a) b=J.aw(b) c=J.aw(c) d=J.aw(d) d=A.oI(A.c6(A.c6(A.c6(A.c6($.om(),s),b),c),d)) return d}, xq(a){var s=A.u(a),r=$.rK if(r==null)A.pn(s) else r.$1(s)}, qr(a){var s,r=null,q=new A.av(""),p=A.d([-1],t.t) A.uM(r,r,r,q,p) p.push(q.a.length) q.a+="," A.uL(B.p,B.ap.jO(a),q) s=q.a return new A.hN(s.charCodeAt(0)==0?s:s,p,r).geN()}, bm(a5){var s,r,q,p,o,n,m,l,k,j,i,h,g,f,e,d,c,b,a,a0,a1,a2,a3=null,a4=a5.length if(a4>=5){s=((a5.charCodeAt(4)^58)*3|a5.charCodeAt(0)^100|a5.charCodeAt(1)^97|a5.charCodeAt(2)^116|a5.charCodeAt(3)^97)>>>0 if(s===0)return A.qq(a4=14)r[7]=a4 q=r[1] if(q>=0)if(A.rp(a5,0,q,20,r)===20)r[7]=q p=r[2]+1 o=r[3] n=r[4] m=r[5] l=r[6] if(lq+3)){i=o>0 if(!(i&&o+1===n)){if(!B.a.E(a5,"\\",n))if(p>0)h=B.a.E(a5,"\\",p-1)||B.a.E(a5,"\\",p-2) else h=!1 else h=!0 if(!h){if(!(mn+2&&B.a.E(a5,"/..",m-3) else h=!0 if(!h)if(q===4){if(B.a.E(a5,"file",0)){if(p<=0){if(!B.a.E(a5,"/",n)){g="file:///" s=3}else{g="file://" s=2}a5=g+B.a.n(a5,n,a4) m+=s l+=s a4=a5.length p=7 o=7 n=7}else if(n===m){++l f=m+1 a5=B.a.aO(a5,n,m,"/");++a4 m=f}j="file"}else if(B.a.E(a5,"http",0)){if(i&&o+3===n&&B.a.E(a5,"80",o+1)){l-=3 e=n-3 m-=3 a5=B.a.aO(a5,o,n,"") a4-=3 n=e}j="http"}}else if(q===5&&B.a.E(a5,"https",0)){if(i&&o+4===n&&B.a.E(a5,"443",o+1)){l-=4 e=n-4 m-=4 a5=B.a.aO(a5,o,n,"") a4-=3 n=e}j="https"}k=!h}}}}if(k)return new A.b4(a40)j=A.nC(a5,0,q) else{if(q===0)A.dL(a5,0,"Invalid empty scheme") j=""}d=a3 if(p>0){c=q+3 b=c9)k.$2("invalid character",s)}else{if(q===3)k.$2(m,s) o=A.aN(B.a.n(a,r,s),null) if(o>255)k.$2(l,r) n=q+1 j[q]=o r=s+1 q=n}}if(q!==3)k.$2(m,c) o=A.aN(B.a.n(a,r,c),null) if(o>255)k.$2(l,r) j[q]=o return j}, qu(a,b,a0){var s,r,q,p,o,n,m,l,k,j,i,h,g,f,e=null,d=new A.li(a),c=new A.lj(d,a) if(a.length<2)d.$2("address is too short",e) s=A.d([],t.t) for(r=b,q=r,p=!1,o=!1;r>>0) s.push((k[2]<<8|k[3])>>>0)}if(p){if(s.length>7)d.$2("an address with a wildcard must have less than 7 parts",e)}else if(s.length!==8)d.$2("an address without a wildcard must contain exactly 8 parts",e) j=new Uint8Array(16) for(l=s.length,i=9-l,r=0,h=0;r")),r=r.h("P.E");s.k();){q=s.d if(q==null)q=r.a(q) if(B.a.M(q,A.J('["*/:<>?\\\\|]',!0,!1,!1,!1)))if(b)throw A.a(A.K("Illegal character in path",null)) else throw A.a(A.H("Illegal character in path: "+q))}}, vp(a,b){var s,r="Illegal drive letter " if(!(65<=a&&a<=90))s=97<=a&&a<=122 else s=!0 if(s)return if(b)throw A.a(A.K(r+A.qg(a),null)) else throw A.a(A.H(r+A.qg(a)))}, vs(a,b){var s=null,r=A.d(a.split("/"),t.s) if(B.a.u(a,"/"))return A.aj(s,s,r,"file") else return A.aj(s,s,r,s)}, vt(a,b){var s,r,q,p,o="\\",n=null,m="file" if(B.a.u(a,"\\\\?\\"))if(B.a.E(a,"UNC\\",4))a=B.a.aO(a,0,7,o) else{a=B.a.K(a,4) if(a.length<3||a.charCodeAt(1)!==58||a.charCodeAt(2)!==92)throw A.a(A.ag(a,"path","Windows paths with \\\\?\\ prefix must be absolute"))}else a=A.bc(a,"/",o) s=a.length if(s>1&&a.charCodeAt(1)===58){A.vp(a.charCodeAt(0),!0) if(s===2||a.charCodeAt(2)!==92)throw A.a(A.ag(a,"path","Windows paths with drive letter must be absolute")) r=A.d(a.split(o),t.s) A.nz(r,!0,1) return A.aj(n,n,r,m)}if(B.a.u(a,o))if(B.a.E(a,o,1)){q=B.a.aW(a,o,2) s=q<0 p=s?B.a.K(a,2):B.a.n(a,2,q) r=A.d((s?"":B.a.K(a,q+1)).split(o),t.s) A.nz(r,!0,0) return A.aj(p,n,r,m)}else{r=A.d(a.split(o),t.s) A.nz(r,!0,0) return A.aj(n,n,r,m)}else{r=A.d(a.split(o),t.s) A.nz(r,!0,0) return A.aj(n,n,r,n)}}, nB(a,b){if(a!=null&&a===A.qV(b))return null return a}, qZ(a,b,c,d){var s,r,q,p,o,n if(a==null)return null if(b===c)return"" if(a.charCodeAt(b)===91){s=c-1 if(a.charCodeAt(s)!==93)A.dL(a,b,"Missing end `]` to match `[` in host") r=b+1 q=A.vq(a,r,s) if(q=b&&q=b&&s>>4]&1<<(p&15))!==0){if(q&&65<=p&&90>=p){if(i==null)i=new A.av("") if(r>>4]&1<<(o&15))!==0){if(p&&65<=o&&90>=o){if(q==null)q=new A.av("") if(r>>4]&1<<(o&15))!==0)A.dL(a,s,"Invalid character") else{j=1 if((o&64512)===55296&&s+1>>4]&1<<(q&15))!==0))A.dL(a,s,"Illegal scheme character") if(65<=q&&q<=90)r=!0}a=B.a.n(a,b,c) return A.vn(r?a.toLowerCase():a)}, vn(a){if(a==="http")return"http" if(a==="file")return"file" if(a==="https")return"https" if(a==="package")return"package" return a}, r1(a,b,c){if(a==null)return"" return A.fl(a,b,c,B.aL,!1,!1)}, r_(a,b,c,d,e,f){var s,r=e==="file",q=r||f if(a==null){if(d==null)return r?"/":"" s=new A.D(d,new A.nA(),A.Q(d).h("D<1,j>")).aq(0,"/")}else if(d!=null)throw A.a(A.K("Both path and pathSegments specified",null)) else s=A.fl(a,b,c,B.a5,!0,!0) if(s.length===0){if(r)return"/"}else if(q&&!B.a.u(s,"/"))s="/"+s return A.vu(s,e,f)}, vu(a,b,c){var s=b.length===0 if(s&&!c&&!B.a.u(a,"/")&&!B.a.u(a,"\\"))return A.p3(a,!s||c) return A.cJ(a)}, r0(a,b,c,d){if(a!=null)return A.fl(a,b,c,B.p,!0,!1) return null}, qY(a,b,c){if(a==null)return null return A.fl(a,b,c,B.p,!0,!1)}, p2(a,b,c){var s,r,q,p,o,n=b+2 if(n>=a.length)return"%" s=a.charCodeAt(b+1) r=a.charCodeAt(n) q=A.o5(s) p=A.o5(r) if(q<0||p<0)return"%" o=q*16+p if(o<127&&(B.ab[B.b.P(o,4)]&1<<(o&15))!==0)return A.aA(c&&65<=o&&90>=o?(o|32)>>>0:o) if(s>=97||r>=97)return B.a.n(a,b,b+3).toUpperCase() return null}, p1(a){var s,r,q,p,o,n="0123456789ABCDEF" if(a<128){s=new Uint8Array(3) s[0]=37 s[1]=n.charCodeAt(a>>>4) s[2]=n.charCodeAt(a&15)}else{if(a>2047)if(a>65535){r=240 q=4}else{r=224 q=3}else{r=192 q=2}s=new Uint8Array(3*q) for(p=0;--q,q>=0;r=128){o=B.b.ja(a,6*q)&63|r s[p]=37 s[p+1]=n.charCodeAt(o>>>4) s[p+2]=n.charCodeAt(o&15) p+=3}}return A.qh(s,0,null)}, fl(a,b,c,d,e,f){var s=A.r3(a,b,c,d,e,f) return s==null?B.a.n(a,b,c):s}, r3(a,b,c,d,e,f){var s,r,q,p,o,n,m,l,k,j,i=null for(s=!e,r=b,q=r,p=i;r>>4]&1<<(o&15))!==0)++r else{n=1 if(o===37){m=A.p2(a,r,!1) if(m==null){r+=3 continue}if("%"===m)m="%25" else n=3}else if(o===92&&f)m="/" else if(s&&o<=93&&(B.a6[o>>>4]&1<<(o&15))!==0){A.dL(a,r,"Invalid character") n=i m=n}else{if((o&64512)===55296){l=r+1 if(l=2&&A.qX(a.charCodeAt(0)))for(s=1;s127||(B.a4[r>>>4]&1<<(r&15))===0)break}return a}, vw(a,b){if(a.kc("package")&&a.c==null)return A.rr(b,0,b.length) return-1}, vr(a,b){var s,r,q for(s=0,r=0;r<2;++r){q=a.charCodeAt(b+r) if(48<=q&&q<=57)s=s*16+q-48 else{q|=32 if(97<=q&&q<=102)s=s*16+q-87 else throw A.a(A.K("Invalid URL encoding",null))}}return s}, p4(a,b,c,d,e){var s,r,q,p,o=b while(!0){if(!(o127)throw A.a(A.K("Illegal percent encoding in URI",null)) if(r===37){if(o+3>q)throw A.a(A.K("Truncated URI",null)) p.push(A.vr(a,o+1)) o+=2}else p.push(r)}}return d.cT(p)}, qX(a){var s=a|32 return 97<=s&&s<=122}, uM(a,b,c,d,e){d.a=d.a}, qq(a,b,c){var s,r,q,p,o,n,m,l,k="Invalid MIME type",j=A.d([b-1],t.t) for(s=a.length,r=b,q=-1,p=null;rb)throw A.a(A.ai(k,a,r)) for(;p!==44;){j.push(r);++r for(o=-1;r=0)j.push(o) else{n=B.c.gC(j) if(p!==44||r!==n+7||!B.a.E(a,"base64",n+1))throw A.a(A.ai("Expecting '='",a,r)) break}}j.push(r) m=r+1 if((j.length&1)===1)a=B.aq.kh(a,m,s) else{l=A.r3(a,m,s,B.p,!0,!1) if(l!=null)a=B.a.aO(a,m,s,l)}return new A.hN(a,j,c)}, uL(a,b,c){var s,r,q,p,o,n="0123456789ABCDEF" for(s=b.length,r=0,q=0;q>>4]&1<<(p&15))!==0){o=A.aA(p) c.a+=o}else{o=A.aA(37) c.a+=o o=A.aA(n.charCodeAt(p>>>4)) c.a+=o o=A.aA(n.charCodeAt(p&15)) c.a+=o}}if((r&4294967040)!==0)for(q=0;q255)throw A.a(A.ag(p,"non-byte value",null))}}, vP(){var s,r,q,p,o,n="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-._~!$&'()*+,;=",m=".",l=":",k="/",j="\\",i="?",h="#",g="/\\",f=J.pU(22,t.p) for(s=0;s<22;++s)f[s]=new Uint8Array(96) r=new A.nN(f) q=new A.nO() p=new A.nP() o=r.$2(0,225) q.$3(o,n,1) q.$3(o,m,14) q.$3(o,l,34) q.$3(o,k,3) q.$3(o,j,227) q.$3(o,i,172) q.$3(o,h,205) o=r.$2(14,225) q.$3(o,n,1) q.$3(o,m,15) q.$3(o,l,34) q.$3(o,g,234) q.$3(o,i,172) q.$3(o,h,205) o=r.$2(15,225) q.$3(o,n,1) q.$3(o,"%",225) q.$3(o,l,34) q.$3(o,k,9) q.$3(o,j,233) q.$3(o,i,172) q.$3(o,h,205) o=r.$2(1,225) q.$3(o,n,1) q.$3(o,l,34) q.$3(o,k,10) q.$3(o,j,234) q.$3(o,i,172) q.$3(o,h,205) o=r.$2(2,235) q.$3(o,n,139) q.$3(o,k,131) q.$3(o,j,131) q.$3(o,m,146) q.$3(o,i,172) q.$3(o,h,205) o=r.$2(3,235) q.$3(o,n,11) q.$3(o,k,68) q.$3(o,j,68) q.$3(o,m,18) q.$3(o,i,172) q.$3(o,h,205) o=r.$2(4,229) q.$3(o,n,5) p.$3(o,"AZ",229) q.$3(o,l,102) q.$3(o,"@",68) q.$3(o,"[",232) q.$3(o,k,138) q.$3(o,j,138) q.$3(o,i,172) q.$3(o,h,205) o=r.$2(5,229) q.$3(o,n,5) p.$3(o,"AZ",229) q.$3(o,l,102) q.$3(o,"@",68) q.$3(o,k,138) q.$3(o,j,138) q.$3(o,i,172) q.$3(o,h,205) o=r.$2(6,231) p.$3(o,"19",7) q.$3(o,"@",68) q.$3(o,k,138) q.$3(o,j,138) q.$3(o,i,172) q.$3(o,h,205) o=r.$2(7,231) p.$3(o,"09",7) q.$3(o,"@",68) q.$3(o,k,138) q.$3(o,j,138) q.$3(o,i,172) q.$3(o,h,205) q.$3(r.$2(8,8),"]",5) o=r.$2(9,235) q.$3(o,n,11) q.$3(o,m,16) q.$3(o,g,234) q.$3(o,i,172) q.$3(o,h,205) o=r.$2(16,235) q.$3(o,n,11) q.$3(o,m,17) q.$3(o,g,234) q.$3(o,i,172) q.$3(o,h,205) o=r.$2(17,235) q.$3(o,n,11) q.$3(o,k,9) q.$3(o,j,233) q.$3(o,i,172) q.$3(o,h,205) o=r.$2(10,235) q.$3(o,n,11) q.$3(o,m,18) q.$3(o,k,10) q.$3(o,j,234) q.$3(o,i,172) q.$3(o,h,205) o=r.$2(18,235) q.$3(o,n,11) q.$3(o,m,19) q.$3(o,g,234) q.$3(o,i,172) q.$3(o,h,205) o=r.$2(19,235) q.$3(o,n,11) q.$3(o,g,234) q.$3(o,i,172) q.$3(o,h,205) o=r.$2(11,235) q.$3(o,n,11) q.$3(o,k,10) q.$3(o,j,234) q.$3(o,i,172) q.$3(o,h,205) o=r.$2(12,236) q.$3(o,n,12) q.$3(o,i,12) q.$3(o,h,205) o=r.$2(13,237) q.$3(o,n,13) q.$3(o,i,13) p.$3(r.$2(20,245),"az",21) o=r.$2(21,245) p.$3(o,"az",21) p.$3(o,"09",21) q.$3(o,"+-.",21) return f}, rp(a,b,c,d,e){var s,r,q,p,o=$.tn() for(s=b;s95?31:q] d=p&31 e[p>>>5]=s}return d}, qN(a){if(a.b===7&&B.a.u(a.a,"package")&&a.c<=0)return A.rr(a.a,a.e,a.f) return-1}, rr(a,b,c){var s,r,q for(s=b,r=0;s=1)return a.$1(b) return a.$0()}, vG(a,b,c,d){if(d>=2)return a.$2(b,c) if(d===1)return a.$1(b) return a.$0()}, vH(a,b,c,d,e){if(e>=3)return a.$3(b,c,d) if(e===2)return a.$2(b,c) if(e===1)return a.$1(b) return a.$0()}, vI(a,b,c,d,e,f){if(f>=4)return a.$4(b,c,d,e) if(f===3)return a.$3(b,c,d) if(f===2)return a.$2(b,c) if(f===1)return a.$1(b) return a.$0()}, vJ(a,b,c,d,e,f,g){if(g>=5)return a.$5(b,c,d,e,f) if(g===4)return a.$4(b,c,d,e) if(g===3)return a.$3(b,c,d) if(g===2)return a.$2(b,c) if(g===1)return a.$1(b) return a.$0()}, rj(a){return a==null||A.bM(a)||typeof a=="number"||typeof a=="string"||t.gj.b(a)||t.p.b(a)||t.go.b(a)||t.dQ.b(a)||t.h7.b(a)||t.an.b(a)||t.bv.b(a)||t.h4.b(a)||t.gN.b(a)||t.E.b(a)||t.fd.b(a)}, xe(a){if(A.rj(a))return a return new A.oa(new A.dw(t.hg)).$1(a)}, cM(a,b,c){return a[b].apply(a,c)}, dT(a,b){var s,r if(b==null)return new a() if(b instanceof Array)switch(b.length){case 0:return new a() case 1:return new a(b[0]) case 2:return new a(b[0],b[1]) case 3:return new a(b[0],b[1],b[2]) case 4:return new a(b[0],b[1],b[2],b[3])}s=[null] B.c.aJ(s,b) r=a.bind.apply(a,s) String(r) return new r()}, a_(a,b){var s=new A.k($.i,b.h("k<0>")),r=new A.a2(s,b.h("a2<0>")) a.then(A.cf(new A.oe(r),1),A.cf(new A.of(r),1)) return s}, ri(a){return a==null||typeof a==="boolean"||typeof a==="number"||typeof a==="string"||a instanceof Int8Array||a instanceof Uint8Array||a instanceof Uint8ClampedArray||a instanceof Int16Array||a instanceof Uint16Array||a instanceof Int32Array||a instanceof Uint32Array||a instanceof Float32Array||a instanceof Float64Array||a instanceof ArrayBuffer||a instanceof DataView}, ry(a){if(A.ri(a))return a return new A.o1(new A.dw(t.hg)).$1(a)}, oa:function oa(a){this.a=a}, oe:function oe(a){this.a=a}, of:function of(a){this.a=a}, o1:function o1(a){this.a=a}, ho:function ho(a){this.a=a}, rF(a,b){return Math.max(a,b)}, xu(a){return Math.sqrt(a)}, xt(a){return Math.sin(a)}, wV(a){return Math.cos(a)}, xA(a){return Math.tan(a)}, ww(a){return Math.acos(a)}, wx(a){return Math.asin(a)}, wR(a){return Math.atan(a)}, nc:function nc(a){this.a=a}, cU:function cU(){}, fP:function fP(){}, hf:function hf(){}, hn:function hn(){}, hK:function hK(){}, tW(a,b){var s=new A.ea(a,b,A.a3(t.S,t.aR),A.eD(null,null,!0,t.al),new A.a2(new A.k($.i,t.D),t.h)) s.hK(a,!1,b) return s}, ea:function ea(a,b,c,d,e){var _=this _.a=a _.c=b _.d=0 _.e=c _.f=d _.r=!1 _.w=e}, jA:function jA(a){this.a=a}, jB:function jB(a,b){this.a=a this.b=b}, ir:function ir(a,b){this.a=a this.b=b}, fL:function fL(){}, fT:function fT(a){this.a=a}, fS:function fS(){}, jC:function jC(a){this.a=a}, jD:function jD(a){this.a=a}, bZ:function bZ(){}, am:function am(a,b){this.a=a this.b=b}, ba:function ba(a,b){this.a=a this.b=b}, aH:function aH(a){this.a=a}, bq:function bq(a,b,c){this.a=a this.b=b this.c=c}, bo:function bo(a){this.a=a}, d3:function d3(a,b){this.a=a this.b=b}, cv:function cv(a,b){this.a=a this.b=b}, bU:function bU(a,b,c,d){var _=this _.a=a _.b=b _.c=c _.d=d}, c1:function c1(a){this.a=a}, bg:function bg(a,b){this.a=a this.b=b}, c0:function c0(a,b){this.a=a this.b=b}, c3:function c3(a,b){this.a=a this.b=b}, bT:function bT(a,b){this.a=a this.b=b}, c4:function c4(a){this.a=a}, c2:function c2(a,b){this.a=a this.b=b}, by:function by(a){this.a=a}, bA:function bA(a){this.a=a}, uC(a,b,c){var s=null,r=t.S,q=A.d([],t.t) r=new A.kC(a,!1,!0,A.a3(r,t.x),A.a3(r,t.g1),q,new A.fd(s,s,t.dn),A.oC(t.gw),new A.a2(new A.k($.i,t.D),t.h),A.eD(s,s,!1,t.bw)) r.hM(a,!1,!0) return r}, kC:function kC(a,b,c,d,e,f,g,h,i,j){var _=this _.a=a _.b=b _.c=c _.d=d _.f=_.e=0 _.r=e _.w=f _.x=g _.y=!1 _.z=h _.Q=i _.as=j}, kH:function kH(a){this.a=a}, kI:function kI(a,b){this.a=a this.b=b}, kJ:function kJ(a,b){this.a=a this.b=b}, kD:function kD(a,b){this.a=a this.b=b}, kE:function kE(a,b){this.a=a this.b=b}, kG:function kG(a,b){this.a=a this.b=b}, kF:function kF(a){this.a=a}, f7:function f7(a,b,c){this.a=a this.b=b this.c=c}, hX:function hX(){}, lK:function lK(a,b){this.a=a this.b=b}, lL:function lL(a,b){this.a=a this.b=b}, lI:function lI(){}, lE:function lE(a,b){this.a=a this.b=b}, lF:function lF(){}, lG:function lG(){}, lD:function lD(){}, lJ:function lJ(){}, lH:function lH(){}, di:function di(a,b){this.a=a this.b=b}, bC:function bC(a,b){this.a=a this.b=b}, xr(a,b){var s,r,q={} q.a=s q.a=null s=new A.bS(new A.a8(new A.k($.i,b.h("k<0>")),b.h("a8<0>")),A.d([],t.bT),b.h("bS<0>")) q.a=s r=t.X A.xs(new A.og(q,a,b),A.kf([B.ah,s],r,r),t.H) return q.a}, rx(){var s=$.i.i(0,B.ah) if(s instanceof A.bS&&s.c)throw A.a(B.Z)}, og:function og(a,b,c){this.a=a this.b=b this.c=c}, bS:function bS(a,b,c){var _=this _.a=a _.b=b _.c=!1 _.$ti=c}, e3:function e3(){}, al:function al(){}, e1:function e1(a,b){this.a=a this.b=b}, cR:function cR(a,b){this.a=a this.b=b}, rd(a){return"SAVEPOINT s"+a}, rb(a){return"RELEASE s"+a}, rc(a){return"ROLLBACK TO s"+a}, jq:function jq(){}, kq:function kq(){}, lb:function lb(){}, kk:function kk(){}, ju:function ju(){}, hm:function hm(){}, jJ:function jJ(){}, i2:function i2(){}, lT:function lT(a,b){this.a=a this.b=b}, lY:function lY(a,b,c){this.a=a this.b=b this.c=c}, lW:function lW(a,b,c){this.a=a this.b=b this.c=c}, lX:function lX(a,b,c){this.a=a this.b=b this.c=c}, lV:function lV(a,b,c){this.a=a this.b=b this.c=c}, lU:function lU(a,b){this.a=a this.b=b}, iE:function iE(){}, fb:function fb(a,b,c,d,e,f,g,h,i){var _=this _.y=a _.z=null _.Q=b _.as=c _.at=d _.ax=e _.ay=f _.ch=g _.e=h _.a=i _.b=0 _.d=_.c=!1}, nn:function nn(a){this.a=a}, no:function no(a){this.a=a}, fQ:function fQ(){}, jz:function jz(a,b){this.a=a this.b=b}, jy:function jy(a){this.a=a}, i3:function i3(a,b){var _=this _.e=a _.a=b _.b=0 _.d=_.c=!1}, eV:function eV(a,b,c){var _=this _.e=a _.f=null _.r=b _.a=c _.b=0 _.d=_.c=!1}, mg:function mg(a,b){this.a=a this.b=b}, q9(a,b){var s,r,q,p=A.a3(t.N,t.S) for(s=a.length,r=0;r3)throw A.a("Expected two or three arguments to regexp") s=a.i(0,0) q=a.i(0,1) if(s==null||q==null)return null if(typeof s!="string"||typeof q!="string")throw A.a("Expected two strings as parameters to regexp") if(g===3){p=a.i(0,2) if(A.bn(p)){k=(p&1)===1 j=(p&2)!==2 i=(p&4)===4 h=(p&8)===8}}r=null try{o=k n=j m=i r=A.J(s,n,h,o,m)}catch(l){if(A.E(l) instanceof A.bs)throw A.a("Invalid regex") else throw l}o=r.b return o.test(q)}, vO(a){var s,r,q=a.a.b if(q<2||q>3)throw A.a("Expected 2 or 3 arguments to moor_contains") s=a.i(0,0) r=a.i(0,1) if(typeof s!="string"||typeof r!="string")throw A.a("First two args to contains must be strings") return q===3&&a.i(0,2)===1?J.pz(s,r):B.a.M(s.toLowerCase(),r.toLowerCase())}, jK:function jK(){}, nX:function nX(a){this.a=a}, hc:function hc(a){var _=this _.a=$ _.b=!1 _.d=null _.e=a}, kc:function kc(a,b){this.a=a this.b=b}, kd:function kd(a,b){this.a=a this.b=b}, bh:function bh(){this.a=null}, kg:function kg(a,b,c){this.a=a this.b=b this.c=c}, kh:function kh(a,b){this.a=a this.b=b}, uP(a,b,c){var s=null,r=new A.hC(t.a7),q=t.X,p=A.eD(s,s,!1,q),o=A.eD(s,s,!1,q),n=A.pR(new A.an(o,A.t(o).h("an<1>")),new A.dH(p),!0,q) r.a=n q=A.pR(new A.an(p,A.t(p).h("an<1>")),new A.dH(o),!0,q) r.b=q a.onmessage=A.bb(new A.lA(b,r,c)) n=n.b n===$&&A.G() new A.an(n,A.t(n).h("an<1>")).eB(new A.lB(c,a),new A.lC(b,a)) return q}, lA:function lA(a,b,c){this.a=a this.b=b this.c=c}, lB:function lB(a,b){this.a=a this.b=b}, lC:function lC(a,b){this.a=a this.b=b}, jv:function jv(a,b,c){var _=this _.a=a _.b=b _.c=c _.d=null}, jx:function jx(a){this.a=a}, jw:function jw(a,b){this.a=a this.b=b}, q8(a){var s $label0$0:{if(a<=0){s=B.u break $label0$0}if(1===a){s=B.aY break $label0$0}if(2===a){s=B.aZ break $label0$0}if(a>2){s=B.v break $label0$0}s=A.y(A.e_(null))}return s}, q7(a){if("v" in a)return A.q8(A.h(A.r(a.v))) else return B.u}, oL(a){var s,r,q,p,o,n,m,l,k,j,i=A.ad(a.type),h=a.payload $label0$0:{if("Error"===i){s=new A.dm(A.ad(t.m.a(h))) break $label0$0}if("ServeDriftDatabase"===i){s=t.m s.a(h) r=A.q7(h) q=A.bm(A.ad(h.sqlite)) s=s.a(h.port) p=A.or(B.aT,A.ad(h.storage)) o=A.ad(h.database) n=t.A.a(h.initPort) m=r.c l=m<2||A.bJ(h.migrations) s=new A.dc(q,s,p,o,n,r,l,m<3||A.bJ(h.new_serialization)) break $label0$0}if("StartFileSystemServer"===i){s=new A.eB(t.m.a(h)) break $label0$0}if("RequestCompatibilityCheck"===i){s=new A.da(A.ad(h)) break $label0$0}if("DedicatedWorkerCompatibilityResult"===i){t.m.a(h) k=A.d([],t.L) if("existing" in h)B.c.aJ(k,A.pL(t.c.a(h.existing))) s=A.bJ(h.supportsNestedWorkers) q=A.bJ(h.canAccessOpfs) p=A.bJ(h.supportsSharedArrayBuffers) o=A.bJ(h.supportsIndexedDb) n=A.bJ(h.indexedDbExists) m=A.bJ(h.opfsExists) m=new A.e9(s,q,p,o,k,A.q7(h),n,m) s=m break $label0$0}if("SharedWorkerCompatibilityResult"===i){s=t.c s.a(h) j=B.c.b8(h,t.y) if(h.length>5){k=A.pL(s.a(h[5])) r=h.length>6?A.q8(A.h(h[6])):B.u}else{k=B.B r=B.u}s=j.a q=J.V(s) p=j.$ti.y[1] s=new A.c5(p.a(q.i(s,0)),p.a(q.i(s,1)),p.a(q.i(s,2)),k,r,p.a(q.i(s,3)),p.a(q.i(s,4))) break $label0$0}if("DeleteDatabase"===i){s=h==null?t.K.a(h):h t.c.a(s) q=$.ps().i(0,A.ad(s[0])) q.toString s=new A.fR(new A.ap(q,A.ad(s[1]))) break $label0$0}s=A.y(A.K("Unknown type "+i,null))}return s}, pL(a){var s,r,q=A.d([],t.L),p=B.c.b8(a,t.m),o=p.$ti p=new A.aZ(p,p.gl(0),o.h("aZ")) o=o.h("z.E") for(;p.k();){s=p.d if(s==null)s=o.a(s) r=$.ps().i(0,A.ad(s.l)) r.toString q.push(new A.ap(r,A.ad(s.n)))}return q}, pK(a){var s,r,q,p,o=A.d([],t.W) for(s=a.length,r=0;r"))) for(;i.k();){l=i.gm() if(J.X(l.name,a)){q=!0 s=1 break $async$outer}}q=!1 s=1 break case 8:k=n.open(a,1) k.onupgradeneeded=A.bb(new A.o_(g,k)) s=10 return A.c(A.jc(k,i),$async$dU) case 10:j=c if(g.a==null)g.a=!0 j.close() s=g.a===!1?11:12 break case 11:s=13 return A.c(A.jc(n.deleteDatabase(a),t.X),$async$dU) case 13:case 12:p=2 s=6 break case 4:p=3 f=o s=6 break case 3:s=2 break case 6:i=g.a q=i===!0 s=1 break case 1:return A.m(q,r) case 2:return A.l(o,r)}}) return A.n($async$dU,r)}, o2(a){var s=0,r=A.o(t.H),q,p var $async$o2=A.p(function(b,c){if(b===1)return A.l(c,r) while(true)switch(s){case 0:q=t.m p=q.a(self) s="indexedDB" in p?2:3 break case 2:s=4 return A.c(A.jc(q.a(p.indexedDB).deleteDatabase(a),t.X),$async$o2) case 4:case 3:return A.m(null,r)}}) return A.n($async$o2,r)}, dW(){var s=0,r=A.o(t.o),q,p=2,o,n=[],m,l,k,j,i,h,g,f,e var $async$dW=A.p(function(a,b){if(a===1){o=b s=p}while(true)switch(s){case 0:f=A.pb() if(f==null){q=B.r s=1 break}i=t.m s=3 return A.c(A.a_(f.getDirectory(),i),$async$dW) case 3:m=b p=5 s=8 return A.c(A.a_(m.getDirectoryHandle("drift_db"),i),$async$dW) case 8:m=b p=2 s=7 break case 5:p=4 e=o q=B.r s=1 break s=7 break case 4:s=2 break case 7:i=m g=t.cO if(!(self.Symbol.asyncIterator in i))A.y(A.K("Target object does not implement the async iterable interface",null)) l=new A.f0(new A.od(),new A.e0(i,g),g.h("f0")) k=A.d([],t.s) i=new A.dG(A.aD(l,"stream",t.K)) p=9 case 12:s=14 return A.c(i.k(),$async$dW) case 14:if(!b){s=13 break}j=i.gm() if(J.X(j.kind,"directory"))J.on(k,j.name) s=12 break case 13:n.push(11) s=10 break case 9:n=[2] case 10:p=2 s=15 return A.c(i.J(),$async$dW) case 15:s=n.pop() break case 11:q=k s=1 break case 1:return A.m(q,r) case 2:return A.l(o,r)}}) return A.n($async$dW,r)}, fr(a){return A.wX(a)}, wX(a){var s=0,r=A.o(t.H),q,p=2,o,n,m,l,k,j var $async$fr=A.p(function(b,c){if(b===1){o=c s=p}while(true)switch(s){case 0:k=A.pb() if(k==null){s=1 break}m=t.m s=3 return A.c(A.a_(k.getDirectory(),m),$async$fr) case 3:n=c p=5 s=8 return A.c(A.a_(n.getDirectoryHandle("drift_db"),m),$async$fr) case 8:n=c s=9 return A.c(A.a_(n.removeEntry(a,{recursive:!0}),t.X),$async$fr) case 9:p=2 s=7 break case 5:p=4 j=o s=7 break case 4:s=2 break case 7:case 1:return A.m(q,r) case 2:return A.l(o,r)}}) return A.n($async$fr,r)}, jc(a,b){var s=new A.k($.i,b.h("k<0>")),r=new A.a8(s,b.h("a8<0>")) A.aB(a,"success",new A.jf(r,a,b),!1) A.aB(a,"error",new A.jg(r,a),!1) A.aB(a,"blocked",new A.jh(r,a),!1) return s}, o_:function o_(a,b){this.a=a this.b=b}, od:function od(){}, fU:function fU(a,b){this.a=a this.b=b}, jI:function jI(a,b){this.a=a this.b=b}, jF:function jF(a){this.a=a}, jE:function jE(a){this.a=a}, jG:function jG(a,b,c){this.a=a this.b=b this.c=c}, jH:function jH(a,b,c){this.a=a this.b=b this.c=c}, m5:function m5(a,b){this.a=a this.b=b}, db:function db(a,b,c){var _=this _.a=a _.b=b _.c=0 _.d=c}, kA:function kA(a){this.a=a}, lm:function lm(a,b){this.a=a this.b=b}, jf:function jf(a,b,c){this.a=a this.b=b this.c=c}, jg:function jg(a,b){this.a=a this.b=b}, jh:function jh(a,b){this.a=a this.b=b}, kK:function kK(a,b){this.a=a this.b=null this.c=b}, kP:function kP(a){this.a=a}, kL:function kL(a,b){this.a=a this.b=b}, kO:function kO(a,b,c){this.a=a this.b=b this.c=c}, kM:function kM(a){this.a=a}, kN:function kN(a,b,c){this.a=a this.b=b this.c=c}, c8:function c8(a,b){this.a=a this.b=b}, bH:function bH(a,b){this.a=a this.b=b}, hT:function hT(a,b,c,d,e){var _=this _.e=a _.f=null _.r=b _.w=c _.x=d _.a=e _.b=0 _.d=_.c=!1}, nH:function nH(a,b,c,d,e,f,g){var _=this _.Q=a _.as=b _.at=c _.b=null _.d=_.c=!1 _.e=d _.f=e _.r=f _.x=g _.y=$ _.a=!1}, jl(a,b){if(a==null)a="." return new A.fM(b,a)}, pa(a){return a}, rs(a,b){var s,r,q,p,o,n,m,l for(s=b.length,r=1;r=1;s=q){q=s-1 if(b[q]!=null)break}p=new A.av("") o=""+(a+"(") p.a=o n=A.Q(b) m=n.h("cw<1>") l=new A.cw(b,0,s,m) l.hN(b,0,s,n.c) m=o+new A.D(l,new A.nY(),m.h("D")).aq(0,", ") p.a=m p.a=m+("): part "+(r-1)+" was null, but part "+r+" was not.") throw A.a(A.K(p.j(0),null))}}, fM:function fM(a,b){this.a=a this.b=b}, jm:function jm(){}, jn:function jn(){}, nY:function nY(){}, dB:function dB(a){this.a=a}, dC:function dC(a){this.a=a}, k7:function k7(){}, d4(a,b){var s,r,q,p,o,n=b.hu(a) b.ab(a) if(n!=null)a=B.a.K(a,n.length) s=t.s r=A.d([],s) q=A.d([],s) s=a.length if(s!==0&&b.D(a.charCodeAt(0))){q.push(a[0]) p=1}else{q.push("") p=0}for(o=p;o")),r=new A.a8(s,b.h("a8<0>")) A.aB(a,"success",new A.jd(r,a,b),!1) A.aB(a,"error",new A.je(r,a),!1) return s}, tU(a,b){var s=new A.k($.i,b.h("k<0>")),r=new A.a8(s,b.h("a8<0>")) A.aB(a,"success",new A.ji(r,a,b),!1) A.aB(a,"error",new A.jj(r,a),!1) A.aB(a,"blocked",new A.jk(r,a),!1) return s}, cD:function cD(a,b){var _=this _.c=_.b=_.a=null _.d=a _.$ti=b}, m6:function m6(a,b){this.a=a this.b=b}, m7:function m7(a,b){this.a=a this.b=b}, jd:function jd(a,b,c){this.a=a this.b=b this.c=c}, je:function je(a,b){this.a=a this.b=b}, ji:function ji(a,b,c){this.a=a this.b=b this.c=c}, jj:function jj(a,b){this.a=a this.b=b}, jk:function jk(a,b){this.a=a this.b=b}, ls(a,b){var s=0,r=A.o(t.g9),q,p,o,n,m,l var $async$ls=A.p(function(c,d){if(c===1)return A.l(d,r) while(true)switch(s){case 0:l={} b.aa(0,new A.lu(l)) p=t.m s=3 return A.c(A.a_(self.WebAssembly.instantiateStreaming(a,l),p),$async$ls) case 3:o=d n=o.instance.exports if("_initialize" in n)t.g.a(n._initialize).call() m=t.N p=new A.hV(A.a3(m,t.g),A.a3(m,p)) p.hO(o.instance) q=p s=1 break case 1:return A.m(q,r)}}) return A.n($async$ls,r)}, hV:function hV(a,b){this.a=a this.b=b}, lu:function lu(a){this.a=a}, lt:function lt(a){this.a=a}, lw(a){var s=0,r=A.o(t.ab),q,p,o var $async$lw=A.p(function(b,c){if(b===1)return A.l(c,r) while(true)switch(s){case 0:p=a.gh7()?new self.URL(a.j(0)):new self.URL(a.j(0),A.eF().j(0)) o=A s=3 return A.c(A.a_(self.fetch(p,null),t.m),$async$lw) case 3:q=o.lv(c) s=1 break case 1:return A.m(q,r)}}) return A.n($async$lw,r)}, lv(a){var s=0,r=A.o(t.ab),q,p,o var $async$lv=A.p(function(b,c){if(b===1)return A.l(c,r) while(true)switch(s){case 0:p=A o=A s=3 return A.c(A.ll(a),$async$lv) case 3:q=new p.hW(new o.lx(c)) s=1 break case 1:return A.m(q,r)}}) return A.n($async$lv,r)}, hW:function hW(a){this.a=a}, dl:function dl(a,b,c,d,e){var _=this _.d=a _.e=b _.r=c _.b=d _.a=e}, hU:function hU(a,b){this.a=a this.b=b this.c=0}, qb(a){var s if(!J.X(a.byteLength,8))throw A.a(A.K("Must be 8 in length",null)) s=self.Int32Array return new A.kz(t.ha.a(A.dT(s,[a])))}, uj(a){return B.h}, uk(a){var s=a.b return new A.S(s.getInt32(0,!1),s.getInt32(4,!1),s.getInt32(8,!1))}, ul(a){var s=a.b return new A.aQ(B.j.cT(A.oF(a.a,16,s.getInt32(12,!1))),s.getInt32(0,!1),s.getInt32(4,!1),s.getInt32(8,!1))}, kz:function kz(a){this.b=a}, bi:function bi(a,b,c){this.a=a this.b=b this.c=c}, ac:function ac(a,b,c,d,e){var _=this _.c=a _.d=b _.a=c _.b=d _.$ti=e}, bw:function bw(){}, aW:function aW(){}, S:function S(a,b,c){this.a=a this.b=b this.c=c}, aQ:function aQ(a,b,c,d){var _=this _.d=a _.a=b _.b=c _.c=d}, hR(a){var s=0,r=A.o(t.ei),q,p,o,n,m,l,k,j,i var $async$hR=A.p(function(b,c){if(b===1)return A.l(c,r) while(true)switch(s){case 0:k=t.m s=3 return A.c(A.a_(A.rN().getDirectory(),k),$async$hR) case 3:j=c i=$.fw().aQ(0,a.root) p=i.length,o=0 case 4:if(!(o")),A.xE(),r.h("az<1,a1>")),t.a))}if(!B.a.M(a,q))return new A.be(A.aG(A.d([A.qn(a)],t.J),t.a)) return new A.be(A.aG(new A.D(A.d(a.split(q),t.s),A.xD(),t.fe),t.a))}, be:function be(a){this.a=a}, j3:function j3(){}, j8:function j8(){}, j7:function j7(){}, j5:function j5(){}, j6:function j6(a){this.a=a}, j4:function j4(a){this.a=a}, u7(a){return A.pO(a)}, pO(a){return A.h0(a,new A.jU(a))}, u6(a){return A.u3(a)}, u3(a){return A.h0(a,new A.jS(a))}, u0(a){return A.h0(a,new A.jP(a))}, u4(a){return A.u1(a)}, u1(a){return A.h0(a,new A.jQ(a))}, u5(a){return A.u2(a)}, u2(a){return A.h0(a,new A.jR(a))}, h1(a){if(B.a.M(a,$.rQ()))return A.bm(a) else if(B.a.M(a,$.rR()))return A.qU(a,!0) else if(B.a.u(a,"/"))return A.qU(a,!1) if(B.a.M(a,"\\"))return $.ty().hp(a) return A.bm(a)}, h0(a,b){var s,r try{s=b.$0() return s}catch(r){if(A.E(r) instanceof A.bs)return new A.bl(A.aj(null,"unparsed",null,null),a) else throw r}}, N:function N(a,b,c,d){var _=this _.a=a _.b=b _.c=c _.d=d}, jU:function jU(a){this.a=a}, jS:function jS(a){this.a=a}, jT:function jT(a){this.a=a}, jP:function jP(a){this.a=a}, jQ:function jQ(a){this.a=a}, jR:function jR(a){this.a=a}, hd:function hd(a){this.a=a this.b=$}, qm(a){if(t.a.b(a))return a if(a instanceof A.be)return a.ho() return new A.hd(new A.l7(a))}, qn(a){var s,r,q try{if(a.length===0){r=A.qj(A.d([],t.e),null) return r}if(B.a.M(a,$.tr())){r=A.uH(a) return r}if(B.a.M(a,"\tat ")){r=A.uG(a) return r}if(B.a.M(a,$.th())||B.a.M(a,$.tf())){r=A.uF(a) return r}if(B.a.M(a,u.q)){r=A.tO(a).ho() return r}if(B.a.M(a,$.tk())){r=A.qk(a) return r}r=A.ql(a) return r}catch(q){r=A.E(q) if(r instanceof A.bs){s=r throw A.a(A.ai(s.a+"\nStack trace:\n"+a,null,null))}else throw q}}, uJ(a){return A.ql(a)}, ql(a){var s=A.aG(A.uK(a),t.B) return new A.a1(s)}, uK(a){var s,r=B.a.eM(a),q=$.pw(),p=t.U,o=new A.aT(A.d(A.bc(r,q,"").split("\n"),t.s),new A.l8(),p) if(!o.gt(0).k())return A.d([],t.e) r=A.oJ(o,o.gl(0)-1,p.h("f.E")) r=A.en(r,A.x2(),A.t(r).h("f.E"),t.B) s=A.ay(r,!0,A.t(r).h("f.E")) if(!J.tC(o.gC(0),".da"))B.c.v(s,A.pO(o.gC(0))) return s}, uH(a){var s=A.b3(A.d(a.split("\n"),t.s),1,null,t.N).hF(0,new A.l6()),r=t.B r=A.aG(A.en(s,A.rA(),s.$ti.h("f.E"),r),r) return new A.a1(r)}, uG(a){var s=A.aG(new A.az(new A.aT(A.d(a.split("\n"),t.s),new A.l5(),t.U),A.rA(),t.M),t.B) return new A.a1(s)}, uF(a){var s=A.aG(new A.az(new A.aT(A.d(B.a.eM(a).split("\n"),t.s),new A.l3(),t.U),A.x0(),t.M),t.B) return new A.a1(s)}, uI(a){return A.qk(a)}, qk(a){var s=a.length===0?A.d([],t.e):new A.az(new A.aT(A.d(B.a.eM(a).split("\n"),t.s),new A.l4(),t.U),A.x1(),t.M) s=A.aG(s,t.B) return new A.a1(s)}, qj(a,b){var s=A.aG(a,t.B) return new A.a1(s)}, a1:function a1(a){this.a=a}, l7:function l7(a){this.a=a}, l8:function l8(){}, l6:function l6(){}, l5:function l5(){}, l3:function l3(){}, l4:function l4(){}, la:function la(){}, l9:function l9(a){this.a=a}, bl:function bl(a,b){this.a=a this.w=b}, e5:function e5(a){var _=this _.b=_.a=$ _.c=null _.d=!1 _.$ti=a}, eP:function eP(a,b,c){this.a=a this.b=b this.$ti=c}, eO:function eO(a,b){this.b=a this.a=b}, pR(a,b,c,d){var s,r={} r.a=a s=new A.ef(d.h("ef<0>")) s.hL(b,!0,r,d) return s}, ef:function ef(a){var _=this _.b=_.a=$ _.c=null _.d=!1 _.$ti=a}, k0:function k0(a,b){this.a=a this.b=b}, k_:function k_(a){this.a=a}, eY:function eY(a,b,c,d){var _=this _.a=a _.b=b _.c=c _.e=_.d=!1 _.r=_.f=null _.w=d}, hC:function hC(a){this.b=this.a=$ this.$ti=a}, eC:function eC(){}, aB(a,b,c,d){var s if(c==null)s=null else{s=A.rt(new A.me(c),t.m) s=s==null?null:A.bb(s)}s=new A.ic(a,b,s,!1) s.e3() return s}, rt(a,b){var s=$.i if(s===B.d)return a return s.eg(a,b)}, os:function os(a,b){this.a=a this.$ti=b}, eU:function eU(a,b,c,d){var _=this _.a=a _.b=b _.c=c _.$ti=d}, ic:function ic(a,b,c,d){var _=this _.a=0 _.b=a _.c=b _.d=c _.e=d}, me:function me(a){this.a=a}, mf:function mf(a){this.a=a}, pn(a){if(typeof dartPrint=="function"){dartPrint(a) return}if(typeof console=="object"&&typeof console.log!="undefined"){console.log(a) return}if(typeof print=="function"){print(a) return}throw"Unable to print message: "+String(a)}, ui(a){return a}, oy(a,b){var s,r,q,p,o,n if(b.length===0)return!1 s=b.split(".") r=t.m.a(self) for(q=s.length,p=t.A,o=0;o=65&&a<=90))s=a>=97&&a<=122 else s=!0 return s}, rz(a,b){var s,r,q=null,p=a.length,o=b+2 if(p0)throw A.a(A.jL("BigInt value exceeds the range of 64 bits")) return a}, ky(a){var s=0,r=A.o(t.E),q var $async$ky=A.p(function(b,c){if(b===1)return A.l(c,r) while(true)switch(s){case 0:s=3 return A.c(A.a_(a.arrayBuffer(),t.bZ),$async$ky) case 3:q=c s=1 break case 1:return A.m(q,r)}}) return A.n($async$ky,r)}, qe(a,b,c){var s=self.DataView,r=[a] r.push(b) r.push(c) return t.gT.a(A.dT(s,r))}, oF(a,b,c){var s=self.Uint8Array,r=[a] r.push(b) r.push(c) return t.Z.a(A.dT(s,r))}, tL(a,b){self.Atomics.notify(a,b,1/0)}, rN(){var s=self.navigator if("storage" in s)return s.storage return null}, jM(a,b,c){return a.read(b,c)}, ot(a,b,c){return a.write(b,c)}, pN(a,b){return A.a_(a.removeEntry(b,{recursive:!1}),t.X)}, ov(a,b){var s,r for(s=b,r=0;r<16;++r)s+=A.aA("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012346789".charCodeAt(a.hc(61))) return s.charCodeAt(0)==0?s:s}, xg(){var s=t.m.a(self) if(A.oy(s,"DedicatedWorkerGlobalScope"))new A.jv(s,new A.bh(),new A.fU(A.a3(t.N,t.fE),null)).T() else if(A.oy(s,"SharedWorkerGlobalScope"))new A.kK(s,new A.fU(A.a3(t.N,t.fE),null)).T()}},B={} var w=[A,J,B] var $={} A.oA.prototype={} J.h7.prototype={ O(a,b){return a===b}, gB(a){return A.ev(a)}, j(a){return"Instance of '"+A.kp(a)+"'"}, gV(a){return A.bO(A.p8(this))}} J.h8.prototype={ j(a){return String(a)}, gB(a){return a?519018:218159}, gV(a){return A.bO(t.y)}, $iL:1, $iU:1} J.ej.prototype={ O(a,b){return null==b}, j(a){return"null"}, gB(a){return 0}, $iL:1, $iF:1} J.ek.prototype={$iA:1} J.bY.prototype={ gB(a){return 0}, j(a){return String(a)}} J.hr.prototype={} J.cy.prototype={} J.bW.prototype={ j(a){var s=a[$.dX()] if(s==null)return this.hG(a) return"JavaScript function for "+J.aU(s)}} J.aY.prototype={ gB(a){return 0}, j(a){return String(a)}} J.el.prototype={ gB(a){return 0}, j(a){return String(a)}} J.w.prototype={ b8(a,b){return new A.ah(a,A.Q(a).h("@<1>").H(b).h("ah<1,2>"))}, v(a,b){if(!!a.fixed$length)A.y(A.H("add")) a.push(b)}, d6(a,b){var s if(!!a.fixed$length)A.y(A.H("removeAt")) s=a.length if(b>=s)throw A.a(A.kt(b,null)) return a.splice(b,1)[0]}, cY(a,b,c){var s if(!!a.fixed$length)A.y(A.H("insert")) s=a.length if(b>s)throw A.a(A.kt(b,null)) a.splice(b,0,c)}, ev(a,b,c){var s,r if(!!a.fixed$length)A.y(A.H("insertAll")) A.qa(b,0,a.length,"index") if(!t.Q.b(c))c=J.iU(c) s=J.ae(c) a.length=a.length+s r=b+s this.Z(a,r,a.length,a,b) this.aj(a,b,r,c)}, hi(a){if(!!a.fixed$length)A.y(A.H("removeLast")) if(a.length===0)throw A.a(A.dV(a,-1)) return a.pop()}, A(a,b){var s if(!!a.fixed$length)A.y(A.H("remove")) for(s=0;s").H(c).h("D<1,2>"))}, aq(a,b){var s,r=A.b_(a.length,"",!1,t.N) for(s=0;ss)throw A.a(A.a4(b,0,s,"start",null)) if(cs)throw A.a(A.a4(c,b,s,"end",null)) if(b===c)return A.d([],A.Q(a)) return A.d(a.slice(b,c),A.Q(a))}, co(a,b,c){A.b9(b,c,a.length) return A.b3(a,b,c,A.Q(a).c)}, gG(a){if(a.length>0)return a[0] throw A.a(A.ak())}, gC(a){var s=a.length if(s>0)return a[s-1] throw A.a(A.ak())}, Z(a,b,c,d,e){var s,r,q,p,o if(!!a.immutable$list)A.y(A.H("setRange")) A.b9(b,c,a.length) s=c-b if(s===0)return A.ab(e,"skipCount") if(t.j.b(d)){r=d q=e}else{r=J.dZ(d,e).az(0,!1) q=0}p=J.V(r) if(q+s>p.gl(r))throw A.a(A.pT()) if(q=0;--o)a[b+o]=p.i(r,q+o) else for(o=0;o0){a[0]=q a[1]=r}return}p=0 if(A.Q(a).c.b(null))for(o=0;o0)this.iW(a,p)}, hB(a){return this.hC(a,null)}, iW(a,b){var s,r=a.length for(;s=r-1,r>0;r=s)if(a[s]===null){a[s]=void 0;--b if(b===0)break}}, d0(a,b){var s,r=a.length,q=r-1 if(q<0)return-1 q>=r for(s=q;s>=0;--s)if(J.X(a[s],b))return s return-1}, gF(a){return a.length===0}, j(a){return A.ox(a,"[","]")}, az(a,b){var s=A.d(a.slice(0),A.Q(a)) return s}, cj(a){return this.az(a,!0)}, gt(a){return new J.fz(a,a.length,A.Q(a).h("fz<1>"))}, gB(a){return A.ev(a)}, gl(a){return a.length}, i(a,b){if(!(b>=0&&b=0&&b=p){r.d=null return!1}r.d=q[s] r.c=s+1 return!0}} J.cZ.prototype={ ag(a,b){var s if(ab)return 1 else if(a===b){if(a===0){s=this.gey(b) if(this.gey(a)===s)return 0 if(this.gey(a))return-1 return 1}return 0}else if(isNaN(a)){if(isNaN(b))return 0 return 1}else return-1}, gey(a){return a===0?1/a<0:a<0}, kE(a){var s if(a>=-2147483648&&a<=2147483647)return a|0 if(isFinite(a)){s=a<0?Math.ceil(a):Math.floor(a) return s+0}throw A.a(A.H(""+a+".toInt()"))}, jE(a){var s,r if(a>=0){if(a<=2147483647){s=a|0 return a===s?s:s+1}}else if(a>=-2147483648)return a|0 r=Math.ceil(a) if(isFinite(r))return r throw A.a(A.H(""+a+".ceil()"))}, j(a){if(a===0&&1/a<0)return"-0.0" else return""+a}, gB(a){var s,r,q,p,o=a|0 if(a===o)return o&536870911 s=Math.abs(a) r=Math.log(s)/0.6931471805599453|0 q=Math.pow(2,r) p=s<1?s/q:q/s return((p*9007199254740992|0)+(p*3542243181176521|0))*599197+r*1259&536870911}, aA(a,b){var s=a%b if(s===0)return 0 if(s>0)return s return s+b}, eX(a,b){if((a|0)===a)if(b>=1||b<-1)return a/b|0 return this.fL(a,b)}, I(a,b){return(a|0)===a?a/b|0:this.fL(a,b)}, fL(a,b){var s=a/b if(s>=-2147483648&&s<=2147483647)return s|0 if(s>0){if(s!==1/0)return Math.floor(s)}else if(s>-1/0)return Math.ceil(s) throw A.a(A.H("Result of truncating division is "+A.u(s)+": "+A.u(a)+" ~/ "+b))}, b0(a,b){if(b<0)throw A.a(A.dS(b)) return b>31?0:a<>>0}, bl(a,b){var s if(b<0)throw A.a(A.dS(b)) if(a>0)s=this.e2(a,b) else{s=b>31?31:b s=a>>s>>>0}return s}, P(a,b){var s if(a>0)s=this.e2(a,b) else{s=b>31?31:b s=a>>s>>>0}return s}, ja(a,b){if(0>b)throw A.a(A.dS(b)) return this.e2(a,b)}, e2(a,b){return b>31?0:a>>>b}, gV(a){return A.bO(t.v)}, $iI:1, $ib5:1} J.ei.prototype={ gfV(a){var s,r=a<0?-a-1:a,q=r for(s=32;q>=4294967296;){q=this.I(q,4294967296) s+=32}return s-Math.clz32(q)}, gV(a){return A.bO(t.S)}, $iL:1, $ib:1} J.h9.prototype={ gV(a){return A.bO(t.i)}, $iL:1} J.bV.prototype={ jG(a,b){if(b<0)throw A.a(A.dV(a,b)) if(b>=a.length)A.y(A.dV(a,b)) return a.charCodeAt(b)}, cM(a,b,c){var s=b.length if(c>s)throw A.a(A.a4(c,0,s,null,null)) return new A.iA(b,a,c)}, ed(a,b){return this.cM(a,b,0)}, ha(a,b,c){var s,r,q=null if(c<0||c>b.length)throw A.a(A.a4(c,0,b.length,q,q)) s=a.length if(c+s>b.length)return q for(r=0;rr)return!1 return b===this.K(a,r-s)}, hl(a,b,c){A.qa(0,0,a.length,"startIndex") return A.xz(a,b,c,0)}, aQ(a,b){if(typeof b=="string")return A.d(a.split(b),t.s) else if(b instanceof A.cs&&b.gfp().exec("").length-2===0)return A.d(a.split(b.b),t.s) else return this.i6(a,b)}, aO(a,b,c,d){var s=A.b9(b,c,a.length) return A.po(a,b,s,d)}, i6(a,b){var s,r,q,p,o,n,m=A.d([],t.s) for(s=J.oo(b,a),s=s.gt(s),r=0,q=1;s.k();){p=s.gm() o=p.gcq() n=p.gby() q=n-o if(q===0&&r===o)continue m.push(this.n(a,r,o)) r=n}if(r0)m.push(this.K(a,r)) return m}, E(a,b,c){var s if(c<0||c>a.length)throw A.a(A.a4(c,0,a.length,null,null)) if(typeof b=="string"){s=c+b.length if(s>a.length)return!1 return b===a.substring(c,s)}return J.tF(b,a,c)!=null}, u(a,b){return this.E(a,b,0)}, n(a,b,c){return a.substring(b,A.b9(b,c,a.length))}, K(a,b){return this.n(a,b,null)}, eM(a){var s,r,q,p=a.trim(),o=p.length if(o===0)return p if(p.charCodeAt(0)===133){s=J.ue(p,1) if(s===o)return""}else s=0 r=o-1 q=p.charCodeAt(r)===133?J.uf(p,r):o if(s===0&&q===o)return p return p.substring(s,q)}, bI(a,b){var s,r if(0>=b)return"" if(b===1||a.length===0)return a if(b!==b>>>0)throw A.a(B.aB) for(s=a,r="";!0;){if((b&1)===1)r=s+r b=b>>>1 if(b===0)break s+=s}return r}, kl(a,b,c){var s=b-a.length if(s<=0)return a return this.bI(c,s)+a}, hd(a,b){var s=b-a.length if(s<=0)return a return a+this.bI(" ",s)}, aW(a,b,c){var s if(c<0||c>a.length)throw A.a(A.a4(c,0,a.length,null,null)) s=a.indexOf(b,c) return s}, k7(a,b){return this.aW(a,b,0)}, h9(a,b,c){var s,r if(c==null)c=a.length else if(c<0||c>a.length)throw A.a(A.a4(c,0,a.length,null,null)) s=b.length r=a.length if(c+s>r)c=r-s return a.lastIndexOf(b,c)}, d0(a,b){return this.h9(a,b,null)}, M(a,b){return A.xv(a,b,0)}, ag(a,b){var s if(a===b)s=0 else s=a>6}r=r+((r&67108863)<<3)&536870911 r^=r>>11 return r+((r&16383)<<15)&536870911}, gV(a){return A.bO(t.N)}, gl(a){return a.length}, i(a,b){if(!(b>=0&&b"))}, gl(a){return J.ae(this.gan())}, gF(a){return J.iR(this.gan())}, X(a,b){var s=A.t(this) return A.e4(J.dZ(this.gan(),b),s.c,s.y[1])}, ah(a,b){var s=A.t(this) return A.e4(J.iT(this.gan(),b),s.c,s.y[1])}, N(a,b){return A.t(this).y[1].a(J.fx(this.gan(),b))}, gG(a){return A.t(this).y[1].a(J.fy(this.gan()))}, gC(a){return A.t(this).y[1].a(J.iS(this.gan()))}, j(a){return J.aU(this.gan())}} A.fJ.prototype={ k(){return this.a.k()}, gm(){return this.$ti.y[1].a(this.a.gm())}} A.ck.prototype={ gan(){return this.a}} A.eS.prototype={$iv:1} A.eN.prototype={ i(a,b){return this.$ti.y[1].a(J.aO(this.a,b))}, q(a,b,c){J.px(this.a,b,this.$ti.c.a(c))}, co(a,b,c){var s=this.$ti return A.e4(J.tE(this.a,b,c),s.c,s.y[1])}, Z(a,b,c,d,e){var s=this.$ti J.tG(this.a,b,c,A.e4(d,s.y[1],s.c),e)}, aj(a,b,c,d){return this.Z(0,b,c,d,0)}, $iv:1, $iq:1} A.ah.prototype={ b8(a,b){return new A.ah(this.a,this.$ti.h("@<1>").H(b).h("ah<1,2>"))}, gan(){return this.a}} A.bX.prototype={ j(a){return"LateInitializationError: "+this.a}} A.e6.prototype={ gl(a){return this.a.length}, i(a,b){return this.a.charCodeAt(b)}} A.oc.prototype={ $0(){return A.aX(null,t.P)}, $S:14} A.kB.prototype={} A.v.prototype={} A.P.prototype={ gt(a){var s=this return new A.aZ(s,s.gl(s),A.t(s).h("aZ"))}, gF(a){return this.gl(this)===0}, gG(a){if(this.gl(this)===0)throw A.a(A.ak()) return this.N(0,0)}, gC(a){var s=this if(s.gl(s)===0)throw A.a(A.ak()) return s.N(0,s.gl(s)-1)}, aq(a,b){var s,r,q,p=this,o=p.gl(p) if(b.length!==0){if(o===0)return"" s=A.u(p.N(0,0)) if(o!==p.gl(p))throw A.a(A.ax(p)) for(r=s,q=1;q").H(c).h("D<1,2>"))}, k5(a,b,c){var s,r,q=this,p=q.gl(q) for(s=b,r=0;rs)throw A.a(A.a4(r,0,s,"start",null))}}, gie(){var s=J.ae(this.a),r=this.c if(r==null||r>s)return s return r}, gjf(){var s=J.ae(this.a),r=this.b if(r>s)return s return r}, gl(a){var s,r=J.ae(this.a),q=this.b if(q>=r)return 0 s=this.c if(s==null||s>=r)return r-q return s-q}, N(a,b){var s=this,r=s.gjf()+b if(b<0||r>=s.gie())throw A.a(A.h4(b,s.gl(0),s,null,"index")) return J.fx(s.a,r)}, X(a,b){var s,r,q=this A.ab(b,"count") s=q.b+b r=q.c if(r!=null&&s>=r)return new A.cq(q.$ti.h("cq<1>")) return A.b3(q.a,s,r,q.$ti.c)}, ah(a,b){var s,r,q,p=this A.ab(b,"count") s=p.c r=p.b q=r+b if(s==null)return A.b3(p.a,r,q,p.$ti.c) else{if(s=o){r.d=null return!1}r.d=p.N(q,s);++r.c return!0}} A.az.prototype={ gt(a){return new A.b0(J.M(this.a),this.b,A.t(this).h("b0<1,2>"))}, gl(a){return J.ae(this.a)}, gF(a){return J.iR(this.a)}, gG(a){return this.b.$1(J.fy(this.a))}, gC(a){return this.b.$1(J.iS(this.a))}, N(a,b){return this.b.$1(J.fx(this.a,b))}} A.cp.prototype={$iv:1} A.b0.prototype={ k(){var s=this,r=s.b if(r.k()){s.a=s.c.$1(r.gm()) return!0}s.a=null return!1}, gm(){var s=this.a return s==null?this.$ti.y[1].a(s):s}} A.D.prototype={ gl(a){return J.ae(this.a)}, N(a,b){return this.b.$1(J.fx(this.a,b))}} A.aT.prototype={ gt(a){return new A.eH(J.M(this.a),this.b)}, bd(a,b,c){return new A.az(this,b,this.$ti.h("@<1>").H(c).h("az<1,2>"))}} A.eH.prototype={ k(){var s,r for(s=this.a,r=this.b;s.k();)if(r.$1(s.gm()))return!0 return!1}, gm(){return this.a.gm()}} A.ed.prototype={ gt(a){return new A.fY(J.M(this.a),this.b,B.a0,this.$ti.h("fY<1,2>"))}} A.fY.prototype={ gm(){var s=this.d return s==null?this.$ti.y[1].a(s):s}, k(){var s,r,q=this,p=q.c if(p==null)return!1 for(s=q.a,r=q.b;!p.k();){q.d=null if(s.k()){q.c=null p=J.M(r.$1(s.gm())) q.c=p}else return!1}q.d=q.c.gm() return!0}} A.cx.prototype={ gt(a){return new A.hF(J.M(this.a),this.b,A.t(this).h("hF<1>"))}} A.eb.prototype={ gl(a){var s=J.ae(this.a),r=this.b if(s>r)return r return s}, $iv:1} A.hF.prototype={ k(){if(--this.b>=0)return this.a.k() this.b=-1 return!1}, gm(){if(this.b<0){this.$ti.c.a(null) return null}return this.a.gm()}} A.bB.prototype={ X(a,b){A.bR(b,"count") A.ab(b,"count") return new A.bB(this.a,this.b+b,A.t(this).h("bB<1>"))}, gt(a){return new A.hy(J.M(this.a),this.b)}} A.cV.prototype={ gl(a){var s=J.ae(this.a)-this.b if(s>=0)return s return 0}, X(a,b){A.bR(b,"count") A.ab(b,"count") return new A.cV(this.a,this.b+b,this.$ti)}, $iv:1} A.hy.prototype={ k(){var s,r for(s=this.a,r=0;r"))}, X(a,b){A.ab(b,"count") return this}, ah(a,b){A.ab(b,"count") return this}} A.fV.prototype={ k(){return!1}, gm(){throw A.a(A.ak())}} A.eI.prototype={ gt(a){return new A.hY(J.M(this.a),this.$ti.h("hY<1>"))}} A.hY.prototype={ k(){var s,r for(s=this.a,r=this.$ti.c;s.k();)if(r.b(s.gm()))return!0 return!1}, gm(){return this.$ti.c.a(this.a.gm())}} A.bt.prototype={ gl(a){return J.ae(this.a)}, gF(a){return J.iR(this.a)}, gG(a){return new A.ap(this.b,J.fy(this.a))}, N(a,b){return new A.ap(b+this.b,J.fx(this.a,b))}, ah(a,b){A.bR(b,"count") A.ab(b,"count") return new A.bt(J.iT(this.a,b),this.b,A.t(this).h("bt<1>"))}, X(a,b){A.bR(b,"count") A.ab(b,"count") return new A.bt(J.dZ(this.a,b),b+this.b,A.t(this).h("bt<1>"))}, gt(a){return new A.eg(J.M(this.a),this.b)}} A.co.prototype={ gC(a){var s,r=this.a,q=J.V(r),p=q.gl(r) if(p<=0)throw A.a(A.ak()) s=q.gC(r) if(p!==q.gl(r))throw A.a(A.ax(this)) return new A.ap(p-1+this.b,s)}, ah(a,b){A.bR(b,"count") A.ab(b,"count") return new A.co(J.iT(this.a,b),this.b,this.$ti)}, X(a,b){A.bR(b,"count") A.ab(b,"count") return new A.co(J.dZ(this.a,b),this.b+b,this.$ti)}, $iv:1} A.eg.prototype={ k(){if(++this.c>=0&&this.a.k())return!0 this.c=-2 return!1}, gm(){var s=this.c return s>=0?new A.ap(this.b+s,this.a.gm()):A.y(A.ak())}} A.ee.prototype={} A.hJ.prototype={ q(a,b,c){throw A.a(A.H("Cannot modify an unmodifiable list"))}, Z(a,b,c,d,e){throw A.a(A.H("Cannot modify an unmodifiable list"))}, aj(a,b,c,d){return this.Z(0,b,c,d,0)}} A.dh.prototype={} A.ew.prototype={ gl(a){return J.ae(this.a)}, N(a,b){var s=this.a,r=J.V(s) return r.N(s,r.gl(s)-1-b)}} A.hE.prototype={ gB(a){var s=this._hashCode if(s!=null)return s s=664597*B.a.gB(this.a)&536870911 this._hashCode=s return s}, j(a){return'Symbol("'+this.a+'")'}, O(a,b){if(b==null)return!1 return b instanceof A.hE&&this.a===b.a}} A.fn.prototype={} A.ap.prototype={$r:"+(1,2)",$s:1} A.cH.prototype={$r:"+file,outFlags(1,2)",$s:2} A.e7.prototype={ j(a){return A.oD(this)}, gel(){return new A.dJ(this.jP(),A.t(this).h("dJ>"))}, jP(){var s=this return function(){var r=0,q=1,p,o,n,m return function $async$gel(a,b,c){if(b===1){p=c r=q}while(true)switch(r){case 0:o=s.ga_(),o=o.gt(o),n=A.t(s).h("bv<1,2>") case 2:if(!o.k()){r=3 break}m=o.gm() r=4 return a.b=new A.bv(m,s.i(0,m),n),1 case 4:r=2 break case 3:return 0 case 1:return a.c=p,3}}}}, $iaa:1} A.e8.prototype={ gl(a){return this.b.length}, gfl(){var s=this.$keys if(s==null){s=Object.keys(this.a) this.$keys=s}return s}, a4(a){if(typeof a!="string")return!1 if("__proto__"===a)return!1 return this.a.hasOwnProperty(a)}, i(a,b){if(!this.a4(b))return null return this.b[this.a[b]]}, aa(a,b){var s,r,q=this.gfl(),p=this.b for(s=q.length,r=0;r"))}, gaP(){return new A.cG(this.b,this.$ti.h("cG<2>"))}} A.cG.prototype={ gl(a){return this.a.length}, gF(a){return 0===this.a.length}, gt(a){var s=this.a return new A.il(s,s.length,this.$ti.h("il<1>"))}} A.il.prototype={ gm(){var s=this.d return s==null?this.$ti.c.a(s):s}, k(){var s=this,r=s.c if(r>=s.b){s.d=null return!1}s.d=s.a[r] s.c=r+1 return!0}} A.k3.prototype={ O(a,b){if(b==null)return!1 return b instanceof A.eh&&this.a.O(0,b.a)&&A.pi(this)===A.pi(b)}, gB(a){return A.es(this.a,A.pi(this),B.f,B.f)}, j(a){var s=B.c.aq([A.bO(this.$ti.c)],", ") return this.a.j(0)+" with "+("<"+s+">")}} A.eh.prototype={ $2(a,b){return this.a.$1$2(a,b,this.$ti.y[0])}, $4(a,b,c,d){return this.a.$1$4(a,b,c,d,this.$ti.y[0])}, $S(){return A.xb(A.o0(this.a),this.$ti)}} A.lc.prototype={ ar(a){var s,r,q=this,p=new RegExp(q.a).exec(a) if(p==null)return null s=Object.create(null) r=q.b if(r!==-1)s.arguments=p[r+1] r=q.c if(r!==-1)s.argumentsExpr=p[r+1] r=q.d if(r!==-1)s.expr=p[r+1] r=q.e if(r!==-1)s.method=p[r+1] r=q.f if(r!==-1)s.receiver=p[r+1] return s}} A.er.prototype={ j(a){return"Null check operator used on a null value"}} A.hb.prototype={ j(a){var s,r=this,q="NoSuchMethodError: method not found: '",p=r.b if(p==null)return"NoSuchMethodError: "+r.a s=r.c if(s==null)return q+p+"' ("+r.a+")" return q+p+"' on '"+s+"' ("+r.a+")"}} A.hI.prototype={ j(a){var s=this.a return s.length===0?"Error":"Error: "+s}} A.hp.prototype={ j(a){return"Throw of null ('"+(this.a===null?"null":"undefined")+"' from JavaScript)"}, $ia5:1} A.ec.prototype={} A.fa.prototype={ j(a){var s,r=this.b if(r!=null)return r r=this.a s=r!==null&&typeof r==="object"?r.stack:null return this.b=s==null?"":s}, $ia0:1} A.cl.prototype={ j(a){var s=this.constructor,r=s==null?null:s.name return"Closure '"+A.rO(r==null?"unknown":r)+"'"}, gkI(){return this}, $C:"$1", $R:1, $D:null} A.j9.prototype={$C:"$0",$R:0} A.ja.prototype={$C:"$2",$R:2} A.l2.prototype={} A.kT.prototype={ j(a){var s=this.$static_name if(s==null)return"Closure of unknown static method" return"Closure '"+A.rO(s)+"'"}} A.e2.prototype={ O(a,b){if(b==null)return!1 if(this===b)return!0 if(!(b instanceof A.e2))return!1 return this.$_target===b.$_target&&this.a===b.a}, gB(a){return(A.pm(this.a)^A.ev(this.$_target))>>>0}, j(a){return"Closure '"+this.$_name+"' of "+("Instance of '"+A.kp(this.a)+"'")}} A.i8.prototype={ j(a){return"Reading static variable '"+this.a+"' during its initialization"}} A.hv.prototype={ j(a){return"RuntimeError: "+this.a}} A.bu.prototype={ gl(a){return this.a}, gF(a){return this.a===0}, ga_(){return new A.b8(this,A.t(this).h("b8<1>"))}, gaP(){var s=A.t(this) return A.en(new A.b8(this,s.h("b8<1>")),new A.kb(this),s.c,s.y[1])}, a4(a){var s,r if(typeof a=="string"){s=this.b if(s==null)return!1 return s[a]!=null}else if(typeof a=="number"&&(a&0x3fffffff)===a){r=this.c if(r==null)return!1 return r[a]!=null}else return this.k8(a)}, k8(a){var s=this.d if(s==null)return!1 return this.d_(s[this.cZ(a)],a)>=0}, aJ(a,b){b.aa(0,new A.ka(this))}, i(a,b){var s,r,q,p,o=null if(typeof b=="string"){s=this.b if(s==null)return o r=s[b] q=r==null?o:r.b return q}else if(typeof b=="number"&&(b&0x3fffffff)===b){p=this.c if(p==null)return o r=p[b] q=r==null?o:r.b return q}else return this.k9(b)}, k9(a){var s,r,q=this.d if(q==null)return null s=q[this.cZ(a)] r=this.d_(s,a) if(r<0)return null return s[r].b}, q(a,b,c){var s,r,q=this if(typeof b=="string"){s=q.b q.eY(s==null?q.b=q.dW():s,b,c)}else if(typeof b=="number"&&(b&0x3fffffff)===b){r=q.c q.eY(r==null?q.c=q.dW():r,b,c)}else q.kb(b,c)}, kb(a,b){var s,r,q,p=this,o=p.d if(o==null)o=p.d=p.dW() s=p.cZ(a) r=o[s] if(r==null)o[s]=[p.dq(a,b)] else{q=p.d_(r,a) if(q>=0)r[q].b=b else r.push(p.dq(a,b))}}, hg(a,b){var s,r,q=this if(q.a4(a)){s=q.i(0,a) return s==null?A.t(q).y[1].a(s):s}r=b.$0() q.q(0,a,r) return r}, A(a,b){var s=this if(typeof b=="string")return s.eZ(s.b,b) else if(typeof b=="number"&&(b&0x3fffffff)===b)return s.eZ(s.c,b) else return s.ka(b)}, ka(a){var s,r,q,p,o=this,n=o.d if(n==null)return null s=o.cZ(a) r=n[s] q=o.d_(r,a) if(q<0)return null p=r.splice(q,1)[0] o.f_(p) if(r.length===0)delete n[s] return p.b}, c1(a){var s=this if(s.a>0){s.b=s.c=s.d=s.e=s.f=null s.a=0 s.dn()}}, aa(a,b){var s=this,r=s.e,q=s.r for(;r!=null;){b.$2(r.a,r.b) if(q!==s.r)throw A.a(A.ax(s)) r=r.c}}, eY(a,b,c){var s=a[b] if(s==null)a[b]=this.dq(b,c) else s.b=c}, eZ(a,b){var s if(a==null)return null s=a[b] if(s==null)return null this.f_(s) delete a[b] return s.b}, dn(){this.r=this.r+1&1073741823}, dq(a,b){var s,r=this,q=new A.ke(a,b) if(r.e==null)r.e=r.f=q else{s=r.f s.toString q.d=s r.f=s.c=q}++r.a r.dn() return q}, f_(a){var s=this,r=a.d,q=a.c if(r==null)s.e=q else r.c=q if(q==null)s.f=r else q.d=r;--s.a s.dn()}, cZ(a){return J.aw(a)&1073741823}, d_(a,b){var s,r if(a==null)return-1 s=a.length for(r=0;r"]=s delete s[""] return s}} A.kb.prototype={ $1(a){var s=this.a,r=s.i(0,a) return r==null?A.t(s).y[1].a(r):r}, $S(){return A.t(this.a).h("2(1)")}} A.ka.prototype={ $2(a,b){this.a.q(0,a,b)}, $S(){return A.t(this.a).h("~(1,2)")}} A.ke.prototype={} A.b8.prototype={ gl(a){return this.a.a}, gF(a){return this.a.a===0}, gt(a){var s=this.a,r=new A.he(s,s.r) r.c=s.e return r}} A.he.prototype={ gm(){return this.d}, k(){var s,r=this,q=r.a if(r.b!==q.r)throw A.a(A.ax(q)) s=r.c if(s==null){r.d=null return!1}else{r.d=s.a r.c=s.c return!0}}} A.o6.prototype={ $1(a){return this.a(a)}, $S:44} A.o7.prototype={ $2(a,b){return this.a(a,b)}, $S:62} A.o8.prototype={ $1(a){return this.a(a)}, $S:109} A.f6.prototype={ j(a){return this.fP(!1)}, fP(a){var s,r,q,p,o,n=this.ih(),m=this.fi(),l=(a?""+"Record ":"")+"(" for(s=n.length,r="",q=0;q0;){--q;--s j[q]=r[s]}}return A.aG(j,k)}} A.is.prototype={ fi(){return[this.a,this.b]}, O(a,b){if(b==null)return!1 return b instanceof A.is&&this.$s===b.$s&&J.X(this.a,b.a)&&J.X(this.b,b.b)}, gB(a){return A.es(this.$s,this.a,this.b,B.f)}} A.cs.prototype={ j(a){return"RegExp/"+this.a+"/"+this.b.flags}, gfq(){var s=this,r=s.c if(r!=null)return r r=s.b return s.c=A.oz(s.a,r.multiline,!r.ignoreCase,r.unicode,r.dotAll,!0)}, gfp(){var s=this,r=s.d if(r!=null)return r r=s.b return s.d=A.oz(s.a+"|()",r.multiline,!r.ignoreCase,r.unicode,r.dotAll,!0)}, a9(a){var s=this.b.exec(a) if(s==null)return null return new A.dz(s)}, cM(a,b,c){var s=b.length if(c>s)throw A.a(A.a4(c,0,s,null,null)) return new A.hZ(this,b,c)}, ed(a,b){return this.cM(0,b,0)}, fe(a,b){var s,r=this.gfq() r.lastIndex=b s=r.exec(a) if(s==null)return null return new A.dz(s)}, ig(a,b){var s,r=this.gfp() r.lastIndex=b s=r.exec(a) if(s==null)return null if(s.pop()!=null)return null return new A.dz(s)}, ha(a,b,c){if(c<0||c>b.length)throw A.a(A.a4(c,0,b.length,null,null)) return this.ig(b,c)}} A.dz.prototype={ gcq(){return this.b.index}, gby(){var s=this.b return s.index+s[0].length}, i(a,b){return this.b[b]}, aN(a){var s,r=this.b.groups if(r!=null){s=r[a] if(s!=null||a in r)return s}throw A.a(A.ag(a,"name","Not a capture group name"))}, $ieo:1, $ihs:1} A.hZ.prototype={ gt(a){return new A.lO(this.a,this.b,this.c)}} A.lO.prototype={ gm(){var s=this.d return s==null?t.cz.a(s):s}, k(){var s,r,q,p,o,n,m=this,l=m.b if(l==null)return!1 s=m.c r=l.length if(s<=r){q=m.a p=q.fe(l,s) if(p!=null){m.d=p o=p.gby() if(p.b.index===o){s=!1 if(q.b.unicode){q=m.c n=q+1 if(n=55296&&r<=56319){s=l.charCodeAt(n) s=s>=56320&&s<=57343}}}o=(s?o+1:o)+1}m.c=o return!0}}m.b=m.d=null return!1}} A.dg.prototype={ gby(){return this.a+this.c.length}, i(a,b){if(b!==0)A.y(A.kt(b,null)) return this.c}, $ieo:1, gcq(){return this.a}} A.iA.prototype={ gt(a){return new A.ns(this.a,this.b,this.c)}, gG(a){var s=this.b,r=this.a.indexOf(s,this.c) if(r>=0)return new A.dg(r,s) throw A.a(A.ak())}} A.ns.prototype={ k(){var s,r,q=this,p=q.c,o=q.b,n=o.length,m=q.a,l=m.length if(p+n>l){q.d=null return!1}s=m.indexOf(o,p) if(s<0){q.c=l+1 q.d=null return!1}r=s+n q.d=new A.dg(s,o) q.c=r===q.c?r+1:r return!0}, gm(){var s=this.d s.toString return s}} A.m3.prototype={ af(){var s=this.b if(s===this)throw A.a(A.ug(this.a)) return s}} A.d_.prototype={ gV(a){return B.b7}, $iL:1, $id_:1, $iop:1} A.ep.prototype={ iu(a,b,c,d){var s=A.a4(b,0,c,d,null) throw A.a(s)}, f7(a,b,c,d){if(b>>>0!==b||b>c)this.iu(a,b,c,d)}} A.d0.prototype={ gV(a){return B.b8}, $iL:1, $id0:1, $ioq:1} A.d2.prototype={ gl(a){return a.length}, fI(a,b,c,d,e){var s,r,q=a.length this.f7(a,b,q,"start") this.f7(a,c,q,"end") if(b>c)throw A.a(A.a4(b,0,c,null,null)) s=c-b if(e<0)throw A.a(A.K(e,null)) r=d.length if(r-e0){s=Date.now()-r.c if(s>(p+1)*o)p=B.b.eX(s,o)}q.c=p r.d.$1(q)}, $S:9} A.i_.prototype={ L(a){var s,r=this if(a==null)a=r.$ti.c.a(a) if(!r.b)r.a.b1(a) else{s=r.a if(r.$ti.h("C<1>").b(a))s.f6(a) else s.bq(a)}}, bx(a,b){var s=this.a if(this.b)s.W(a,b) else s.aE(a,b)}} A.nI.prototype={ $1(a){return this.a.$2(0,a)}, $S:16} A.nJ.prototype={ $2(a,b){this.a.$2(1,new A.ec(a,b))}, $S:45} A.nZ.prototype={ $2(a,b){this.a(a,b)}, $S:54} A.iB.prototype={ gm(){return this.b}, iY(a,b){var s,r,q a=a b=b s=this.a for(;!0;)try{r=s(this,a,b) return r}catch(q){b=q a=1}}, k(){var s,r,q,p,o=this,n=null,m=0 for(;!0;){s=o.d if(s!=null)try{if(s.k()){o.b=s.gm() return!0}else o.d=null}catch(r){n=r m=1 o.d=null}q=o.iY(m,n) if(1===q)return!0 if(0===q){o.b=null p=o.e if(p==null||p.length===0){o.a=A.qO return!1}o.a=p.pop() m=0 n=null continue}if(2===q){m=0 n=null continue}if(3===q){n=o.c o.c=null p=o.e if(p==null||p.length===0){o.b=null o.a=A.qO throw n return!1}o.a=p.pop() m=1 continue}throw A.a(A.B("sync*"))}return!1}, kJ(a){var s,r,q=this if(a instanceof A.dJ){s=a.a() r=q.e if(r==null)r=q.e=[] r.push(q.a) q.a=s return 2}else{q.d=J.M(a) return 2}}} A.dJ.prototype={ gt(a){return new A.iB(this.a())}} A.cS.prototype={ j(a){return A.u(this.a)}, $iO:1, gbJ(){return this.b}} A.eM.prototype={} A.cB.prototype={ al(){}, am(){}} A.cA.prototype={ gbM(){return this.c<4}, fC(a){var s=a.CW,r=a.ch if(s==null)this.d=r else s.ch=r if(r==null)this.e=s else r.CW=s a.CW=a a.ch=a}, fK(a,b,c,d){var s,r,q,p,o,n,m,l,k,j=this if((j.c&4)!==0){s=$.i r=new A.eR(s) A.oh(r.gfs()) if(c!=null)r.c=s.au(c,t.H) return r}s=A.t(j) r=$.i q=d?1:0 p=b!=null?32:0 o=A.i5(r,a,s.c) n=A.i6(r,b) m=c==null?A.rv():c l=new A.cB(j,o,n,r.au(m,t.H),r,q|p,s.h("cB<1>")) l.CW=l l.ch=l l.ay=j.c&1 k=j.e j.e=l l.ch=null l.CW=k if(k==null)j.d=l else k.ch=l if(j.d===l)A.iM(j.a) return l}, fu(a){var s,r=this A.t(r).h("cB<1>").a(a) if(a.ch===a)return null s=a.ay if((s&2)!==0)a.ay=s|4 else{r.fC(a) if((r.c&2)===0&&r.d==null)r.du()}return null}, fv(a){}, fw(a){}, bK(){if((this.c&4)!==0)return new A.b2("Cannot add new events after calling close") return new A.b2("Cannot add new events while doing an addStream")}, v(a,b){if(!this.gbM())throw A.a(this.bK()) this.b3(b)}, a3(a,b){var s A.aD(a,"error",t.K) if(!this.gbM())throw A.a(this.bK()) s=$.i.aL(a,b) if(s!=null){a=s.a b=s.b}this.b5(a,b)}, p(){var s,r,q=this if((q.c&4)!==0){s=q.r s.toString return s}if(!q.gbM())throw A.a(q.bK()) q.c|=4 r=q.r if(r==null)r=q.r=new A.k($.i,t.D) q.b4() return r}, dK(a){var s,r,q,p=this,o=p.c if((o&2)!==0)throw A.a(A.B(u.o)) s=p.d if(s==null)return r=o&1 p.c=o^3 for(;s!=null;){o=s.ay if((o&1)===r){s.ay=o|2 a.$1(s) o=s.ay^=1 q=s.ch if((o&4)!==0)p.fC(s) s.ay&=4294967293 s=q}else s=s.ch}p.c&=4294967293 if(p.d==null)p.du()}, du(){if((this.c&4)!==0){var s=this.r if((s.a&30)===0)s.b1(null)}A.iM(this.b)}, $ia9:1} A.fd.prototype={ gbM(){return A.cA.prototype.gbM.call(this)&&(this.c&2)===0}, bK(){if((this.c&2)!==0)return new A.b2(u.o) return this.hI()}, b3(a){var s=this,r=s.d if(r==null)return if(r===s.e){s.c|=2 r.bp(a) s.c&=4294967293 if(s.d==null)s.du() return}s.dK(new A.nt(s,a))}, b5(a,b){if(this.d==null)return this.dK(new A.nv(this,a,b))}, b4(){var s=this if(s.d!=null)s.dK(new A.nu(s)) else s.r.b1(null)}} A.nt.prototype={ $1(a){a.bp(this.b)}, $S(){return this.a.$ti.h("~(af<1>)")}} A.nv.prototype={ $1(a){a.bn(this.b,this.c)}, $S(){return this.a.$ti.h("~(af<1>)")}} A.nu.prototype={ $1(a){a.cv()}, $S(){return this.a.$ti.h("~(af<1>)")}} A.jX.prototype={ $0(){var s,r,q,p=null try{p=this.a.$0()}catch(q){s=A.E(q) r=A.R(q) A.p6(this.b,s,r) return}this.b.b2(p)}, $S:0} A.jV.prototype={ $0(){this.c.a(null) this.b.b2(null)}, $S:0} A.jZ.prototype={ $2(a,b){var s=this,r=s.a,q=--r.b if(r.a!=null){r.a=null r.d=a r.c=b if(q===0||s.c)s.d.W(a,b)}else if(q===0&&!s.c){q=r.d q.toString r=r.c r.toString s.d.W(q,r)}}, $S:6} A.jY.prototype={ $1(a){var s,r,q,p,o,n,m=this,l=m.a,k=--l.b,j=l.a if(j!=null){J.px(j,m.b,a) if(J.X(k,0)){l=m.d s=A.d([],l.h("w<0>")) for(q=j,p=q.length,o=0;o")) r=b==null?1:3 this.ct(new A.cc(s,r,a,b,this.$ti.h("@<1>").H(c).h("cc<1,2>"))) return s}, bF(a,b){return this.bG(a,null,b)}, fN(a,b,c){var s=new A.k($.i,c.h("k<0>")) this.ct(new A.cc(s,19,a,b,this.$ti.h("@<1>").H(c).h("cc<1,2>"))) return s}, ai(a){var s=this.$ti,r=$.i,q=new A.k(r,s) if(r!==B.d)a=r.au(a,t.z) this.ct(new A.cc(q,8,a,null,s.h("cc<1,1>"))) return q}, j8(a){this.a=this.a&1|16 this.c=a}, cu(a){this.a=a.a&30|this.a&1 this.c=a.c}, ct(a){var s=this,r=s.a if(r<=3){a.a=s.c s.c=a}else{if((r&4)!==0){r=s.c if((r.a&24)===0){r.ct(a) return}s.cu(r)}s.b.b_(new A.mj(s,a))}}, dY(a){var s,r,q,p,o,n=this,m={} m.a=a if(a==null)return s=n.a if(s<=3){r=n.c n.c=a if(r!=null){q=a.a for(p=a;q!=null;p=q,q=o)o=q.a p.a=r}}else{if((s&4)!==0){s=n.c if((s.a&24)===0){s.dY(a) return}n.cu(s)}m.a=n.cF(a) n.b.b_(new A.mq(m,n))}}, cE(){var s=this.c this.c=null return this.cF(s)}, cF(a){var s,r,q for(s=a,r=null;s!=null;r=s,s=q){q=s.a s.a=r}return r}, f5(a){var s,r,q,p=this p.a^=2 try{a.bG(new A.mn(p),new A.mo(p),t.P)}catch(q){s=A.E(q) r=A.R(q) A.oh(new A.mp(p,s,r))}}, b2(a){var s,r=this,q=r.$ti if(q.h("C<1>").b(a))if(q.b(a))A.oV(a,r) else r.f5(a) else{s=r.cE() r.a=8 r.c=a A.du(r,s)}}, bq(a){var s=this,r=s.cE() s.a=8 s.c=a A.du(s,r)}, W(a,b){var s=this.cE() this.j8(A.iV(a,b)) A.du(this,s)}, b1(a){if(this.$ti.h("C<1>").b(a)){this.f6(a) return}this.f4(a)}, f4(a){this.a^=2 this.b.b_(new A.ml(this,a))}, f6(a){if(this.$ti.b(a)){A.v1(a,this) return}this.f5(a)}, aE(a,b){this.a^=2 this.b.b_(new A.mk(this,a,b))}, $iC:1} A.mj.prototype={ $0(){A.du(this.a,this.b)}, $S:0} A.mq.prototype={ $0(){A.du(this.b,this.a.a)}, $S:0} A.mn.prototype={ $1(a){var s,r,q,p=this.a p.a^=2 try{p.bq(p.$ti.c.a(a))}catch(q){s=A.E(q) r=A.R(q) p.W(s,r)}}, $S:25} A.mo.prototype={ $2(a,b){this.a.W(a,b)}, $S:80} A.mp.prototype={ $0(){this.a.W(this.b,this.c)}, $S:0} A.mm.prototype={ $0(){A.oV(this.a.a,this.b)}, $S:0} A.ml.prototype={ $0(){this.a.bq(this.b)}, $S:0} A.mk.prototype={ $0(){this.a.W(this.b,this.c)}, $S:0} A.mt.prototype={ $0(){var s,r,q,p,o,n,m=this,l=null try{q=m.a.a l=q.b.b.bg(q.d,t.z)}catch(p){s=A.E(p) r=A.R(p) q=m.c&&m.b.a.c.a===s o=m.a if(q)o.c=m.b.a.c else o.c=A.iV(s,r) o.b=!0 return}if(l instanceof A.k&&(l.a&24)!==0){if((l.a&16)!==0){q=m.a q.c=l.c q.b=!0}return}if(l instanceof A.k){n=m.b.a q=m.a q.c=l.bF(new A.mu(n),t.z) q.b=!1}}, $S:0} A.mu.prototype={ $1(a){return this.a}, $S:82} A.ms.prototype={ $0(){var s,r,q,p,o,n try{q=this.a p=q.a o=p.$ti q.c=p.b.b.bh(p.d,this.b,o.h("2/"),o.c)}catch(n){s=A.E(n) r=A.R(n) q=this.a q.c=A.iV(s,r) q.b=!0}}, $S:0} A.mr.prototype={ $0(){var s,r,q,p,o,n,m=this try{s=m.a.a.c p=m.b if(p.a.kg(s)&&p.a.e!=null){p.c=p.a.k6(s) p.b=!1}}catch(o){r=A.E(o) q=A.R(o) p=m.a.a.c n=m.b if(p.a===r)n.c=p else n.c=A.iV(r,q) n.b=!0}}, $S:0} A.i0.prototype={} A.Y.prototype={ gl(a){var s={},r=new A.k($.i,t.gR) s.a=0 this.R(new A.l_(s,this),!0,new A.l0(s,r),r.gdB()) return r}, gG(a){var s=new A.k($.i,A.t(this).h("k")),r=this.R(null,!0,new A.kY(s),s.gdB()) r.c9(new A.kZ(this,r,s)) return s}, k0(a,b){var s=new A.k($.i,A.t(this).h("k")),r=this.R(null,!0,new A.kW(null,s),s.gdB()) r.c9(new A.kX(this,b,r,s)) return s}} A.l_.prototype={ $1(a){++this.a.a}, $S(){return A.t(this.b).h("~(Y.T)")}} A.l0.prototype={ $0(){this.b.b2(this.a.a)}, $S:0} A.kY.prototype={ $0(){var s,r,q,p try{q=A.ak() throw A.a(q)}catch(p){s=A.E(p) r=A.R(p) A.p6(this.a,s,r)}}, $S:0} A.kZ.prototype={ $1(a){A.r8(this.b,this.c,a)}, $S(){return A.t(this.a).h("~(Y.T)")}} A.kW.prototype={ $0(){var s,r,q,p try{q=A.ak() throw A.a(q)}catch(p){s=A.E(p) r=A.R(p) A.p6(this.b,s,r)}}, $S:0} A.kX.prototype={ $1(a){var s=this.c,r=this.d A.wo(new A.kU(this.b,a),new A.kV(s,r,a),A.vL(s,r))}, $S(){return A.t(this.a).h("~(Y.T)")}} A.kU.prototype={ $0(){return this.a.$1(this.b)}, $S:22} A.kV.prototype={ $1(a){if(a)A.r8(this.a,this.b,this.c)}, $S:39} A.hD.prototype={} A.cI.prototype={ giM(){if((this.b&8)===0)return this.a return this.a.ge6()}, dH(){var s,r=this if((r.b&8)===0){s=r.a return s==null?r.a=new A.f5():s}s=r.a.ge6() return s}, gaT(){var s=this.a return(this.b&8)!==0?s.ge6():s}, ds(){if((this.b&4)!==0)return new A.b2("Cannot add event after closing") return new A.b2("Cannot add event while adding a stream")}, fc(){var s=this.c if(s==null)s=this.c=(this.b&2)!==0?$.ci():new A.k($.i,t.D) return s}, v(a,b){var s=this,r=s.b if(r>=4)throw A.a(s.ds()) if((r&1)!==0)s.b3(b) else if((r&3)===0)s.dH().v(0,new A.dq(b))}, a3(a,b){var s,r,q=this A.aD(a,"error",t.K) if(q.b>=4)throw A.a(q.ds()) s=$.i.aL(a,b) if(s!=null){a=s.a b=s.b}else if(b==null)b=A.fD(a) r=q.b if((r&1)!==0)q.b5(a,b) else if((r&3)===0)q.dH().v(0,new A.eQ(a,b))}, jz(a){return this.a3(a,null)}, p(){var s=this,r=s.b if((r&4)!==0)return s.fc() if(r>=4)throw A.a(s.ds()) r=s.b=r|4 if((r&1)!==0)s.b4() else if((r&3)===0)s.dH().v(0,B.y) return s.fc()}, fK(a,b,c,d){var s,r,q,p,o=this if((o.b&3)!==0)throw A.a(A.B("Stream has already been listened to.")) s=A.v_(o,a,b,c,d,A.t(o).c) r=o.giM() q=o.b|=1 if((q&8)!==0){p=o.a p.se6(s) p.bf()}else o.a=s s.j9(r) s.dL(new A.nq(o)) return s}, fu(a){var s,r,q,p,o,n,m,l=this,k=null if((l.b&8)!==0)k=l.a.J() l.a=null l.b=l.b&4294967286|2 s=l.r if(s!=null)if(k==null)try{r=s.$0() if(r instanceof A.k)k=r}catch(o){q=A.E(o) p=A.R(o) n=new A.k($.i,t.D) n.aE(q,p) k=n}else k=k.ai(s) m=new A.np(l) if(k!=null)k=k.ai(m) else m.$0() return k}, fv(a){if((this.b&8)!==0)this.a.bB() A.iM(this.e)}, fw(a){if((this.b&8)!==0)this.a.bf() A.iM(this.f)}, $ia9:1} A.nq.prototype={ $0(){A.iM(this.a.d)}, $S:0} A.np.prototype={ $0(){var s=this.a.c if(s!=null&&(s.a&30)===0)s.b1(null)}, $S:0} A.iC.prototype={ b3(a){this.gaT().bp(a)}, b5(a,b){this.gaT().bn(a,b)}, b4(){this.gaT().cv()}} A.i1.prototype={ b3(a){this.gaT().bo(new A.dq(a))}, b5(a,b){this.gaT().bo(new A.eQ(a,b))}, b4(){this.gaT().bo(B.y)}} A.dn.prototype={} A.dK.prototype={} A.an.prototype={ gB(a){return(A.ev(this.a)^892482866)>>>0}, O(a,b){if(b==null)return!1 if(this===b)return!0 return b instanceof A.an&&b.a===this.a}} A.cb.prototype={ cB(){return this.w.fu(this)}, al(){this.w.fv(this)}, am(){this.w.fw(this)}} A.dH.prototype={ v(a,b){this.a.v(0,b)}, a3(a,b){this.a.a3(a,b)}, p(){return this.a.p()}, $ia9:1} A.af.prototype={ j9(a){var s=this if(a==null)return s.r=a if(a.c!=null){s.e=(s.e|128)>>>0 a.cp(s)}}, c9(a){this.a=A.i5(this.d,a,A.t(this).h("af.T"))}, eF(a){var s=this s.e=(s.e&4294967263)>>>0 s.b=A.i6(s.d,a)}, bB(){var s,r,q=this,p=q.e if((p&8)!==0)return s=(p+256|4)>>>0 q.e=s if(p<256){r=q.r if(r!=null)if(r.a===1)r.a=3}if((p&4)===0&&(s&64)===0)q.dL(q.gbN())}, bf(){var s=this,r=s.e if((r&8)!==0)return if(r>=256){r=s.e=r-256 if(r<256)if((r&128)!==0&&s.r.c!=null)s.r.cp(s) else{r=(r&4294967291)>>>0 s.e=r if((r&64)===0)s.dL(s.gbO())}}}, J(){var s=this,r=(s.e&4294967279)>>>0 s.e=r if((r&8)===0)s.dv() r=s.f return r==null?$.ci():r}, dv(){var s,r=this,q=r.e=(r.e|8)>>>0 if((q&128)!==0){s=r.r if(s.a===1)s.a=3}if((q&64)===0)r.r=null r.f=r.cB()}, bp(a){var s=this.e if((s&8)!==0)return if(s<64)this.b3(a) else this.bo(new A.dq(a))}, bn(a,b){var s=this.e if((s&8)!==0)return if(s<64)this.b5(a,b) else this.bo(new A.eQ(a,b))}, cv(){var s=this,r=s.e if((r&8)!==0)return r=(r|2)>>>0 s.e=r if(r<64)s.b4() else s.bo(B.y)}, al(){}, am(){}, cB(){return null}, bo(a){var s,r=this,q=r.r if(q==null)q=r.r=new A.f5() q.v(0,a) s=r.e if((s&128)===0){s=(s|128)>>>0 r.e=s if(s<256)q.cp(r)}}, b3(a){var s=this,r=s.e s.e=(r|64)>>>0 s.d.ci(s.a,a,A.t(s).h("af.T")) s.e=(s.e&4294967231)>>>0 s.dw((r&4)!==0)}, b5(a,b){var s,r=this,q=r.e,p=new A.m2(r,a,b) if((q&1)!==0){r.e=(q|16)>>>0 r.dv() s=r.f if(s!=null&&s!==$.ci())s.ai(p) else p.$0()}else{p.$0() r.dw((q&4)!==0)}}, b4(){var s,r=this,q=new A.m1(r) r.dv() r.e=(r.e|16)>>>0 s=r.f if(s!=null&&s!==$.ci())s.ai(q) else q.$0()}, dL(a){var s=this,r=s.e s.e=(r|64)>>>0 a.$0() s.e=(s.e&4294967231)>>>0 s.dw((r&4)!==0)}, dw(a){var s,r,q=this,p=q.e if((p&128)!==0&&q.r.c==null){p=q.e=(p&4294967167)>>>0 s=!1 if((p&4)!==0)if(p<256){s=q.r s=s==null?null:s.c==null s=s!==!1}if(s){p=(p&4294967291)>>>0 q.e=p}}for(;!0;a=r){if((p&8)!==0){q.r=null return}r=(p&4)!==0 if(a===r)break q.e=(p^64)>>>0 if(r)q.al() else q.am() p=(q.e&4294967231)>>>0 q.e=p}if((p&128)!==0&&p<256)q.r.cp(q)}} A.m2.prototype={ $0(){var s,r,q,p=this.a,o=p.e if((o&8)!==0&&(o&16)===0)return p.e=(o|64)>>>0 s=p.b o=this.b r=t.K q=p.d if(t.da.b(s))q.hn(s,o,this.c,r,t.l) else q.ci(s,o,r) p.e=(p.e&4294967231)>>>0}, $S:0} A.m1.prototype={ $0(){var s=this.a,r=s.e if((r&16)===0)return s.e=(r|74)>>>0 s.d.cg(s.c) s.e=(s.e&4294967231)>>>0}, $S:0} A.dF.prototype={ R(a,b,c,d){return this.a.fK(a,d,c,b===!0)}, aX(a,b,c){return this.R(a,null,b,c)}, kf(a){return this.R(a,null,null,null)}, eB(a,b){return this.R(a,null,b,null)}} A.ia.prototype={ gc8(){return this.a}, sc8(a){return this.a=a}} A.dq.prototype={ eH(a){a.b3(this.b)}} A.eQ.prototype={ eH(a){a.b5(this.b,this.c)}} A.mc.prototype={ eH(a){a.b4()}, gc8(){return null}, sc8(a){throw A.a(A.B("No events after a done."))}} A.f5.prototype={ cp(a){var s=this,r=s.a if(r===1)return if(r>=1){s.a=1 return}A.oh(new A.nf(s,a)) s.a=1}, v(a,b){var s=this,r=s.c if(r==null)s.b=s.c=b else{r.sc8(b) s.c=b}}} A.nf.prototype={ $0(){var s,r,q=this.a,p=q.a q.a=0 if(p===3)return s=q.b r=s.gc8() q.b=r if(r==null)q.c=null s.eH(this.b)}, $S:0} A.eR.prototype={ c9(a){}, eF(a){}, bB(){var s=this.a if(s>=0)this.a=s+2}, bf(){var s=this,r=s.a-2 if(r<0)return if(r===0){s.a=1 A.oh(s.gfs())}else s.a=r}, J(){this.a=-1 this.c=null return $.ci()}, iI(){var s,r=this,q=r.a-1 if(q===0){r.a=-1 s=r.c if(s!=null){r.c=null r.b.cg(s)}}else r.a=q}} A.dG.prototype={ gm(){if(this.c)return this.b return null}, k(){var s,r=this,q=r.a if(q!=null){if(r.c){s=new A.k($.i,t.k) r.b=s r.c=!1 q.bf() return s}throw A.a(A.B("Already waiting for next."))}return r.it()}, it(){var s,r,q=this,p=q.b if(p!=null){s=new A.k($.i,t.k) q.b=s r=p.R(q.giC(),!0,q.giE(),q.giG()) if(q.b!=null)q.a=r return s}return $.rS()}, J(){var s=this,r=s.a,q=s.b s.b=null if(r!=null){s.a=null if(!s.c)q.b1(!1) else s.c=!1 return r.J()}return $.ci()}, iD(a){var s,r,q=this if(q.a==null)return s=q.b q.b=a q.c=!0 s.b2(!0) if(q.c){r=q.a if(r!=null)r.bB()}}, iH(a,b){var s=this,r=s.a,q=s.b s.b=s.a=null if(r!=null)q.W(a,b) else q.aE(a,b)}, iF(){var s=this,r=s.a,q=s.b s.b=s.a=null if(r!=null)q.bq(!1) else q.f4(!1)}} A.nL.prototype={ $0(){return this.a.W(this.b,this.c)}, $S:0} A.nK.prototype={ $2(a,b){A.vK(this.a,this.b,a,b)}, $S:6} A.nM.prototype={ $0(){return this.a.b2(this.b)}, $S:0} A.eW.prototype={ R(a,b,c,d){var s=this.$ti,r=$.i,q=b===!0?1:0,p=d!=null?32:0,o=A.i5(r,a,s.y[1]),n=A.i6(r,d) s=new A.ds(this,o,n,r.au(c,t.H),r,q|p,s.h("ds<1,2>")) s.x=this.a.aX(s.gdM(),s.gdO(),s.gdQ()) return s}, aX(a,b,c){return this.R(a,null,b,c)}} A.ds.prototype={ bp(a){if((this.e&2)!==0)return this.dm(a)}, bn(a,b){if((this.e&2)!==0)return this.bm(a,b)}, al(){var s=this.x if(s!=null)s.bB()}, am(){var s=this.x if(s!=null)s.bf()}, cB(){var s=this.x if(s!=null){this.x=null return s.J()}return null}, dN(a){this.w.im(a,this)}, dR(a,b){this.bn(a,b)}, dP(){this.cv()}} A.f0.prototype={ im(a,b){var s,r,q,p,o,n,m=null try{m=this.b.$1(a)}catch(q){s=A.E(q) r=A.R(q) p=s o=r n=$.i.aL(p,o) if(n!=null){p=n.a o=n.b}b.bn(p,o) return}b.bp(m)}} A.eT.prototype={ v(a,b){var s=this.a if((s.e&2)!==0)A.y(A.B("Stream is already closed")) s.dm(b)}, a3(a,b){var s=this.a if((s.e&2)!==0)A.y(A.B("Stream is already closed")) s.bm(a,b)}, p(){var s=this.a if((s.e&2)!==0)A.y(A.B("Stream is already closed")) s.eW()}, $ia9:1} A.dD.prototype={ al(){var s=this.x if(s!=null)s.bB()}, am(){var s=this.x if(s!=null)s.bf()}, cB(){var s=this.x if(s!=null){this.x=null return s.J()}return null}, dN(a){var s,r,q,p try{q=this.w q===$&&A.G() q.v(0,a)}catch(p){s=A.E(p) r=A.R(p) if((this.e&2)!==0)A.y(A.B("Stream is already closed")) this.bm(s,r)}}, dR(a,b){var s,r,q,p,o=this,n="Stream is already closed" try{q=o.w q===$&&A.G() q.a3(a,b)}catch(p){s=A.E(p) r=A.R(p) if(s===a){if((o.e&2)!==0)A.y(A.B(n)) o.bm(a,b)}else{if((o.e&2)!==0)A.y(A.B(n)) o.bm(s,r)}}}, dP(){var s,r,q,p,o=this try{o.x=null q=o.w q===$&&A.G() q.p()}catch(p){s=A.E(p) r=A.R(p) if((o.e&2)!==0)A.y(A.B("Stream is already closed")) o.bm(s,r)}}} A.fc.prototype={ ee(a){return new A.eL(this.a,a,this.$ti.h("eL<1,2>"))}} A.eL.prototype={ R(a,b,c,d){var s=this.$ti,r=$.i,q=b===!0?1:0,p=d!=null?32:0,o=A.i5(r,a,s.y[1]),n=A.i6(r,d),m=new A.dD(o,n,r.au(c,t.H),r,q|p,s.h("dD<1,2>")) m.w=this.a.$1(new A.eT(m)) m.x=this.b.aX(m.gdM(),m.gdO(),m.gdQ()) return m}, aX(a,b,c){return this.R(a,null,b,c)}} A.dv.prototype={ v(a,b){var s,r=this.d if(r==null)throw A.a(A.B("Sink is closed")) this.$ti.y[1].a(b) s=r.a if((s.e&2)!==0)A.y(A.B("Stream is already closed")) s.dm(b)}, a3(a,b){var s A.aD(a,"error",t.K) s=this.d if(s==null)throw A.a(A.B("Sink is closed")) s.a3(a,b)}, p(){var s=this.d if(s==null)return this.d=null this.c.$1(s)}, $ia9:1} A.dE.prototype={ ee(a){return this.hJ(a)}} A.nr.prototype={ $1(a){var s=this return new A.dv(s.a,s.b,s.c,a,s.e.h("@<0>").H(s.d).h("dv<1,2>"))}, $S(){return this.e.h("@<0>").H(this.d).h("dv<1,2>(a9<2>)")}} A.au.prototype={} A.iI.prototype={$ioO:1} A.dM.prototype={$iZ:1} A.iH.prototype={ bP(a,b,c){var s,r,q,p,o,n,m,l,k=this.gdS(),j=k.a if(j===B.d){A.fq(b,c) return}s=k.b r=j.ga1() m=j.ghe() m.toString q=m p=$.i try{$.i=q s.$5(j,r,a,b,c) $.i=p}catch(l){o=A.E(l) n=A.R(l) $.i=p m=b===o?c:n q.bP(j,o,m)}}, $ix:1} A.i7.prototype={ gf3(){var s=this.at return s==null?this.at=new A.dM(this):s}, ga1(){return this.ax.gf3()}, gbb(){return this.as.a}, cg(a){var s,r,q try{this.bg(a,t.H)}catch(q){s=A.E(q) r=A.R(q) this.bP(this,s,r)}}, ci(a,b,c){var s,r,q try{this.bh(a,b,t.H,c)}catch(q){s=A.E(q) r=A.R(q) this.bP(this,s,r)}}, hn(a,b,c,d,e){var s,r,q try{this.eK(a,b,c,t.H,d,e)}catch(q){s=A.E(q) r=A.R(q) this.bP(this,s,r)}}, ef(a,b){return new A.m9(this,this.au(a,b),b)}, fU(a,b,c){return new A.mb(this,this.be(a,b,c),c,b)}, cQ(a){return new A.m8(this,this.au(a,t.H))}, eg(a,b){return new A.ma(this,this.be(a,t.H,b),b)}, i(a,b){var s,r=this.ay,q=r.i(0,b) if(q!=null||r.a4(b))return q s=this.ax.i(0,b) if(s!=null)r.q(0,b,s) return s}, c4(a,b){this.bP(this,a,b)}, h4(a,b){var s=this.Q,r=s.a return s.b.$5(r,r.ga1(),this,a,b)}, bg(a){var s=this.a,r=s.a return s.b.$4(r,r.ga1(),this,a)}, bh(a,b){var s=this.b,r=s.a return s.b.$5(r,r.ga1(),this,a,b)}, eK(a,b,c){var s=this.c,r=s.a return s.b.$6(r,r.ga1(),this,a,b,c)}, au(a){var s=this.d,r=s.a return s.b.$4(r,r.ga1(),this,a)}, be(a){var s=this.e,r=s.a return s.b.$4(r,r.ga1(),this,a)}, d5(a){var s=this.f,r=s.a return s.b.$4(r,r.ga1(),this,a)}, aL(a,b){var s,r A.aD(a,"error",t.K) s=this.r r=s.a if(r===B.d)return null return s.b.$5(r,r.ga1(),this,a,b)}, b_(a){var s=this.w,r=s.a return s.b.$4(r,r.ga1(),this,a)}, ei(a,b){var s=this.x,r=s.a return s.b.$5(r,r.ga1(),this,a,b)}, hf(a){var s=this.z,r=s.a return s.b.$4(r,r.ga1(),this,a)}, gfE(){return this.a}, gfG(){return this.b}, gfF(){return this.c}, gfA(){return this.d}, gfB(){return this.e}, gfz(){return this.f}, gfd(){return this.r}, ge1(){return this.w}, gfa(){return this.x}, gf9(){return this.y}, gft(){return this.z}, gfg(){return this.Q}, gdS(){return this.as}, ghe(){return this.ax}, gfm(){return this.ay}} A.m9.prototype={ $0(){return this.a.bg(this.b,this.c)}, $S(){return this.c.h("0()")}} A.mb.prototype={ $1(a){var s=this return s.a.bh(s.b,a,s.d,s.c)}, $S(){return this.d.h("@<0>").H(this.c).h("1(2)")}} A.m8.prototype={ $0(){return this.a.cg(this.b)}, $S:0} A.ma.prototype={ $1(a){return this.a.ci(this.b,a,this.c)}, $S(){return this.c.h("~(0)")}} A.nS.prototype={ $0(){A.pM(this.a,this.b)}, $S:0} A.iw.prototype={ gfE(){return B.bC}, gfG(){return B.bE}, gfF(){return B.bD}, gfA(){return B.bB}, gfB(){return B.bw}, gfz(){return B.bH}, gfd(){return B.by}, ge1(){return B.bF}, gfa(){return B.bx}, gf9(){return B.bG}, gft(){return B.bA}, gfg(){return B.bz}, gdS(){return B.bv}, ghe(){return null}, gfm(){return $.t8()}, gf3(){var s=$.ni return s==null?$.ni=new A.dM(this):s}, ga1(){var s=$.ni return s==null?$.ni=new A.dM(this):s}, gbb(){return this}, cg(a){var s,r,q try{if(B.d===$.i){a.$0() return}A.nT(null,null,this,a)}catch(q){s=A.E(q) r=A.R(q) A.fq(s,r)}}, ci(a,b){var s,r,q try{if(B.d===$.i){a.$1(b) return}A.nV(null,null,this,a,b)}catch(q){s=A.E(q) r=A.R(q) A.fq(s,r)}}, hn(a,b,c){var s,r,q try{if(B.d===$.i){a.$2(b,c) return}A.nU(null,null,this,a,b,c)}catch(q){s=A.E(q) r=A.R(q) A.fq(s,r)}}, ef(a,b){return new A.nk(this,a,b)}, fU(a,b,c){return new A.nm(this,a,c,b)}, cQ(a){return new A.nj(this,a)}, eg(a,b){return new A.nl(this,a,b)}, i(a,b){return null}, c4(a,b){A.fq(a,b)}, h4(a,b){return A.rk(null,null,this,a,b)}, bg(a){if($.i===B.d)return a.$0() return A.nT(null,null,this,a)}, bh(a,b){if($.i===B.d)return a.$1(b) return A.nV(null,null,this,a,b)}, eK(a,b,c){if($.i===B.d)return a.$2(b,c) return A.nU(null,null,this,a,b,c)}, au(a){return a}, be(a){return a}, d5(a){return a}, aL(a,b){return null}, b_(a){A.nW(null,null,this,a)}, ei(a,b){return A.oK(a,b)}, hf(a){A.pn(a)}} A.nk.prototype={ $0(){return this.a.bg(this.b,this.c)}, $S(){return this.c.h("0()")}} A.nm.prototype={ $1(a){var s=this return s.a.bh(s.b,a,s.d,s.c)}, $S(){return this.d.h("@<0>").H(this.c).h("1(2)")}} A.nj.prototype={ $0(){return this.a.cg(this.b)}, $S:0} A.nl.prototype={ $1(a){return this.a.ci(this.b,a,this.c)}, $S(){return this.c.h("~(0)")}} A.cE.prototype={ gl(a){return this.a}, gF(a){return this.a===0}, ga_(){return new A.cF(this,A.t(this).h("cF<1>"))}, gaP(){var s=A.t(this) return A.en(new A.cF(this,s.h("cF<1>")),new A.mv(this),s.c,s.y[1])}, a4(a){var s,r if(typeof a=="string"&&a!=="__proto__"){s=this.b return s==null?!1:s[a]!=null}else if(typeof a=="number"&&(a&1073741823)===a){r=this.c return r==null?!1:r[a]!=null}else return this.i4(a)}, i4(a){var s=this.d if(s==null)return!1 return this.aR(this.fh(s,a),a)>=0}, i(a,b){var s,r,q if(typeof b=="string"&&b!=="__proto__"){s=this.b r=s==null?null:A.qH(s,b) return r}else if(typeof b=="number"&&(b&1073741823)===b){q=this.c r=q==null?null:A.qH(q,b) return r}else return this.ik(b)}, ik(a){var s,r,q=this.d if(q==null)return null s=this.fh(q,a) r=this.aR(s,a) return r<0?null:s[r+1]}, q(a,b,c){var s,r,q=this if(typeof b=="string"&&b!=="__proto__"){s=q.b q.f1(s==null?q.b=A.oW():s,b,c)}else if(typeof b=="number"&&(b&1073741823)===b){r=q.c q.f1(r==null?q.c=A.oW():r,b,c)}else q.j7(b,c)}, j7(a,b){var s,r,q,p=this,o=p.d if(o==null)o=p.d=A.oW() s=p.dC(a) r=o[s] if(r==null){A.oX(o,s,[a,b]);++p.a p.e=null}else{q=p.aR(r,a) if(q>=0)r[q+1]=b else{r.push(a,b);++p.a p.e=null}}}, aa(a,b){var s,r,q,p,o,n=this,m=n.f8() for(s=m.length,r=A.t(n).y[1],q=0;q"))}} A.ih.prototype={ gm(){var s=this.d return s==null?this.$ti.c.a(s):s}, k(){var s=this,r=s.b,q=s.c,p=s.a if(r!==p.e)throw A.a(A.ax(p)) else if(q>=r.length){s.d=null return!1}else{s.d=r[q] s.c=q+1 return!0}}} A.eZ.prototype={ gt(a){var s=this,r=new A.dy(s,s.r,s.$ti.h("dy<1>")) r.c=s.e return r}, gl(a){return this.a}, gF(a){return this.a===0}, M(a,b){var s,r if(b!=="__proto__"){s=this.b if(s==null)return!1 return s[b]!=null}else{r=this.i3(b) return r}}, i3(a){var s=this.d if(s==null)return!1 return this.aR(s[B.a.gB(a)&1073741823],a)>=0}, gG(a){var s=this.e if(s==null)throw A.a(A.B("No elements")) return s.a}, gC(a){var s=this.f if(s==null)throw A.a(A.B("No elements")) return s.a}, v(a,b){var s,r,q=this if(typeof b=="string"&&b!=="__proto__"){s=q.b return q.f0(s==null?q.b=A.oY():s,b)}else if(typeof b=="number"&&(b&1073741823)===b){r=q.c return q.f0(r==null?q.c=A.oY():r,b)}else return q.hT(b)}, hT(a){var s,r,q=this,p=q.d if(p==null)p=q.d=A.oY() s=J.aw(a)&1073741823 r=p[s] if(r==null)p[s]=[q.dX(a)] else{if(q.aR(r,a)>=0)return!1 r.push(q.dX(a))}return!0}, A(a,b){var s if(typeof b=="string"&&b!=="__proto__")return this.iV(this.b,b) else{s=this.iU(b) return s}}, iU(a){var s,r,q,p,o=this.d if(o==null)return!1 s=J.aw(a)&1073741823 r=o[s] q=this.aR(r,a) if(q<0)return!1 p=r.splice(q,1)[0] if(0===r.length)delete o[s] this.fR(p) return!0}, f0(a,b){if(a[b]!=null)return!1 a[b]=this.dX(b) return!0}, iV(a,b){var s if(a==null)return!1 s=a[b] if(s==null)return!1 this.fR(s) delete a[b] return!0}, fo(){this.r=this.r+1&1073741823}, dX(a){var s,r=this,q=new A.ne(a) if(r.e==null)r.e=r.f=q else{s=r.f s.toString q.c=s r.f=s.b=q}++r.a r.fo() return q}, fR(a){var s=this,r=a.c,q=a.b if(r==null)s.e=q else r.b=q if(q==null)s.f=r else q.c=r;--s.a s.fo()}, aR(a,b){var s,r if(a==null)return-1 s=a.length for(r=0;r"))}, gl(a){return this.b}, gG(a){var s if(this.b===0)throw A.a(A.B("No such element")) s=this.c s.toString return s}, gC(a){var s if(this.b===0)throw A.a(A.B("No such element")) s=this.c.c s.toString return s}, gF(a){return this.b===0}, dT(a,b,c){var s,r,q=this if(b.a!=null)throw A.a(A.B("LinkedListEntry is already in a LinkedList"));++q.a b.a=q s=q.b if(s===0){b.b=b q.c=b.c=b q.b=s+1 return}r=a.c r.toString b.c=r b.b=a a.c=r.b=b q.b=s+1}, e4(a){var s,r,q=this;++q.a s=a.b s.c=a.c a.c.b=s r=--q.b a.a=a.b=a.c=null if(r===0)q.c=null else if(a===q.c)q.c=s}} A.io.prototype={ gm(){var s=this.c return s==null?this.$ti.c.a(s):s}, k(){var s=this,r=s.a if(s.b!==r.a)throw A.a(A.ax(s)) if(r.b!==0)r=s.e&&s.d===r.gG(0) else r=!0 if(r){s.c=null return!1}s.e=!0 r=s.d s.c=r s.d=r.b return!0}} A.aF.prototype={ gcc(){var s=this.a if(s==null||this===s.gG(0))return null return this.c}} A.z.prototype={ gt(a){return new A.aZ(a,this.gl(a),A.aE(a).h("aZ"))}, N(a,b){return this.i(a,b)}, gF(a){return this.gl(a)===0}, gG(a){if(this.gl(a)===0)throw A.a(A.ak()) return this.i(a,0)}, gC(a){if(this.gl(a)===0)throw A.a(A.ak()) return this.i(a,this.gl(a)-1)}, bd(a,b,c){return new A.D(a,b,A.aE(a).h("@").H(c).h("D<1,2>"))}, X(a,b){return A.b3(a,b,null,A.aE(a).h("z.E"))}, ah(a,b){return A.b3(a,0,A.aD(b,"count",t.S),A.aE(a).h("z.E"))}, az(a,b){var s,r,q,p,o=this if(o.gF(a)){s=J.pW(0,A.aE(a).h("z.E")) return s}r=o.i(a,0) q=A.b_(o.gl(a),r,!0,A.aE(a).h("z.E")) for(p=1;p").H(b).h("ah<1,2>"))}, a0(a,b,c){var s=this.gl(a) A.b9(b,c,s) return A.pZ(this.co(a,b,c),!0,A.aE(a).h("z.E"))}, co(a,b,c){A.b9(b,c,this.gl(a)) return A.b3(a,b,c,A.aE(a).h("z.E"))}, en(a,b,c,d){var s A.b9(b,c,this.gl(a)) for(s=b;s").b(d)){r=e q=d}else{q=J.dZ(d,e).az(0,!1) r=0}p=J.V(q) if(r+s>p.gl(q))throw A.a(A.pT()) if(r=0;--o)this.q(a,b+o,p.i(q,r+o)) else for(o=0;o"))}, gl(a){return J.ae(this.ga_())}, gF(a){return J.iR(this.ga_())}, gaP(){return new A.f_(this,A.t(this).h("f_"))}, j(a){return A.oD(this)}, $iaa:1} A.ki.prototype={ $1(a){var s=this.a,r=s.i(0,a) if(r==null)r=A.t(s).h("T.V").a(r) return new A.bv(a,r,A.t(s).h("bv"))}, $S(){return A.t(this.a).h("bv(T.K)")}} A.kj.prototype={ $2(a,b){var s,r=this.a if(!r.a)this.b.a+=", " r.a=!1 r=this.b s=A.u(a) s=r.a+=s r.a=s+": " s=A.u(b) r.a+=s}, $S:53} A.f_.prototype={ gl(a){var s=this.a return s.gl(s)}, gF(a){var s=this.a return s.gF(s)}, gG(a){var s=this.a s=s.i(0,J.fy(s.ga_())) return s==null?this.$ti.y[1].a(s):s}, gC(a){var s=this.a s=s.i(0,J.iS(s.ga_())) return s==null?this.$ti.y[1].a(s):s}, gt(a){var s=this.a return new A.ip(J.M(s.ga_()),s,this.$ti.h("ip<1,2>"))}} A.ip.prototype={ k(){var s=this,r=s.a if(r.k()){s.c=s.b.i(0,r.gm()) return!0}s.c=null return!1}, gm(){var s=this.c return s==null?this.$ti.y[1].a(s):s}} A.dd.prototype={ gF(a){return this.a===0}, bd(a,b,c){return new A.cp(this,b,this.$ti.h("@<1>").H(c).h("cp<1,2>"))}, j(a){return A.ox(this,"{","}")}, ah(a,b){return A.oJ(this,b,this.$ti.c)}, X(a,b){return A.qf(this,b,this.$ti.c)}, gG(a){var s,r=A.im(this,this.r,this.$ti.c) if(!r.k())throw A.a(A.ak()) s=r.d return s==null?r.$ti.c.a(s):s}, gC(a){var s,r,q=A.im(this,this.r,this.$ti.c) if(!q.k())throw A.a(A.ak()) s=q.$ti.c do{r=q.d if(r==null)r=s.a(r)}while(q.k()) return r}, N(a,b){var s,r,q,p=this A.ab(b,"index") s=A.im(p,p.r,p.$ti.c) for(r=b;s.k();){if(r===0){q=s.d return q==null?s.$ti.c.a(q):q}--r}throw A.a(A.h4(b,b-r,p,null,"index"))}, $iv:1, $if:1} A.f8.prototype={} A.nE.prototype={ $0(){var s,r try{s=new TextDecoder("utf-8",{fatal:true}) return s}catch(r){}return null}, $S:28} A.nD.prototype={ $0(){var s,r try{s=new TextDecoder("utf-8",{fatal:false}) return s}catch(r){}return null}, $S:28} A.fA.prototype={ jO(a){return B.ao.a5(a)}} A.iF.prototype={ a5(a){var s,r,q,p=A.b9(0,null,a.length),o=new Uint8Array(p) for(s=~this.a,r=0;r=0){g="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charCodeAt(f) if(g===k)continue k=g}else{if(f===-1){if(o<0){e=p==null?null:p.a.length if(e==null)e=0 o=e+(r-q) n=r}++m if(k===61)continue}k=g}if(f!==-2){if(p==null){p=new A.av("") e=p}else e=p e.a+=B.a.n(a0,q,r) d=A.aA(k) e.a+=d q=l continue}}throw A.a(A.ai("Invalid base64 data",a0,r))}if(p!=null){e=B.a.n(a0,q,a2) e=p.a+=e d=e.length if(o>=0)A.pA(a0,n,a2,o,m,d) else{c=B.b.aA(d-1,4)+1 if(c===1)throw A.a(A.ai(a,a0,a2)) for(;c<4;){e+="=" p.a=e;++c}}e=p.a return B.a.aO(a0,a1,a2,e.charCodeAt(0)==0?e:e)}b=a2-a1 if(o>=0)A.pA(a0,n,a2,o,m,b) else{c=B.b.aA(b,4) if(c===1)throw A.a(A.ai(a,a0,a2)) if(c>1)a0=B.a.aO(a0,a2,a2,c===2?"==":"=")}return a0}} A.fG.prototype={} A.cm.prototype={} A.cn.prototype={} A.fW.prototype={} A.hO.prototype={ cT(a){return new A.fm(!1).dD(a,0,null,!0)}} A.hP.prototype={ a5(a){var s,r,q=A.b9(0,null,a.length) if(q===0)return new Uint8Array(0) s=new Uint8Array(q*3) r=new A.nF(s) if(r.ij(a,0,q)!==q)r.e9() return B.e.a0(s,0,r.b)}} A.nF.prototype={ e9(){var s=this,r=s.c,q=s.b,p=s.b=q+1 r[q]=239 q=s.b=p+1 r[p]=191 s.b=q+1 r[q]=189}, jm(a,b){var s,r,q,p,o=this if((b&64512)===56320){s=65536+((a&1023)<<10)|b&1023 r=o.c q=o.b p=o.b=q+1 r[q]=s>>>18|240 q=o.b=p+1 r[p]=s>>>12&63|128 p=o.b=q+1 r[q]=s>>>6&63|128 o.b=p+1 r[p]=s&63|128 return!0}else{o.e9() return!1}}, ij(a,b,c){var s,r,q,p,o,n,m,l=this if(b!==c&&(a.charCodeAt(c-1)&64512)===55296)--c for(s=l.c,r=s.length,q=b;q=r)break l.b=o+1 s[o]=p}else{o=p&64512 if(o===55296){if(l.b+4>r)break n=q+1 if(l.jm(p,a.charCodeAt(n)))q=n}else if(o===56320){if(l.b+3>r)break l.e9()}else if(p<=2047){o=l.b m=o+1 if(m>=r)break l.b=m s[o]=p>>>6|192 l.b=m+1 s[m]=p&63|128}else{o=l.b if(o+2>=r)break m=l.b=o+1 s[o]=p>>>12|224 o=l.b=m+1 s[m]=p>>>6&63|128 l.b=o+1 s[o]=p&63|128}}}return q}} A.fm.prototype={ dD(a,b,c,d){var s,r,q,p,o,n,m=this,l=A.b9(b,c,J.ae(a)) if(b===l)return"" if(a instanceof Uint8Array){s=a r=s q=0}else{r=A.vz(a,b,l) l-=b q=b b=0}if(d&&l-b>=15){p=m.a o=A.vy(p,r,b,l) if(o!=null){if(!p)return o if(o.indexOf("\ufffd")<0)return o}}o=m.dF(r,b,l,d) p=m.b if((p&1)!==0){n=A.vA(p) m.b=0 throw A.a(A.ai(n,a,q+m.c))}return o}, dF(a,b,c,d){var s,r,q=this if(c-b>1000){s=B.b.I(b+c,2) r=q.dF(a,b,s,!1) if((q.b&1)!==0)return r return r+q.dF(a,s,c,d)}return q.jK(a,b,c,d)}, jK(a,b,c,d){var s,r,q,p,o,n,m,l=this,k=65533,j=l.b,i=l.c,h=new A.av(""),g=b+1,f=a[b] $label0$0:for(s=l.a;!0;){for(;!0;g=p){r="AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFFFFFFFFFFFFFFFFGGGGGGGGGGGGGGGGHHHHHHHHHHHHHHHHHHHHHHHHHHHIHHHJEEBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBKCCCCCCCCCCCCDCLONNNMEEEEEEEEEEE".charCodeAt(f)&31 i=j<=32?f&61694>>>r:(f&63|i<<6)>>>0 j=" \x000:XECCCCCN:lDb \x000:XECCCCCNvlDb \x000:XECCCCCN:lDb AAAAA\x00\x00\x00\x00\x00AAAAA00000AAAAA:::::AAAAAGG000AAAAA00KKKAAAAAG::::AAAAA:IIIIAAAAA000\x800AAAAA\x00\x00\x00\x00 AAAAA".charCodeAt(j+r) if(j===0){q=A.aA(i) h.a+=q if(g===c)break $label0$0 break}else if((j&1)!==0){if(s)switch(j){case 69:case 67:q=A.aA(k) h.a+=q break case 65:q=A.aA(k) h.a+=q;--g break default:q=A.aA(k) q=h.a+=q h.a=q+A.aA(k) break}else{l.b=j l.c=g-1 return""}j=0}if(g===c)break $label0$0 p=g+1 f=a[g]}p=g+1 f=a[g] if(f<128){while(!0){if(!(p=128){o=n-1 p=n break}p=n}if(o-g<20)for(m=g;m32)if(s){s=A.aA(k) h.a+=s}else{l.b=77 l.c=c return""}l.b=j l.c=i s=h.a return s.charCodeAt(0)==0?s:s}} A.a6.prototype={ aB(a){var s,r,q=this,p=q.c if(p===0)return q s=!q.a r=q.b p=A.aJ(p,r) return new A.a6(p===0?!1:s,r,p)}, ib(a){var s,r,q,p,o,n,m=this.c if(m===0)return $.b7() s=m+a r=this.b q=new Uint16Array(s) for(p=m-1;p>=0;--p)q[p+a]=r[p] o=this.a n=A.aJ(s,q) return new A.a6(n===0?!1:o,q,n)}, ic(a){var s,r,q,p,o,n,m,l=this,k=l.c if(k===0)return $.b7() s=k-a if(s<=0)return l.a?$.pv():$.b7() r=l.b q=new Uint16Array(s) for(p=a;p>>0!==0)return l.dl(0,$.fv()) for(k=0;k=0)return q.cs(b,r) return b.cs(q,!r)}, dl(a,b){var s,r,q=this,p=q.c if(p===0)return b.aB(0) s=b.c if(s===0)return q r=q.a if(r!==b.a)return q.dr(b,r) if(A.lZ(q.b,p,b.b,s)>=0)return q.cs(b,r) return b.cs(q,!r)}, bI(a,b){var s,r,q,p,o,n,m,l=this.c,k=b.c if(l===0||k===0)return $.b7() s=l+k r=this.b q=b.b p=new Uint16Array(s) for(o=0;o0?p.aB(0):p}, iT(a){var s,r,q,p=this if(p.c0)q=q.bl(0,$.oR.af()) return p.a&&q.c>0?q.aB(0):q}, fb(a){var s,r,q,p,o,n,m,l,k,j,i,h,g,f,e,d=this,c=d.c if(c===$.qA&&a.c===$.qC&&d.b===$.qz&&a.b===$.qB)return s=a.b r=a.c q=16-B.b.gfV(s[r-1]) if(q>0){p=new Uint16Array(r+5) o=A.qy(s,r,q,p) n=new Uint16Array(c+5) m=A.qy(d.b,c,q,n)}else{n=A.oS(d.b,0,c,c+2) o=r p=s m=c}l=p[o-1] k=m-o j=new Uint16Array(m) i=A.oT(p,o,k,j) h=m+1 if(A.lZ(n,m,j,i)>=0){n[m]=1 A.i4(n,h,j,i,n)}else n[m]=0 g=new Uint16Array(o+2) g[o]=1 A.i4(g,o+1,p,o,g) f=m-1 for(;k>0;){e=A.uW(l,n,f);--k A.qE(e,g,0,n,k,o) if(n[f]1;){q=$.pu() if(q.c===0)A.y(B.as) p=r.iT(q).j(0) s.push(p) o=p.length if(o===1)s.push("000") if(o===2)s.push("00") if(o===3)s.push("0") r=r.ia(q)}s.push(B.b.j(r.b[0])) if(m)s.push("-") return new A.ew(s,t.bJ).c5(0)}} A.m_.prototype={ $2(a,b){a=a+b&536870911 a=a+((a&524287)<<10)&536870911 return a^a>>>6}, $S:4} A.m0.prototype={ $1(a){a=a+((a&67108863)<<3)&536870911 a^=a>>>11 return a+((a&16383)<<15)&536870911}, $S:12} A.ie.prototype={ h_(a){var s=this.a if(s!=null)s.unregister(a)}} A.fN.prototype={ O(a,b){if(b==null)return!1 return b instanceof A.fN&&this.a===b.a&&this.b===b.b&&this.c===b.c}, gB(a){return A.es(this.a,this.b,B.f,B.f)}, ag(a,b){var s=B.b.ag(this.a,b.a) if(s!==0)return s return B.b.ag(this.b,b.b)}, j(a){var s=this,r=A.tV(A.ux(s)),q=A.fO(A.uv(s)),p=A.fO(A.ur(s)),o=A.fO(A.us(s)),n=A.fO(A.uu(s)),m=A.fO(A.uw(s)),l=A.pI(A.ut(s)),k=s.b,j=k===0?"":A.pI(k) k=r+"-"+q if(s.c)return k+"-"+p+" "+o+":"+n+":"+m+"."+l+j+"Z" else return k+"-"+p+" "+o+":"+n+":"+m+"."+l+j}} A.bp.prototype={ O(a,b){if(b==null)return!1 return b instanceof A.bp&&this.a===b.a}, gB(a){return B.b.gB(this.a)}, ag(a,b){return B.b.ag(this.a,b.a)}, j(a){var s,r,q,p,o,n=this.a,m=B.b.I(n,36e8),l=n%36e8 if(n<0){m=0-m n=0-l s="-"}else{n=l s=""}r=B.b.I(n,6e7) n%=6e7 q=r<10?"0":"" p=B.b.I(n,1e6) o=p<10?"0":"" return s+m+":"+q+r+":"+o+p+"."+B.a.kl(B.b.j(n%1e6),6,"0")}} A.md.prototype={ j(a){return this.ae()}} A.O.prototype={ gbJ(){return A.uq(this)}} A.fC.prototype={ j(a){var s=this.a if(s!=null)return"Assertion failed: "+A.fX(s) return"Assertion failed"}} A.bD.prototype={} A.aV.prototype={ gdJ(){return"Invalid argument"+(!this.a?"(s)":"")}, gdI(){return""}, j(a){var s=this,r=s.c,q=r==null?"":" ("+r+")",p=s.d,o=p==null?"":": "+A.u(p),n=s.gdJ()+q+o if(!s.a)return n return n+s.gdI()+": "+A.fX(s.gex())}, gex(){return this.b}} A.d7.prototype={ gex(){return this.b}, gdJ(){return"RangeError"}, gdI(){var s,r=this.e,q=this.f if(r==null)s=q!=null?": Not less than or equal to "+A.u(q):"" else if(q==null)s=": Not greater than or equal to "+A.u(r) else if(q>r)s=": Not in inclusive range "+A.u(r)+".."+A.u(q) else s=qe.length else s=!1 if(s)f=null if(f==null){if(e.length>78)e=B.a.n(e,0,75)+"..." return g+"\n"+e}for(r=1,q=0,p=!1,o=0;o1?g+(" (at line "+r+", character "+(f-q+1)+")\n"):g+(" (at character "+(f+1)+")\n") m=e.length for(o=f;o78){k="..." if(f-q<75){j=q+75 i=q}else{if(m-f<75){i=m-75 j=m k=""}else{i=f-36 j=f+36}l="..."}}else{j=m i=q k=""}return g+l+B.a.n(e,i,j)+k+"\n"+B.a.bI(" ",f-i+l.length)+"^\n"}else return f!=null?g+(" (at offset "+A.u(f)+")"):g}, $ia5:1} A.h6.prototype={ gbJ(){return null}, j(a){return"IntegerDivisionByZeroException"}, $iO:1, $ia5:1} A.f.prototype={ b8(a,b){return A.e4(this,A.t(this).h("f.E"),b)}, bd(a,b,c){return A.en(this,b,A.t(this).h("f.E"),c)}, az(a,b){return A.ay(this,b,A.t(this).h("f.E"))}, cj(a){return this.az(0,!0)}, gl(a){var s,r=this.gt(this) for(s=0;r.k();)++s return s}, gF(a){return!this.gt(this).k()}, ah(a,b){return A.oJ(this,b,A.t(this).h("f.E"))}, X(a,b){return A.qf(this,b,A.t(this).h("f.E"))}, hA(a,b){return new A.ex(this,b,A.t(this).h("ex"))}, gG(a){var s=this.gt(this) if(!s.k())throw A.a(A.ak()) return s.gm()}, gC(a){var s,r=this.gt(this) if(!r.k())throw A.a(A.ak()) do s=r.gm() while(r.k()) return s}, N(a,b){var s,r A.ab(b,"index") s=this.gt(this) for(r=b;s.k();){if(r===0)return s.gm();--r}throw A.a(A.h4(b,b-r,this,null,"index"))}, j(a){return A.ub(this,"(",")")}} A.bv.prototype={ j(a){return"MapEntry("+A.u(this.a)+": "+A.u(this.b)+")"}} A.F.prototype={ gB(a){return A.e.prototype.gB.call(this,0)}, j(a){return"null"}} A.e.prototype={$ie:1, O(a,b){return this===b}, gB(a){return A.ev(this)}, j(a){return"Instance of '"+A.kp(this)+"'"}, gV(a){return A.x5(this)}, toString(){return this.j(this)}} A.dI.prototype={ j(a){return this.a}, $ia0:1} A.av.prototype={ gl(a){return this.a.length}, j(a){var s=this.a return s.charCodeAt(0)==0?s:s}} A.lh.prototype={ $2(a,b){throw A.a(A.ai("Illegal IPv4 address, "+a,this.a,b))}, $S:75} A.li.prototype={ $2(a,b){throw A.a(A.ai("Illegal IPv6 address, "+a,this.a,b))}, $S:76} A.lj.prototype={ $2(a,b){var s if(b-a>4)this.a.$2("an IPv6 part can only contain a maximum of 4 hex digits",a) s=A.aN(B.a.n(this.b,a,b),16) if(s<0||s>65535)this.a.$2("each part must be in the range of `0x0..0xFFFF`",a) return s}, $S:4} A.fj.prototype={ gfM(){var s,r,q,p,o=this,n=o.w if(n===$){s=o.a r=s.length!==0?""+s+":":"" q=o.c p=q==null if(!p||s==="file"){s=r+"//" r=o.b if(r.length!==0)s=s+r+"@" if(!p)s+=q r=o.d if(r!=null)s=s+":"+A.u(r)}else s=r s+=o.e r=o.f if(r!=null)s=s+"?"+r r=o.r if(r!=null)s=s+"#"+r n!==$&&A.oj() n=o.w=s.charCodeAt(0)==0?s:s}return n}, gkm(){var s,r,q=this,p=q.x if(p===$){s=q.e if(s.length!==0&&s.charCodeAt(0)===47)s=B.a.K(s,1) r=s.length===0?B.r:A.aG(new A.D(A.d(s.split("/"),t.s),A.wU(),t.do),t.N) q.x!==$&&A.oj() p=q.x=r}return p}, gB(a){var s,r=this,q=r.y if(q===$){s=B.a.gB(r.gfM()) r.y!==$&&A.oj() r.y=s q=s}return q}, geO(){return this.b}, gbc(){var s=this.c if(s==null)return"" if(B.a.u(s,"["))return B.a.n(s,1,s.length-1) return s}, gcb(){var s=this.d return s==null?A.qV(this.a):s}, gcd(){var s=this.f return s==null?"":s}, gcW(){var s=this.r return s==null?"":s}, kc(a){var s=this.a if(a.length!==s.length)return!1 return A.vM(a,s,0)>=0}, hk(a){var s,r,q,p,o,n,m,l=this a=A.nC(a,0,a.length) s=a==="file" r=l.b q=l.d if(a!==l.a)q=A.nB(q,a) p=l.c if(!(p!=null))p=r.length!==0||q!=null||s?"":null o=l.e if(!s)n=p!=null&&o.length!==0 else n=!0 if(n&&!B.a.u(o,"/"))o="/"+o m=o return A.fk(a,r,p,q,m,l.f,l.r)}, gh7(){if(this.a!==""){var s=this.r s=(s==null?"":s)===""}else s=!1 return s}, fn(a,b){var s,r,q,p,o,n,m for(s=0,r=0;B.a.E(b,"../",r);){r+=3;++s}q=B.a.d0(a,"/") while(!0){if(!(q>0&&s>0))break p=B.a.h9(a,"/",q-1) if(p<0)break o=q-p n=o!==2 m=!1 if(!n||o===3)if(a.charCodeAt(p+1)===46)n=!n||a.charCodeAt(p+2)===46 else n=m else n=m if(n)break;--s q=p}return B.a.aO(a,q+1,null,B.a.K(b,r-3*s))}, hm(a){return this.ce(A.bm(a))}, ce(a){var s,r,q,p,o,n,m,l,k,j,i,h=this if(a.gY().length!==0)return a else{s=h.a if(a.geq()){r=a.hk(s) return r}else{q=h.b p=h.c o=h.d n=h.e if(a.gh5())m=a.gcX()?a.gcd():h.f else{l=A.vw(h,n) if(l>0){k=B.a.n(n,0,l) n=a.gep()?k+A.cJ(a.gac()):k+A.cJ(h.fn(B.a.K(n,k.length),a.gac()))}else if(a.gep())n=A.cJ(a.gac()) else if(n.length===0)if(p==null)n=s.length===0?a.gac():A.cJ(a.gac()) else n=A.cJ("/"+a.gac()) else{j=h.fn(n,a.gac()) r=s.length===0 if(!r||p!=null||B.a.u(n,"/"))n=A.cJ(j) else n=A.p3(j,!r||p!=null)}m=a.gcX()?a.gcd():null}}}i=a.ger()?a.gcW():null return A.fk(s,q,p,o,n,m,i)}, geq(){return this.c!=null}, gcX(){return this.f!=null}, ger(){return this.r!=null}, gh5(){return this.e.length===0}, gep(){return B.a.u(this.e,"/")}, eL(){var s,r=this,q=r.a if(q!==""&&q!=="file")throw A.a(A.H("Cannot extract a file path from a "+q+" URI")) q=r.f if((q==null?"":q)!=="")throw A.a(A.H(u.y)) q=r.r if((q==null?"":q)!=="")throw A.a(A.H(u.l)) if(r.c!=null&&r.gbc()!=="")A.y(A.H(u.j)) s=r.gkm() A.vo(s,!1) q=A.oH(B.a.u(r.e,"/")?""+"/":"",s,"/") q=q.charCodeAt(0)==0?q:q return q}, j(a){return this.gfM()}, O(a,b){var s,r,q,p=this if(b==null)return!1 if(p===b)return!0 s=!1 if(t.dD.b(b))if(p.a===b.gY())if(p.c!=null===b.geq())if(p.b===b.geO())if(p.gbc()===b.gbc())if(p.gcb()===b.gcb())if(p.e===b.gac()){r=p.f q=r==null if(!q===b.gcX()){if(q)r="" if(r===b.gcd()){r=p.r q=r==null if(!q===b.ger()){s=q?"":r s=s===b.gcW()}}}}return s}, $ihM:1, gY(){return this.a}, gac(){return this.e}} A.nA.prototype={ $1(a){return A.vx(B.aN,a,B.j,!1)}, $S:8} A.hN.prototype={ geN(){var s,r,q,p,o=this,n=null,m=o.c if(m==null){m=o.a s=o.b[0]+1 r=B.a.aW(m,"?",s) q=m.length if(r>=0){p=A.fl(m,r+1,q,B.p,!1,!1) q=r}else p=n m=o.c=new A.i9("data","",n,n,A.fl(m,s,q,B.a5,!1,!1),p,n)}return m}, j(a){var s=this.a return this.b[0]===-1?"data:"+s:s}} A.nN.prototype={ $2(a,b){var s=this.a[a] B.e.en(s,0,96,b) return s}, $S:78} A.nO.prototype={ $3(a,b,c){var s,r for(s=b.length,r=0;r>>0]=c}, $S:23} A.b4.prototype={ geq(){return this.c>0}, ges(){return this.c>0&&this.d+10&&this.r>=this.a.length}, gY(){var s=this.w return s==null?this.w=this.i2():s}, i2(){var s,r=this,q=r.b if(q<=0)return"" s=q===4 if(s&&B.a.u(r.a,"http"))return"http" if(q===5&&B.a.u(r.a,"https"))return"https" if(s&&B.a.u(r.a,"file"))return"file" if(q===7&&B.a.u(r.a,"package"))return"package" return B.a.n(r.a,0,q)}, geO(){var s=this.c,r=this.b+3 return s>r?B.a.n(this.a,r,s-1):""}, gbc(){var s=this.c return s>0?B.a.n(this.a,s,this.d):""}, gcb(){var s,r=this if(r.ges())return A.aN(B.a.n(r.a,r.d+1,r.e),null) s=r.b if(s===4&&B.a.u(r.a,"http"))return 80 if(s===5&&B.a.u(r.a,"https"))return 443 return 0}, gac(){return B.a.n(this.a,this.e,this.f)}, gcd(){var s=this.f,r=this.r return s=q.length)return s return new A.b4(B.a.n(q,0,r),s.b,s.c,s.d,s.e,s.f,r,s.w)}, hk(a){var s,r,q,p,o,n,m,l,k,j,i,h=this,g=null a=A.nC(a,0,a.length) s=!(h.b===a.length&&B.a.u(h.a,a)) r=a==="file" q=h.c p=q>0?B.a.n(h.a,h.b+3,q):"" o=h.ges()?h.gcb():g if(s)o=A.nB(o,a) q=h.c if(q>0)n=B.a.n(h.a,q,h.d) else n=p.length!==0||o!=null||r?"":g q=h.a m=h.f l=B.a.n(q,h.e,m) if(!r)k=n!=null&&l.length!==0 else k=!0 if(k&&!B.a.u(l,"/"))l="/"+l k=h.r j=m0)return b s=b.c if(s>0){r=a.b if(r<=0)return b q=r===4 if(q&&B.a.u(a.a,"file"))p=b.e!==b.f else if(q&&B.a.u(a.a,"http"))p=!b.fk("80") else p=!(r===5&&B.a.u(a.a,"https"))||!b.fk("443") if(p){o=r+1 return new A.b4(B.a.n(a.a,0,o)+B.a.K(b.a,c+1),r,s+o,b.d+o,b.e+o,b.f+o,b.r+o,a.w)}else return this.fO().ce(b)}n=b.e c=b.f if(n===c){s=b.r if(c0?l:m o=k-n return new A.b4(B.a.n(a.a,0,k)+B.a.K(s,n),a.b,a.c,a.d,m,c+o,b.r+o,a.w)}j=a.e i=a.f if(j===i&&a.c>0){for(;B.a.E(s,"../",n);)n+=3 o=j-n+1 return new A.b4(B.a.n(a.a,0,j)+"/"+B.a.K(s,n),a.b,a.c,a.d,j,c+o,b.r+o,a.w)}h=a.a l=A.qN(this) if(l>=0)g=l else for(g=j;B.a.E(h,"../",g);)g+=3 f=0 while(!0){e=n+3 if(!(e<=c&&B.a.E(s,"../",n)))break;++f n=e}for(d="";i>g;){--i if(h.charCodeAt(i)===47){if(f===0){d="/" break}--f d="/"}}if(i===g&&a.b<=0&&!B.a.E(h,"/",j)){n-=f*3 d=""}o=i-n+d.length return new A.b4(B.a.n(h,0,i)+d+B.a.K(s,n),a.b,a.c,a.d,j,c+o,b.r+o,a.w)}, eL(){var s,r=this,q=r.b if(q>=0){s=!(q===4&&B.a.u(r.a,"file")) q=s}else q=!1 if(q)throw A.a(A.H("Cannot extract a file path from a "+r.gY()+" URI")) q=r.f s=r.a if(q0?s.gbc():r,n=s.ges()?s.gcb():r,m=s.a,l=s.f,k=B.a.n(m,s.e,l),j=s.r l=l864e13)A.y(A.a4(r,-864e13,864e13,"millisecondsSinceEpoch",null)) A.aD(!0,"isUtc",t.y) return new A.fN(r,0,!0)}if(a instanceof RegExp)throw A.a(A.K("structured clone of RegExp",null)) if(typeof Promise!="undefined"&&a instanceof Promise)return A.a_(a,t.X) q=Object.getPrototypeOf(a) if(q===Object.prototype||q===null){p=t.X o=A.a3(p,p) s.q(0,a,o) n=Object.keys(a) m=[] for(s=J.aM(n),p=s.gt(n);p.k();)m.push(A.ry(p.gm())) for(l=0;l4294967296)throw A.a(new A.d7(j,j,!1,j,j,"max must be in range 0 < max \u2264 2^32, was "+a)) if(a>255)if(a>65535)s=a>16777215?4:3 else s=2 else s=1 r=this.a r.setUint32(0,0,!1) q=4-s p=A.h(Math.pow(256,s)) for(o=a-1,n=(a&o)===0;!0;){m=r.buffer m=new Uint8Array(m,q,s) crypto.getRandomValues(m) l=r.getUint32(0,!1) if(n)return(l&o)>>>0 k=l%a if(l-k+a>>0)&2147483647 r^=r>>>6}r=r+(r<<3>>>0)&2147483647 r^=r>>>11 return r+(r<<15>>>0)&2147483647}} A.hn.prototype={} A.hK.prototype={} A.ea.prototype={ hK(a,b,c){var s=this.a.a s===$&&A.G() s.eB(this.gip(),new A.jA(this))}, hb(){return this.d++}, p(){var s=0,r=A.o(t.H),q,p=this,o var $async$p=A.p(function(a,b){if(a===1)return A.l(b,r) while(true)switch(s){case 0:if(p.r||(p.w.a.a&30)!==0){s=1 break}p.r=!0 o=p.a.b o===$&&A.G() o.p() s=3 return A.c(p.w.a,$async$p) case 3:case 1:return A.m(q,r)}}) return A.n($async$p,r)}, iq(a){var s,r=this if(r.c){a.toString a=B.a_.ej(a)}if(a instanceof A.ba){s=r.e.A(0,a.a) if(s!=null)s.a.L(a.b)}else if(a instanceof A.bq){s=r.e.A(0,a.a) if(s!=null)s.fX(new A.fT(a.b),a.c)}else if(a instanceof A.am)r.f.v(0,a) else if(a instanceof A.bo){s=r.e.A(0,a.a) if(s!=null)s.fW(B.Z)}}, bv(a){var s,r,q=this if(q.r||(q.w.a.a&30)!==0)throw A.a(A.B("Tried to send "+a.j(0)+" over isolate channel, but the connection was closed!")) s=q.a.b s===$&&A.G() r=q.c?B.a_.dk(a):a s.a.v(0,r)}, ku(a,b,c){var s,r=this if(r.r||(r.w.a.a&30)!==0)return s=a.a if(b instanceof A.e3)r.bv(new A.bo(s)) else r.bv(new A.bq(s,b,c))}, hx(a){var s=this.f new A.an(s,A.t(s).h("an<1>")).kf(new A.jB(this,a))}} A.jA.prototype={ $0(){var s,r,q,p,o for(s=this.a,r=s.e,q=r.gaP(),p=A.t(q),q=new A.b0(J.M(q.a),q.b,p.h("b0<1,2>")),p=p.y[1];q.k();){o=q.a;(o==null?p.a(o):o).fW(B.ar)}r.c1(0) s.w.aV()}, $S:0} A.jB.prototype={ $1(a){return this.hs(a)}, hs(a){var s=0,r=A.o(t.H),q,p=2,o,n=this,m,l,k,j,i,h var $async$$1=A.p(function(b,c){if(b===1){o=c s=p}while(true)switch(s){case 0:i=null p=4 k=n.b.$1(a) s=7 return A.c(t.cG.b(k)?k:A.eX(k,t.O),$async$$1) case 7:i=c p=2 s=6 break case 4:p=3 h=o m=A.E(h) l=A.R(h) k=n.a.ku(a,m,l) q=k s=1 break s=6 break case 3:s=2 break case 6:k=n.a if(!(k.r||(k.w.a.a&30)!==0))k.bv(new A.ba(a.a,i)) case 1:return A.m(q,r) case 2:return A.l(o,r)}}) return A.n($async$$1,r)}, $S:86} A.ir.prototype={ fX(a,b){var s if(b==null)s=this.b else{s=A.d([],t.J) if(b instanceof A.be)B.c.aJ(s,b.a) else s.push(A.qm(b)) s.push(A.qm(this.b)) s=new A.be(A.aG(s,t.a))}this.a.bx(a,s)}, fW(a){return this.fX(a,null)}} A.fL.prototype={ j(a){return"Channel was closed before receiving a response"}, $ia5:1} A.fT.prototype={ j(a){return J.aU(this.a)}, $ia5:1} A.fS.prototype={ dk(a){var s,r if(a instanceof A.am)return[0,a.a,this.h0(a.b)] else if(a instanceof A.bq){s=J.aU(a.b) r=a.c r=r==null?null:r.j(0) return[2,a.a,s,r]}else if(a instanceof A.ba)return[1,a.a,this.h0(a.b)] else if(a instanceof A.bo)return A.d([3,a.a],t.t) else return null}, ej(a){var s,r,q,p if(!t.j.b(a))throw A.a(B.aE) s=J.V(a) r=A.h(s.i(a,0)) q=A.h(s.i(a,1)) switch(r){case 0:return new A.am(q,t.ah.a(this.fZ(s.i(a,2)))) case 2:p=A.vC(s.i(a,3)) s=s.i(a,2) if(s==null)s=t.K.a(s) return new A.bq(q,s,p!=null?new A.dI(p):null) case 1:return new A.ba(q,t.O.a(this.fZ(s.i(a,2)))) case 3:return new A.bo(q)}throw A.a(B.aF)}, h0(a){var s,r,q,p,o,n,m,l,k,j,i,h,g,f if(a==null)return a if(a instanceof A.d3)return a.a else if(a instanceof A.bU){s=a.a r=a.b q=[] for(p=a.c,o=p.length,n=0;n")),g=g.h("P.E");s.k();){a8=s.d h.push(this.dE(a8==null?g.a(a8):a8))}l.push(new A.cR(i,h))}f=J.iS(a7.a) $label1$2:{if(f==null){s=a6 break $label1$2}A.h(f) s=f break $label1$2}return new A.bg(new A.e1(n,l),s) case 5:return new A.c3(B.aa[q.$1(1)],p.$1(2)) case 6:return new A.bT(q.$1(1),p.$1(2)) case 13:s.toString return new A.c4(A.or(B.ac,A.ad(J.aO(s,1)))) case 7:return new A.c2(new A.et(p.$1(1),q.$1(2)),q.$1(3)) case 8:e=A.d([],t.be) s=t.j k=1 while(!0){m=a7.a m.toString if(!(k")).k0(0,new A.kF(r))}, jM(a,b){var s,r,q for(s=this.z,s=A.im(s,s.r,s.$ti.c),r=s.$ti.c;s.k();){q=s.d if(q==null)q=r.a(q) if(q!==b)q.bv(new A.am(q.d++,a))}}} A.kH.prototype={ $1(a){var s=this.a s.i_() s.as.p()}, $S:89} A.kI.prototype={ $1(a){return this.a.is(this.b,a)}, $S:93} A.kJ.prototype={ $1(a){return this.a.z.A(0,this.b)}, $S:24} A.kD.prototype={ $0(){var s=this.b return this.a.aG(s.a,s.b,s.c,s.d)}, $S:110} A.kE.prototype={ $0(){return this.a.r.A(0,this.b.a)}, $S:116} A.kG.prototype={ $0(){var s,r=this.b if(r==null)return this.a.w.length===0 else{s=this.a.w return s.length!==0&&B.c.gG(s)===r}}, $S:22} A.kF.prototype={ $1(a){return this.a.$0()}, $S:24} A.f7.prototype={ cN(a,b){return this.jC(a,b)}, jC(a,b){var s=0,r=A.o(t.H),q=1,p,o=[],n=this,m,l,k,j,i var $async$cN=A.p(function(c,d){if(c===1){p=d s=q}while(true)switch(s){case 0:j=n.a i=j.dZ(a,!0) q=2 m=n.b l=m.hb() k=new A.k($.i,t.D) m.e.q(0,l,new A.ir(new A.a2(k,t.h),A.oG())) m.bv(new A.am(l,new A.c2(b,i))) s=5 return A.c(k,$async$cN) case 5:o.push(4) s=3 break case 2:o=[1] case 3:q=1 j.cD(i) s=o.pop() break case 4:return A.m(null,r) case 1:return A.l(p,r)}}) return A.n($async$cN,r)}} A.hX.prototype={ dk(a){var s,r,q $label0$0:{if(a instanceof A.am){s=new A.ap(0,{i:a.a,p:this.j4(a.b)}) break $label0$0}if(a instanceof A.ba){s=new A.ap(1,{i:a.a,p:this.j5(a.b)}) break $label0$0}if(a instanceof A.bq){r=a.c q=J.aU(a.b) s=r==null?null:r.j(0) s=new A.ap(2,[a.a,q,s]) break $label0$0}if(a instanceof A.bo){s=new A.ap(3,a.a) break $label0$0}s=null}return A.d([s.a,s.b],t.f)}, ej(a){var s,r,q,p,o,n,m=null,l="Pattern matching error",k={} k.a=null s=a.length===2 if(s){r=a[0] q=k.a=a[1]}else{q=m r=q}if(!s)throw A.a(A.B(l)) r=A.h(A.r(r)) $label0$0:{if(0===r){s=new A.lK(k,this).$0() break $label0$0}if(1===r){s=new A.lL(k,this).$0() break $label0$0}if(2===r){t.c.a(q) s=q.length===3 p=m o=m if(s){n=q[0] p=q[1] o=q[2]}else n=m if(!s)A.y(A.B(l)) n=A.h(A.r(n)) A.ad(p) s=new A.bq(n,p,o!=null?new A.dI(A.ad(o)):m) break $label0$0}if(3===r){s=new A.bo(A.h(A.r(q))) break $label0$0}s=A.y(A.K("Unknown message tag "+r,m))}return s}, j4(a){var s,r,q,p,o,n,m,l,k,j,i,h=null $label0$0:{s=h if(a==null)break $label0$0 if(a instanceof A.bU){s=a.a r=a.b q=[] for(p=a.c,o=p.length,n=0;n") q=new A.by(A.ay(new A.D(s,new A.lD(),q),!0,q.h("P.E"))) s=q break $label0$0}s=A.y(A.K("Unknown request tag "+r,m))}return s}, j5(a){var s,r $label0$0:{s=null if(a==null)break $label0$0 if(a instanceof A.aH){r=a.a s=A.bM(r)?r:A.h(r) break $label0$0}if(a instanceof A.bA){s=this.j6(a) break $label0$0}}return s}, j6(a){var s,r,q,p=a.a,o=J.V(p) if(o.gF(p)){p=self return{c:new p.Array(),r:new p.Array()}}else{s=J.cQ(o.gG(p).ga_(),new A.lJ(),t.N).cj(0) r=A.d([],t.fk) for(p=o.gt(p);p.k();){q=[] for(o=J.M(p.gm().gaP());o.k();)q.push(this.e8(o.gm())) r.push(q)}return{c:s,r:r}}}, i9(a){var s,r,q,p,o,n,m,l,k,j if(a==null)return null else if(typeof a==="boolean")return new A.aH(A.bJ(a)) else if(typeof a==="number")return new A.aH(A.h(A.r(a))) else{t.m.a(a) s=a.c s=t.o.b(s)?s:new A.ah(s,A.Q(s).h("ah<1,j>")) r=t.N s=J.cQ(s,new A.lH(),r) q=A.ay(s,!0,s.$ti.h("P.E")) p=A.d([],t.d) s=a.r s=J.M(t.e9.b(s)?s:new A.ah(s,A.Q(s).h("ah<1,w>"))) o=t.X for(;s.k();){n=s.gm() m=A.a3(r,o) n=A.ua(n,0,o) l=J.M(n.a) n=n.b k=new A.eg(l,n) for(;k.k();){j=k.c j=j>=0?new A.ap(n+j,l.gm()):A.y(A.ak()) m.q(0,q[j.a],this.e7(j.b))}p.push(m)}return new A.bA(p)}}, e8(a){var s $label0$0:{if(a==null){s=null break $label0$0}if(A.bn(a)){s=a break $label0$0}if(A.bM(a)){s=a break $label0$0}if(typeof a=="string"){s=a break $label0$0}if(typeof a=="number"){s=A.d([15,a],t.n) break $label0$0}if(a instanceof A.a6){s=A.d([14,a.j(0)],t.f) break $label0$0}if(t.I.b(a)){s=new Uint8Array(A.iJ(a)) break $label0$0}s=A.y(A.K("Unknown db value: "+A.u(a),null))}return s}, e7(a){var s,r,q,p=null if(a!=null)if(typeof a==="number")return A.h(A.r(a)) else if(typeof a==="boolean")return A.bJ(a) else if(typeof a==="string")return A.ad(a) else if(A.oy(a,"Uint8Array"))return t.Z.a(a) else{t.c.a(a) s=a.length===2 if(s){r=a[0] q=a[1]}else{q=p r=q}if(!s)throw A.a(A.B("Pattern matching error")) if(r==14)return A.oU(A.ad(q),p) else return A.r(q)}else return p}} A.lK.prototype={ $0(){var s=t.m.a(this.a.a) return new A.am(s.i,this.b.i8(s.p))}, $S:38} A.lL.prototype={ $0(){var s=t.m.a(this.a.a) return new A.ba(s.i,this.b.i9(s.p))}, $S:37} A.lI.prototype={ $1(a){return a}, $S:8} A.lE.prototype={ $0(){var s,r,q,p,o,n,m=this.b,l=J.V(m),k=t.c,j=k.a(l.i(m,1)),i=t.o.b(j)?j:new A.ah(j,A.Q(j).h("ah<1,j>")) i=J.cQ(i,new A.lF(),t.N) s=A.ay(i,!0,i.$ti.h("P.E")) i=l.gl(m) r=A.d([],t.g7) for(i=l.X(m,2).ah(0,i-3),k=A.e4(i,i.$ti.h("f.E"),k),k=A.en(k,new A.lG(),A.t(k).h("f.E"),t.ee),i=A.t(k),k=new A.b0(J.M(k.a),k.b,i.h("b0<1,2>")),q=this.a.gjk(),i=i.y[1];k.k();){p=k.a if(p==null)p=i.a(p) o=J.V(p) n=A.h(A.r(o.i(p,0))) p=o.X(p,1) o=p.$ti.h("D") r.push(new A.cR(n,A.ay(new A.D(p,q,o),!0,o.h("P.E"))))}m=l.i(m,l.gl(m)-1) m=m==null?null:A.h(A.r(m)) return new A.bg(new A.e1(s,r),m)}, $S:40} A.lF.prototype={ $1(a){return a}, $S:8} A.lG.prototype={ $1(a){return a}, $S:41} A.lD.prototype={ $1(a){var s,r,q t.c.a(a) s=a.length===2 if(s){r=a[0] q=a[1]}else{r=null q=null}if(!s)throw A.a(A.B("Pattern matching error")) A.ad(r) return new A.bC(q==null?null:B.a7[A.h(A.r(q))],r)}, $S:42} A.lJ.prototype={ $1(a){return a}, $S:8} A.lH.prototype={ $1(a){return a}, $S:8} A.di.prototype={ ae(){return"UpdateKind."+this.b}} A.bC.prototype={ gB(a){return A.es(this.a,this.b,B.f,B.f)}, O(a,b){if(b==null)return!1 return b instanceof A.bC&&b.a==this.a&&b.b===this.b}, j(a){return"TableUpdate("+this.b+", kind: "+A.u(this.a)+")"}} A.og.prototype={ $0(){return this.a.a.a.L(A.jW(this.b,this.c))}, $S:0} A.bS.prototype={ J(){var s,r if(this.c)return for(s=this.b,r=0;!1;++r)s[r].$0() this.c=!0}} A.e3.prototype={ j(a){return"Operation was cancelled"}, $ia5:1} A.al.prototype={ p(){var s=0,r=A.o(t.H) var $async$p=A.p(function(a,b){if(a===1)return A.l(b,r) while(true)switch(s){case 0:return A.m(null,r)}}) return A.n($async$p,r)}} A.e1.prototype={ gB(a){return A.es(B.o.h6(this.a),B.o.h6(this.b),B.f,B.f)}, O(a,b){if(b==null)return!1 return b instanceof A.e1&&B.o.em(b.a,this.a)&&B.o.em(b.b,this.b)}, j(a){return"BatchedStatements("+A.u(this.a)+", "+A.u(this.b)+")"}} A.cR.prototype={ gB(a){return A.es(this.a,B.o,B.f,B.f)}, O(a,b){if(b==null)return!1 return b instanceof A.cR&&b.a===this.a&&B.o.em(b.b,this.b)}, j(a){return"ArgumentsForBatchedStatement("+this.a+", "+A.u(this.b)+")"}} A.jq.prototype={} A.kq.prototype={} A.lb.prototype={} A.kk.prototype={} A.ju.prototype={} A.hm.prototype={} A.jJ.prototype={} A.i2.prototype={ gez(){return!1}, gc6(){return!1}, b6(a,b){if(this.gez()||this.b>0)return this.a.cr(new A.lT(a,b),b) else return a.$0()}, cz(a,b){this.gc6()}, ad(a,b){return this.kB(a,b)}, kB(a,b){var s=0,r=A.o(t.aS),q,p=this,o var $async$ad=A.p(function(c,d){if(c===1)return A.l(d,r) while(true)switch(s){case 0:s=3 return A.c(p.b6(new A.lY(p,a,b),t.aj),$async$ad) case 3:o=d.gjB(0) q=A.ay(o,!0,o.$ti.h("P.E")) s=1 break case 1:return A.m(q,r)}}) return A.n($async$ad,r)}, cf(a,b){return this.b6(new A.lW(this,a,b),t.S)}, aw(a,b){return this.b6(new A.lX(this,a,b),t.S)}, a8(a,b){return this.b6(new A.lV(this,b,a),t.H)}, kx(a){return this.a8(a,null)}, av(a){return this.b6(new A.lU(this,a),t.H)}, cO(){return new A.eV(this,new A.a2(new A.k($.i,t.D),t.h),new A.bh())}, cP(){return this.aU(this)}} A.lT.prototype={ $0(){A.rx() return this.a.$0()}, $S(){return this.b.h("C<0>()")}} A.lY.prototype={ $0(){var s=this.a,r=this.b,q=this.c s.cz(r,q) return s.gaM().ad(r,q)}, $S:43} A.lW.prototype={ $0(){var s=this.a,r=this.b,q=this.c s.cz(r,q) return s.gaM().d8(r,q)}, $S:36} A.lX.prototype={ $0(){var s=this.a,r=this.b,q=this.c s.cz(r,q) return s.gaM().aw(r,q)}, $S:36} A.lV.prototype={ $0(){var s,r,q=this.b if(q==null)q=B.t s=this.a r=this.c s.cz(r,q) return s.gaM().a8(r,q)}, $S:3} A.lU.prototype={ $0(){var s=this.a s.gc6() return s.gaM().av(this.b)}, $S:3} A.iE.prototype={ hZ(){this.c=!0 if(this.d)throw A.a(A.B("A transaction was used after being closed. Please check that you're awaiting all database operations inside a `transaction` block."))}, aU(a){throw A.a(A.H("Nested transactions aren't supported."))}, gao(){return B.m}, gc6(){return!1}, gez(){return!0}, $ihG:1} A.fb.prototype={ ap(a){var s,r,q=this q.hZ() s=q.z if(s==null){s=q.z=new A.a2(new A.k($.i,t.k),t.co) r=q.as;++r.b r.b6(new A.nn(q),t.P).ai(new A.no(r))}return s.a}, gaM(){return this.e.e}, aU(a){var s=this.at+1 return new A.fb(this.y,new A.a2(new A.k($.i,t.D),t.h),a,s,A.rd(s),A.rb(s),A.rc(s),this.e,new A.bh())}, bj(){var s=0,r=A.o(t.H),q,p=this var $async$bj=A.p(function(a,b){if(a===1)return A.l(b,r) while(true)switch(s){case 0:if(!p.c){s=1 break}s=3 return A.c(p.a8(p.ay,B.t),$async$bj) case 3:p.f2() case 1:return A.m(q,r)}}) return A.n($async$bj,r)}, bD(){var s=0,r=A.o(t.H),q,p=2,o,n=[],m=this var $async$bD=A.p(function(a,b){if(a===1){o=b s=p}while(true)switch(s){case 0:if(!m.c){s=1 break}p=3 s=6 return A.c(m.a8(m.ch,B.t),$async$bD) case 6:n.push(5) s=4 break case 3:n=[2] case 4:p=2 m.f2() s=n.pop() break case 5:case 1:return A.m(q,r) case 2:return A.l(o,r)}}) return A.n($async$bD,r)}, f2(){var s=this if(s.at===0)s.e.e.a=!1 s.Q.aV() s.d=!0}} A.nn.prototype={ $0(){var s=0,r=A.o(t.P),q=1,p,o=this,n,m,l,k,j var $async$$0=A.p(function(a,b){if(a===1){p=b s=q}while(true)switch(s){case 0:q=3 l=o.a s=6 return A.c(l.kx(l.ax),$async$$0) case 6:l.e.e.a=!0 l.z.L(!0) q=1 s=5 break case 3:q=2 j=p n=A.E(j) m=A.R(j) o.a.z.bx(n,m) s=5 break case 2:s=1 break case 5:s=7 return A.c(o.a.Q.a,$async$$0) case 7:return A.m(null,r) case 1:return A.l(p,r)}}) return A.n($async$$0,r)}, $S:14} A.no.prototype={ $0(){return this.a.b--}, $S:46} A.fQ.prototype={ gaM(){return this.e}, gao(){return B.m}, ap(a){return this.x.cr(new A.jz(this,a),t.y)}, bt(a){return this.j0(a)}, j0(a){var s=0,r=A.o(t.H),q=this,p,o,n,m var $async$bt=A.p(function(b,c){if(b===1)return A.l(c,r) while(true)switch(s){case 0:n=q.e m=n.y m===$&&A.G() p=a.c s=m instanceof A.hm?2:4 break case 2:o=p s=3 break case 4:s=m instanceof A.f9?5:7 break case 5:s=8 return A.c(A.aX(m.a.gkG(),t.S),$async$bt) case 8:o=c s=6 break case 7:throw A.a(A.jL("Invalid delegate: "+n.j(0)+". The versionDelegate getter must not subclass DBVersionDelegate directly")) case 6:case 3:if(o===0)o=null s=9 return A.c(a.cN(new A.i3(q,new A.bh()),new A.et(o,p)),$async$bt) case 9:s=m instanceof A.f9&&o!==p?10:11 break case 10:m.a.h1("PRAGMA user_version = "+p+";") s=12 return A.c(A.aX(null,t.H),$async$bt) case 12:case 11:return A.m(null,r)}}) return A.n($async$bt,r)}, aU(a){var s=$.i return new A.fb(B.az,new A.a2(new A.k(s,t.D),t.h),a,0,"BEGIN TRANSACTION","COMMIT TRANSACTION","ROLLBACK TRANSACTION",this,new A.bh())}, p(){return this.x.cr(new A.jy(this),t.H)}, gc6(){return this.r}, gez(){return this.w}} A.jz.prototype={ $0(){var s=0,r=A.o(t.y),q,p=2,o,n=this,m,l,k,j,i,h,g,f,e var $async$$0=A.p(function(a,b){if(a===1){o=b s=p}while(true)switch(s){case 0:f=n.a if(f.d){q=A.pQ(new A.b2("Can't re-open a database after closing it. Please create a new database connection and open that instead."),null,t.y) s=1 break}k=f.f if(k!=null)A.pM(k.a,k.b) j=f.e i=t.y h=A.aX(j.d,i) s=3 return A.c(t.bF.b(h)?h:A.eX(h,i),$async$$0) case 3:if(b){q=f.c=!0 s=1 break}i=n.b s=4 return A.c(j.ca(i),$async$$0) case 4:f.c=!0 p=6 s=9 return A.c(f.bt(i),$async$$0) case 9:q=!0 s=1 break p=2 s=8 break case 6:p=5 e=o m=A.E(e) l=A.R(e) f.f=new A.ap(m,l) throw e s=8 break case 5:s=2 break case 8:case 1:return A.m(q,r) case 2:return A.l(o,r)}}) return A.n($async$$0,r)}, $S:47} A.jy.prototype={ $0(){var s=this.a if(s.c&&!s.d){s.d=!0 s.c=!1 return s.e.p()}else return A.aX(null,t.H)}, $S:3} A.i3.prototype={ aU(a){return this.e.aU(a)}, ap(a){this.c=!0 return A.aX(!0,t.y)}, gaM(){return this.e.e}, gc6(){return!1}, gao(){return B.m}} A.eV.prototype={ gao(){return this.e.gao()}, ap(a){var s,r,q,p=this,o=p.f if(o!=null)return o.a else{p.c=!0 s=new A.k($.i,t.k) r=new A.a2(s,t.co) p.f=r q=p.e;++q.b q.b6(new A.mg(p,r),t.P) return s}}, gaM(){return this.e.gaM()}, aU(a){return this.e.aU(a)}, p(){this.r.aV() return A.aX(null,t.H)}} A.mg.prototype={ $0(){var s=0,r=A.o(t.P),q=this,p var $async$$0=A.p(function(a,b){if(a===1)return A.l(b,r) while(true)switch(s){case 0:q.b.L(!0) p=q.a s=2 return A.c(p.r.a,$async$$0) case 2:--p.e.b return A.m(null,r)}}) return A.n($async$$0,r)}, $S:14} A.d6.prototype={ gjB(a){var s=this.b return new A.D(s,new A.ks(this),A.Q(s).h("D<1,aa>"))}} A.ks.prototype={ $1(a){var s,r,q,p,o,n,m,l=A.a3(t.N,t.z) for(s=this.a,r=s.a,q=r.length,s=s.c,p=J.V(a),o=0;o")).gG(0)) q.toString q.a7()}p.q(0,a,s)}return new A.ap(s,A.h(A.r(n.call(null,r)))===0)}} A.f9.prototype={} A.ko.prototype={ jN(){var s,r,q,p,o,n for(s=this.b,r=s.gaP(),q=A.t(r),r=new A.b0(J.M(r.a),r.b,q.h("b0<1,2>")),q=q.y[1];r.k();){p=r.a if(p==null)p=q.a(p) o=p.c if(!o.d){n=$.dY().a if(n!=null)n.unregister(p) if(!o.d){o.d=!0 if(!o.c){n=o.b A.h(A.r(n.c.id.call(null,n.b))) o.c=!0}n=o.b n.ba() A.h(A.r(n.c.to.call(null,n.b)))}p=p.b if(!p.e)B.c.A(p.c.d,o)}}s.c1(0)}} A.jK.prototype={ $1(a){return Date.now()}, $S:49} A.nX.prototype={ $1(a){var s=a.i(0,0) if(typeof s=="number")return this.a.$1(s) else return null}, $S:26} A.hc.prototype={ gi7(){var s=this.a s===$&&A.G() return s}, gao(){if(this.b){var s=this.a s===$&&A.G() s=B.m!==s.gao()}else s=!1 if(s)throw A.a(A.jL("LazyDatabase created with "+B.m.j(0)+", but underlying database is "+this.gi7().gao().j(0)+".")) return B.m}, hV(){var s,r,q=this if(q.b)return A.aX(null,t.H) else{s=q.d if(s!=null)return s.a else{s=new A.k($.i,t.D) r=q.d=new A.a2(s,t.h) A.jW(q.e,t.x).bG(new A.kc(q,r),r.gjI(),t.P) return s}}}, cO(){var s=this.a s===$&&A.G() return s.cO()}, cP(){var s=this.a s===$&&A.G() return s.cP()}, ap(a){return this.hV().bF(new A.kd(this,a),t.y)}, av(a){var s=this.a s===$&&A.G() return s.av(a)}, a8(a,b){var s=this.a s===$&&A.G() return s.a8(a,b)}, cf(a,b){var s=this.a s===$&&A.G() return s.cf(a,b)}, aw(a,b){var s=this.a s===$&&A.G() return s.aw(a,b)}, ad(a,b){var s=this.a s===$&&A.G() return s.ad(a,b)}, p(){if(this.b){var s=this.a s===$&&A.G() return s.p()}else return A.aX(null,t.H)}} A.kc.prototype={ $1(a){var s=this.a s.a!==$&&A.pq() s.a=a s.b=!0 this.b.aV()}, $S:51} A.kd.prototype={ $1(a){var s=this.a.a s===$&&A.G() return s.ap(this.b)}, $S:52} A.bh.prototype={ cr(a,b){var s=this.a,r=new A.k($.i,t.D) this.a=r r=new A.kg(a,new A.a2(r,t.h),b) if(s!=null)return s.bF(new A.kh(r,b),b) else return r.$0()}} A.kg.prototype={ $0(){return A.jW(this.a,this.c).ai(this.b.gjH())}, $S(){return this.c.h("C<0>()")}} A.kh.prototype={ $1(a){return this.a.$0()}, $S(){return this.b.h("C<0>(~)")}} A.lA.prototype={ $1(a){var s,r=this,q=a.data if(r.a&&J.X(q,"_disconnect")){s=r.b.a s===$&&A.G() s=s.a s===$&&A.G() s.p()}else{s=r.b.a if(r.c){s===$&&A.G() s=s.a s===$&&A.G() s.v(0,B.a3.ej(t.c.a(q)))}else{s===$&&A.G() s=s.a s===$&&A.G() s.v(0,A.ry(q))}}}, $S:10} A.lB.prototype={ $1(a){var s=this.b if(this.a)s.postMessage(B.a3.dk(t.fJ.a(a))) else s.postMessage(A.xe(a))}, $S:7} A.lC.prototype={ $0(){if(this.a)this.b.postMessage("_disconnect") this.b.close()}, $S:0} A.jv.prototype={ T(){A.aB(this.a,"message",new A.jx(this),!1)}, ak(a){return this.ir(a)}, ir(a6){var s=0,r=A.o(t.H),q=1,p,o=this,n,m,l,k,j,i,h,g,f,e,d,c,b,a,a0,a1,a2,a3,a4,a5 var $async$ak=A.p(function(a7,a8){if(a7===1){p=a8 s=q}while(true)switch(s){case 0:a3={} k=a6 instanceof A.da j=k?a6.a:null s=k?3:4 break case 3:a3.a=a3.b=!1 s=5 return A.c(o.b.cr(new A.jw(a3,o),t.P),$async$ak) case 5:i=o.c.a.i(0,j) h=A.d([],t.L) g=!1 s=a3.b?6:7 break case 6:a5=J s=8 return A.c(A.dW(),$async$ak) case 8:k=a5.M(a8) case 9:if(!k.k()){s=10 break}f=k.gm() h.push(new A.ap(B.F,f)) if(f===j)g=!0 s=9 break case 10:case 7:s=i!=null?11:13 break case 11:k=i.a e=k===B.w||k===B.E g=k===B.ak||k===B.al s=12 break case 13:a5=a3.a if(a5){s=14 break}else a8=a5 s=15 break case 14:s=16 return A.c(A.dU(j),$async$ak) case 16:case 15:e=a8 case 12:k=t.m.a(self) d="Worker" in k f=a3.b c=a3.a new A.e9(d,f,"SharedArrayBuffer" in k,c,h,B.v,e,g).di(o.a) s=2 break case 4:if(a6 instanceof A.dc){o.c.eT(a6) s=2 break}k=a6 instanceof A.eB b=k?a6.a:null s=k?17:18 break case 17:s=19 return A.c(A.hR(b),$async$ak) case 19:a=a8 o.a.postMessage(!0) s=20 return A.c(a.T(),$async$ak) case 20:s=2 break case 18:n=null m=null a0=a6 instanceof A.fR if(a0){a1=a6.a n=a1.a m=a1.b}s=a0?21:22 break case 21:q=24 case 27:switch(n){case B.am:s=29 break case B.F:s=30 break default:s=28 break}break case 29:s=31 return A.c(A.o2(m),$async$ak) case 31:s=28 break case 30:s=32 return A.c(A.fr(m),$async$ak) case 32:s=28 break case 28:a6.di(o.a) q=1 s=26 break case 24:q=23 a4=p l=A.E(a4) new A.dm(J.aU(l)).di(o.a) s=26 break case 23:s=1 break case 26:s=2 break case 22:s=2 break case 2:return A.m(null,r) case 1:return A.l(p,r)}}) return A.n($async$ak,r)}} A.jx.prototype={ $1(a){this.a.ak(A.oL(t.m.a(a.data)))}, $S:1} A.jw.prototype={ $0(){var s=0,r=A.o(t.P),q=this,p,o,n,m,l var $async$$0=A.p(function(a,b){if(a===1)return A.l(b,r) while(true)switch(s){case 0:o=q.b n=o.d m=q.a s=n!=null?2:4 break case 2:m.b=n.b m.a=n.a s=3 break case 4:l=m s=5 return A.c(A.cN(),$async$$0) case 5:l.b=b s=6 return A.c(A.iN(),$async$$0) case 6:p=b m.a=p o.d=new A.lm(p,m.b) case 3:return A.m(null,r)}}) return A.n($async$$0,r)}, $S:14} A.d5.prototype={ ae(){return"ProtocolVersion."+this.b}} A.lo.prototype={ dj(a){this.aC(new A.lr(a))}, eS(a){this.aC(new A.lq(a))}, di(a){this.aC(new A.lp(a))}} A.lr.prototype={ $2(a,b){var s=b==null?B.A:b this.a.postMessage(a,s)}, $S:19} A.lq.prototype={ $2(a,b){var s=b==null?B.A:b this.a.postMessage(a,s)}, $S:19} A.lp.prototype={ $2(a,b){var s=b==null?B.A:b this.a.postMessage(a,s)}, $S:19} A.jb.prototype={} A.c5.prototype={ aC(a){var s=this A.dN(a,"SharedWorkerCompatibilityResult",A.d([s.e,s.f,s.r,s.c,s.d,A.pK(s.a),s.b.c],t.f),null)}} A.dm.prototype={ aC(a){A.dN(a,"Error",this.a,null)}, j(a){return"Error in worker: "+this.a}, $ia5:1} A.dc.prototype={ aC(a){var s,r,q=this,p={} p.sqlite=q.a.j(0) s=q.b p.port=s p.storage=q.c.b p.database=q.d r=q.e p.initPort=r p.migrations=q.r p.new_serialization=q.w p.v=q.f.c s=A.d([s],t.W) if(r!=null)s.push(r) A.dN(a,"ServeDriftDatabase",p,s)}} A.da.prototype={ aC(a){A.dN(a,"RequestCompatibilityCheck",this.a,null)}} A.e9.prototype={ aC(a){var s=this,r={} r.supportsNestedWorkers=s.e r.canAccessOpfs=s.f r.supportsIndexedDb=s.w r.supportsSharedArrayBuffers=s.r r.indexedDbExists=s.c r.opfsExists=s.d r.existing=A.pK(s.a) r.v=s.b.c A.dN(a,"DedicatedWorkerCompatibilityResult",r,null)}} A.eB.prototype={ aC(a){A.dN(a,"StartFileSystemServer",this.a,null)}} A.fR.prototype={ aC(a){var s=this.a A.dN(a,"DeleteDatabase",A.d([s.a.b,s.b],t.s),null)}} A.o_.prototype={ $1(a){this.b.transaction.abort() this.a.a=!1}, $S:10} A.od.prototype={ $1(a){return t.m.a(a[1])}, $S:56} A.fU.prototype={ eT(a){var s=a.w this.a.hg(a.d,new A.jI(this,a)).hv(A.uP(a.b,a.f.c>=1,s),!s)}, aY(a,b,c,d,e){return this.kj(a,b,c,d,e)}, kj(a,b,c,d,a0){var s=0,r=A.o(t.x),q,p=this,o,n,m,l,k,j,i,h,g,f,e var $async$aY=A.p(function(a1,a2){if(a1===1)return A.l(a2,r) while(true)switch(s){case 0:s=3 return A.c(A.lw(d),$async$aY) case 3:f=a2 e=null case 4:switch(a0.a){case 0:s=6 break case 1:s=7 break case 3:s=8 break case 2:s=9 break case 4:s=10 break default:s=11 break}break case 6:s=12 return A.c(A.hx("drift_db/"+a),$async$aY) case 12:o=a2 e=o.gb9() s=5 break case 7:s=13 return A.c(p.cw(a),$async$aY) case 13:o=a2 e=o.gb9() s=5 break case 8:case 9:s=14 return A.c(A.h5(a),$async$aY) case 14:o=a2 e=o.gb9() s=5 break case 10:o=A.ow(null) s=5 break case 11:o=null case 5:s=c!=null&&o.ck("/database",0)===0?15:16 break case 15:n=c.$0() s=17 return A.c(t.eY.b(n)?n:A.eX(n,t.aD),$async$aY) case 17:m=a2 if(m!=null){l=o.aZ(new A.ez("/database"),4).a l.bH(m,0) l.cl()}case 16:n=f.a n=n.b k=n.c0(B.i.a5(o.a),1) j=n.c.e i=j.a j.q(0,i,o) h=A.h(A.r(n.y.call(null,k,i,1))) n=$.rP() n.a.set(o,h) n=A.uh(t.N,t.eT) g=new A.hT(new A.nH(f,"/database",null,p.b,!0,b,new A.ko(n)),!1,!0,new A.bh(),new A.bh()) if(e!=null){q=A.tK(g,new A.m5(e,g)) s=1 break}else{q=g s=1 break}case 1:return A.m(q,r)}}) return A.n($async$aY,r)}, cw(a){return this.iy(a)}, iy(a){var s=0,r=A.o(t.aT),q,p,o,n,m,l,k,j,i var $async$cw=A.p(function(b,c){if(b===1)return A.l(c,r) while(true)switch(s){case 0:k=self j=new k.SharedArrayBuffer(8) i=k.Int32Array i=t.ha.a(A.dT(i,[j])) k.Atomics.store(i,0,-1) i={clientVersion:1,root:"drift_db/"+a,synchronizationBuffer:j,communicationBuffer:new k.SharedArrayBuffer(67584)} p=new k.Worker(A.eF().j(0)) new A.eB(i).dj(p) s=3 return A.c(new A.eU(p,"message",!1,t.fF).gG(0),$async$cw) case 3:o=A.qb(i.synchronizationBuffer) i=i.communicationBuffer n=A.qe(i,65536,2048) k=k.Uint8Array k=t.Z.a(A.dT(k,[i])) m=A.jl("/",$.cP()) l=$.iP() q=new A.dl(o,new A.bi(i,n,k),m,l,"dart-sqlite3-vfs") s=1 break case 1:return A.m(q,r)}}) return A.n($async$cw,r)}} A.jI.prototype={ $0(){var s=this.b,r=s.e,q=r!=null?new A.jF(r):null,p=this.a,o=A.uC(new A.hc(new A.jG(p,s,q)),!1,!0),n=new A.k($.i,t.D),m=new A.db(s.c,o,new A.a8(n,t.F)) n.ai(new A.jH(p,s,m)) return m}, $S:57} A.jF.prototype={ $0(){var s=new A.k($.i,t.fX),r=this.a r.postMessage(!0) r.onmessage=A.bb(new A.jE(new A.a2(s,t.fu))) return s}, $S:58} A.jE.prototype={ $1(a){var s=t.dE.a(a.data),r=s==null?null:s this.a.L(r)}, $S:10} A.jG.prototype={ $0(){var s=this.b return this.a.aY(s.d,s.r,this.c,s.a,s.c)}, $S:59} A.jH.prototype={ $0(){this.a.a.A(0,this.b.d) this.c.b.hy()}, $S:9} A.m5.prototype={ c2(a){return this.jF(a)}, jF(a){var s=0,r=A.o(t.H),q=this,p var $async$c2=A.p(function(b,c){if(b===1)return A.l(c,r) while(true)switch(s){case 0:s=2 return A.c(a.p(),$async$c2) case 2:s=q.b===a?3:4 break case 3:p=q.a.$0() s=5 return A.c(p instanceof A.k?p:A.eX(p,t.H),$async$c2) case 5:case 4:return A.m(null,r)}}) return A.n($async$c2,r)}} A.db.prototype={ hv(a,b){var s,r,q;++this.c s=t.X s=A.v9(new A.kA(this),s,s).gjD().$1(a.ghD()) r=a.$ti q=new A.e5(r.h("e5<1>")) q.b=new A.eO(q,a.ghz()) q.a=new A.eP(s,q,r.h("eP<1>")) this.b.hw(q,b)}} A.kA.prototype={ $1(a){var s=this.a if(--s.c===0)s.d.aV() s=a.a if((s.e&2)!==0)A.y(A.B("Stream is already closed")) s.eW()}, $S:60} A.lm.prototype={} A.jf.prototype={ $1(a){this.a.L(this.c.a(this.b.result))}, $S:1} A.jg.prototype={ $1(a){var s=this.b.error if(s==null)s=a this.a.aK(s)}, $S:1} A.jh.prototype={ $1(a){var s=this.b.error if(s==null)s=a this.a.aK(s)}, $S:1} A.kK.prototype={ T(){A.aB(this.a,"connect",new A.kP(this),!1)}, dV(a){return this.iB(a)}, iB(a){var s=0,r=A.o(t.H),q=this,p,o var $async$dV=A.p(function(b,c){if(b===1)return A.l(c,r) while(true)switch(s){case 0:p=a.ports o=J.aO(t.cl.b(p)?p:new A.ah(p,A.Q(p).h("ah<1,A>")),0) o.start() A.aB(o,"message",new A.kL(q,o),!1) return A.m(null,r)}}) return A.n($async$dV,r)}, cA(a,b){return this.iz(a,b)}, iz(a,b){var s=0,r=A.o(t.H),q=1,p,o=this,n,m,l,k,j,i,h,g var $async$cA=A.p(function(c,d){if(c===1){p=d s=q}while(true)switch(s){case 0:q=3 n=A.oL(t.m.a(b.data)) m=n l=null i=m instanceof A.da if(i)l=m.a s=i?7:8 break case 7:s=9 return A.c(o.bW(l),$async$cA) case 9:k=d k.eS(a) s=6 break case 8:if(m instanceof A.dc&&B.w===m.c){o.c.eT(n) s=6 break}if(m instanceof A.dc){i=o.b i.toString n.dj(i) s=6 break}i=A.K("Unknown message",null) throw A.a(i) case 6:q=1 s=5 break case 3:q=2 g=p j=A.E(g) new A.dm(J.aU(j)).eS(a) a.close() s=5 break case 2:s=1 break case 5:return A.m(null,r) case 1:return A.l(p,r)}}) return A.n($async$cA,r)}, bW(a){return this.je(a)}, je(a){var s=0,r=A.o(t.fM),q,p=this,o,n,m,l,k,j,i,h,g,f,e,d,c var $async$bW=A.p(function(b,a0){if(b===1)return A.l(a0,r) while(true)switch(s){case 0:l={} k=t.m.a(self) j="Worker" in k s=3 return A.c(A.iN(),$async$bW) case 3:i=a0 s=!j?4:6 break case 4:l=p.c.a.i(0,a) if(l==null)o=null else{l=l.a l=l===B.w||l===B.E o=l}h=A g=!1 f=!1 e=i d=B.B c=B.v s=o==null?7:9 break case 7:s=10 return A.c(A.dU(a),$async$bW) case 10:s=8 break case 9:a0=o case 8:q=new h.c5(g,f,e,d,c,a0,!1) s=1 break s=5 break case 6:n=p.b if(n==null)n=p.b=new k.Worker(A.eF().j(0)) new A.da(a).dj(n) k=new A.k($.i,t.a9) l.a=l.b=null m=new A.kO(l,new A.a2(k,t.bi),i) l.b=A.aB(n,"message",new A.kM(m),!1) l.a=A.aB(n,"error",new A.kN(p,m,n),!1) q=k s=1 break case 5:case 1:return A.m(q,r)}}) return A.n($async$bW,r)}} A.kP.prototype={ $1(a){return this.a.dV(a)}, $S:1} A.kL.prototype={ $1(a){return this.a.cA(this.b,a)}, $S:1} A.kO.prototype={ $4(a,b,c,d){var s,r=this.b if((r.a.a&30)===0){r.L(new A.c5(!0,a,this.c,d,B.v,c,b)) r=this.a s=r.b if(s!=null)s.J() r=r.a if(r!=null)r.J()}}, $S:61} A.kM.prototype={ $1(a){var s=t.ed.a(A.oL(t.m.a(a.data))) this.a.$4(s.f,s.d,s.c,s.a)}, $S:1} A.kN.prototype={ $1(a){this.b.$4(!1,!1,!1,B.B) this.c.terminate() this.a.b=null}, $S:1} A.c8.prototype={ ae(){return"WasmStorageImplementation."+this.b}} A.bH.prototype={ ae(){return"WebStorageApi."+this.b}} A.hT.prototype={} A.nH.prototype={ kk(){var s=this.Q.ca(this.as) return s}, bs(){var s=0,r=A.o(t.H),q var $async$bs=A.p(function(a,b){if(a===1)return A.l(b,r) while(true)switch(s){case 0:q=A.eX(null,t.H) s=2 return A.c(q,$async$bs) case 2:return A.m(null,r)}}) return A.n($async$bs,r)}, bu(a,b){return this.j2(a,b)}, j2(a,b){var s=0,r=A.o(t.z),q=this var $async$bu=A.p(function(c,d){if(c===1)return A.l(d,r) while(true)switch(s){case 0:q.kD(a,b) s=!q.a?2:3 break case 2:s=4 return A.c(q.bs(),$async$bu) case 4:case 3:return A.m(null,r)}}) return A.n($async$bu,r)}, a8(a,b){return this.ky(a,b)}, ky(a,b){var s=0,r=A.o(t.H),q=this var $async$a8=A.p(function(c,d){if(c===1)return A.l(d,r) while(true)switch(s){case 0:s=2 return A.c(q.bu(a,b),$async$a8) case 2:return A.m(null,r)}}) return A.n($async$a8,r)}, aw(a,b){return this.kz(a,b)}, kz(a,b){var s=0,r=A.o(t.S),q,p=this,o,n var $async$aw=A.p(function(c,d){if(c===1)return A.l(d,r) while(true)switch(s){case 0:s=3 return A.c(p.bu(a,b),$async$aw) case 3:o=p.b.b n=t.C.a(o.a.x2.call(null,o.b)) q=A.h(self.Number(n)) s=1 break case 1:return A.m(q,r)}}) return A.n($async$aw,r)}, d8(a,b){return this.kC(a,b)}, kC(a,b){var s=0,r=A.o(t.S),q,p=this,o var $async$d8=A.p(function(c,d){if(c===1)return A.l(d,r) while(true)switch(s){case 0:s=3 return A.c(p.bu(a,b),$async$d8) case 3:o=p.b.b q=A.h(A.r(o.a.x1.call(null,o.b))) s=1 break case 1:return A.m(q,r)}}) return A.n($async$d8,r)}, av(a){return this.kw(a)}, kw(a){var s=0,r=A.o(t.H),q=this var $async$av=A.p(function(b,c){if(b===1)return A.l(c,r) while(true)switch(s){case 0:q.kv(a) s=!q.a?2:3 break case 2:s=4 return A.c(q.bs(),$async$av) case 4:case 3:return A.m(null,r)}}) return A.n($async$av,r)}, p(){var s=0,r=A.o(t.H),q=this var $async$p=A.p(function(a,b){if(a===1)return A.l(b,r) while(true)switch(s){case 0:s=2 return A.c(q.hH(),$async$p) case 2:q.b.a7() s=3 return A.c(q.bs(),$async$p) case 3:return A.m(null,r)}}) return A.n($async$p,r)}} A.fM.prototype={ fS(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o){var s A.rs("absolute",A.d([a,b,c,d,e,f,g,h,i,j,k,l,m,n,o],t.d4)) s=this.a s=s.S(a)>0&&!s.ab(a) if(s)return a s=this.b return this.h8(0,s==null?A.pf():s,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o)}, aI(a){var s=null return this.fS(a,s,s,s,s,s,s,s,s,s,s,s,s,s,s)}, h8(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q){var s=A.d([b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q],t.d4) A.rs("join",s) return this.ke(new A.eI(s,t.eJ))}, kd(a,b,c){var s=null return this.h8(0,b,c,s,s,s,s,s,s,s,s,s,s,s,s,s,s)}, ke(a){var s,r,q,p,o,n,m,l,k for(s=a.gt(0),r=new A.eH(s,new A.jm()),q=this.a,p=!1,o=!1,n="";r.k();){m=s.gm() if(q.ab(m)&&o){l=A.d4(m,q) k=n.charCodeAt(0)==0?n:n n=B.a.n(k,0,q.bE(k,!0)) l.b=n if(q.c7(n))l.e[0]=q.gbk() n=""+l.j(0)}else if(q.S(m)>0){o=!q.ab(m) n=""+m}else{if(!(m.length!==0&&q.eh(m[0])))if(p)n+=q.gbk() n+=m}p=q.c7(m)}return n.charCodeAt(0)==0?n:n}, aQ(a,b){var s=A.d4(b,this.a),r=s.d,q=A.Q(r).h("aT<1>") q=A.ay(new A.aT(r,new A.jn(),q),!0,q.h("f.E")) s.d=q r=s.b if(r!=null)B.c.cY(q,0,r) return s.d}, bA(a){var s if(!this.iA(a))return a s=A.d4(a,this.a) s.eE() return s.j(0)}, iA(a){var s,r,q,p,o,n,m,l,k=this.a,j=k.S(a) if(j!==0){if(k===$.fu())for(s=0;s0)return o.bA(a) if(m.S(a)<=0||m.ab(a))a=o.aI(a) if(m.S(a)<=0&&m.S(b)>0)throw A.a(A.q1(n+a+'" from "'+b+'".')) s=A.d4(b,m) s.eE() r=A.d4(a,m) r.eE() q=s.d if(q.length!==0&&J.X(q[0],"."))return r.j(0) q=s.b p=r.b if(q!=p)q=q==null||p==null||!m.eG(q,p) else q=!1 if(q)return r.j(0) while(!0){q=s.d if(q.length!==0){p=r.d q=p.length!==0&&m.eG(q[0],p[0])}else q=!1 if(!q)break B.c.d6(s.d,0) B.c.d6(s.e,1) B.c.d6(r.d,0) B.c.d6(r.e,1)}q=s.d if(q.length!==0&&J.X(q[0],".."))throw A.a(A.q1(n+a+'" from "'+b+'".')) q=t.N B.c.ev(r.d,0,A.b_(s.d.length,"..",!1,q)) p=r.e p[0]="" B.c.ev(p,1,A.b_(s.d.length,m.gbk(),!1,q)) m=r.d q=m.length if(q===0)return"." if(q>1&&J.X(B.c.gC(m),".")){B.c.hi(r.d) m=r.e m.pop() m.pop() m.push("")}r.b="" r.hj() return r.j(0)}, ks(a){return this.eJ(a,null)}, iv(a,b){var s,r,q,p,o,n,m,l,k=this a=a b=b r=k.a q=r.S(a)>0 p=r.S(b)>0 if(q&&!p){b=k.aI(b) if(r.ab(a))a=k.aI(a)}else if(p&&!q){a=k.aI(a) if(r.ab(b))b=k.aI(b)}else if(p&&q){o=r.ab(b) n=r.ab(a) if(o&&!n)b=k.aI(b) else if(n&&!o)a=k.aI(a)}m=k.iw(a,b) if(m!==B.n)return m s=null try{s=k.eJ(b,a)}catch(l){if(A.E(l) instanceof A.eu)return B.k else throw l}if(r.S(s)>0)return B.k if(J.X(s,"."))return B.W if(J.X(s,".."))return B.k return J.ae(s)>=3&&J.tH(s,"..")&&r.D(J.tA(s,2))?B.k:B.X}, iw(a,b){var s,r,q,p,o,n,m,l,k,j,i,h,g,f,e=this if(a===".")a="" s=e.a r=s.S(a) q=s.S(b) if(r!==q)return B.k for(p=0;pq.aQ(0,s).length?s:r}} A.jm.prototype={ $1(a){return a!==""}, $S:2} A.jn.prototype={ $1(a){return a.length!==0}, $S:2} A.nY.prototype={ $1(a){return a==null?"null":'"'+a+'"'}, $S:63} A.dB.prototype={ j(a){return this.a}} A.dC.prototype={ j(a){return this.a}} A.k7.prototype={ hu(a){var s=this.S(a) if(s>0)return B.a.n(a,0,s) return this.ab(a)?a[0]:null}, hh(a){var s,r=null,q=a.length if(q===0)return A.aj(r,r,r,r) s=A.jl(r,this).aQ(0,a) if(this.D(a.charCodeAt(q-1)))B.c.v(s,"") return A.aj(r,r,s,r)}, cR(a,b){return a===b}, eG(a,b){return a===b}} A.km.prototype={ geu(){var s=this.d if(s.length!==0)s=J.X(B.c.gC(s),"")||!J.X(B.c.gC(this.e),"") else s=!1 return s}, hj(){var s,r,q=this while(!0){s=q.d if(!(s.length!==0&&J.X(B.c.gC(s),"")))break B.c.hi(q.d) q.e.pop()}s=q.e r=s.length if(r!==0)s[r-1]=""}, eE(){var s,r,q,p,o,n,m=this,l=A.d([],t.s) for(s=m.d,r=s.length,q=0,p=0;p0){s=B.a.aW(a,"\\",s+1) if(s>0)return s}return r}if(r<3)return 0 if(!A.rD(a.charCodeAt(0)))return 0 if(a.charCodeAt(1)!==58)return 0 r=a.charCodeAt(2) if(!(r===47||r===92))return 0 return 3}, S(a){return this.bE(a,!1)}, ab(a){return this.S(a)===1}, d3(a){var s,r if(a.gY()!==""&&a.gY()!=="file")throw A.a(A.K("Uri "+a.j(0)+" must have scheme 'file:'.",null)) s=a.gac() if(a.gbc()===""){if(s.length>=3&&B.a.u(s,"/")&&A.rz(s,1)!=null)s=B.a.hl(s,"/","")}else s="\\\\"+a.gbc()+s r=A.bc(s,"/","\\") return A.p4(r,0,r.length,B.j,!1)}, ec(a){var s,r,q=A.d4(a,this),p=q.b p.toString if(B.a.u(p,"\\\\")){s=new A.aT(A.d(p.split("\\"),t.s),new A.lN(),t.U) B.c.cY(q.d,0,s.gC(0)) if(q.geu())B.c.v(q.d,"") return A.aj(s.gG(0),null,q.d,"file")}else{if(q.d.length===0||q.geu())B.c.v(q.d,"") p=q.d r=q.b r.toString r=A.bc(r,"/","") B.c.cY(p,0,A.bc(r,"\\","")) return A.aj(null,null,q.d,"file")}}, cR(a,b){var s if(a===b)return!0 if(a===47)return b===92 if(a===92)return b===47 if((a^b)!==32)return!1 s=a|32 return s>=97&&s<=122}, eG(a,b){var s,r if(a===b)return!0 s=a.length if(s!==b.length)return!1 for(r=0;r")).aq(0,", ")}return q.charCodeAt(0)==0?q:q}, $ia5:1} A.kS.prototype={ $1(a){if(t.p.b(a))return"blob ("+a.length+" bytes)" else return J.aU(a)}, $S:64} A.cj.prototype={} A.ku.prototype={} A.hB.prototype={} A.kv.prototype={} A.kx.prototype={} A.kw.prototype={} A.d8.prototype={} A.d9.prototype={} A.h_.prototype={ a7(){var s,r,q,p,o,n,m for(s=this.d,r=s.length,q=0;q255)A.y(A.ag(e,"functionName","Must not exceed 255 bytes when utf-8 encoded")) s=new Uint8Array(A.iJ(l)) r=c?526337:2049 q=m.a p=q.c0(s,1) m=A.cM(q.w,"call",[null,m.b,p,a.a,r,q.c.kr(new A.ht(new A.jt(d),n,n))]) o=A.h(m) q.e.call(null,p) if(o!==0)A.iO(this,o,n,n,n)}, a6(a,b,c,d){return this.fY(a,b,!0,c,d)}, a7(){var s,r,q,p=this if(p.e)return $.dY().h_(p) p.e=!0 for(s=p.d,r=0;!1;++r)s[r].p() s=p.b q=s.a q.c.r=null q.Q.call(null,s.b,-1) p.c.a7()}, h1(a){var s,r,q,p,o=this,n=B.t if(J.ae(n)===0){if(o.e)A.y(A.B("This database has already been closed")) r=o.b q=r.a s=q.c0(B.i.a5(a),1) p=A.h(A.cM(q.dx,"call",[null,r.b,s,0,0,0])) q.e.call(null,s) if(p!==0)A.iO(o,p,"executing",a,n)}else{s=o.d4(a,!0) try{s.h2(new A.cr(n))}finally{s.a7()}}}, iN(a,b,c,a0,a1){var s,r,q,p,o,n,m,l,k,j,i,h,g,f,e,d=this if(d.e)A.y(A.B("This database has already been closed")) s=B.i.a5(a) r=d.b q=r.a p=q.bw(s) o=q.d n=A.h(A.r(o.call(null,4))) o=A.h(A.r(o.call(null,4))) m=new A.lz(r,p,n,o) l=A.d([],t.bb) k=new A.js(m,l) for(r=s.length,q=q.b,j=0;jb||b>=o)A.y(A.h4(b,o,this,null,"index")) s=this.b[b] r=p.i(0,b) p=r.a q=r.b switch(A.h(A.r(p.jU.call(null,q)))){case 1:q=t.C.a(p.jV.call(null,q)) return A.h(self.Number(q)) case 2:return A.r(p.jW.call(null,q)) case 3:o=A.h(A.r(p.h3.call(null,q))) return A.c9(p.b,A.h(A.r(p.jX.call(null,q))),o) case 4:o=A.h(A.r(p.h3.call(null,q))) return A.qv(p.b,A.h(A.r(p.jY.call(null,q))),o) case 5:default:return null}}, q(a,b,c){throw A.a(A.K("The argument list is unmodifiable",null))}} A.br.prototype={} A.o4.prototype={ $1(a){a.a7()}, $S:66} A.kR.prototype={ ca(a){var s,r,q,p,o,n,m,l,k switch(2){case 2:break}s=this.a r=s.b q=r.c0(B.i.a5(a),1) p=A.h(A.r(r.d.call(null,4))) o=A.h(A.r(A.cM(r.ay,"call",[null,q,p,6,0]))) n=A.ct(r.b.buffer,0,null)[B.b.P(p,2)] m=r.e m.call(null,q) m.call(null,0) m=new A.ln(r,n) if(o!==0){l=A.pe(s,m,o,"opening the database",null,null) A.h(A.r(r.ch.call(null,n))) throw A.a(l)}A.h(A.r(r.db.call(null,n,1))) r=A.d([],t.eC) k=new A.h_(s,m,A.d([],t.eV)) r=new A.jr(s,m,k,r) s=$.dY().a if(s!=null)s.register(r,k,r) return r}} A.cX.prototype={ a7(){var s,r=this if(!r.d){r.d=!0 r.bR() s=r.b s.ba() A.h(A.r(s.c.to.call(null,s.b)))}}, bR(){if(!this.c){var s=this.b A.h(A.r(s.c.id.call(null,s.b))) this.c=!0}}} A.df.prototype={ gi0(){var s,r,q,p,o,n=this.a,m=n.c,l=n.b,k=A.h(A.r(m.fy.call(null,l))) n=A.d([],t.s) for(s=m.go,m=m.b,r=0;r."))}return s}, dt(a){$label0$0:{this.hW(a.a) break $label0$0}}, a7(){var s,r=this.c if(!r.d){$.dY().h_(this) r.a7() s=this.b if(!s.e)B.c.A(s.c.d,r)}}, eR(a){var s=this if(s.c.d)A.y(A.B(u.D)) s.bR() s.dt(a) return s.j3()}, h2(a){var s=this if(s.c.d)A.y(A.B(u.D)) s.bR() s.dt(a) s.ff()}} A.jo.prototype={ hY(){var s,r,q,p,o=A.a3(t.N,t.S) for(s=this.a,r=s.length,q=0;q")).R(a,b,c,d)}, aX(a,b,c){return this.R(a,null,b,c)}} A.iW.prototype={ $0(){var s,r=this,q=r.c.next(),p=r.a p.a=q s=r.d A.a_(q,t.m).bG(new A.iY(p,r.b,s,r),s.gfT(),t.P)}, $S:0} A.iY.prototype={ $1(a){var s,r,q=this,p=a.done if(p==null)p=null s=a.value r=q.c if(p===!0){r.p() q.a.a=null}else{r.v(0,s==null?q.b.$ti.c.a(s):s) q.a.a=null p=r.b if(!((p&1)!==0?(r.gaT().e&4)!==0:(p&2)===0))q.d.$0()}}, $S:10} A.iX.prototype={ $0(){var s,r if(this.a.a==null){s=this.b r=s.b s=!((r&1)!==0?(s.gaT().e&4)!==0:(r&2)===0)}else s=!1 if(s)this.c.$0()}, $S:0} A.cD.prototype={ J(){var s=0,r=A.o(t.H),q=this,p var $async$J=A.p(function(a,b){if(a===1)return A.l(b,r) while(true)switch(s){case 0:p=q.b if(p!=null)p.J() p=q.c if(p!=null)p.J() q.c=q.b=null return A.m(null,r)}}) return A.n($async$J,r)}, gm(){var s=this.a return s==null?A.y(A.B("Await moveNext() first")):s}, k(){var s,r,q=this,p=q.a if(p!=null)p.continue() p=new A.k($.i,t.k) s=new A.a8(p,t.fa) r=q.d q.b=A.aB(r,"success",new A.m6(q,s),!1) q.c=A.aB(r,"error",new A.m7(q,s),!1) return p}} A.m6.prototype={ $1(a){var s,r=this.a r.J() s=r.$ti.h("1?").a(r.d.result) r.a=s this.b.L(s!=null)}, $S:1} A.m7.prototype={ $1(a){var s=this.a s.J() s=s.d.error if(s==null)s=a this.b.aK(s)}, $S:1} A.jd.prototype={ $1(a){this.a.L(this.c.a(this.b.result))}, $S:1} A.je.prototype={ $1(a){var s=this.b.error if(s==null)s=a this.a.aK(s)}, $S:1} A.ji.prototype={ $1(a){this.a.L(this.c.a(this.b.result))}, $S:1} A.jj.prototype={ $1(a){var s=this.b.error if(s==null)s=a this.a.aK(s)}, $S:1} A.jk.prototype={ $1(a){var s=this.b.error if(s==null)s=a this.a.aK(s)}, $S:1} A.hV.prototype={ hO(a){var s,r,q,p,o,n,m=self,l=m.Object.keys(a.exports) l=B.c.gt(l) s=this.b r=t.m q=this.a p=t.g for(;l.k();){o=A.ad(l.gm()) n=a.exports[o] if(typeof n==="function")q.q(0,o,p.a(n)) else if(n instanceof m.WebAssembly.Global)s.q(0,o,r.a(n))}}} A.lu.prototype={ $2(a,b){var s={} this.a[a]=s b.aa(0,new A.lt(s))}, $S:67} A.lt.prototype={ $2(a,b){this.a[a]=b}, $S:68} A.hW.prototype={} A.dl.prototype={ j_(a,b){var s,r,q=this.e q.hq(b) s=this.d.b r=self r.Atomics.store(s,1,-1) r.Atomics.store(s,0,a.a) A.tL(s,0) r.Atomics.wait(s,1,-1) s=r.Atomics.load(s,1) if(s!==0)throw A.a(A.cz(s)) return a.d.$1(q)}, a2(a,b){var s=t.cb return this.j_(a,b,s,s)}, ck(a,b){return this.a2(B.H,new A.aQ(a,b,0,0)).a}, da(a,b){this.a2(B.G,new A.aQ(a,b,0,0))}, dc(a){var s=this.r.aI(a) if($.iQ().iv("/",s)!==B.X)throw A.a(B.ai) return s}, aZ(a,b){var s=a.a,r=this.a2(B.S,new A.aQ(s==null?A.ov(this.b,"/"):s,b,0,0)) return new A.cH(new A.hU(this,r.b),r.a)}, de(a){this.a2(B.M,new A.S(B.b.I(a.a,1000),0,0))}, p(){this.a2(B.I,B.h)}} A.hU.prototype={ geP(){return 2048}, eI(a,b){var s,r,q,p,o,n,m,l,k,j=a.length for(s=this.a,r=this.b,q=s.e.a,p=t.Z,o=0;j>0;){n=Math.min(65536,j) j-=n m=s.a2(B.Q,new A.S(r,b+o,n)).a l=self.Uint8Array k=[q] k.push(0) k.push(m) A.ha(a,"set",p.a(A.dT(l,k)),o,null,null) o+=m if(m0;){o=Math.min(65536,k) if(o===k)n=a else{m=a.buffer l=a.byteOffset n=new Uint8Array(m,l,o)}A.ha(r,"set",n,0,null,null) s.a2(B.L,new A.S(q,b+p,o)) p+=o k-=o}}} A.kz.prototype={} A.bi.prototype={ hq(a){var s,r if(!(a instanceof A.aW))if(a instanceof A.S){s=this.b s.setInt32(0,a.a,!1) s.setInt32(4,a.b,!1) s.setInt32(8,a.c,!1) if(a instanceof A.aQ){r=B.i.a5(a.d) s.setInt32(12,r.length,!1) B.e.aD(this.c,16,r)}}else throw A.a(A.H("Message "+a.j(0)))}} A.ac.prototype={ ae(){return"WorkerOperation."+this.b}, kq(a){return this.c.$1(a)}} A.bw.prototype={} A.aW.prototype={} A.S.prototype={} A.aQ.prototype={} A.it.prototype={} A.eG.prototype={ bS(a,b){return this.iX(a,b)}, fD(a){return this.bS(a,!1)}, iX(a,b){var s=0,r=A.o(t.eg),q,p=this,o,n,m,l,k,j,i,h,g var $async$bS=A.p(function(c,d){if(c===1)return A.l(d,r) while(true)switch(s){case 0:j=$.fw() i=j.eJ(a,"/") h=j.aQ(0,i) g=h.length j=g>=1 o=null if(j){n=g-1 m=B.c.a0(h,0,n) o=h[n]}else m=null if(!j)throw A.a(A.B("Pattern matching error")) l=p.c j=m.length,n=t.m,k=0 case 3:if(!(k") l=A.ay(new A.b8(j,m),!0,m.h("f.E")) B.c.hB(l) s=3 return A.c(A.ou(new A.D(l,new A.j_(new A.j0(o,a),b),A.Q(l).h("D<1,C<~>>")),t.H),$async$b7) case 3:s=b.c!==n.length?4:5 break case 4:k=new A.cD(p.objectStore("files").openCursor(a),t.V) s=6 return A.c(k.k(),$async$b7) case 6:s=7 return A.c(A.bf(k.gm().update({name:n.name,length:b.c}),t.X),$async$b7) case 7:case 5:return A.m(null,r)}}) return A.n($async$b7,r)}, bi(a,b,c){return this.kF(0,b,c)}, kF(a,b,c){var s=0,r=A.o(t.H),q=this,p,o,n,m,l,k var $async$bi=A.p(function(d,e){if(d===1)return A.l(e,r) while(true)switch(s){case 0:k=q.a k.toString p=k.transaction($.ok(),"readwrite") o=p.objectStore("files") n=p.objectStore("blocks") s=2 return A.c(q.e0(p,b),$async$bi) case 2:m=e s=m.length>c?3:4 break case 3:s=5 return A.c(A.bf(n.delete(q.iP(b,B.b.I(c,4096)*4096+1)),t.X),$async$bi) case 5:case 4:l=new A.cD(o.openCursor(b),t.V) s=6 return A.c(l.k(),$async$bi) case 6:s=7 return A.c(A.bf(l.gm().update({name:m.name,length:c}),t.X),$async$bi) case 7:return A.m(null,r)}}) return A.n($async$bi,r)}, cU(a){return this.jL(a)}, jL(a){var s=0,r=A.o(t.H),q=this,p,o,n var $async$cU=A.p(function(b,c){if(b===1)return A.l(c,r) while(true)switch(s){case 0:n=q.a n.toString p=n.transaction(A.d(["files","blocks"],t.s),"readwrite") o=q.e_(a,9007199254740992,0) n=t.X s=2 return A.c(A.ou(A.d([A.bf(p.objectStore("blocks").delete(o),n),A.bf(p.objectStore("files").delete(a),n)],t.fG),t.H),$async$cU) case 2:return A.m(null,r)}}) return A.n($async$cU,r)}} A.j1.prototype={ $1(a){var s=t.m.a(this.a.result) if(J.X(a.oldVersion,0)){s.createObjectStore("files",{autoIncrement:!0}).createIndex("fileName","name",{unique:!0}) s.createObjectStore("blocks")}}, $S:10} A.iZ.prototype={ $1(a){if(a==null)throw A.a(A.ag(this.a,"fileId","File not found in database")) else return a}, $S:70} A.j2.prototype={ $0(){var s=0,r=A.o(t.H),q=this,p,o,n,m var $async$$0=A.p(function(a,b){if(a===1)return A.l(b,r) while(true)switch(s){case 0:p=B.e o=q.b n=q.c m=A s=2 return A.c(A.ky(t.m.a(q.a.value)),$async$$0) case 2:p.aD(o,n,m.bj(b,0,q.d)) return A.m(null,r)}}) return A.n($async$$0,r)}, $S:3} A.j0.prototype={ hr(a,b){var s=0,r=A.o(t.H),q=this,p,o,n,m,l,k var $async$$2=A.p(function(c,d){if(c===1)return A.l(d,r) while(true)switch(s){case 0:p=q.a o=self n=q.b m=t.n s=2 return A.c(A.bf(p.openCursor(o.IDBKeyRange.only(A.d([n,a],m))),t.A),$async$$2) case 2:l=d k=new o.Blob(A.d([b],t.as)) o=t.X s=l==null?3:5 break case 3:s=6 return A.c(A.bf(p.put(k,A.d([n,a],m)),o),$async$$2) case 6:s=4 break case 5:s=7 return A.c(A.bf(l.update(k),o),$async$$2) case 7:case 4:return A.m(null,r)}}) return A.n($async$$2,r)}, $2(a,b){return this.hr(a,b)}, $S:71} A.j_.prototype={ $1(a){var s=this.b.b.i(0,a) s.toString return this.a.$2(a,s)}, $S:72} A.mh.prototype={ ji(a,b,c){B.e.aD(this.b.hg(a,new A.mi(this,a)),b,c)}, jA(a,b){var s,r,q,p,o,n,m,l,k for(s=b.length,r=0;rp)B.e.aD(s,0,A.bj(r.buffer,r.byteOffset+p,Math.min(4096,q-p))) return s}, $S:73} A.iq.prototype={} A.cY.prototype={ bX(a){var s=this if(s.e||s.d.a==null)A.y(A.cz(10)) if(a.ew(s.w)){s.fJ() return a.d.a}else return A.aX(null,t.H)}, fJ(){var s,r,q=this if(q.f==null&&!q.w.gF(0)){s=q.w r=q.f=s.gG(0) s.A(0,r) r.d.L(A.u8(r.gd7(),t.H).ai(new A.k2(q)))}}, p(){var s=0,r=A.o(t.H),q,p=this,o,n var $async$p=A.p(function(a,b){if(a===1)return A.l(b,r) while(true)switch(s){case 0:if(!p.e){o=p.bX(new A.dt(p.d.gb9(),new A.a8(new A.k($.i,t.D),t.F))) p.e=!0 q=o s=1 break}else{n=p.w if(!n.gF(0)){q=n.gC(0).d.a s=1 break}}case 1:return A.m(q,r)}}) return A.n($async$p,r)}, br(a){return this.ii(a)}, ii(a){var s=0,r=A.o(t.S),q,p=this,o,n var $async$br=A.p(function(b,c){if(b===1)return A.l(c,r) while(true)switch(s){case 0:n=p.y s=n.a4(a)?3:5 break case 3:n=n.i(0,a) n.toString q=n s=1 break s=4 break case 5:s=6 return A.c(p.d.cV(a),$async$br) case 6:o=c o.toString n.q(0,a,o) q=o s=1 break case 4:case 1:return A.m(q,r)}}) return A.n($async$br,r)}, bQ(){var s=0,r=A.o(t.H),q=this,p,o,n,m,l,k,j var $async$bQ=A.p(function(a,b){if(a===1)return A.l(b,r) while(true)switch(s){case 0:m=q.d s=2 return A.c(m.d1(),$async$bQ) case 2:l=b q.y.aJ(0,l) p=l.gel(),p=p.gt(p),o=q.r.d case 3:if(!p.k()){s=4 break}n=p.gm() k=o j=n.a s=5 return A.c(m.bC(n.b),$async$bQ) case 5:k.q(0,j,b) s=3 break case 4:return A.m(null,r)}}) return A.n($async$bQ,r)}, ck(a,b){return this.r.d.a4(a)?1:0}, da(a,b){var s=this s.r.d.A(0,a) if(!s.x.A(0,a))s.bX(new A.dr(s,a,new A.a8(new A.k($.i,t.D),t.F)))}, dc(a){return $.fw().bA("/"+a)}, aZ(a,b){var s,r,q,p=this,o=a.a if(o==null)o=A.ov(p.b,"/") s=p.r r=s.d.a4(o)?1:0 q=s.aZ(new A.ez(o),b) if(r===0)if((b&8)!==0)p.x.v(0,o) else p.bX(new A.cC(p,o,new A.a8(new A.k($.i,t.D),t.F))) return new A.cH(new A.ij(p,q.a,o),0)}, de(a){}} A.k2.prototype={ $0(){var s=this.a s.f=null s.fJ()}, $S:9} A.ij.prototype={ eQ(a,b){this.b.eQ(a,b)}, geP(){return 0}, d9(){return this.b.d>=2?1:0}, cl(){}, cm(){return this.b.cm()}, dd(a){this.b.d=a return null}, df(a){}, cn(a){var s=this,r=s.a if(r.e||r.d.a==null)A.y(A.cz(10)) s.b.cn(a) if(!r.x.M(0,s.c))r.bX(new A.dt(new A.mw(s,a),new A.a8(new A.k($.i,t.D),t.F)))}, dg(a){this.b.d=a return null}, bH(a,b){var s,r,q,p,o,n=this.a if(n.e||n.d.a==null)A.y(A.cz(10)) s=this.c r=n.r.d.i(0,s) if(r==null)r=new Uint8Array(0) this.b.bH(a,b) if(!n.x.M(0,s)){q=new Uint8Array(a.length) B.e.aD(q,0,a) p=A.d([],t.gQ) o=$.i p.push(new A.iq(b,q)) n.bX(new A.cK(n,s,r,p,new A.a8(new A.k(o,t.D),t.F)))}}, $idj:1} A.mw.prototype={ $0(){var s=0,r=A.o(t.H),q,p=this,o,n,m var $async$$0=A.p(function(a,b){if(a===1)return A.l(b,r) while(true)switch(s){case 0:o=p.a n=o.a m=n.d s=3 return A.c(n.br(o.c),$async$$0) case 3:q=m.bi(0,b,p.b) s=1 break case 1:return A.m(q,r)}}) return A.n($async$$0,r)}, $S:3} A.ao.prototype={ ew(a){a.dT(a.c,this,!1) return!0}} A.dt.prototype={ U(){return this.w.$0()}} A.dr.prototype={ ew(a){var s,r,q,p if(!a.gF(0)){s=a.gC(0) for(r=this.x;s!=null;)if(s instanceof A.dr)if(s.x===r)return!1 else s=s.gcc() else if(s instanceof A.cK){q=s.gcc() if(s.x===r){p=s.a p.toString p.e4(A.t(s).h("aF.E").a(s))}s=q}else if(s instanceof A.cC){if(s.x===r){r=s.a r.toString r.e4(A.t(s).h("aF.E").a(s)) return!1}s=s.gcc()}else break}a.dT(a.c,this,!1) return!0}, U(){var s=0,r=A.o(t.H),q=this,p,o,n var $async$U=A.p(function(a,b){if(a===1)return A.l(b,r) while(true)switch(s){case 0:p=q.w o=q.x s=2 return A.c(p.br(o),$async$U) case 2:n=b p.y.A(0,o) s=3 return A.c(p.d.cU(n),$async$U) case 3:return A.m(null,r)}}) return A.n($async$U,r)}} A.cC.prototype={ U(){var s=0,r=A.o(t.H),q=this,p,o,n,m var $async$U=A.p(function(a,b){if(a===1)return A.l(b,r) while(true)switch(s){case 0:p=q.w o=q.x n=p.y m=o s=2 return A.c(p.d.cS(o),$async$U) case 2:n.q(0,m,b) return A.m(null,r)}}) return A.n($async$U,r)}} A.cK.prototype={ ew(a){var s,r=a.b===0?null:a.gC(0) for(s=this.x;r!=null;)if(r instanceof A.cK)if(r.x===s){B.c.aJ(r.z,this.z) return!1}else r=r.gcc() else if(r instanceof A.cC){if(r.x===s)break r=r.gcc()}else break a.dT(a.c,this,!1) return!0}, U(){var s=0,r=A.o(t.H),q=this,p,o,n,m,l,k var $async$U=A.p(function(a,b){if(a===1)return A.l(b,r) while(true)switch(s){case 0:m=q.y l=new A.mh(m,A.a3(t.S,t.p),m.length) for(m=q.z,p=m.length,o=0;o=2?1:0}, cl(){if(this.c)this.a.d.A(0,this.b)}, cm(){return this.a.d.i(0,this.b).length}, dd(a){this.d=a}, df(a){}, cn(a){var s=this.a.d,r=this.b,q=s.i(0,r),p=new Uint8Array(a) if(q!=null)B.e.aj(p,0,Math.min(a,q.length),q) s.q(0,r,p)}, dg(a){this.d=a}, bH(a,b){var s,r,q,p,o=this.a.d,n=this.b,m=o.i(0,n) if(m==null)m=new Uint8Array(0) s=b+a.length r=m.length q=s-r if(q<=0)B.e.aj(m,b,s,a) else{p=new Uint8Array(r+q) B.e.aD(p,0,m) B.e.aD(p,b,a) o.q(0,n,p)}}} A.cW.prototype={ ae(){return"FileType."+this.b}} A.de.prototype={ dU(a,b){var s=this.e,r=b?1:0 s[a.a]=r A.ot(this.d,s,{at:0})}, ck(a,b){var s,r=$.ol().i(0,a) if(r==null)return this.r.d.a4(a)?1:0 else{s=this.e A.jM(this.d,s,{at:0}) return s[r.a]}}, da(a,b){var s=$.ol().i(0,a) if(s==null){this.r.d.A(0,a) return null}else this.dU(s,!1)}, dc(a){return $.fw().bA("/"+a)}, aZ(a,b){var s,r,q,p=this,o=a.a if(o==null)return p.r.aZ(a,b) s=$.ol().i(0,o) if(s==null)return p.r.aZ(a,b) r=p.e A.jM(p.d,r,{at:0}) r=r[s.a] q=p.f.i(0,s) q.toString if(r===0)if((b&4)!==0){q.truncate(0) p.dU(s,!0)}else throw A.a(B.ai) return new A.cH(new A.iz(p,s,q,(b&8)!==0),0)}, de(a){}, p(){var s,r,q this.d.close() for(s=this.f.gaP(),r=A.t(s),s=new A.b0(J.M(s.a),s.b,r.h("b0<1,2>")),r=r.y[1];s.k();){q=s.a if(q==null)q=r.a(q) q.close()}}} A.kQ.prototype={ ht(a){var s=0,r=A.o(t.m),q,p=this,o,n var $async$$1=A.p(function(b,c){if(b===1)return A.l(c,r) while(true)switch(s){case 0:o=t.m n=A s=4 return A.c(A.a_(p.a.getFileHandle(a,{create:!0}),o),$async$$1) case 4:s=3 return A.c(n.a_(c.createSyncAccessHandle(),o),$async$$1) case 3:q=c s=1 break case 1:return A.m(q,r)}}) return A.n($async$$1,r)}, $1(a){return this.ht(a)}, $S:74} A.iz.prototype={ eI(a,b){return A.jM(this.c,a,{at:b})}, d9(){return this.e>=2?1:0}, cl(){var s=this s.c.flush() if(s.d)s.a.dU(s.b,!1)}, cm(){return this.c.getSize()}, dd(a){this.e=a}, df(a){this.c.flush()}, cn(a){this.c.truncate(a)}, dg(a){this.e=a}, bH(a,b){if(A.ot(this.c,a,{at:b})q.c)throw A.a(A.cz(14)) s=A.bj(q.d.buffer,0,null) r=q.e B.e.aD(s,r,p) s[r+o]=0}, $S:0} A.n6.prototype={ $3(a,b,c){var s=this.a.d.e.i(0,a) s.toString return A.aL(new A.mM(s,this.b,c,b))}, $S:27} A.mM.prototype={ $0(){var s=this s.a.kH(A.bj(s.b.buffer,s.c,s.d))}, $S:0} A.n7.prototype={ $2(a,b){var s=this.a.d.e.i(0,a) s.toString return A.aL(new A.mL(s,b))}, $S:4} A.mL.prototype={ $0(){this.a.de(A.pJ(this.b,0))}, $S:0} A.n8.prototype={ $2(a,b){var s this.a.d.e.i(0,a).toString s=Date.now() s=self.BigInt(s) A.ha(A.q_(this.b.buffer,0,null),"setBigInt64",b,s,!0,null)}, $S:79} A.n9.prototype={ $1(a){return this.a.d.f.i(0,a).geP()}, $S:12} A.na.prototype={ $1(a){var s=this.a,r=s.d.f.i(0,a) r.toString return A.aL(new A.mK(s,r,a))}, $S:12} A.mK.prototype={ $0(){this.b.cl() this.a.d.f.A(0,this.c)}, $S:0} A.nb.prototype={ $4(a,b,c,d){var s=this.a.d.f.i(0,a) s.toString return A.aL(new A.mJ(s,this.b,b,c,d))}, $S:32} A.mJ.prototype={ $0(){var s=this s.a.eQ(A.bj(s.b.buffer,s.c,s.d),A.h(self.Number(s.e)))}, $S:0} A.mQ.prototype={ $4(a,b,c,d){var s=this.a.d.f.i(0,a) s.toString return A.aL(new A.mI(s,this.b,b,c,d))}, $S:32} A.mI.prototype={ $0(){var s=this s.a.bH(A.bj(s.b.buffer,s.c,s.d),A.h(self.Number(s.e)))}, $S:0} A.mR.prototype={ $2(a,b){var s=this.a.d.f.i(0,a) s.toString return A.aL(new A.mH(s,b))}, $S:81} A.mH.prototype={ $0(){return this.a.cn(A.h(self.Number(this.b)))}, $S:0} A.mS.prototype={ $2(a,b){var s=this.a.d.f.i(0,a) s.toString return A.aL(new A.mG(s,b))}, $S:4} A.mG.prototype={ $0(){return this.a.df(this.b)}, $S:0} A.mT.prototype={ $2(a,b){var s=this.a.d.f.i(0,a) s.toString return A.aL(new A.mF(s,this.b,b))}, $S:4} A.mF.prototype={ $0(){var s=this.a.cm() A.ct(this.b.buffer,0,null)[B.b.P(this.c,2)]=s}, $S:0} A.mU.prototype={ $2(a,b){var s=this.a.d.f.i(0,a) s.toString return A.aL(new A.mA(s,b))}, $S:4} A.mA.prototype={ $0(){return this.a.dd(this.b)}, $S:0} A.mV.prototype={ $2(a,b){var s=this.a.d.f.i(0,a) s.toString return A.aL(new A.mz(s,b))}, $S:4} A.mz.prototype={ $0(){return this.a.dg(this.b)}, $S:0} A.mW.prototype={ $2(a,b){var s=this.a.d.f.i(0,a) s.toString return A.aL(new A.my(s,this.b,b))}, $S:4} A.my.prototype={ $0(){var s=this.a.d9() A.ct(this.b.buffer,0,null)[B.b.P(this.c,2)]=s}, $S:0} A.mX.prototype={ $3(a,b,c){var s=this.a,r=s.a r===$&&A.G() r=s.d.b.i(0,A.h(A.r(r.xr.call(null,a)))).a s=s.a r.$2(new A.c7(s,a),new A.dk(s,b,c))}, $S:18} A.mY.prototype={ $3(a,b,c){var s=this.a,r=s.a r===$&&A.G() r=s.d.b.i(0,A.h(A.r(r.xr.call(null,a)))).b s=s.a r.$2(new A.c7(s,a),new A.dk(s,b,c))}, $S:18} A.mZ.prototype={ $3(a,b,c){var s=this.a,r=s.a r===$&&A.G() s.d.b.i(0,A.h(A.r(r.xr.call(null,a)))).toString s=s.a null.$2(new A.c7(s,a),new A.dk(s,b,c))}, $S:18} A.n0.prototype={ $1(a){var s=this.a,r=s.a r===$&&A.G() s.d.b.i(0,A.h(A.r(r.xr.call(null,a)))).c.$1(new A.c7(s.a,a))}, $S:15} A.n1.prototype={ $1(a){var s=this.a,r=s.a r===$&&A.G() s.d.b.i(0,A.h(A.r(r.xr.call(null,a)))).toString null.$1(new A.c7(s.a,a))}, $S:15} A.n2.prototype={ $1(a){this.a.d.b.A(0,a)}, $S:15} A.n3.prototype={ $5(a,b,c,d,e){var s=this.b,r=A.oM(s,c,b),q=A.oM(s,e,d) this.a.d.b.i(0,a).toString return null.$2(r,q)}, $S:29} A.n4.prototype={ $5(a,b,c,d,e){A.c9(this.b,d,null)}, $S:83} A.jp.prototype={ kr(a){var s=this.a++ this.b.q(0,s,a) return s}} A.ht.prototype={} A.be.prototype={ ho(){var s=this.a return A.qj(new A.ed(s,new A.j8(),A.Q(s).h("ed<1,N>")),null)}, j(a){var s=this.a,r=A.Q(s) return new A.D(s,new A.j6(new A.D(s,new A.j7(),r.h("D<1,b>")).eo(0,0,B.x)),r.h("D<1,j>")).aq(0,u.q)}, $ia0:1} A.j3.prototype={ $1(a){return a.length!==0}, $S:2} A.j8.prototype={ $1(a){return a.gc3()}, $S:84} A.j7.prototype={ $1(a){var s=a.gc3() return new A.D(s,new A.j5(),A.Q(s).h("D<1,b>")).eo(0,0,B.x)}, $S:85} A.j5.prototype={ $1(a){return a.gbz().length}, $S:34} A.j6.prototype={ $1(a){var s=a.gc3() return new A.D(s,new A.j4(this.a),A.Q(s).h("D<1,j>")).c5(0)}, $S:87} A.j4.prototype={ $1(a){return B.a.hd(a.gbz(),this.a)+" "+A.u(a.geC())+"\n"}, $S:35} A.N.prototype={ geA(){var s=this.a if(s.gY()==="data")return"data:..." return $.iQ().ko(s)}, gbz(){var s,r=this,q=r.b if(q==null)return r.geA() s=r.c if(s==null)return r.geA()+" "+A.u(q) return r.geA()+" "+A.u(q)+":"+A.u(s)}, j(a){return this.gbz()+" in "+A.u(this.d)}, geC(){return this.d}} A.jU.prototype={ $0(){var s,r,q,p,o,n,m,l=null,k=this.a if(k==="...")return new A.N(A.aj(l,l,l,l),l,l,"...") s=$.tu().a9(k) if(s==null)return new A.bl(A.aj(l,"unparsed",l,l),k) k=s.b r=k[1] r.toString q=$.td() r=A.bc(r,q,"") p=A.bc(r,"","") r=k[2] q=r q.toString if(B.a.u(q,"1?A.aN(n[1],l):l return new A.N(o,m,k>2?A.aN(n[2],l):l,p)}, $S:11} A.jS.prototype={ $0(){var s,r,q,p,o,n="",m=this.a,l=$.tt().a9(m) if(l!=null){s=l.aN("member") m=l.aN("uri") m.toString r=A.h1(m) m=l.aN("index") m.toString q=l.aN("offset") q.toString p=A.aN(q,16) if(!(s==null))m=s return new A.N(r,1,p+1,m)}l=$.tp().a9(m) if(l!=null){m=new A.jT(m) q=l.b o=q[2] if(o!=null){o=o o.toString q=q[1] q.toString q=A.bc(q,"",n) q=A.bc(q,"Anonymous function",n) return m.$2(o,A.bc(q,"(anonymous function)",n))}else{q=q[3] q.toString return m.$2(q,n)}}return new A.bl(A.aj(null,"unparsed",null,null),m)}, $S:11} A.jT.prototype={ $2(a,b){var s,r,q,p,o,n=null,m=$.to(),l=m.a9(a) for(;l!=null;a=s){s=l.b[1] s.toString l=m.a9(s)}if(a==="native")return new A.N(A.bm("native"),n,n,b) r=$.tq().a9(a) if(r==null)return new A.bl(A.aj(n,"unparsed",n,n),this.a) m=r.b s=m[1] s.toString q=A.h1(s) s=m[2] s.toString p=A.aN(s,n) o=m[3] return new A.N(q,p,o!=null?A.aN(o,n):n,b)}, $S:90} A.jP.prototype={ $0(){var s,r,q,p,o=null,n=this.a,m=$.te().a9(n) if(m==null)return new A.bl(A.aj(o,"unparsed",o,o),n) n=m.b s=n[1] s.toString r=A.bc(s,"/<","") s=n[2] s.toString q=A.h1(s) n=n[3] n.toString p=A.aN(n,o) return new A.N(q,p,o,r.length===0||r==="anonymous"?"":r)}, $S:11} A.jQ.prototype={ $0(){var s,r,q,p,o,n,m,l,k=null,j=this.a,i=$.tg().a9(j) if(i!=null){s=i.b r=s[3] q=r q.toString if(B.a.M(q," line "))return A.u0(j) j=r j.toString p=A.h1(j) o=s[1] if(o!=null){j=s[2] j.toString o+=B.c.c5(A.b_(B.a.ed("/",j).gl(0),".",!1,t.N)) if(o==="")o="" o=B.a.hl(o,$.tl(),"")}else o="" j=s[4] if(j==="")n=k else{j=j j.toString n=A.aN(j,k)}j=s[5] if(j==null||j==="")m=k else{j=j j.toString m=A.aN(j,k)}return new A.N(p,n,m,o)}i=$.ti().a9(j) if(i!=null){j=i.aN("member") j.toString s=i.aN("uri") s.toString p=A.h1(s) s=i.aN("index") s.toString r=i.aN("offset") r.toString l=A.aN(r,16) if(!(j.length!==0))j=s return new A.N(p,1,l+1,j)}i=$.tm().a9(j) if(i!=null){j=i.aN("member") j.toString return new A.N(A.aj(k,"wasm code",k,k),k,k,j)}return new A.bl(A.aj(k,"unparsed",k,k),j)}, $S:11} A.jR.prototype={ $0(){var s,r,q,p,o=null,n=this.a,m=$.tj().a9(n) if(m==null)throw A.a(A.ai("Couldn't parse package:stack_trace stack trace line '"+n+"'.",o,o)) n=m.b s=n[1] if(s==="data:...")r=A.qr("") else{s=s s.toString r=A.bm(s)}if(r.gY()===""){s=$.iQ() r=s.hp(s.fS(s.a.d3(A.pa(r)),o,o,o,o,o,o,o,o,o,o,o,o,o,o))}s=n[2] if(s==null)q=o else{s=s s.toString q=A.aN(s,o)}s=n[3] if(s==null)p=o else{s=s s.toString p=A.aN(s,o)}return new A.N(r,q,p,n[4])}, $S:11} A.hd.prototype={ gfQ(){var s,r=this,q=r.b if(q===$){s=r.a.$0() r.b!==$&&A.oj() r.b=s q=s}return q}, gc3(){return this.gfQ().gc3()}, j(a){return this.gfQ().j(0)}, $ia0:1, $ia1:1} A.a1.prototype={ j(a){var s=this.a,r=A.Q(s) return new A.D(s,new A.l9(new A.D(s,new A.la(),r.h("D<1,b>")).eo(0,0,B.x)),r.h("D<1,j>")).c5(0)}, $ia0:1, gc3(){return this.a}} A.l7.prototype={ $0(){return A.qn(this.a.j(0))}, $S:91} A.l8.prototype={ $1(a){return a.length!==0}, $S:2} A.l6.prototype={ $1(a){return!B.a.u(a,$.ts())}, $S:2} A.l5.prototype={ $1(a){return a!=="\tat "}, $S:2} A.l3.prototype={ $1(a){return a.length!==0&&a!=="[native code]"}, $S:2} A.l4.prototype={ $1(a){return!B.a.u(a,"=====")}, $S:2} A.la.prototype={ $1(a){return a.gbz().length}, $S:34} A.l9.prototype={ $1(a){if(a instanceof A.bl)return a.j(0)+"\n" return B.a.hd(a.gbz(),this.a)+" "+A.u(a.geC())+"\n"}, $S:35} A.bl.prototype={ j(a){return this.w}, $iN:1, gbz(){return"unparsed"}, geC(){return this.w}} A.e5.prototype={} A.eP.prototype={ R(a,b,c,d){var s,r=this.b if(r.d){a=null d=null}s=this.a.R(a,b,c,d) if(!r.d)r.c=s return s}, aX(a,b,c){return this.R(a,null,b,c)}, eB(a,b){return this.R(a,null,b,null)}} A.eO.prototype={ p(){var s,r=this.hE(),q=this.b q.d=!0 s=q.c if(s!=null){s.c9(null) s.eF(null)}return r}} A.ef.prototype={ ghD(){var s=this.b s===$&&A.G() return new A.an(s,A.t(s).h("an<1>"))}, ghz(){var s=this.a s===$&&A.G() return s}, hL(a,b,c,d){var s=this,r=$.i s.a!==$&&A.pq() s.a=new A.eY(a,s,new A.a2(new A.k(r,t.eI),t.fz),!0) r=A.eD(null,new A.k0(c,s),!0,d) s.b!==$&&A.pq() s.b=r}, iJ(){var s,r this.d=!0 s=this.c if(s!=null)s.J() r=this.b r===$&&A.G() r.p()}} A.k0.prototype={ $0(){var s,r,q=this.b if(q.d)return s=this.a.a r=q.b r===$&&A.G() q.c=s.aX(r.gjy(r),new A.k_(q),r.gfT())}, $S:0} A.k_.prototype={ $0(){var s=this.a,r=s.a r===$&&A.G() r.iK() s=s.b s===$&&A.G() s.p()}, $S:0} A.eY.prototype={ v(a,b){if(this.e)throw A.a(A.B("Cannot add event after closing.")) if(this.d)return this.a.a.v(0,b)}, a3(a,b){if(this.e)throw A.a(A.B("Cannot add event after closing.")) if(this.d)return this.il(a,b)}, il(a,b){this.a.a.a3(a,b) return}, p(){var s=this if(s.e)return s.c.a s.e=!0 if(!s.d){s.b.iJ() s.c.L(s.a.a.p())}return s.c.a}, iK(){this.d=!0 var s=this.c if((s.a.a&30)===0)s.aV() return}, $ia9:1} A.hC.prototype={} A.eC.prototype={} A.os.prototype={} A.eU.prototype={ R(a,b,c,d){return A.aB(this.a,this.b,a,!1)}, aX(a,b,c){return this.R(a,null,b,c)}} A.ic.prototype={ J(){var s=this,r=A.aX(null,t.H) if(s.b==null)return r s.e5() s.d=s.b=null return r}, c9(a){var s,r=this if(r.b==null)throw A.a(A.B("Subscription has been canceled.")) r.e5() if(a==null)s=null else{s=A.rt(new A.mf(a),t.m) s=s==null?null:A.bb(s)}r.d=s r.e3()}, eF(a){}, bB(){if(this.b==null)return;++this.a this.e5()}, bf(){var s=this if(s.b==null||s.a<=0)return;--s.a s.e3()}, e3(){var s=this,r=s.d if(r!=null&&s.a<=0)s.b.addEventListener(s.c,r,!1)}, e5(){var s=this.d if(s!=null)this.b.removeEventListener(this.c,s,!1)}} A.me.prototype={ $1(a){return this.a.$1(a)}, $S:1} A.mf.prototype={ $1(a){return this.a.$1(a)}, $S:1};(function aliases(){var s=J.bY.prototype s.hG=s.j s=A.cA.prototype s.hI=s.bK s=A.af.prototype s.dm=s.bp s.bm=s.bn s.eW=s.cv s=A.fc.prototype s.hJ=s.ee s=A.z.prototype s.eV=s.Z s=A.f.prototype s.hF=s.hA s=A.cU.prototype s.hE=s.p s=A.ey.prototype s.hH=s.p})();(function installTearOffs(){var s=hunkHelpers._static_2,r=hunkHelpers._static_1,q=hunkHelpers._static_0,p=hunkHelpers.installStaticTearOff,o=hunkHelpers._instance_0u,n=hunkHelpers.installInstanceTearOff,m=hunkHelpers._instance_2u,l=hunkHelpers._instance_1i,k=hunkHelpers._instance_1u s(J,"vZ","ud",92) r(A,"wy","uR",21) r(A,"wz","uS",21) r(A,"wA","uT",21) q(A,"rw","wr",0) r(A,"wB","wb",16) s(A,"wC","wd",6) q(A,"rv","wc",0) p(A,"wI",5,null,["$5"],["wm"],94,0) p(A,"wN",4,null,["$1$4","$4"],["nT",function(a,b,c,d){return A.nT(a,b,c,d,t.z)}],95,0) p(A,"wP",5,null,["$2$5","$5"],["nV",function(a,b,c,d,e){var i=t.z return A.nV(a,b,c,d,e,i,i)}],96,0) p(A,"wO",6,null,["$3$6","$6"],["nU",function(a,b,c,d,e,f){var i=t.z return A.nU(a,b,c,d,e,f,i,i,i)}],97,0) p(A,"wL",4,null,["$1$4","$4"],["rm",function(a,b,c,d){return A.rm(a,b,c,d,t.z)}],98,0) p(A,"wM",4,null,["$2$4","$4"],["rn",function(a,b,c,d){var i=t.z return A.rn(a,b,c,d,i,i)}],99,0) p(A,"wK",4,null,["$3$4","$4"],["rl",function(a,b,c,d){var i=t.z return A.rl(a,b,c,d,i,i,i)}],100,0) p(A,"wG",5,null,["$5"],["wl"],101,0) p(A,"wQ",4,null,["$4"],["nW"],102,0) p(A,"wF",5,null,["$5"],["wk"],103,0) p(A,"wE",5,null,["$5"],["wj"],104,0) p(A,"wJ",4,null,["$4"],["wn"],105,0) r(A,"wD","wf",106) p(A,"wH",5,null,["$5"],["rk"],107,0) var j o(j=A.cB.prototype,"gbN","al",0) o(j,"gbO","am",0) n(A.dp.prototype,"gjI",0,1,null,["$2","$1"],["bx","aK"],33,0,0) n(A.a2.prototype,"gjH",0,0,null,["$1","$0"],["L","aV"],77,0,0) m(A.k.prototype,"gdB","W",6) l(j=A.cI.prototype,"gjy","v",7) n(j,"gfT",0,1,null,["$2","$1"],["a3","jz"],33,0,0) o(j=A.cb.prototype,"gbN","al",0) o(j,"gbO","am",0) o(j=A.af.prototype,"gbN","al",0) o(j,"gbO","am",0) o(A.eR.prototype,"gfs","iI",0) k(j=A.dG.prototype,"giC","iD",7) m(j,"giG","iH",6) o(j,"giE","iF",0) o(j=A.ds.prototype,"gbN","al",0) o(j,"gbO","am",0) k(j,"gdM","dN",7) m(j,"gdQ","dR",117) o(j,"gdO","dP",0) o(j=A.dD.prototype,"gbN","al",0) o(j,"gbO","am",0) k(j,"gdM","dN",7) m(j,"gdQ","dR",6) o(j,"gdO","dP",0) k(A.dE.prototype,"gjD","ee","Y<2>(e?)") r(A,"wU","uO",8) p(A,"xm",2,null,["$1$2","$2"],["rF",function(a,b){return A.rF(a,b,t.v)}],108,0) r(A,"xo","xu",5) r(A,"xn","xt",5) r(A,"xl","wV",5) r(A,"xp","xA",5) r(A,"xi","ww",5) r(A,"xj","wx",5) r(A,"xk","wR",5) k(A.ea.prototype,"gip","iq",7) k(A.fS.prototype,"gi5","dE",13) k(A.hX.prototype,"gjk","e7",13) r(A,"yV","rd",20) r(A,"yT","rb",20) r(A,"yU","rc",20) r(A,"rH","we",26) r(A,"rI","wh",111) r(A,"rG","vO",112) o(A.dl.prototype,"gb9","p",0) r(A,"bQ","uj",113) r(A,"b6","uk",114) r(A,"pp","ul",115) k(A.eG.prototype,"giR","iS",69) o(A.fE.prototype,"gb9","p",0) o(A.cY.prototype,"gb9","p",3) o(A.dt.prototype,"gd7","U",0) o(A.dr.prototype,"gd7","U",3) o(A.cC.prototype,"gd7","U",3) o(A.cK.prototype,"gd7","U",3) o(A.de.prototype,"gb9","p",0) r(A,"x2","u7",17) r(A,"rA","u6",17) r(A,"x0","u4",17) r(A,"x1","u5",17) r(A,"xE","uJ",30) r(A,"xD","uI",30)})();(function inheritance(){var s=hunkHelpers.mixin,r=hunkHelpers.inherit,q=hunkHelpers.inheritMany r(A.e,null) q(A.e,[A.oA,J.h7,J.fz,A.f,A.fJ,A.O,A.z,A.cl,A.kB,A.aZ,A.b0,A.eH,A.fY,A.hF,A.hy,A.hz,A.fV,A.hY,A.eg,A.ee,A.hJ,A.hE,A.f6,A.e7,A.il,A.lc,A.hp,A.ec,A.fa,A.T,A.ke,A.he,A.cs,A.dz,A.lO,A.dg,A.ns,A.m3,A.b1,A.ig,A.ny,A.iD,A.i_,A.iB,A.cS,A.Y,A.af,A.cA,A.dp,A.cc,A.k,A.i0,A.hD,A.cI,A.iC,A.i1,A.dH,A.ia,A.mc,A.f5,A.eR,A.dG,A.eT,A.dv,A.au,A.iI,A.dM,A.iH,A.ih,A.dd,A.ne,A.dy,A.io,A.aF,A.ip,A.cm,A.cn,A.nF,A.fm,A.a6,A.ie,A.fN,A.bp,A.md,A.hq,A.eA,A.id,A.bs,A.h6,A.bv,A.F,A.dI,A.av,A.fj,A.hN,A.b4,A.fZ,A.ho,A.nc,A.cU,A.fP,A.hf,A.hn,A.hK,A.ea,A.ir,A.fL,A.fT,A.fS,A.bZ,A.aH,A.bU,A.c1,A.bg,A.c3,A.bT,A.c4,A.c2,A.by,A.bA,A.kC,A.f7,A.hX,A.bC,A.bS,A.e3,A.al,A.e1,A.cR,A.kq,A.lb,A.ju,A.d6,A.kr,A.et,A.ko,A.bh,A.jv,A.lo,A.fU,A.db,A.lm,A.kK,A.fM,A.dB,A.dC,A.l1,A.km,A.eu,A.hA,A.cj,A.ku,A.hB,A.kv,A.kx,A.kw,A.d8,A.d9,A.br,A.jr,A.kR,A.cT,A.jo,A.ix,A.nh,A.cr,A.aI,A.ez,A.bF,A.fH,A.cD,A.hV,A.kz,A.bi,A.bw,A.it,A.eG,A.dA,A.fE,A.mh,A.iq,A.ij,A.hS,A.mx,A.jp,A.ht,A.be,A.N,A.hd,A.a1,A.bl,A.eC,A.eY,A.hC,A.os,A.ic]) q(J.h7,[J.h8,J.ej,J.ek,J.aY,J.el,J.cZ,J.bV]) q(J.ek,[J.bY,J.w,A.d_,A.ep]) q(J.bY,[J.hr,J.cy,J.bW]) r(J.k9,J.w) q(J.cZ,[J.ei,J.h9]) q(A.f,[A.ca,A.v,A.az,A.aT,A.ed,A.cx,A.bB,A.ex,A.eI,A.bt,A.cG,A.hZ,A.iA,A.dJ,A.em]) q(A.ca,[A.ck,A.fn]) r(A.eS,A.ck) r(A.eN,A.fn) r(A.ah,A.eN) q(A.O,[A.bX,A.bD,A.hb,A.hI,A.i8,A.hv,A.ib,A.fC,A.aV,A.hL,A.hH,A.b2,A.fK]) q(A.z,[A.dh,A.hQ,A.dk]) r(A.e6,A.dh) q(A.cl,[A.j9,A.k3,A.ja,A.l2,A.kb,A.o6,A.o8,A.lQ,A.lP,A.nI,A.nt,A.nv,A.nu,A.jY,A.mn,A.mu,A.l_,A.kZ,A.kX,A.kV,A.nr,A.mb,A.ma,A.nm,A.nl,A.mv,A.ki,A.m0,A.nA,A.nO,A.nP,A.oa,A.oe,A.of,A.o1,A.jB,A.jC,A.jD,A.kH,A.kI,A.kJ,A.kF,A.lI,A.lF,A.lG,A.lD,A.lJ,A.lH,A.ks,A.jK,A.nX,A.kc,A.kd,A.kh,A.lA,A.lB,A.jx,A.o_,A.od,A.jE,A.kA,A.jf,A.jg,A.jh,A.kP,A.kL,A.kO,A.kM,A.kN,A.jm,A.jn,A.nY,A.lN,A.kS,A.o4,A.iY,A.m6,A.m7,A.jd,A.je,A.ji,A.jj,A.jk,A.j1,A.iZ,A.j_,A.kQ,A.mN,A.mO,A.mP,A.n_,A.n5,A.n6,A.n9,A.na,A.nb,A.mQ,A.mX,A.mY,A.mZ,A.n0,A.n1,A.n2,A.n3,A.n4,A.j3,A.j8,A.j7,A.j5,A.j6,A.j4,A.l8,A.l6,A.l5,A.l3,A.l4,A.la,A.l9,A.me,A.mf]) q(A.j9,[A.oc,A.lR,A.lS,A.nx,A.nw,A.jX,A.jV,A.mj,A.mq,A.mp,A.mm,A.ml,A.mk,A.mt,A.ms,A.mr,A.l0,A.kY,A.kW,A.kU,A.nq,A.np,A.m2,A.m1,A.nf,A.nL,A.nM,A.m9,A.m8,A.nS,A.nk,A.nj,A.nE,A.nD,A.jA,A.kD,A.kE,A.kG,A.lK,A.lL,A.lE,A.og,A.lT,A.lY,A.lW,A.lX,A.lV,A.lU,A.nn,A.no,A.jz,A.jy,A.mg,A.kg,A.lC,A.jw,A.jI,A.jF,A.jG,A.jH,A.js,A.iW,A.iX,A.j2,A.mi,A.k2,A.mw,A.mE,A.mD,A.mC,A.mB,A.mM,A.mL,A.mK,A.mJ,A.mI,A.mH,A.mG,A.mF,A.mA,A.mz,A.my,A.jU,A.jS,A.jP,A.jQ,A.jR,A.l7,A.k0,A.k_]) q(A.v,[A.P,A.cq,A.b8,A.cF,A.f_]) q(A.P,[A.cw,A.D,A.ew]) r(A.cp,A.az) r(A.eb,A.cx) r(A.cV,A.bB) r(A.co,A.bt) r(A.is,A.f6) q(A.is,[A.ap,A.cH]) r(A.e8,A.e7) r(A.eh,A.k3) r(A.er,A.bD) q(A.l2,[A.kT,A.e2]) q(A.T,[A.bu,A.cE]) q(A.ja,[A.ka,A.o7,A.nJ,A.nZ,A.jZ,A.mo,A.nK,A.k1,A.kj,A.m_,A.lh,A.li,A.lj,A.nN,A.lr,A.lq,A.lp,A.jt,A.lu,A.lt,A.j0,A.n7,A.n8,A.mR,A.mS,A.mT,A.mU,A.mV,A.mW,A.jT]) q(A.ep,[A.d0,A.d2]) q(A.d2,[A.f1,A.f3]) r(A.f2,A.f1) r(A.c_,A.f2) r(A.f4,A.f3) r(A.aR,A.f4) q(A.c_,[A.hg,A.hh]) q(A.aR,[A.hi,A.d1,A.hj,A.hk,A.hl,A.eq,A.bx]) r(A.fe,A.ib) q(A.Y,[A.dF,A.eW,A.eL,A.e0,A.eP,A.eU]) r(A.an,A.dF) r(A.eM,A.an) q(A.af,[A.cb,A.ds,A.dD]) r(A.cB,A.cb) r(A.fd,A.cA) q(A.dp,[A.a2,A.a8]) q(A.cI,[A.dn,A.dK]) q(A.ia,[A.dq,A.eQ]) r(A.f0,A.eW) r(A.fc,A.hD) r(A.dE,A.fc) q(A.iH,[A.i7,A.iw]) r(A.dw,A.cE) r(A.f8,A.dd) r(A.eZ,A.f8) q(A.cm,[A.fW,A.fF]) q(A.fW,[A.fA,A.hO]) q(A.cn,[A.iF,A.fG,A.hP]) r(A.fB,A.iF) q(A.aV,[A.d7,A.h3]) r(A.i9,A.fj) q(A.bZ,[A.am,A.ba,A.bq,A.bo]) q(A.md,[A.d3,A.cv,A.c0,A.di,A.cu,A.d5,A.c8,A.bH,A.kl,A.ac,A.cW]) r(A.jq,A.kq) r(A.kk,A.lb) q(A.ju,[A.hm,A.jJ]) q(A.al,[A.i2,A.dx,A.hc]) q(A.i2,[A.iE,A.fQ,A.i3,A.eV]) r(A.fb,A.iE) r(A.ik,A.dx) r(A.ey,A.jq) r(A.f9,A.jJ) q(A.lo,[A.jb,A.dm,A.dc,A.da,A.eB,A.fR]) q(A.jb,[A.c5,A.e9]) r(A.m5,A.kr) r(A.hT,A.fQ) r(A.nH,A.ey) r(A.k7,A.l1) q(A.k7,[A.kn,A.lk,A.lM]) q(A.br,[A.h_,A.cX]) r(A.df,A.cT) r(A.iu,A.jo) r(A.iv,A.iu) r(A.hu,A.iv) r(A.iy,A.ix) r(A.bk,A.iy) r(A.fI,A.bF) r(A.lx,A.ku) r(A.ln,A.kv) r(A.lz,A.kx) r(A.ly,A.kw) r(A.c7,A.d8) r(A.bG,A.d9) r(A.hW,A.kR) q(A.fI,[A.dl,A.cY,A.h2,A.de]) q(A.fH,[A.hU,A.ii,A.iz]) q(A.bw,[A.aW,A.S]) r(A.aQ,A.S) r(A.ao,A.aF) q(A.ao,[A.dt,A.dr,A.cC,A.cK]) q(A.eC,[A.e5,A.ef]) r(A.eO,A.cU) s(A.dh,A.hJ) s(A.fn,A.z) s(A.f1,A.z) s(A.f2,A.ee) s(A.f3,A.z) s(A.f4,A.ee) s(A.dn,A.i1) s(A.dK,A.iC) s(A.iu,A.z) s(A.iv,A.hn) s(A.ix,A.hK) s(A.iy,A.T)})() var v={typeUniverse:{eC:new Map(),tR:{},eT:{},tPV:{},sEA:[]},mangledGlobalNames:{b:"int",I:"double",b5:"num",j:"String",U:"bool",F:"Null",q:"List",e:"Object",aa:"Map"},mangledNames:{},types:["~()","~(A)","U(j)","C<~>()","b(b,b)","I(b5)","~(e,a0)","~(e?)","j(j)","F()","F(A)","N()","b(b)","e?(e?)","C()","F(b)","~(@)","N(j)","F(b,b,b)","~(A?,q?)","j(b)","~(~())","U()","~(at,j,b)","U(~)","F(@)","b5?(q)","b(b,b,b)","@()","b(b,b,b,b,b)","a1(j)","b(b,b,b,b)","b(b,b,b,aY)","~(e[a0?])","b(N)","j(N)","C()","ba()","am()","F(U)","bg()","q(w)","bC(e?)","C()","@(@)","F(@,a0)","b()","C()","aa(q)","b(q)","~(@,@)","F(al)","C(~)","~(e?,e?)","~(b,@)","F(~())","A(w)","db()","C()","C()","~(a9)","~(U,U,U,q<+(bH,j)>)","@(@,j)","j(j?)","j(e?)","~(d8,q)","~(br)","~(j,aa)","~(j,e?)","~(dA)","A(A?)","C<~>(b,at)","C<~>(b)","at()","C(j)","~(j,b)","~(j,b?)","~([e?])","at(@,@)","F(b,b)","F(e,a0)","b(b,aY)","k<@>(@)","F(b,b,b,b,aY)","q(a1)","b(a1)","C<~>(am)","j(a1)","b?(b)","F(~)","N(j,j)","a1()","b(@,@)","bz?/(am)","~(x?,Z?,x,e,a0)","0^(x?,Z?,x,0^())","0^(x?,Z?,x,0^(1^),1^)","0^(x?,Z?,x,0^(1^,2^),1^,2^)","0^()(x,Z,x,0^())","0^(1^)(x,Z,x,0^(1^))","0^(1^,2^)(x,Z,x,0^(1^,2^))","cS?(x,Z,x,e,a0?)","~(x?,Z?,x,~())","eE(x,Z,x,bp,~())","eE(x,Z,x,bp,~(eE))","~(x,Z,x,j)","~(j)","x(x?,Z?,x,oO?,aa?)","0^(0^,0^)","@(j)","C()","U?(q)","U(q<@>)","aW(bi)","S(bi)","aQ(bi)","bS<@>?()","~(@,a0)"],interceptorsByTag:null,leafTags:null,arrayRti:Symbol("$ti"),rttc:{"2;":(a,b)=>c=>c instanceof A.ap&&a.b(c.a)&&b.b(c.b),"2;file,outFlags":(a,b)=>c=>c instanceof A.cH&&a.b(c.a)&&b.b(c.b)}} A.vk(v.typeUniverse,JSON.parse('{"bW":"bY","hr":"bY","cy":"bY","w":{"q":["1"],"v":["1"],"A":[],"f":["1"],"ar":["1"]},"h8":{"U":[],"L":[]},"ej":{"F":[],"L":[]},"ek":{"A":[]},"bY":{"A":[]},"k9":{"w":["1"],"q":["1"],"v":["1"],"A":[],"f":["1"],"ar":["1"]},"cZ":{"I":[],"b5":[]},"ei":{"I":[],"b":[],"b5":[],"L":[]},"h9":{"I":[],"b5":[],"L":[]},"bV":{"j":[],"ar":["@"],"L":[]},"ca":{"f":["2"]},"ck":{"ca":["1","2"],"f":["2"],"f.E":"2"},"eS":{"ck":["1","2"],"ca":["1","2"],"v":["2"],"f":["2"],"f.E":"2"},"eN":{"z":["2"],"q":["2"],"ca":["1","2"],"v":["2"],"f":["2"]},"ah":{"eN":["1","2"],"z":["2"],"q":["2"],"ca":["1","2"],"v":["2"],"f":["2"],"z.E":"2","f.E":"2"},"bX":{"O":[]},"e6":{"z":["b"],"q":["b"],"v":["b"],"f":["b"],"z.E":"b"},"v":{"f":["1"]},"P":{"v":["1"],"f":["1"]},"cw":{"P":["1"],"v":["1"],"f":["1"],"f.E":"1","P.E":"1"},"az":{"f":["2"],"f.E":"2"},"cp":{"az":["1","2"],"v":["2"],"f":["2"],"f.E":"2"},"D":{"P":["2"],"v":["2"],"f":["2"],"f.E":"2","P.E":"2"},"aT":{"f":["1"],"f.E":"1"},"ed":{"f":["2"],"f.E":"2"},"cx":{"f":["1"],"f.E":"1"},"eb":{"cx":["1"],"v":["1"],"f":["1"],"f.E":"1"},"bB":{"f":["1"],"f.E":"1"},"cV":{"bB":["1"],"v":["1"],"f":["1"],"f.E":"1"},"ex":{"f":["1"],"f.E":"1"},"cq":{"v":["1"],"f":["1"],"f.E":"1"},"eI":{"f":["1"],"f.E":"1"},"bt":{"f":["+(b,1)"],"f.E":"+(b,1)"},"co":{"bt":["1"],"v":["+(b,1)"],"f":["+(b,1)"],"f.E":"+(b,1)"},"dh":{"z":["1"],"q":["1"],"v":["1"],"f":["1"]},"ew":{"P":["1"],"v":["1"],"f":["1"],"f.E":"1","P.E":"1"},"e7":{"aa":["1","2"]},"e8":{"e7":["1","2"],"aa":["1","2"]},"cG":{"f":["1"],"f.E":"1"},"er":{"bD":[],"O":[]},"hb":{"O":[]},"hI":{"O":[]},"hp":{"a5":[]},"fa":{"a0":[]},"i8":{"O":[]},"hv":{"O":[]},"bu":{"T":["1","2"],"aa":["1","2"],"T.V":"2","T.K":"1"},"b8":{"v":["1"],"f":["1"],"f.E":"1"},"dz":{"hs":[],"eo":[]},"hZ":{"f":["hs"],"f.E":"hs"},"dg":{"eo":[]},"iA":{"f":["eo"],"f.E":"eo"},"d_":{"A":[],"op":[],"L":[]},"d0":{"oq":[],"A":[],"L":[]},"d1":{"aR":[],"k5":[],"z":["b"],"q":["b"],"aP":["b"],"v":["b"],"A":[],"ar":["b"],"f":["b"],"L":[],"z.E":"b"},"bx":{"aR":[],"at":[],"z":["b"],"q":["b"],"aP":["b"],"v":["b"],"A":[],"ar":["b"],"f":["b"],"L":[],"z.E":"b"},"ep":{"A":[]},"d2":{"aP":["1"],"A":[],"ar":["1"]},"c_":{"z":["I"],"q":["I"],"aP":["I"],"v":["I"],"A":[],"ar":["I"],"f":["I"]},"aR":{"z":["b"],"q":["b"],"aP":["b"],"v":["b"],"A":[],"ar":["b"],"f":["b"]},"hg":{"c_":[],"jN":[],"z":["I"],"q":["I"],"aP":["I"],"v":["I"],"A":[],"ar":["I"],"f":["I"],"L":[],"z.E":"I"},"hh":{"c_":[],"jO":[],"z":["I"],"q":["I"],"aP":["I"],"v":["I"],"A":[],"ar":["I"],"f":["I"],"L":[],"z.E":"I"},"hi":{"aR":[],"k4":[],"z":["b"],"q":["b"],"aP":["b"],"v":["b"],"A":[],"ar":["b"],"f":["b"],"L":[],"z.E":"b"},"hj":{"aR":[],"k6":[],"z":["b"],"q":["b"],"aP":["b"],"v":["b"],"A":[],"ar":["b"],"f":["b"],"L":[],"z.E":"b"},"hk":{"aR":[],"le":[],"z":["b"],"q":["b"],"aP":["b"],"v":["b"],"A":[],"ar":["b"],"f":["b"],"L":[],"z.E":"b"},"hl":{"aR":[],"lf":[],"z":["b"],"q":["b"],"aP":["b"],"v":["b"],"A":[],"ar":["b"],"f":["b"],"L":[],"z.E":"b"},"eq":{"aR":[],"lg":[],"z":["b"],"q":["b"],"aP":["b"],"v":["b"],"A":[],"ar":["b"],"f":["b"],"L":[],"z.E":"b"},"ib":{"O":[]},"fe":{"bD":[],"O":[]},"cS":{"O":[]},"k":{"C":["1"]},"um":{"a9":["1"]},"af":{"af.T":"1"},"dv":{"a9":["1"]},"dJ":{"f":["1"],"f.E":"1"},"eM":{"an":["1"],"dF":["1"],"Y":["1"],"Y.T":"1"},"cB":{"cb":["1"],"af":["1"],"af.T":"1"},"cA":{"a9":["1"]},"fd":{"cA":["1"],"a9":["1"]},"a2":{"dp":["1"]},"a8":{"dp":["1"]},"cI":{"a9":["1"]},"dn":{"cI":["1"],"a9":["1"]},"dK":{"cI":["1"],"a9":["1"]},"an":{"dF":["1"],"Y":["1"],"Y.T":"1"},"cb":{"af":["1"],"af.T":"1"},"dH":{"a9":["1"]},"dF":{"Y":["1"]},"eW":{"Y":["2"]},"ds":{"af":["2"],"af.T":"2"},"f0":{"eW":["1","2"],"Y":["2"],"Y.T":"2"},"eT":{"a9":["1"]},"dD":{"af":["2"],"af.T":"2"},"eL":{"Y":["2"],"Y.T":"2"},"dE":{"fc":["1","2"]},"iI":{"oO":[]},"dM":{"Z":[]},"iH":{"x":[]},"i7":{"x":[]},"iw":{"x":[]},"cE":{"T":["1","2"],"aa":["1","2"],"T.V":"2","T.K":"1"},"dw":{"cE":["1","2"],"T":["1","2"],"aa":["1","2"],"T.V":"2","T.K":"1"},"cF":{"v":["1"],"f":["1"],"f.E":"1"},"eZ":{"f8":["1"],"dd":["1"],"v":["1"],"f":["1"]},"em":{"f":["1"],"f.E":"1"},"z":{"q":["1"],"v":["1"],"f":["1"]},"T":{"aa":["1","2"]},"f_":{"v":["2"],"f":["2"],"f.E":"2"},"dd":{"v":["1"],"f":["1"]},"f8":{"dd":["1"],"v":["1"],"f":["1"]},"fA":{"cm":["j","q"]},"iF":{"cn":["j","q"]},"fB":{"cn":["j","q"]},"fF":{"cm":["q","j"]},"fG":{"cn":["q","j"]},"fW":{"cm":["j","q"]},"hO":{"cm":["j","q"]},"hP":{"cn":["j","q"]},"I":{"b5":[]},"b":{"b5":[]},"q":{"v":["1"],"f":["1"]},"hs":{"eo":[]},"fC":{"O":[]},"bD":{"O":[]},"aV":{"O":[]},"d7":{"O":[]},"h3":{"O":[]},"hL":{"O":[]},"hH":{"O":[]},"b2":{"O":[]},"fK":{"O":[]},"hq":{"O":[]},"eA":{"O":[]},"id":{"a5":[]},"bs":{"a5":[]},"h6":{"a5":[],"O":[]},"dI":{"a0":[]},"fj":{"hM":[]},"b4":{"hM":[]},"i9":{"hM":[]},"ho":{"a5":[]},"cU":{"a9":["1"]},"fL":{"a5":[]},"fT":{"a5":[]},"am":{"bZ":[]},"ba":{"bZ":[]},"bg":{"as":[]},"by":{"as":[]},"aH":{"bz":[]},"bq":{"bZ":[]},"bo":{"bZ":[]},"d3":{"as":[]},"bU":{"as":[]},"c1":{"as":[]},"c3":{"as":[]},"bT":{"as":[]},"c4":{"as":[]},"c2":{"as":[]},"bA":{"bz":[]},"e3":{"a5":[]},"i2":{"al":[]},"iE":{"hG":[],"al":[]},"fb":{"hG":[],"al":[]},"fQ":{"al":[]},"i3":{"al":[]},"eV":{"al":[]},"dx":{"al":[]},"ik":{"hG":[],"al":[]},"hc":{"al":[]},"dm":{"a5":[]},"hT":{"al":[]},"eu":{"a5":[]},"hA":{"a5":[]},"h_":{"br":[]},"hQ":{"z":["e?"],"q":["e?"],"v":["e?"],"f":["e?"],"z.E":"e?"},"cX":{"br":[]},"df":{"cT":[]},"bk":{"T":["j","@"],"aa":["j","@"],"T.V":"@","T.K":"j"},"hu":{"z":["bk"],"q":["bk"],"v":["bk"],"f":["bk"],"z.E":"bk"},"aI":{"a5":[]},"fI":{"bF":[]},"fH":{"dj":[]},"bG":{"d9":[]},"c7":{"d8":[]},"dk":{"z":["bG"],"q":["bG"],"v":["bG"],"f":["bG"],"z.E":"bG"},"e0":{"Y":["1"],"Y.T":"1"},"dl":{"bF":[]},"hU":{"dj":[]},"aW":{"bw":[]},"S":{"bw":[]},"aQ":{"S":[],"bw":[]},"cY":{"bF":[]},"ao":{"aF":["ao"]},"ij":{"dj":[]},"dt":{"ao":[],"aF":["ao"],"aF.E":"ao"},"dr":{"ao":[],"aF":["ao"],"aF.E":"ao"},"cC":{"ao":[],"aF":["ao"],"aF.E":"ao"},"cK":{"ao":[],"aF":["ao"],"aF.E":"ao"},"h2":{"bF":[]},"ii":{"dj":[]},"de":{"bF":[]},"iz":{"dj":[]},"be":{"a0":[]},"hd":{"a1":[],"a0":[]},"a1":{"a0":[]},"bl":{"N":[]},"e5":{"eC":["1"]},"eP":{"Y":["1"],"Y.T":"1"},"eO":{"a9":["1"]},"ef":{"eC":["1"]},"eY":{"a9":["1"]},"eU":{"Y":["1"],"Y.T":"1"},"k6":{"q":["b"],"v":["b"],"f":["b"]},"at":{"q":["b"],"v":["b"],"f":["b"]},"lg":{"q":["b"],"v":["b"],"f":["b"]},"k4":{"q":["b"],"v":["b"],"f":["b"]},"le":{"q":["b"],"v":["b"],"f":["b"]},"k5":{"q":["b"],"v":["b"],"f":["b"]},"lf":{"q":["b"],"v":["b"],"f":["b"]},"jN":{"q":["I"],"v":["I"],"f":["I"]},"jO":{"q":["I"],"v":["I"],"f":["I"]}}')) A.vj(v.typeUniverse,JSON.parse('{"eH":1,"hy":1,"hz":1,"fV":1,"eg":1,"ee":1,"hJ":1,"dh":1,"fn":2,"he":1,"d2":1,"a9":1,"iB":1,"hD":2,"iC":1,"i1":1,"dH":1,"ia":1,"dq":1,"f5":1,"eR":1,"dG":1,"eT":1,"au":1,"fZ":1,"cU":1,"fP":1,"hf":1,"hn":1,"hK":2,"ey":1,"tJ":1,"hB":1,"eO":1,"eY":1,"ic":1}')) var u={q:"===== asynchronous gap ===========================\n",l:"Cannot extract a file path from a URI with a fragment component",y:"Cannot extract a file path from a URI with a query component",j:"Cannot extract a non-Windows file path from a file URI with an authority",o:"Cannot fire new event. Controller is already firing an event",c:"Error handler must accept one Object or one Object and a StackTrace as arguments, and return a value of the returned future's type",D:"Tried to operate on a released prepared statement"} var t=(function rtii(){var s=A.aq return{b9:s("tJ"),cO:s("e0>"),E:s("op"),fd:s("oq"),g1:s("bS<@>"),eT:s("cT"),ed:s("e9"),gw:s("ea"),Q:s("v<@>"),q:s("aW"),w:s("O"),g8:s("a5"),ez:s("cW"),G:s("S"),h4:s("jN"),gN:s("jO"),B:s("N"),b8:s("xM"),bF:s("C"),cG:s("C"),eY:s("C"),bd:s("cY"),dQ:s("k4"),an:s("k5"),gj:s("k6"),dP:s("f"),g7:s("w"),cf:s("w"),eV:s("w"),e:s("w"),fG:s("w>"),fk:s("w>"),W:s("w"),gP:s("w>"),gz:s("w>"),d:s("w>"),eC:s("w>"),as:s("w"),f:s("w"),L:s("w<+(bH,j)>"),bb:s("w"),s:s("w"),be:s("w"),J:s("w"),gQ:s("w"),n:s("w"),gn:s("w<@>"),t:s("w"),c:s("w"),d4:s("w"),r:s("w"),Y:s("w"),bT:s("w<~()>"),aP:s("ar<@>"),T:s("ej"),m:s("A"),C:s("aY"),g:s("bW"),aU:s("aP<@>"),au:s("em"),e9:s("q>"),cl:s("q"),aS:s("q>"),o:s("q"),j:s("q<@>"),I:s("q"),ee:s("q"),dY:s("aa"),g6:s("aa"),cv:s("aa"),M:s("az"),fe:s("D"),do:s("D"),fJ:s("bZ"),cb:s("bw"),eN:s("aQ"),bZ:s("d_"),gT:s("d0"),ha:s("d1"),aV:s("c_"),eB:s("aR"),Z:s("bx"),bw:s("by"),P:s("F"),K:s("e"),x:s("al"),aj:s("d6"),fl:s("xQ"),bQ:s("+()"),cz:s("hs"),gy:s("ht"),al:s("am"),cc:s("bz"),bJ:s("ew"),fE:s("db"),fM:s("c5"),gW:s("de"),l:s("a0"),a7:s("hC"),N:s("j"),aF:s("eE"),a:s("a1"),u:s("hG"),dm:s("L"),eK:s("bD"),h7:s("le"),bv:s("lf"),go:s("lg"),p:s("at"),ak:s("cy"),dD:s("hM"),ei:s("eG"),fL:s("bF"),ga:s("dj"),h2:s("hS"),g9:s("hV"),ab:s("hW"),aT:s("dl"),U:s("aT"),eJ:s("eI"),R:s("ac"),dx:s("ac"),b0:s("ac"),bi:s("a2"),co:s("a2"),fz:s("a2<@>"),fu:s("a2"),h:s("a2<~>"),V:s("cD"),fF:s("eU"),et:s("k"),a9:s("k"),k:s("k"),eI:s("k<@>"),gR:s("k"),fX:s("k"),D:s("k<~>"),hg:s("dw"),cT:s("dA"),aR:s("ir"),eg:s("it"),dn:s("fd<~>"),bh:s("a8"),fa:s("a8"),F:s("a8<~>"),y:s("U"),i:s("I"),z:s("@"),bI:s("@(e)"),b:s("@(e,a0)"),S:s("b"),aw:s("0&*"),_:s("e*"),eH:s("C?"),A:s("A?"),dE:s("bx?"),X:s("e?"),ah:s("as?"),O:s("bz?"),aD:s("at?"),h6:s("b?"),v:s("b5"),H:s("~"),d5:s("~(e)"),da:s("~(e,a0)")}})();(function constants(){var s=hunkHelpers.makeConstList B.aG=J.h7.prototype B.c=J.w.prototype B.b=J.ei.prototype B.aH=J.cZ.prototype B.a=J.bV.prototype B.aI=J.bW.prototype B.aJ=J.ek.prototype B.e=A.bx.prototype B.ag=J.hr.prototype B.D=J.cy.prototype B.an=new A.cj(0) B.l=new A.cj(1) B.q=new A.cj(2) B.Y=new A.cj(3) B.bJ=new A.cj(-1) B.ao=new A.fB(127) B.x=new A.eh(A.xm(),A.aq("eh")) B.ap=new A.fA() B.bK=new A.fG() B.aq=new A.fF() B.Z=new A.e3() B.ar=new A.fL() B.bL=new A.fP() B.a_=new A.fS() B.a0=new A.fV() B.h=new A.aW() B.as=new A.h6() B.a1=function getTagFallback(o) { var s = Object.prototype.toString.call(o); return s.substring(8, s.length - 1); } B.at=function() { var toStringFunction = Object.prototype.toString; function getTag(o) { var s = toStringFunction.call(o); return s.substring(8, s.length - 1); } function getUnknownTag(object, tag) { if (/^HTML[A-Z].*Element$/.test(tag)) { var name = toStringFunction.call(object); if (name == "[object Object]") return null; return "HTMLElement"; } } function getUnknownTagGenericBrowser(object, tag) { if (object instanceof HTMLElement) return "HTMLElement"; return getUnknownTag(object, tag); } function prototypeForTag(tag) { if (typeof window == "undefined") return null; if (typeof window[tag] == "undefined") return null; var constructor = window[tag]; if (typeof constructor != "function") return null; return constructor.prototype; } function discriminator(tag) { return null; } var isBrowser = typeof HTMLElement == "function"; return { getTag: getTag, getUnknownTag: isBrowser ? getUnknownTagGenericBrowser : getUnknownTag, prototypeForTag: prototypeForTag, discriminator: discriminator }; } B.ay=function(getTagFallback) { return function(hooks) { if (typeof navigator != "object") return hooks; var userAgent = navigator.userAgent; if (typeof userAgent != "string") return hooks; if (userAgent.indexOf("DumpRenderTree") >= 0) return hooks; if (userAgent.indexOf("Chrome") >= 0) { function confirm(p) { return typeof window == "object" && window[p] && window[p].name == p; } if (confirm("Window") && confirm("HTMLElement")) return hooks; } hooks.getTag = getTagFallback; }; } B.au=function(hooks) { if (typeof dartExperimentalFixupGetTag != "function") return hooks; hooks.getTag = dartExperimentalFixupGetTag(hooks.getTag); } B.ax=function(hooks) { if (typeof navigator != "object") return hooks; var userAgent = navigator.userAgent; if (typeof userAgent != "string") return hooks; if (userAgent.indexOf("Firefox") == -1) return hooks; var getTag = hooks.getTag; var quickMap = { "BeforeUnloadEvent": "Event", "DataTransfer": "Clipboard", "GeoGeolocation": "Geolocation", "Location": "!Location", "WorkerMessageEvent": "MessageEvent", "XMLDocument": "!Document"}; function getTagFirefox(o) { var tag = getTag(o); return quickMap[tag] || tag; } hooks.getTag = getTagFirefox; } B.aw=function(hooks) { if (typeof navigator != "object") return hooks; var userAgent = navigator.userAgent; if (typeof userAgent != "string") return hooks; if (userAgent.indexOf("Trident/") == -1) return hooks; var getTag = hooks.getTag; var quickMap = { "BeforeUnloadEvent": "Event", "DataTransfer": "Clipboard", "HTMLDDElement": "HTMLElement", "HTMLDTElement": "HTMLElement", "HTMLPhraseElement": "HTMLElement", "Position": "Geoposition" }; function getTagIE(o) { var tag = getTag(o); var newTag = quickMap[tag]; if (newTag) return newTag; if (tag == "Object") { if (window.DataView && (o instanceof window.DataView)) return "DataView"; } return tag; } function prototypeForTagIE(tag) { var constructor = window[tag]; if (constructor == null) return null; return constructor.prototype; } hooks.getTag = getTagIE; hooks.prototypeForTag = prototypeForTagIE; } B.av=function(hooks) { var getTag = hooks.getTag; var prototypeForTag = hooks.prototypeForTag; function getTagFixed(o) { var tag = getTag(o); if (tag == "Document") { if (!!o.xmlVersion) return "!Document"; return "!HTMLDocument"; } return tag; } function prototypeForTagFixed(tag) { if (tag == "Document") return null; return prototypeForTag(tag); } hooks.getTag = getTagFixed; hooks.prototypeForTag = prototypeForTagFixed; } B.a2=function(hooks) { return hooks; } B.o=new A.hf() B.az=new A.kk() B.aA=new A.hm() B.aB=new A.hq() B.f=new A.kB() B.j=new A.hO() B.i=new A.hP() B.a3=new A.hX() B.y=new A.mc() B.d=new A.iw() B.z=new A.bp(0) B.aE=new A.bs("Cannot read message",null,null) B.aF=new A.bs("Unknown tag",null,null) B.aK=A.d(s([11]),t.t) B.aL=A.d(s([0,0,32722,12287,65534,34815,65534,18431]),t.t) B.p=A.d(s([0,0,65490,45055,65535,34815,65534,18431]),t.t) B.aM=A.d(s([0,0,32754,11263,65534,34815,65534,18431]),t.t) B.a4=A.d(s([0,0,26624,1023,65534,2047,65534,2047]),t.t) B.aN=A.d(s([0,0,32722,12287,65535,34815,65534,18431]),t.t) B.a5=A.d(s([0,0,65490,12287,65535,34815,65534,18431]),t.t) B.a6=A.d(s([0,0,32776,33792,1,10240,0,0]),t.t) B.F=new A.bH(0,"opfs") B.am=new A.bH(1,"indexedDb") B.aO=A.d(s([B.F,B.am]),A.aq("w")) B.bj=new A.di(0,"insert") B.bk=new A.di(1,"update") B.bl=new A.di(2,"delete") B.a7=A.d(s([B.bj,B.bk,B.bl]),A.aq("w")) B.H=new A.ac(A.pp(),A.b6(),0,"xAccess",t.b0) B.G=new A.ac(A.pp(),A.bQ(),1,"xDelete",A.aq("ac")) B.S=new A.ac(A.pp(),A.b6(),2,"xOpen",t.b0) B.Q=new A.ac(A.b6(),A.b6(),3,"xRead",t.dx) B.L=new A.ac(A.b6(),A.bQ(),4,"xWrite",t.R) B.M=new A.ac(A.b6(),A.bQ(),5,"xSleep",t.R) B.N=new A.ac(A.b6(),A.bQ(),6,"xClose",t.R) B.R=new A.ac(A.b6(),A.b6(),7,"xFileSize",t.dx) B.O=new A.ac(A.b6(),A.bQ(),8,"xSync",t.R) B.P=new A.ac(A.b6(),A.bQ(),9,"xTruncate",t.R) B.J=new A.ac(A.b6(),A.bQ(),10,"xLock",t.R) B.K=new A.ac(A.b6(),A.bQ(),11,"xUnlock",t.R) B.I=new A.ac(A.bQ(),A.bQ(),12,"stopServer",A.aq("ac")) B.aP=A.d(s([B.H,B.G,B.S,B.Q,B.L,B.M,B.N,B.R,B.O,B.P,B.J,B.K,B.I]),A.aq("w>")) B.A=A.d(s([]),t.W) B.aQ=A.d(s([]),t.gz) B.aR=A.d(s([]),t.f) B.r=A.d(s([]),t.s) B.t=A.d(s([]),t.c) B.B=A.d(s([]),t.L) B.ak=new A.c8(0,"opfsShared") B.al=new A.c8(1,"opfsLocks") B.w=new A.c8(2,"sharedIndexedDb") B.E=new A.c8(3,"unsafeIndexedDb") B.bs=new A.c8(4,"inMemory") B.aT=A.d(s([B.ak,B.al,B.w,B.E,B.bs]),A.aq("w")) B.b3=new A.cv(0,"custom") B.b4=new A.cv(1,"deleteOrUpdate") B.b5=new A.cv(2,"insert") B.b6=new A.cv(3,"select") B.a8=A.d(s([B.b3,B.b4,B.b5,B.b6]),A.aq("w")) B.aD=new A.cW("/database",0,"database") B.aC=new A.cW("/database-journal",1,"journal") B.a9=A.d(s([B.aD,B.aC]),A.aq("w")) B.ad=new A.c0(0,"beginTransaction") B.aV=new A.c0(1,"commit") B.aW=new A.c0(2,"rollback") B.ae=new A.c0(3,"startExclusive") B.af=new A.c0(4,"endExclusive") B.aa=A.d(s([B.ad,B.aV,B.aW,B.ae,B.af]),A.aq("w")) B.ab=A.d(s([0,0,24576,1023,65534,34815,65534,18431]),t.t) B.m=new A.cu(0,"sqlite") B.b0=new A.cu(1,"mysql") B.b1=new A.cu(2,"postgres") B.b2=new A.cu(3,"mariadb") B.ac=A.d(s([B.m,B.b0,B.b1,B.b2]),A.aq("w")) B.aX={} B.aU=new A.e8(B.aX,[],A.aq("e8")) B.C=new A.d3(0,"terminateAll") B.bM=new A.kl(2,"readWriteCreate") B.u=new A.d5(0,0,"legacy") B.aY=new A.d5(1,1,"v1") B.aZ=new A.d5(2,2,"v2") B.v=new A.d5(3,3,"v3") B.aS=A.d(s([]),t.d) B.b_=new A.bA(B.aS) B.ah=new A.hE("drift.runtime.cancellation") B.b7=A.bd("op") B.b8=A.bd("oq") B.b9=A.bd("jN") B.ba=A.bd("jO") B.bb=A.bd("k4") B.bc=A.bd("k5") B.bd=A.bd("k6") B.be=A.bd("e") B.bf=A.bd("le") B.bg=A.bd("lf") B.bh=A.bd("lg") B.bi=A.bd("at") B.bm=new A.aI(10) B.bn=new A.aI(12) B.ai=new A.aI(14) B.bo=new A.aI(2570) B.bp=new A.aI(3850) B.bq=new A.aI(522) B.aj=new A.aI(778) B.br=new A.aI(8) B.T=new A.dB("above root") B.U=new A.dB("at root") B.bt=new A.dB("reaches root") B.V=new A.dB("below root") B.k=new A.dC("different") B.W=new A.dC("equal") B.n=new A.dC("inconclusive") B.X=new A.dC("within") B.bu=new A.dI("") B.bv=new A.au(B.d,A.wI()) B.bw=new A.au(B.d,A.wM()) B.bx=new A.au(B.d,A.wF()) B.by=new A.au(B.d,A.wG()) B.bz=new A.au(B.d,A.wH()) B.bA=new A.au(B.d,A.wJ()) B.bB=new A.au(B.d,A.wL()) B.bC=new A.au(B.d,A.wN()) B.bD=new A.au(B.d,A.wO()) B.bE=new A.au(B.d,A.wP()) B.bF=new A.au(B.d,A.wQ()) B.bG=new A.au(B.d,A.wE()) B.bH=new A.au(B.d,A.wK()) B.bI=new A.iI(null,null,null,null,null,null,null,null,null,null,null,null,null)})();(function staticFields(){$.nd=null $.cO=A.d([],t.f) $.rK=null $.q3=null $.pF=null $.pE=null $.rB=null $.ru=null $.rL=null $.o3=null $.o9=null $.pj=null $.ng=A.d([],A.aq("w?>")) $.dO=null $.fo=null $.fp=null $.p9=!1 $.i=B.d $.ni=null $.qz=null $.qA=null $.qB=null $.qC=null $.oP=A.m4("_lastQuoRemDigits") $.oQ=A.m4("_lastQuoRemUsed") $.eK=A.m4("_lastRemUsed") $.oR=A.m4("_lastRem_nsh") $.qs="" $.qt=null $.ra=null $.nQ=null})();(function lazyInitializers(){var s=hunkHelpers.lazyFinal,r=hunkHelpers.lazy s($,"xH","dX",()=>A.x4("_$dart_dartClosure")) s($,"yX","tx",()=>B.d.bg(new A.oc(),A.aq("C"))) s($,"xX","rU",()=>A.bE(A.ld({ toString:function(){return"$receiver$"}}))) s($,"xY","rV",()=>A.bE(A.ld({$method$:null, toString:function(){return"$receiver$"}}))) s($,"xZ","rW",()=>A.bE(A.ld(null))) s($,"y_","rX",()=>A.bE(function(){var $argumentsExpr$="$arguments$" try{null.$method$($argumentsExpr$)}catch(q){return q.message}}())) s($,"y2","t_",()=>A.bE(A.ld(void 0))) s($,"y3","t0",()=>A.bE(function(){var $argumentsExpr$="$arguments$" try{(void 0).$method$($argumentsExpr$)}catch(q){return q.message}}())) s($,"y1","rZ",()=>A.bE(A.qo(null))) s($,"y0","rY",()=>A.bE(function(){try{null.$method$}catch(q){return q.message}}())) s($,"y5","t2",()=>A.bE(A.qo(void 0))) s($,"y4","t1",()=>A.bE(function(){try{(void 0).$method$}catch(q){return q.message}}())) s($,"y7","pt",()=>A.uQ()) s($,"xO","ci",()=>A.aq("k").a($.tx())) s($,"xN","rS",()=>A.v0(!1,B.d,t.y)) s($,"yh","t8",()=>{var q=t.z return A.pS(q,q)}) s($,"yl","tc",()=>A.q0(4096)) s($,"yj","ta",()=>new A.nE().$0()) s($,"yk","tb",()=>new A.nD().$0()) s($,"y8","t3",()=>A.un(A.iJ(A.d([-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-1,-2,-2,-2,-2,-2,62,-2,62,-2,63,52,53,54,55,56,57,58,59,60,61,-2,-2,-2,-1,-2,-2,-2,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-2,-2,-2,-2,63,-2,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,-2,-2,-2,-2,-2],t.t)))) s($,"yf","b7",()=>A.eJ(0)) s($,"yd","fv",()=>A.eJ(1)) s($,"ye","t6",()=>A.eJ(2)) s($,"yb","pv",()=>$.fv().aB(0)) s($,"y9","pu",()=>A.eJ(1e4)) r($,"yc","t5",()=>A.J("^\\s*([+-]?)((0x[a-f0-9]+)|(\\d+)|([a-z0-9]+))\\s*$",!1,!1,!1,!1)) s($,"ya","t4",()=>A.q0(8)) s($,"yg","t7",()=>typeof FinalizationRegistry=="function"?FinalizationRegistry:null) s($,"yi","t9",()=>A.J("^[\\-\\.0-9A-Z_a-z~]*$",!0,!1,!1,!1)) s($,"yE","om",()=>A.pm(B.be)) s($,"yH","tn",()=>A.vP()) s($,"xP","iP",()=>{var q=new A.nc(new DataView(new ArrayBuffer(A.vN(8)))) q.hQ() return q}) s($,"y6","ps",()=>A.tY(B.aO,A.aq("bH"))) s($,"z0","ty",()=>A.jl(null,$.fu())) s($,"yZ","fw",()=>A.jl(null,$.cP())) s($,"yR","iQ",()=>new A.fM($.pr(),null)) s($,"xU","rT",()=>new A.kn(A.J("/",!0,!1,!1,!1),A.J("[^/]$",!0,!1,!1,!1),A.J("^/",!0,!1,!1,!1))) s($,"xW","fu",()=>new A.lM(A.J("[/\\\\]",!0,!1,!1,!1),A.J("[^/\\\\]$",!0,!1,!1,!1),A.J("^(\\\\\\\\[^\\\\]+\\\\[^\\\\/]+|[a-zA-Z]:[/\\\\])",!0,!1,!1,!1),A.J("^[/\\\\](?![/\\\\])",!0,!1,!1,!1))) s($,"xV","cP",()=>new A.lk(A.J("/",!0,!1,!1,!1),A.J("(^[a-zA-Z][-+.a-zA-Z\\d]*://|[^/])$",!0,!1,!1,!1),A.J("[a-zA-Z][-+.a-zA-Z\\d]*://[^/]*",!0,!1,!1,!1),A.J("^/",!0,!1,!1,!1))) s($,"xT","pr",()=>A.uE()) s($,"yQ","tw",()=>A.pC("-9223372036854775808")) s($,"yP","tv",()=>A.pC("9223372036854775807")) s($,"yW","dY",()=>{var q=$.t7() q=q==null?null:new q(A.cf(A.xF(new A.o4(),A.aq("br")),1)) return new A.ie(q,A.aq("ie
"))}) s($,"xG","ok",()=>A.ui(A.d(["files","blocks"],t.s))) s($,"xJ","ol",()=>{var q,p,o=A.a3(t.N,t.ez) for(q=0;q<2;++q){p=B.a9[q] o.q(0,p.c,p)}return o}) s($,"xI","rP",()=>new A.fZ(new WeakMap())) s($,"yO","tu",()=>A.J("^#\\d+\\s+(\\S.*) \\((.+?)((?::\\d+){0,2})\\)$",!0,!1,!1,!1)) s($,"yJ","tp",()=>A.J("^\\s*at (?:(\\S.*?)(?: \\[as [^\\]]+\\])? \\((.*)\\)|(.*))$",!0,!1,!1,!1)) s($,"yK","tq",()=>A.J("^(.*?):(\\d+)(?::(\\d+))?$|native$",!0,!1,!1,!1)) s($,"yN","tt",()=>A.J("^\\s*at (?:(?.+) )?(?:\\(?(?:(?\\S+):wasm-function\\[(?\\d+)\\]\\:0x(?[0-9a-fA-F]+))\\)?)$",!0,!1,!1,!1)) s($,"yI","to",()=>A.J("^eval at (?:\\S.*?) \\((.*)\\)(?:, .*?:\\d+:\\d+)?$",!0,!1,!1,!1)) s($,"yx","te",()=>A.J("(\\S+)@(\\S+) line (\\d+) >.* (Function|eval):\\d+:\\d+",!0,!1,!1,!1)) s($,"yz","tg",()=>A.J("^(?:([^@(/]*)(?:\\(.*\\))?((?:/[^/]*)*)(?:\\(.*\\))?@)?(.*?):(\\d*)(?::(\\d*))?$",!0,!1,!1,!1)) s($,"yB","ti",()=>A.J("^(?.*?)@(?:(?\\S+).*?:wasm-function\\[(?\\d+)\\]:0x(?[0-9a-fA-F]+))$",!0,!1,!1,!1)) s($,"yG","tm",()=>A.J("^.*?wasm-function\\[(?.*)\\]@\\[wasm code\\]$",!0,!1,!1,!1)) s($,"yC","tj",()=>A.J("^(\\S+)(?: (\\d+)(?::(\\d+))?)?\\s+([^\\d].*)$",!0,!1,!1,!1)) s($,"yw","td",()=>A.J("<(|[^>]+)_async_body>",!0,!1,!1,!1)) s($,"yF","tl",()=>A.J("^\\.",!0,!1,!1,!1)) s($,"xK","rQ",()=>A.J("^[a-zA-Z][-+.a-zA-Z\\d]*://",!0,!1,!1,!1)) s($,"xL","rR",()=>A.J("^([a-zA-Z]:[\\\\/]|\\\\\\\\)",!0,!1,!1,!1)) s($,"yL","tr",()=>A.J("\\n ?at ",!0,!1,!1,!1)) s($,"yM","ts",()=>A.J(" ?at ",!0,!1,!1,!1)) s($,"yy","tf",()=>A.J("@\\S+ line \\d+ >.* (Function|eval):\\d+:\\d+",!0,!1,!1,!1)) s($,"yA","th",()=>A.J("^(([.0-9A-Za-z_$/<]|\\(.*\\))*@)?[^\\s]*:\\d*$",!0,!1,!0,!1)) s($,"yD","tk",()=>A.J("^[^\\s<][^\\s]*( \\d+(:\\d+)?)?[ \\t]+[^\\s]+$",!0,!1,!0,!1)) s($,"z_","pw",()=>A.J("^\\n?$",!0,!1,!0,!1))})();(function nativeSupport(){!function(){var s=function(a){var m={} m[a]=1 return Object.keys(hunkHelpers.convertToFastObject(m))[0]} v.getIsolateTag=function(a){return s("___dart_"+a+v.isolateTag)} var r="___dart_isolate_tags_" var q=Object[r]||(Object[r]=Object.create(null)) var p="_ZxYxX" for(var o=0;;o++){var n=s(p+"_"+o+"_") if(!(n in q)){q[n]=1 v.isolateTag=n break}}v.dispatchPropertyName=v.getIsolateTag("dispatch_record")}() hunkHelpers.setOrUpdateInterceptorsByTag({ArrayBuffer:A.d_,ArrayBufferView:A.ep,DataView:A.d0,Float32Array:A.hg,Float64Array:A.hh,Int16Array:A.hi,Int32Array:A.d1,Int8Array:A.hj,Uint16Array:A.hk,Uint32Array:A.hl,Uint8ClampedArray:A.eq,CanvasPixelArray:A.eq,Uint8Array:A.bx}) hunkHelpers.setOrUpdateLeafTags({ArrayBuffer:true,ArrayBufferView:false,DataView:true,Float32Array:true,Float64Array:true,Int16Array:true,Int32Array:true,Int8Array:true,Uint16Array:true,Uint32Array:true,Uint8ClampedArray:true,CanvasPixelArray:true,Uint8Array:false}) A.d2.$nativeSuperclassTag="ArrayBufferView" A.f1.$nativeSuperclassTag="ArrayBufferView" A.f2.$nativeSuperclassTag="ArrayBufferView" A.c_.$nativeSuperclassTag="ArrayBufferView" A.f3.$nativeSuperclassTag="ArrayBufferView" A.f4.$nativeSuperclassTag="ArrayBufferView" A.aR.$nativeSuperclassTag="ArrayBufferView"})() Function.prototype.$0=function(){return this()} Function.prototype.$1=function(a){return this(a)} Function.prototype.$2=function(a,b){return this(a,b)} Function.prototype.$1$1=function(a){return this(a)} Function.prototype.$3=function(a,b,c){return this(a,b,c)} Function.prototype.$4=function(a,b,c,d){return this(a,b,c,d)} Function.prototype.$3$1=function(a){return this(a)} Function.prototype.$2$1=function(a){return this(a)} Function.prototype.$3$3=function(a,b,c){return this(a,b,c)} Function.prototype.$2$2=function(a,b){return this(a,b)} Function.prototype.$2$3=function(a,b,c){return this(a,b,c)} Function.prototype.$1$2=function(a,b){return this(a,b)} Function.prototype.$5=function(a,b,c,d,e){return this(a,b,c,d,e)} Function.prototype.$6=function(a,b,c,d,e,f){return this(a,b,c,d,e,f)} Function.prototype.$1$0=function(){return this()} convertAllToFastObject(w) convertToFastObject($);(function(a){if(typeof document==="undefined"){a(null) return}if(typeof document.currentScript!="undefined"){a(document.currentScript) return}var s=document.scripts function onLoad(b){for(var q=0;q myapp ================================================ FILE: web/manifest.json ================================================ { "name": "Hiddify", "short_name": "Hiddify", "start_url": ".", "display": "standalone", "background_color": "#0175C2", "theme_color": "#0175C2", "description": "Hiddify", "orientation": "portrait-primary", "prefer_related_applications": false, "icons": [ { "src": "icon.png", "sizes": "512x512", "type": "image/png" } ] } ================================================ FILE: windows/.gitignore ================================================ flutter/ephemeral/ # Visual Studio user-specific files. *.suo *.user *.userosscache *.sln.docstates # Visual Studio build-related files. x64/ x86/ # Visual Studio cache files # files ending in .cache can be ignored *.[Cc]ache # but keep track of directories ending in .cache !*.[Cc]ache/ ================================================ FILE: windows/.stignore ================================================ /flutter/ephemeral/ *.suo *.user *.userosscache *.sln.docstates /x64/ /x86/ ================================================ FILE: windows/CMakeLists.txt ================================================ # Project-level configuration. cmake_minimum_required(VERSION 3.14) project(hiddify LANGUAGES CXX) # The name of the executable created for the application. Change this to change # the on-disk name of your application. set(BINARY_NAME "Hiddify") # Explicitly opt in to modern CMake behaviors to avoid warnings with recent # versions of CMake. cmake_policy(SET CMP0063 NEW) # Define build configuration option. get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) if(IS_MULTICONFIG) set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" CACHE STRING "" FORCE) else() if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) set(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Flutter build mode" FORCE) set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Profile" "Release") endif() endif() # Define settings for the Profile build mode. set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") # Use Unicode for all projects. add_definitions(-DUNICODE -D_UNICODE) # Compilation settings that should be applied to most targets. # # Be cautious about adding new options here, as plugins use this function by # default. In most cases, you should add new options to specific targets instead # of modifying this function. function(APPLY_STANDARD_SETTINGS TARGET) target_compile_features(${TARGET} PUBLIC cxx_std_17) target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") target_compile_options(${TARGET} PRIVATE /EHsc) target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") endfunction() # Flutter library and tool build rules. set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") add_subdirectory(${FLUTTER_MANAGED_DIR}) # Application build; see runner/CMakeLists.txt. add_subdirectory("runner") # Generated plugin build rules, which manage building the plugins and adding # them to the application. include(flutter/generated_plugins.cmake) # === Installation === # Support files are copied into place next to the executable, so that it can # run in place. This is done instead of making a separate bundle (as on Linux) # so that building and running from within Visual Studio will work. set(BUILD_BUNDLE_DIR "$") # Make the "install" step default, as it's required to run. set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) endif() set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" COMPONENT Runtime) install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" COMPONENT Runtime) # install(FILES "../hiddify-core/bin/hiddify-core.dll" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" # COMPONENT Runtime) install(FILES "../hiddify-core/bin/hiddify-core.dll" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" COMPONENT Runtime RENAME hiddify-core.dll) install(FILES "../hiddify-core/bin/libcronet.dll" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" COMPONENT Runtime RENAME libcronet.dll) install(FILES "../hiddify-core/bin/HiddifyCli.exe" DESTINATION "${CMAKE_INSTALL_PREFIX}" COMPONENT Runtime RENAME HiddifyCli.exe) if(PLUGIN_BUNDLED_LIBRARIES) install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" COMPONENT Runtime) endif() # Fully re-copy the assets directory on each build to avoid having stale files # from a previous install. set(FLUTTER_ASSET_DIR_NAME "flutter_assets") install(CODE " file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") " COMPONENT Runtime) install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) # Install the AOT library on non-Debug builds only. install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" CONFIGURATIONS Profile;Release COMPONENT Runtime) ================================================ FILE: windows/flutter/CMakeLists.txt ================================================ # This file controls Flutter-level build steps. It should not be edited. cmake_minimum_required(VERSION 3.14) set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") # Configuration provided via flutter tool. include(${EPHEMERAL_DIR}/generated_config.cmake) # TODO: Move the rest of this into files in ephemeral. See # https://github.com/flutter/flutter/issues/57146. set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") # Set fallback configurations for older versions of the flutter tool. if (NOT DEFINED FLUTTER_TARGET_PLATFORM) set(FLUTTER_TARGET_PLATFORM "windows-x64") endif() # === Flutter Library === set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") # Published to parent scope for install step. set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) list(APPEND FLUTTER_LIBRARY_HEADERS "flutter_export.h" "flutter_windows.h" "flutter_messenger.h" "flutter_plugin_registrar.h" "flutter_texture_registrar.h" ) list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") add_library(flutter INTERFACE) target_include_directories(flutter INTERFACE "${EPHEMERAL_DIR}" ) target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") add_dependencies(flutter flutter_assemble) # === Wrapper === list(APPEND CPP_WRAPPER_SOURCES_CORE "core_implementations.cc" "standard_codec.cc" ) list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") list(APPEND CPP_WRAPPER_SOURCES_PLUGIN "plugin_registrar.cc" ) list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") list(APPEND CPP_WRAPPER_SOURCES_APP "flutter_engine.cc" "flutter_view_controller.cc" ) list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") # Wrapper sources needed for a plugin. add_library(flutter_wrapper_plugin STATIC ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} ) apply_standard_settings(flutter_wrapper_plugin) set_target_properties(flutter_wrapper_plugin PROPERTIES POSITION_INDEPENDENT_CODE ON) set_target_properties(flutter_wrapper_plugin PROPERTIES CXX_VISIBILITY_PRESET hidden) target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) target_include_directories(flutter_wrapper_plugin PUBLIC "${WRAPPER_ROOT}/include" ) add_dependencies(flutter_wrapper_plugin flutter_assemble) # Wrapper sources needed for the runner. add_library(flutter_wrapper_app STATIC ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_APP} ) apply_standard_settings(flutter_wrapper_app) target_link_libraries(flutter_wrapper_app PUBLIC flutter) target_include_directories(flutter_wrapper_app PUBLIC "${WRAPPER_ROOT}/include" ) add_dependencies(flutter_wrapper_app flutter_assemble) # === Flutter tool backend === # _phony_ is a non-existent file to force this command to run every time, # since currently there's no way to get a full input/output list from the # flutter tool. set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) add_custom_command( OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} ${CPP_WRAPPER_SOURCES_APP} ${PHONY_OUTPUT} COMMAND ${CMAKE_COMMAND} -E env ${FLUTTER_TOOL_ENVIRONMENT} "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" ${FLUTTER_TARGET_PLATFORM} $ VERBATIM ) add_custom_target(flutter_assemble DEPENDS "${FLUTTER_LIBRARY}" ${FLUTTER_LIBRARY_HEADERS} ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} ${CPP_WRAPPER_SOURCES_APP} ) ================================================ FILE: windows/flutter/generated_plugin_registrant.cc ================================================ // // Generated file. Do not edit. // // clang-format off #include "generated_plugin_registrant.h" #include #include #include #include #include #include #include #include #include #include void RegisterPlugins(flutter::PluginRegistry* registry) { AppLinksPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("AppLinksPluginCApi")); DynamicColorPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("DynamicColorPluginCApi")); ScreenRetrieverWindowsPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("ScreenRetrieverWindowsPluginCApi")); SentryFlutterPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("SentryFlutterPlugin")); SharePlusWindowsPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi")); Sqlite3FlutterLibsPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("Sqlite3FlutterLibsPlugin")); TrayManagerPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("TrayManagerPlugin")); UrlLauncherWindowsRegisterWithRegistrar( registry->GetRegistrarForPlugin("UrlLauncherWindows")); VclibsPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("VclibsPluginCApi")); WindowManagerPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("WindowManagerPlugin")); } ================================================ FILE: windows/flutter/generated_plugin_registrant.h ================================================ // // Generated file. Do not edit. // // clang-format off #ifndef GENERATED_PLUGIN_REGISTRANT_ #define GENERATED_PLUGIN_REGISTRANT_ #include // Registers Flutter plugins. void RegisterPlugins(flutter::PluginRegistry* registry); #endif // GENERATED_PLUGIN_REGISTRANT_ ================================================ FILE: windows/flutter/generated_plugins.cmake ================================================ # # Generated file, do not edit. # list(APPEND FLUTTER_PLUGIN_LIST app_links dynamic_color screen_retriever_windows sentry_flutter share_plus sqlite3_flutter_libs tray_manager url_launcher_windows vclibs window_manager ) list(APPEND FLUTTER_FFI_PLUGIN_LIST ) set(PLUGIN_BUNDLED_LIBRARIES) foreach(plugin ${FLUTTER_PLUGIN_LIST}) add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) list(APPEND PLUGIN_BUNDLED_LIBRARIES $) list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) endforeach(plugin) foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) endforeach(ffi_plugin) ================================================ FILE: windows/packaging/exe/inno_setup.sas ================================================ [Setup] AppId={{APP_ID}} AppVersion={{APP_VERSION}} AppName={{DISPLAY_NAME}} AppPublisher={{PUBLISHER_NAME}} AppPublisherURL={{PUBLISHER_URL}} AppSupportURL={{PUBLISHER_URL}} AppUpdatesURL={{PUBLISHER_URL}} DefaultDirName={{INSTALL_DIR_NAME}} DisableProgramGroupPage=yes OutputDir=. OutputBaseFilename={{OUTPUT_BASE_FILENAME}} Compression=lzma SolidCompression=yes SetupIconFile={{SETUP_ICON_FILE}} WizardStyle=modern PrivilegesRequired={{PRIVILEGES_REQUIRED}} ArchitecturesAllowed=x64 ArchitecturesInstallIn64BitMode=x64 CloseApplications=force [Languages] {% for locale in LOCALES %} {% if locale == 'en' %}Name: "english"; MessagesFile: "compiler:Default.isl"{% endif %} {% if locale == 'hy' %}Name: "armenian"; MessagesFile: "compiler:Languages\\Armenian.isl"{% endif %} {% if locale == 'bg' %}Name: "bulgarian"; MessagesFile: "compiler:Languages\\Bulgarian.isl"{% endif %} {% if locale == 'ca' %}Name: "catalan"; MessagesFile: "compiler:Languages\\Catalan.isl"{% endif %} {% if locale == 'zh' %}Name: "chinesesimplified"; MessagesFile: "compiler:Languages\\ChineseSimplified.isl"{% endif %} {% if locale == 'co' %}Name: "corsican"; MessagesFile: "compiler:Languages\\Corsican.isl"{% endif %} {% if locale == 'cs' %}Name: "czech"; MessagesFile: "compiler:Languages\\Czech.isl"{% endif %} {% if locale == 'da' %}Name: "danish"; MessagesFile: "compiler:Languages\\Danish.isl"{% endif %} {% if locale == 'nl' %}Name: "dutch"; MessagesFile: "compiler:Languages\\Dutch.isl"{% endif %} {% if locale == 'fi' %}Name: "finnish"; MessagesFile: "compiler:Languages\\Finnish.isl"{% endif %} {% if locale == 'fr' %}Name: "french"; MessagesFile: "compiler:Languages\\French.isl"{% endif %} {% if locale == 'de' %}Name: "german"; MessagesFile: "compiler:Languages\\German.isl"{% endif %} {% if locale == 'he' %}Name: "hebrew"; MessagesFile: "compiler:Languages\\Hebrew.isl"{% endif %} {% if locale == 'is' %}Name: "icelandic"; MessagesFile: "compiler:Languages\\Icelandic.isl"{% endif %} {% if locale == 'it' %}Name: "italian"; MessagesFile: "compiler:Languages\\Italian.isl"{% endif %} {% if locale == 'ja' %}Name: "japanese"; MessagesFile: "compiler:Languages\\Japanese.isl"{% endif %} {% if locale == 'no' %}Name: "norwegian"; MessagesFile: "compiler:Languages\\Norwegian.isl"{% endif %} {% if locale == 'pl' %}Name: "polish"; MessagesFile: "compiler:Languages\\Polish.isl"{% endif %} {% if locale == 'pt' %}Name: "portuguese"; MessagesFile: "compiler:Languages\\Portuguese.isl"{% endif %} {% if locale == 'ru' %}Name: "russian"; MessagesFile: "compiler:Languages\\Russian.isl"{% endif %} {% if locale == 'sk' %}Name: "slovak"; MessagesFile: "compiler:Languages\\Slovak.isl"{% endif %} {% if locale == 'sl' %}Name: "slovenian"; MessagesFile: "compiler:Languages\\Slovenian.isl"{% endif %} {% if locale == 'es' %}Name: "spanish"; MessagesFile: "compiler:Languages\\Spanish.isl"{% endif %} {% if locale == 'tr' %}Name: "turkish"; MessagesFile: "compiler:Languages\\Turkish.isl"{% endif %} {% if locale == 'uk' %}Name: "ukrainian"; MessagesFile: "compiler:Languages\\Ukrainian.isl"{% endif %} {% endfor %} [Tasks] Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: {% if CREATE_DESKTOP_ICON != true %}unchecked{% else %}checkedonce{% endif %} Name: "launchAtStartup"; Description: "{cm:AutoStartProgram,{{DISPLAY_NAME}}}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: {% if LAUNCH_AT_STARTUP != true %}unchecked{% else %}checkedonce{% endif %} [Files] Source: "{{SOURCE_DIR}}\\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs ; NOTE: Don't use "Flags: ignoreversion" on any shared system files [Icons] Name: "{autoprograms}\\{{DISPLAY_NAME}}"; Filename: "{app}\\{{EXECUTABLE_NAME}}" Name: "{autodesktop}\\{{DISPLAY_NAME}}"; Filename: "{app}\\{{EXECUTABLE_NAME}}"; Tasks: desktopicon Name: "{userstartup}\\{{DISPLAY_NAME}}"; Filename: "{app}\\{{EXECUTABLE_NAME}}"; WorkingDir: "{app}"; Tasks: launchAtStartup [Run] Filename: "{app}\\{{EXECUTABLE_NAME}}"; Description: "{cm:LaunchProgram,{{DISPLAY_NAME}}}"; Flags: {% if PRIVILEGES_REQUIRED == 'admin' %}runascurrentuser{% endif %} nowait postinstall skipifsilent [UninstallDelete] Type: filesandordirs; Name: "{userappdata}\Hiddify" [Code] function InitializeSetup(): Boolean; var ResultCode: Integer; begin Exec('taskkill', '/F /IM hiddify.exe', '', SW_HIDE, ewWaitUntilTerminated, ResultCode) Exec('net', 'stop "HiddifyTunnelService"', '', SW_HIDE, ewWaitUntilTerminated, ResultCode) Exec('sc.exe', 'delete "HiddifyTunnelService"', '', SW_HIDE, ewWaitUntilTerminated, ResultCode) Result := True; end; ================================================ FILE: windows/packaging/exe/make_config.yaml ================================================ app_id: 6L903538-42B1-4596-G479-BJ779F21A65D publisher: Hiddify publisher_url: https://github.com/hiddify/hiddify-app display_name: Hiddify create_desktop_icon: true install_dir_name: "{autopf64}\\Hiddify" setup_icon_file: windows\runner\resources\app_icon.ico locales: - ar - en - es - fr - pt - ru - tr script_template: inno_setup.sas ================================================ FILE: windows/packaging/msix/make_config.yaml ================================================ display_name: Hiddify publisher_display_name: Hiddify identity_name: Hiddify.HiddifyNext msix_version: 4.1.2.0 logo_path: windows\runner\resources\app_icon.ico capabilities: internetClient, internetClientServer, privateNetworkClientServer languages: ar, en-us, es-es, fa-ir, fr-fr, id-id, pt-br, ru-ru, tr-tr, zh-cn, zh-tw protocol_activation: hiddify, v2ray, v2rayn, v2rayng, clash, clashmeta, sing-box execution_alias: hiddify certificate_path: windows\sign.pfx certificate_password: publisher: CN=8CB43675-F44B-4AA5-9372-E8727781BDC4 install_certificate: "false" enable_at_startup: "true" startup_task: parameters: --autostart # For testing with a self-signed certificate # publisher: CN=Hiddify-Test # certificate_password: "1234" # install_certificate: "true" ================================================ FILE: windows/runner/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.14) project(runner LANGUAGES CXX) # Define the application target. To change its name, change BINARY_NAME in the # top-level CMakeLists.txt, not the value here, or `flutter run` will no longer # work. # # Any new source files that you add to the application should be added here. add_executable(${BINARY_NAME} WIN32 "flutter_window.cpp" "main.cpp" "utils.cpp" "win32_window.cpp" "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" "Runner.rc" "runner.exe.manifest" ) # Apply the standard set of build settings. This can be removed for applications # that need different build settings. apply_standard_settings(${BINARY_NAME}) # Add preprocessor definitions for the build version. target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") # Disable Windows macros that collide with C++ standard library functions. target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") # Add dependency libraries and include directories. Add any application-specific # dependencies here. target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib") target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") # Run the Flutter tool portions of the build. This must not be removed. add_dependencies(${BINARY_NAME} flutter_assemble) ================================================ FILE: windows/runner/Runner.rc ================================================ // Microsoft Visual C++ generated resource script. // #pragma code_page(65001) #include "resource.h" #define APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 2 resource. // #include "winres.h" ///////////////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // English (United States) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US #ifdef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // TEXTINCLUDE // 1 TEXTINCLUDE BEGIN "resource.h\0" END 2 TEXTINCLUDE BEGIN "#include ""winres.h""\r\n" "\0" END 3 TEXTINCLUDE BEGIN "\r\n" "\0" END #endif // APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Icon // // Icon with lowest ID value placed first to ensure application icon // remains consistent on all systems. IDI_APP_ICON ICON "resources\\app_icon.ico" ///////////////////////////////////////////////////////////////////////////// // // Version // #if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) #define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD #else #define VERSION_AS_NUMBER 1,0,0,0 #endif #if defined(FLUTTER_VERSION) #define VERSION_AS_STRING FLUTTER_VERSION #else #define VERSION_AS_STRING "1.0.0" #endif VS_VERSION_INFO VERSIONINFO FILEVERSION VERSION_AS_NUMBER PRODUCTVERSION VERSION_AS_NUMBER FILEFLAGSMASK VS_FFI_FILEFLAGSMASK #ifdef _DEBUG FILEFLAGS VS_FF_DEBUG #else FILEFLAGS 0x0L #endif FILEOS VOS__WINDOWS32 FILETYPE VFT_APP FILESUBTYPE 0x0L BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904e4" BEGIN VALUE "CompanyName", "Hiddify" "\0" VALUE "FileDescription", "Hiddify" "\0" VALUE "FileVersion", VERSION_AS_STRING "\0" VALUE "InternalName", "hiddify" "\0" VALUE "LegalCopyright", "Copyright (C) 2023 Hiddify.com. All rights reserved." "\0" VALUE "OriginalFilename", "Hiddify.exe" "\0" VALUE "ProductName", "hiddify" "\0" VALUE "ProductVersion", VERSION_AS_STRING "\0" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x409, 1252 END END #endif // English (United States) resources ///////////////////////////////////////////////////////////////////////////// #ifndef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 3 resource. // ///////////////////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED ================================================ FILE: windows/runner/flutter_window.cpp ================================================ #include "flutter_window.h" #include #include "flutter/generated_plugin_registrant.h" FlutterWindow::FlutterWindow(const flutter::DartProject& project) : project_(project) {} FlutterWindow::~FlutterWindow() {} bool FlutterWindow::OnCreate() { if (!Win32Window::OnCreate()) { return false; } RECT frame = GetClientArea(); // The size here must match the window dimensions to avoid unnecessary surface // creation / destruction in the startup path. flutter_controller_ = std::make_unique( frame.right - frame.left, frame.bottom - frame.top, project_); // Ensure that basic setup of the controller was successful. if (!flutter_controller_->engine() || !flutter_controller_->view()) { return false; } RegisterPlugins(flutter_controller_->engine()); SetChildContent(flutter_controller_->view()->GetNativeWindow()); flutter_controller_->engine()->SetNextFrameCallback([&]() { // this->Show(); window_manager hidden at launch ""; }); // Flutter can complete the first frame before the "show window" callback is // registered. The following call ensures a frame is pending to ensure the // window is shown. It is a no-op if the first frame hasn't completed yet. flutter_controller_->ForceRedraw(); return true; } void FlutterWindow::OnDestroy() { if (flutter_controller_) { flutter_controller_ = nullptr; } Win32Window::OnDestroy(); } LRESULT FlutterWindow::MessageHandler(HWND hwnd, UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept { // Give Flutter, including plugins, an opportunity to handle window messages. if (flutter_controller_) { std::optional result = flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, lparam); if (result) { return *result; } } switch (message) { case WM_FONTCHANGE: flutter_controller_->engine()->ReloadSystemFonts(); break; } return Win32Window::MessageHandler(hwnd, message, wparam, lparam); } ================================================ FILE: windows/runner/flutter_window.h ================================================ #ifndef RUNNER_FLUTTER_WINDOW_H_ #define RUNNER_FLUTTER_WINDOW_H_ #include #include #include #include "win32_window.h" // A window that does nothing but host a Flutter view. class FlutterWindow : public Win32Window { public: // Creates a new FlutterWindow hosting a Flutter view running |project|. explicit FlutterWindow(const flutter::DartProject& project); virtual ~FlutterWindow(); protected: // Win32Window: bool OnCreate() override; void OnDestroy() override; LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept override; private: // The project to run. flutter::DartProject project_; // The Flutter instance hosted by this window. std::unique_ptr flutter_controller_; }; #endif // RUNNER_FLUTTER_WINDOW_H_ ================================================ FILE: windows/runner/main.cpp ================================================ #include #include #include #include "flutter_window.h" #include "utils.h" #include "app_links/app_links_plugin_c_api.h" // #include bool SendAppLinkToInstance(const std::wstring &title) { // Find our exact window HWND hwnd = ::FindWindow(L"FLUTTER_RUNNER_WIN32_WINDOW", title.c_str()); if (hwnd) { // Dispatch new link to current window SendAppLink(hwnd); // (Optional) Restore our window to front in same state WINDOWPLACEMENT place = {sizeof(WINDOWPLACEMENT)}; GetWindowPlacement(hwnd, &place); switch (place.showCmd) { case SW_SHOWMAXIMIZED: ShowWindow(hwnd, SW_SHOWMAXIMIZED); break; case SW_SHOWMINIMIZED: ShowWindow(hwnd, SW_RESTORE); break; default: ShowWindow(hwnd, SW_NORMAL); break; } SetWindowPos(0, HWND_TOP, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE); SetForegroundWindow(hwnd); // END (Optional) Restore // Window has been found, don't create another one. return true; } return false; } int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, _In_ wchar_t *command_line, _In_ int show_command) { // Replace "example" with the generated title found as parameter of `window.Create` in this file. // You may ignore the result if you need to create another window. if (SendAppLinkToInstance(L"Hiddify")) { return EXIT_SUCCESS; } HANDLE hMutexInstance = CreateMutex(NULL, TRUE, L"HiddifyMutex"); HWND handle = FindWindowA(NULL, "Hiddify"); if (GetLastError() == ERROR_ALREADY_EXISTS) { flutter::DartProject project(L"data"); std::vector command_line_arguments = GetCommandLineArguments(); project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); FlutterWindow window(project); if (window.SendAppLinkToInstance(L"Hiddify")) { return false; } WINDOWPLACEMENT place = {sizeof(WINDOWPLACEMENT)}; GetWindowPlacement(handle, &place); ShowWindow(handle, SW_NORMAL); return 0; } // Attach to console when present (e.g., 'flutter run') or create a // new console when running with a debugger. if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { CreateAndAttachConsole(); } // Initialize COM, so that it is available for use in the library and/or // plugins. ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); flutter::DartProject project(L"data"); std::vector command_line_arguments = GetCommandLineArguments(); project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); FlutterWindow window(project); Win32Window::Point origin(10, 10); Win32Window::Size size(1280, 720); if (!window.Create(L"Hiddify", origin, size)) { return EXIT_FAILURE; } window.SetQuitOnClose(true); ::MSG msg; while (::GetMessage(&msg, nullptr, 0, 0)) { ::TranslateMessage(&msg); ::DispatchMessage(&msg); } ::CoUninitialize(); ReleaseMutex(hMutexInstance); return EXIT_SUCCESS; } ================================================ FILE: windows/runner/resource.h ================================================ //{{NO_DEPENDENCIES}} // Microsoft Visual C++ generated include file. // Used by Runner.rc // #define IDI_APP_ICON 101 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 102 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 1001 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif ================================================ FILE: windows/runner/runner.exe.manifest ================================================ PerMonitorV2 ================================================ FILE: windows/runner/utils.cpp ================================================ #include "utils.h" #include #include #include #include #include void CreateAndAttachConsole() { if (::AllocConsole()) { FILE *unused; if (freopen_s(&unused, "CONOUT$", "w", stdout)) { _dup2(_fileno(stdout), 1); } if (freopen_s(&unused, "CONOUT$", "w", stderr)) { _dup2(_fileno(stdout), 2); } std::ios::sync_with_stdio(); FlutterDesktopResyncOutputStreams(); } } std::vector GetCommandLineArguments() { // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. int argc; wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); if (argv == nullptr) { return std::vector(); } std::vector command_line_arguments; // Skip the first argument as it's the binary name. for (int i = 1; i < argc; i++) { command_line_arguments.push_back(Utf8FromUtf16(argv[i])); } ::LocalFree(argv); return command_line_arguments; } std::string Utf8FromUtf16(const wchar_t* utf16_string) { if (utf16_string == nullptr) { return std::string(); } int target_length = ::WideCharToMultiByte( CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, -1, nullptr, 0, nullptr, nullptr) -1; // remove the trailing null character int input_length = (int)wcslen(utf16_string); std::string utf8_string; if (target_length <= 0 || target_length > utf8_string.max_size()) { return utf8_string; } utf8_string.resize(target_length); int converted_length = ::WideCharToMultiByte( CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, input_length, utf8_string.data(), target_length, nullptr, nullptr); if (converted_length == 0) { return std::string(); } return utf8_string; } ================================================ FILE: windows/runner/utils.h ================================================ #ifndef RUNNER_UTILS_H_ #define RUNNER_UTILS_H_ #include #include // Creates a console for the process, and redirects stdout and stderr to // it for both the runner and the Flutter library. void CreateAndAttachConsole(); // Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string // encoded in UTF-8. Returns an empty std::string on failure. std::string Utf8FromUtf16(const wchar_t* utf16_string); // Gets the command line arguments passed in as a std::vector, // encoded in UTF-8. Returns an empty std::vector on failure. std::vector GetCommandLineArguments(); #endif // RUNNER_UTILS_H_ ================================================ FILE: windows/runner/win32_window.cpp ================================================ #include "win32_window.h" #include #include #include "resource.h" // #include namespace { /// Window attribute that enables dark mode window decorations. /// /// Redefined in case the developer's machine has a Windows SDK older than /// version 10.0.22000.0. /// See: https://docs.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute #ifndef DWMWA_USE_IMMERSIVE_DARK_MODE #define DWMWA_USE_IMMERSIVE_DARK_MODE 20 #endif constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; /// Registry key for app theme preference. /// /// A value of 0 indicates apps should use dark mode. A non-zero or missing /// value indicates apps should use light mode. constexpr const wchar_t kGetPreferredBrightnessRegKey[] = L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"; constexpr const wchar_t kGetPreferredBrightnessRegValue[] = L"AppsUseLightTheme"; // The number of Win32Window objects that currently exist. static int g_active_window_count = 0; using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); // Scale helper to convert logical scaler values to physical using passed in // scale factor int Scale(int source, double scale_factor) { return static_cast(source * scale_factor); } // Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. // This API is only needed for PerMonitor V1 awareness mode. void EnableFullDpiSupportIfAvailable(HWND hwnd) { HMODULE user32_module = LoadLibraryA("User32.dll"); if (!user32_module) { return; } auto enable_non_client_dpi_scaling = reinterpret_cast( GetProcAddress(user32_module, "EnableNonClientDpiScaling")); if (enable_non_client_dpi_scaling != nullptr) { enable_non_client_dpi_scaling(hwnd); } FreeLibrary(user32_module); } } // namespace // Manages the Win32Window's window class registration. class WindowClassRegistrar { public: ~WindowClassRegistrar() = default; // Returns the singleton registrar instance. static WindowClassRegistrar *GetInstance() { if (!instance_) { instance_ = new WindowClassRegistrar(); } return instance_; } // Returns the name of the window class, registering the class if it hasn't // previously been registered. const wchar_t *GetWindowClass(); // Unregisters the window class. Should only be called if there are no // instances of the window. void UnregisterWindowClass(); private: WindowClassRegistrar() = default; static WindowClassRegistrar *instance_; bool class_registered_ = false; }; WindowClassRegistrar *WindowClassRegistrar::instance_ = nullptr; const wchar_t *WindowClassRegistrar::GetWindowClass() { if (!class_registered_) { WNDCLASS window_class{}; window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); window_class.lpszClassName = kWindowClassName; window_class.style = CS_HREDRAW | CS_VREDRAW; window_class.cbClsExtra = 0; window_class.cbWndExtra = 0; window_class.hInstance = GetModuleHandle(nullptr); window_class.hIcon = LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); window_class.hbrBackground = 0; window_class.lpszMenuName = nullptr; window_class.lpfnWndProc = Win32Window::WndProc; RegisterClass(&window_class); class_registered_ = true; } return kWindowClassName; } void WindowClassRegistrar::UnregisterWindowClass() { UnregisterClass(kWindowClassName, nullptr); class_registered_ = false; } Win32Window::Win32Window() { ++g_active_window_count; } Win32Window::~Win32Window() { --g_active_window_count; Destroy(); } bool Win32Window::Create(const std::wstring &title, const Point &origin, const Size &size) { if (SendAppLinkToInstance(title)) { return false; } Destroy(); const wchar_t *window_class = WindowClassRegistrar::GetInstance()->GetWindowClass(); const POINT target_point = {static_cast(origin.x), static_cast(origin.y)}; HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); double scale_factor = dpi / 96.0; HWND window = CreateWindow( // window_class, title.c_str(), WS_OVERLAPPEDWINDOW, // window_manager hidden at launch window_class, title.c_str(), WS_OVERLAPPEDWINDOW, // do not add WS_VISIBLE since the window will be shown later Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), Scale(size.width, scale_factor), Scale(size.height, scale_factor), nullptr, nullptr, GetModuleHandle(nullptr), this); if (!window) { return false; } UpdateTheme(window); return OnCreate(); } bool Win32Window::Show() { return ShowWindow(window_handle_, SW_SHOWNORMAL); } bool Win32Window::SendAppLinkToInstance(const std::wstring &title) { // Find our exact window HWND hwnd = ::FindWindow(kWindowClassName, title.c_str()); if (hwnd) { // Dispatch new link to current window // DispatchToProtocolHandler(hwnd); // (Optional) Restore our window to front in same state WINDOWPLACEMENT place = {sizeof(WINDOWPLACEMENT)}; GetWindowPlacement(hwnd, &place); switch (place.showCmd) { case SW_SHOWMAXIMIZED: ShowWindow(hwnd, SW_SHOWMAXIMIZED); break; case SW_SHOWMINIMIZED: ShowWindow(hwnd, SW_RESTORE); break; default: ShowWindow(hwnd, SW_NORMAL); break; } SetWindowPos(0, HWND_TOP, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE); SetForegroundWindow(hwnd); // Window has been found, don't create another one. return true; } return false; } // static LRESULT CALLBACK Win32Window::WndProc(HWND const window, UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept { if (message == WM_NCCREATE) { auto window_struct = reinterpret_cast(lparam); SetWindowLongPtr(window, GWLP_USERDATA, reinterpret_cast(window_struct->lpCreateParams)); auto that = static_cast(window_struct->lpCreateParams); EnableFullDpiSupportIfAvailable(window); that->window_handle_ = window; } else if (Win32Window *that = GetThisFromHandle(window)) { return that->MessageHandler(window, message, wparam, lparam); } return DefWindowProc(window, message, wparam, lparam); } LRESULT Win32Window::MessageHandler(HWND hwnd, UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept { switch (message) { case WM_DESTROY: window_handle_ = nullptr; Destroy(); if (quit_on_close_) { PostQuitMessage(0); } return 0; case WM_DPICHANGED: { auto newRectSize = reinterpret_cast(lparam); LONG newWidth = newRectSize->right - newRectSize->left; LONG newHeight = newRectSize->bottom - newRectSize->top; SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, newHeight, SWP_NOZORDER | SWP_NOACTIVATE); return 0; } case WM_SIZE: { RECT rect = GetClientArea(); if (child_content_ != nullptr) { // Size and position the child window. MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, TRUE); } return 0; } case WM_ACTIVATE: if (child_content_ != nullptr) { SetFocus(child_content_); } return 0; case WM_DWMCOLORIZATIONCOLORCHANGED: UpdateTheme(hwnd); return 0; } return DefWindowProc(window_handle_, message, wparam, lparam); } void Win32Window::Destroy() { OnDestroy(); if (window_handle_) { DestroyWindow(window_handle_); window_handle_ = nullptr; } if (g_active_window_count == 0) { WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); } } Win32Window *Win32Window::GetThisFromHandle(HWND const window) noexcept { return reinterpret_cast( GetWindowLongPtr(window, GWLP_USERDATA)); } void Win32Window::SetChildContent(HWND content) { child_content_ = content; SetParent(content, window_handle_); RECT frame = GetClientArea(); MoveWindow(content, frame.left, frame.top, frame.right - frame.left, frame.bottom - frame.top, true); SetFocus(child_content_); } RECT Win32Window::GetClientArea() { RECT frame; GetClientRect(window_handle_, &frame); return frame; } HWND Win32Window::GetHandle() { return window_handle_; } void Win32Window::SetQuitOnClose(bool quit_on_close) { quit_on_close_ = quit_on_close; } bool Win32Window::OnCreate() { // No-op; provided for subclasses. return true; } void Win32Window::OnDestroy() { // No-op; provided for subclasses. } void Win32Window::UpdateTheme(HWND const window) { DWORD light_mode; DWORD light_mode_size = sizeof(light_mode); LSTATUS result = RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey, kGetPreferredBrightnessRegValue, RRF_RT_REG_DWORD, nullptr, &light_mode, &light_mode_size); if (result == ERROR_SUCCESS) { BOOL enable_dark_mode = light_mode == 0; DwmSetWindowAttribute(window, DWMWA_USE_IMMERSIVE_DARK_MODE, &enable_dark_mode, sizeof(enable_dark_mode)); } } ================================================ FILE: windows/runner/win32_window.h ================================================ #ifndef RUNNER_WIN32_WINDOW_H_ #define RUNNER_WIN32_WINDOW_H_ #include #include #include #include // #include // A class abstraction for a high DPI-aware Win32 Window. Intended to be // inherited from by classes that wish to specialize with custom // rendering and input handling class Win32Window { public: struct Point { unsigned int x; unsigned int y; Point(unsigned int x, unsigned int y) : x(x), y(y) {} }; struct Size { unsigned int width; unsigned int height; Size(unsigned int width, unsigned int height) : width(width), height(height) {} }; Win32Window(); virtual ~Win32Window(); // Creates a win32 window with |title| that is positioned and sized using // |origin| and |size|. New windows are created on the default monitor. Window // sizes are specified to the OS in physical pixels, hence to ensure a // consistent size this function will scale the inputted width and height as // as appropriate for the default monitor. The window is invisible until // |Show| is called. Returns true if the window was created successfully. bool Create(const std::wstring &title, const Point &origin, const Size &size); bool SendAppLinkToInstance(const std::wstring &title); // Show the current window. Returns true if the window was successfully shown. bool Show(); // Release OS resources associated with window. void Destroy(); // Inserts |content| into the window tree. void SetChildContent(HWND content); // Returns the backing Window handle to enable clients to set icon and other // window properties. Returns nullptr if the window has been destroyed. HWND GetHandle(); // If true, closing this window will quit the application. void SetQuitOnClose(bool quit_on_close); // Return a RECT representing the bounds of the current client area. RECT GetClientArea(); protected: // Processes and route salient window messages for mouse handling, // size change and DPI. Delegates handling of these to member overloads that // inheriting classes can handle. virtual LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept; // Called when CreateAndShow is called, allowing subclass window-related // setup. Subclasses should return false if setup fails. virtual bool OnCreate(); // Called when Destroy is called. virtual void OnDestroy(); private: friend class WindowClassRegistrar; // OS callback called by message pump. Handles the WM_NCCREATE message which // is passed when the non-client area is being created and enables automatic // non-client DPI scaling so that the non-client area automatically // responds to changes in DPI. All other messages are handled by // MessageHandler. static LRESULT CALLBACK WndProc(HWND const window, UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept; // Retrieves a class instance pointer for |window| static Win32Window *GetThisFromHandle(HWND const window) noexcept; // Update the window frame's theme to match the system theme. static void UpdateTheme(HWND const window); bool quit_on_close_ = false; // window handle for top level window. HWND window_handle_ = nullptr; // window handle for hosted content. HWND child_content_ = nullptr; }; #endif // RUNNER_WIN32_WINDOW_H_