The response has been limited to 50k tokens of the smallest files in the repo. You can remove this limitation by removing the max tokens filter.
├── .gitattributes
├── .github
    ├── ISSUE_TEMPLATE
    │   ├── 01-bug-report-en.yml
    │   ├── 02-feature-request-en.yml
    │   ├── 03-bug-report-zh-cn.yml
    │   ├── 04-feature-request-zh-cn.yml
    │   └── config.yml
    └── workflows
    │   ├── build-debug.yaml
    │   ├── build-pre-release.yaml
    │   ├── build-release.yaml
    │   └── update-dependencies.yaml
├── .gitignore
├── .gitmodules
├── .idea
    └── codeStyles
    │   ├── Project.xml
    │   └── codeStyleConfig.xml
├── CONTRIBUTING.md
├── LICENSE
├── NOTICE
├── PRIVACY_POLICY.md
├── README.md
├── app
    ├── build.gradle.kts
    ├── proguard-rules.pro
    └── src
    │   └── main
    │       ├── AndroidManifest.xml
    │       ├── ic_launcher-playstore.png
    │       ├── ic_launcher-web.png
    │       ├── java
    │           └── com
    │           │   └── github
    │           │       └── kr328
    │           │           └── clash
    │           │               ├── AccessControlActivity.kt
    │           │               ├── ApkBrokenActivity.kt
    │           │               ├── AppCrashedActivity.kt
    │           │               ├── AppSettingsActivity.kt
    │           │               ├── BaseActivity.kt
    │           │               ├── DialerReceiver.kt
    │           │               ├── ExternalControlActivity.kt
    │           │               ├── FilesActivity.kt
    │           │               ├── HelpActivity.kt
    │           │               ├── LogcatActivity.kt
    │           │               ├── LogcatService.kt
    │           │               ├── LogsActivity.kt
    │           │               ├── MainActivity.kt
    │           │               ├── MainApplication.kt
    │           │               ├── MetaFeatureSettingsActivity.kt
    │           │               ├── NetworkSettingsActivity.kt
    │           │               ├── NewProfileActivity.kt
    │           │               ├── OverrideSettingsActivity.kt
    │           │               ├── ProfilesActivity.kt
    │           │               ├── PropertiesActivity.kt
    │           │               ├── ProvidersActivity.kt
    │           │               ├── ProxyActivity.kt
    │           │               ├── RestartReceiver.kt
    │           │               ├── SettingsActivity.kt
    │           │               ├── TileService.kt
    │           │               ├── log
    │           │                   ├── LogcatCache.kt
    │           │                   ├── LogcatFilter.kt
    │           │                   ├── LogcatReader.kt
    │           │                   ├── LogcatWriter.kt
    │           │                   └── SystemLogcat.kt
    │           │               ├── remote
    │           │                   ├── Broadcasts.kt
    │           │                   ├── FilesClient.kt
    │           │                   ├── Remote.kt
    │           │                   ├── Resource.kt
    │           │                   ├── Service.kt
    │           │                   └── StatusClient.kt
    │           │               ├── store
    │           │                   ├── AppStore.kt
    │           │                   └── TipsStore.kt
    │           │               └── util
    │           │                   ├── Activity.kt
    │           │                   ├── Application.kt
    │           │                   ├── Clash.kt
    │           │                   ├── Content.kt
    │           │                   ├── Files.kt
    │           │                   ├── Remote.kt
    │           │                   ├── Service.kt
    │           │                   └── Uri.kt
    │       └── res
    │           ├── drawable
    │               └── ic_launcher_foreground.xml
    │           ├── mipmap-anydpi-v26
    │               ├── ic_launcher.xml
    │               └── ic_launcher_round.xml
    │           ├── mipmap-hdpi
    │               ├── ic_launcher.png
    │               └── ic_launcher_round.png
    │           ├── mipmap-mdpi
    │               ├── ic_launcher.png
    │               └── ic_launcher_round.png
    │           ├── mipmap-xhdpi
    │               ├── ic_banner.png
    │               ├── ic_launcher.png
    │               └── ic_launcher_round.png
    │           ├── mipmap-xxhdpi
    │               ├── ic_launcher.png
    │               └── ic_launcher_round.png
    │           ├── mipmap-xxxhdpi
    │               ├── ic_launcher.png
    │               └── ic_launcher_round.png
    │           ├── values-night
    │               └── themes.xml
    │           ├── values
    │               ├── colors.xml
    │               ├── ic_banner_background.xml
    │               ├── ic_launcher_background.xml
    │               ├── ids.xml
    │               └── themes.xml
    │           └── xml
    │               ├── full_backup_content.xml
    │               └── network_security_config.xml
├── build.gradle.kts
├── common
    ├── build.gradle.kts
    ├── consumer-rules.pro
    ├── proguard-rules.pro
    └── src
    │   └── main
    │       ├── AndroidManifest.xml
    │       ├── java
    │           └── com
    │           │   └── github
    │           │       └── kr328
    │           │           └── clash
    │           │               └── common
    │           │                   ├── Global.kt
    │           │                   ├── compat
    │           │                       ├── App.kt
    │           │                       ├── Context.kt
    │           │                       ├── Html.kt
    │           │                       ├── Intents.kt
    │           │                       ├── Package.kt
    │           │                       ├── Resource.kt
    │           │                       ├── Services.kt
    │           │                       ├── UI.kt
    │           │                       └── View.kt
    │           │                   ├── constants
    │           │                       ├── Authorities.kt
    │           │                       ├── Components.kt
    │           │                       ├── Intents.kt
    │           │                       ├── Metadata.kt
    │           │                       └── Permissions.kt
    │           │                   ├── id
    │           │                       └── UndefinedIds.kt
    │           │                   ├── log
    │           │                       └── Log.kt
    │           │                   ├── store
    │           │                       ├── Providers.kt
    │           │                       ├── Store.kt
    │           │                       └── StoreProvider.kt
    │           │                   └── util
    │           │                       ├── Components.kt
    │           │                       ├── Global.kt
    │           │                       ├── Intent.kt
    │           │                       ├── Parcelable.kt
    │           │                       ├── Patterns.kt
    │           │                       └── Ticker.kt
    │       └── res
    │           ├── values-ru
    │               └── strings.xml
    │           ├── values-zh-rTW
    │               └── strings.xml
    │           ├── values-zh
    │               └── strings.xml
    │           └── values
    │               └── strings.xml
├── core
    ├── .gitignore
    ├── build.gradle.kts
    ├── consumer-rules.pro
    ├── proguard-rules.pro
    └── src
    │   ├── foss
    │       └── golang
    │       │   ├── go.mod
    │       │   ├── go.sum
    │       │   └── main.go
    │   └── main
    │       ├── AndroidManifest.xml
    │       ├── cpp
    │           ├── CMakeLists.txt
    │           ├── bridge_helper.c
    │           ├── bridge_helper.h
    │           ├── jni_helper.c
    │           ├── jni_helper.h
    │           ├── main.c
    │           └── version.h.in
    │       ├── golang
    │           ├── .idea
    │           │   └── codeStyles
    │           │   │   ├── Project.xml
    │           │   │   └── codeStyleConfig.xml
    │           ├── go.mod
    │           ├── go.sum
    │           └── native
    │           │   ├── all
    │           │       └── imports.go
    │           │   ├── app.go
    │           │   ├── app
    │           │       ├── app.go
    │           │       ├── content.go
    │           │       ├── dns.go
    │           │       ├── tun.go
    │           │       └── ui.go
    │           │   ├── bridge.c
    │           │   ├── bridge.h
    │           │   ├── common
    │           │       └── path.go
    │           │   ├── config.go
    │           │   ├── config
    │           │       ├── defaults.go
    │           │       ├── fetch.go
    │           │       ├── load.go
    │           │       ├── override.go
    │           │       ├── process.go
    │           │       └── provider.go
    │           │   ├── debug.go
    │           │   ├── delegate
    │           │       └── init.go
    │           │   ├── log.go
    │           │   ├── main.go
    │           │   ├── platform
    │           │       ├── limit.go
    │           │       └── procfs.go
    │           │   ├── proxy.go
    │           │   ├── proxy
    │           │       └── http.go
    │           │   ├── trace.c
    │           │   ├── trace.h
    │           │   ├── tun.go
    │           │   ├── tun
    │           │       └── tun.go
    │           │   ├── tunnel.go
    │           │   ├── tunnel
    │           │       ├── conn.go
    │           │       ├── connectivity.go
    │           │       ├── loopback.go
    │           │       ├── providers.go
    │           │       ├── proxies.go
    │           │       ├── state.go
    │           │       ├── statistic.go
    │           │       └── suspend.go
    │           │   └── utils.go
    │       └── java
    │           └── com
    │               └── github
    │                   └── kr328
    │                       └── clash
    │                           └── core
    │                               ├── Clash.kt
    │                               ├── bridge
    │                                   ├── Bridge.kt
    │                                   ├── ClashException.kt
    │                                   ├── Content.kt
    │                                   ├── FetchCallback.kt
    │                                   ├── LogcatInterface.kt
    │                                   └── TunInterface.kt
    │                               ├── model
    │                                   ├── ConfigurationOverride.kt
    │                                   ├── FetchStatus.kt
    │                                   ├── LogMessage.kt
    │                                   ├── Provider.kt
    │                                   ├── ProviderList.kt
    │                                   ├── Proxy.kt
    │                                   ├── ProxyGroup.kt
    │                                   ├── ProxySort.kt
    │                                   ├── Traffic.kt
    │                                   ├── TunnelState.kt
    │                                   └── UiConfiguration.kt
    │                               └── util
    │                                   ├── Net.kt
    │                                   ├── Parcelizer.kt
    │                                   ├── Serializers.kt
    │                                   └── Traffic.kt
├── design
    ├── build.gradle.kts
    ├── consumer-rules.pro
    ├── proguard-rules.pro
    └── src
    │   └── main
    │       ├── AndroidManifest.xml
    │       ├── java
    │           └── com
    │           │   └── github
    │           │       └── kr328
    │           │           └── clash
    │           │               └── design
    │           │                   ├── AccessControlDesign.kt
    │           │                   ├── ApkBrokenDesign.kt
    │           │                   ├── AppCrashedDesign.kt
    │           │                   ├── AppSettingsDesign.kt
    │           │                   ├── Design.kt
    │           │                   ├── FilesDesign.kt
    │           │                   ├── HelpDesign.kt
    │           │                   ├── LogcatDesign.kt
    │           │                   ├── LogsDesign.kt
    │           │                   ├── MainDesign.kt
    │           │                   ├── MetaFeatureSettingsDesign.kt
    │           │                   ├── NetworkSettingsDesign.kt
    │           │                   ├── NewProfileDesign.kt
    │           │                   ├── OverrideSettingsDesign.kt
    │           │                   ├── ProfilesDesign.kt
    │           │                   ├── PropertiesDesign.kt
    │           │                   ├── ProvidersDesign.kt
    │           │                   ├── ProxyDesign.kt
    │           │                   ├── SettingsDesign.kt
    │           │                   ├── adapter
    │           │                       ├── AppAdapter.kt
    │           │                       ├── EditableTextListAdapter.kt
    │           │                       ├── EditableTextMapAdapter.kt
    │           │                       ├── FileAdapter.kt
    │           │                       ├── LogFileAdapter.kt
    │           │                       ├── LogMessageAdapter.kt
    │           │                       ├── PopupListAdapter.kt
    │           │                       ├── ProfileAdapter.kt
    │           │                       ├── ProfileProviderAdapter.kt
    │           │                       ├── ProviderAdapter.kt
    │           │                       ├── ProxyAdapter.kt
    │           │                       └── ProxyPageAdapter.kt
    │           │                   ├── component
    │           │                       ├── AccessControlMenu.kt
    │           │                       ├── ProxyMenu.kt
    │           │                       ├── ProxyPageFactory.kt
    │           │                       ├── ProxyView.kt
    │           │                       ├── ProxyViewConfig.kt
    │           │                       └── ProxyViewState.kt
    │           │                   ├── dialog
    │           │                       ├── Dialogs.kt
    │           │                       ├── Input.kt
    │           │                       └── Progress.kt
    │           │                   ├── model
    │           │                       ├── AppInfo.kt
    │           │                       ├── AppInfoSort.kt
    │           │                       ├── Behavior.kt
    │           │                       ├── DarkMode.kt
    │           │                       ├── File.kt
    │           │                       ├── LogFile.kt
    │           │                       ├── ProfilePageState.kt
    │           │                       ├── ProfileProvider.kt
    │           │                       ├── ProviderState.kt
    │           │                       ├── ProxyPageState.kt
    │           │                       └── ProxyState.kt
    │           │                   ├── preference
    │           │                       ├── Category.kt
    │           │                       ├── Clickable.kt
    │           │                       ├── EditableText.kt
    │           │                       ├── EditableTextList.kt
    │           │                       ├── EditableTextMap.kt
    │           │                       ├── Overlay.kt
    │           │                       ├── Preference.kt
    │           │                       ├── Screen.kt
    │           │                       ├── SelectableList.kt
    │           │                       ├── Switch.kt
    │           │                       ├── Tips.kt
    │           │                       └── Value.kt
    │           │                   ├── store
    │           │                       └── UiStore.kt
    │           │                   ├── ui
    │           │                       ├── DayNight.kt
    │           │                       ├── Insets.kt
    │           │                       ├── ObservableCurrentTime.kt
    │           │                       ├── Surface.kt
    │           │                       └── ToastDuration.kt
    │           │                   ├── util
    │           │                       ├── ActivityBar.kt
    │           │                       ├── App.kt
    │           │                       ├── Binding.kt
    │           │                       ├── Context.kt
    │           │                       ├── Diff.kt
    │           │                       ├── Elevation.kt
    │           │                       ├── I18n.kt
    │           │                       ├── Inserts.kt
    │           │                       ├── Interval.kt
    │           │                       ├── Landscape.kt
    │           │                       ├── ListView.kt
    │           │                       ├── RecyclerView.kt
    │           │                       ├── ScrollView.kt
    │           │                       ├── Theme.kt
    │           │                       ├── Toast.kt
    │           │                       ├── Validator.kt
    │           │                       └── View.kt
    │           │                   └── view
    │           │                       ├── ActionLabel.kt
    │           │                       ├── ActionTextField.kt
    │           │                       ├── ActivityBarLayout.kt
    │           │                       ├── AppRecyclerView.kt
    │           │                       ├── LargeActionCard.kt
    │           │                       ├── LargeActionLabel.kt
    │           │                       ├── ObservableScrollView.kt
    │           │                       └── VerticalScrollableHost.kt
    │       └── res
    │           ├── anim
    │               └── rotate_infinite.xml
    │           ├── drawable
    │               ├── bg_b.xml
    │               ├── bg_bottom_sheet.xml
    │               ├── ic_baseline_adb.xml
    │               ├── ic_baseline_add.xml
    │               ├── ic_baseline_apps.xml
    │               ├── ic_baseline_arrow_back.xml
    │               ├── ic_baseline_assignment.xml
    │               ├── ic_baseline_attach_file.xml
    │               ├── ic_baseline_brightness_4.xml
    │               ├── ic_baseline_clear_all.xml
    │               ├── ic_baseline_close.xml
    │               ├── ic_baseline_cloud_download.xml
    │               ├── ic_baseline_content_copy.xml
    │               ├── ic_baseline_delete.xml
    │               ├── ic_baseline_dns.xml
    │               ├── ic_baseline_domain.xml
    │               ├── ic_baseline_edit.xml
    │               ├── ic_baseline_extension.xml
    │               ├── ic_baseline_flash_on.xml
    │               ├── ic_baseline_get_app.xml
    │               ├── ic_baseline_help_center.xml
    │               ├── ic_baseline_hide.xml
    │               ├── ic_baseline_info.xml
    │               ├── ic_baseline_meta.xml
    │               ├── ic_baseline_more_vert.xml
    │               ├── ic_baseline_publish.xml
    │               ├── ic_baseline_replay.xml
    │               ├── ic_baseline_restore.xml
    │               ├── ic_baseline_save.xml
    │               ├── ic_baseline_search.xml
    │               ├── ic_baseline_settings.xml
    │               ├── ic_baseline_stop.xml
    │               ├── ic_baseline_swap_vert.xml
    │               ├── ic_baseline_swap_vertical_circle.xml
    │               ├── ic_baseline_sync.xml
    │               ├── ic_baseline_update.xml
    │               ├── ic_baseline_view_list.xml
    │               ├── ic_baseline_vpn_lock.xml
    │               ├── ic_baseline_work.xml
    │               ├── ic_clash.xml
    │               ├── ic_outline_article.xml
    │               ├── ic_outline_check_circle.xml
    │               ├── ic_outline_delete.xml
    │               ├── ic_outline_folder.xml
    │               ├── ic_outline_inbox.xml
    │               ├── ic_outline_info.xml
    │               ├── ic_outline_label.xml
    │               ├── ic_outline_not_interested.xml
    │               └── ic_outline_update.xml
    │           ├── layout
    │               ├── adapter_app.xml
    │               ├── adapter_editable_text_list.xml
    │               ├── adapter_editable_text_map.xml
    │               ├── adapter_file.xml
    │               ├── adapter_log_message.xml
    │               ├── adapter_profile.xml
    │               ├── adapter_profile_provider.xml
    │               ├── adapter_provider.xml
    │               ├── adapter_sideload_provider.xml
    │               ├── common_activity_bar.xml
    │               ├── common_recycler_list.xml
    │               ├── component_action_label.xml
    │               ├── component_action_text_field.xml
    │               ├── component_large_action_label.xml
    │               ├── design_about.xml
    │               ├── design_access_control.xml
    │               ├── design_app_crashed.xml
    │               ├── design_files.xml
    │               ├── design_logcat.xml
    │               ├── design_logs.xml
    │               ├── design_main.xml
    │               ├── design_new_profile.xml
    │               ├── design_profiles.xml
    │               ├── design_properties.xml
    │               ├── design_providers.xml
    │               ├── design_proxy.xml
    │               ├── design_settings.xml
    │               ├── design_settings_common.xml
    │               ├── design_settings_meta_feature.xml
    │               ├── design_settings_overide.xml
    │               ├── dialog_editable_map_text_field.xml
    │               ├── dialog_fetch_status.xml
    │               ├── dialog_files_menu.xml
    │               ├── dialog_preference_list.xml
    │               ├── dialog_profiles_menu.xml
    │               ├── dialog_search.xml
    │               ├── dialog_text_field.xml
    │               ├── preference_category.xml
    │               ├── preference_clickable.xml
    │               ├── preference_switch.xml
    │               └── preference_tips.xml
    │           ├── menu
    │               ├── menu_access_control.xml
    │               └── menu_proxy.xml
    │           ├── values-ja-rJP
    │               └── strings.xml
    │           ├── values-ko-rKR
    │               └── strings.xml
    │           ├── values-ru
    │               └── strings.xml
    │           ├── values-v23
    │               └── themes.xml
    │           ├── values-v27
    │               └── themes.xml
    │           ├── values-v29
    │               └── themes.xml
    │           ├── values-vi
    │               └── strings.xml
    │           ├── values-zh-rHK
    │               └── strings.xml
    │           ├── values-zh-rTW
    │               └── strings.xml
    │           ├── values-zh
    │               └── strings.xml
    │           └── values
    │               ├── attrs.xml
    │               ├── colors.xml
    │               ├── dimens.xml
    │               ├── ids.xml
    │               ├── strings.xml
    │               ├── styles.xml
    │               └── themes.xml
├── fastlane
    └── metadata
    │   └── android
    │       ├── en-US
    │           ├── full_description.txt
    │           └── short_description.txt
    │       └── zh-CN
    │           ├── full_description.txt
    │           └── short_description.txt
├── gradle.properties
├── gradle
    ├── libs.versions.toml
    └── wrapper
    │   ├── gradle-wrapper.jar
    │   └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── hideapi
    ├── build.gradle.kts
    ├── consumer-rules.pro
    ├── proguard-rules.pro
    └── src
    │   └── main
    │       ├── AndroidManifest.xml
    │       └── java
    │           └── android
    │               └── app
    │                   └── ActivityThread.java
├── release.keystore
├── renovate.json
├── service
    ├── build.gradle.kts
    ├── consumer-rules.pro
    ├── proguard-rules.pro
    └── src
    │   └── main
    │       ├── AndroidManifest.xml
    │       ├── java
    │           └── com
    │           │   └── github
    │           │       └── kr328
    │           │           └── clash
    │           │               └── service
    │           │                   ├── BaseService.kt
    │           │                   ├── ClashManager.kt
    │           │                   ├── ClashService.kt
    │           │                   ├── FilesProvider.kt
    │           │                   ├── PreferenceProvider.kt
    │           │                   ├── ProfileManager.kt
    │           │                   ├── ProfileProcessor.kt
    │           │                   ├── ProfileReceiver.kt
    │           │                   ├── ProfileWorker.kt
    │           │                   ├── RemoteService.kt
    │           │                   ├── StatusProvider.kt
    │           │                   ├── TunService.kt
    │           │                   ├── clash
    │           │                       ├── ClashRuntime.kt
    │           │                       └── module
    │           │                       │   ├── AppListCacheModule.kt
    │           │                       │   ├── CloseModule.kt
    │           │                       │   ├── ConfigurationModule.kt
    │           │                       │   ├── DynamicNotificationModule.kt
    │           │                       │   ├── Module.kt
    │           │                       │   ├── NetworkObserveModule.kt
    │           │                       │   ├── StaticNotificationModule.kt
    │           │                       │   ├── SuspendModule.kt
    │           │                       │   ├── TimeZoneModule.kt
    │           │                       │   └── TunModule.kt
    │           │                   ├── data
    │           │                       ├── Converters.kt
    │           │                       ├── Daos.kt
    │           │                       ├── Database.kt
    │           │                       ├── Imported.kt
    │           │                       ├── ImportedDao.kt
    │           │                       ├── Pending.kt
    │           │                       ├── PendingDao.kt
    │           │                       ├── Selection.kt
    │           │                       ├── SelectionDao.kt
    │           │                       └── migrations
    │           │                       │   ├── LegacyMigration.kt
    │           │                       │   └── Migrations.kt
    │           │                   ├── document
    │           │                       ├── Document.kt
    │           │                       ├── FileDocument.kt
    │           │                       ├── Flag.kt
    │           │                       ├── Path.kt
    │           │                       ├── Paths.kt
    │           │                       ├── Picker.kt
    │           │                       └── VirtualDocument.kt
    │           │                   ├── model
    │           │                       ├── AccessControlMode.kt
    │           │                       └── Profile.kt
    │           │                   ├── remote
    │           │                       ├── IClashManager.kt
    │           │                       ├── IFetchObserver.kt
    │           │                       ├── ILogObserver.kt
    │           │                       ├── IProfileManager.kt
    │           │                       └── IRemoteService.kt
    │           │                   ├── store
    │           │                       └── ServiceStore.kt
    │           │                   └── util
    │           │                       ├── Address.kt
    │           │                       ├── Broadcast.kt
    │           │                       ├── Connectivity.kt
    │           │                       ├── Coroutine.kt
    │           │                       ├── Database.kt
    │           │                       ├── Files.kt
    │           │                       ├── Intent.kt
    │           │                       ├── Net.kt
    │           │                       └── Serializers.kt
    │       └── res
    │           ├── drawable
    │               └── ic_logo_service.xml
    │           ├── values-ja-rJP
    │               └── strings.xml
    │           ├── values-ko-rKR
    │               └── strings.xml
    │           ├── values-ru
    │               └── strings.xml
    │           ├── values-zh-rHK
    │               └── strings.xml
    │           ├── values-zh-rTW
    │               └── strings.xml
    │           ├── values-zh
    │               └── strings.xml
    │           └── values
    │               ├── arrays.xml
    │               ├── colors.xml
    │               ├── ids.xml
    │               └── strings.xml
└── settings.gradle.kts


/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto eol=lf
2 | 
3 | *.bat text eol=crlf
4 | *.jar binary
5 | 


--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/02-feature-request-en.yml:
--------------------------------------------------------------------------------
 1 | name: "[English] Feature Request"
 2 | description: "Create a report to help us improve"
 3 | title: "[Feature Request] "
 4 | labels: ["enhancement"]
 5 | body:
 6 |   - type: markdown
 7 |     attributes:
 8 |       value: |
 9 |         Thanks for taking the time to fill out this feature request!
10 | 
11 |         NOTE: Be sure to put a clear and concise title **AFTER** `[Feature Request]` in the text box above.
12 | 
13 |         <!-- template -->
14 |   - type: textarea
15 |     id: "description"
16 |     attributes:
17 |       label: "Feature Description"
18 |       description: |
19 |         A clear and concise description of the feature.
20 |     validations:
21 |       required: true
22 |   - type: textarea
23 |     id: "additional"
24 |     attributes:
25 |       label: "Additional"
26 |       description: |
27 |         Add any other context or screenshots about the feature request here.
28 | 


--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/04-feature-request-zh-cn.yml:
--------------------------------------------------------------------------------
 1 | name: "[简体中文] 功能请求"
 2 | description: "您希望的能够在应用中增加功能"
 3 | title: "[Feature Request] "
 4 | labels: ["enhancement"]
 5 | body:
 6 |   - type: markdown
 7 |     attributes:
 8 |       value: |
 9 |         感谢您在百忙之中填写此功能请求报告。
10 | 
11 |         注意: 请务必在上方文本框的 `[Feature Request]` **之后**填写清晰明了的标题。
12 | 
13 |         <!-- template -->
14 |   - type: textarea
15 |     id: "description"
16 |     attributes:
17 |       label: "功能描述"
18 |       description: |
19 |         简介明了的描述此功能。
20 |     validations:
21 |       required: true
22 |   - type: textarea
23 |     id: "additional"
24 |     attributes:
25 |       label: "附加信息"
26 |       description: |
27 |         与此功能相关的其他附加信息。
28 | 


--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
2 | 


--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
 1 | .gradle
 2 | build/
 3 | /app/foss/release
 4 | /app/premium/release
 5 | /captures
 6 | 
 7 | # Ignore Gradle GUI config
 8 | gradle-app.setting
 9 | 
10 | # Avoid ignoring Gradle wrapper jar targetFile (.jar files are usually ignored)
11 | !gradle-wrapper.jar
12 | 
13 | # Cache of project
14 | .gradletasknamecache
15 | 
16 | # # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
17 | # gradle/wrapper/gradle-wrapper.properties
18 | 
19 | # Ignore IDEA config
20 | *.iml
21 | /.idea/*
22 | !/.idea/codeStyles
23 | /core/src/main/golang/.idea/*
24 | !/core/src/main/golang/.idea/codeStyles
25 | /core/src/foss/golang/.idea/*
26 | !/core/src/foss/golang/.idea/codeStyles
27 | /core/src/premium/golang/.idea/*
28 | !/core/src/premium/golang/.idea/codeStyles
29 | 
30 | # Ignore builtin geofiles
31 | app/src/main/assets
32 | 
33 | # KeyStore
34 | signing.properties
35 | *.keystore
36 | *.jks
37 | 
38 | # clion cmake build
39 | cmake-build-*
40 | 
41 | # local.properties
42 | local.properties
43 | 
44 | 
45 | # tracker
46 | tracker.properties
47 | 
48 | # vscode
49 | .vscode
50 | 
51 | # cxx
52 | .cxx
53 | 
54 | *.hprof
55 | 
56 | # firebase
57 | google-services.json
58 | 
59 | # Dolphin
60 | .directory
61 | 
62 | # logs
63 | *.log
64 | 
65 | # MacOS
66 | .DS_Store
67 | 


--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "clash-foss"]
2 | 	path = core/src/foss/golang/clash
3 | 	url = https://github.com/MetaCubeX/mihomo
4 | 	branch = Alpha
5 | 


--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 | <component name="ProjectCodeStyleConfiguration">
2 |   <state>
3 |     <option name="USE_PER_PROJECT_SETTINGS" value="true" />
4 |   </state>
5 | </component>


--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
 1 | ## Contributing to Clash for Android
 2 | 
 3 | #### Code Style
 4 | 
 5 | Please use `Android Studio` or `Intellij IDEA` to open the project and use the project code style profile.
 6 | 
 7 | `File` -> `Settings` -> `Editor` -> `Code Style` -> `C/C++ and Kotlin` -> `Scheme` -> `Project`
 8 | 
 9 | 
10 | 
11 | #### License
12 | 
13 | Contributing to Clash for Android that assumes you allow code to be merged into closed-source branch of Clash for Android. Other terms follow the [GPLv3](https://www.gnu.org/licenses/gpl-3.0.html)
14 | 
15 | 


--------------------------------------------------------------------------------
/app/src/main/ic_launcher-playstore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MetaCubeX/ClashMetaForAndroid/61093eb8d655cbd89dee761bbb79634481c3f5b6/app/src/main/ic_launcher-playstore.png


--------------------------------------------------------------------------------
/app/src/main/ic_launcher-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MetaCubeX/ClashMetaForAndroid/61093eb8d655cbd89dee761bbb79634481c3f5b6/app/src/main/ic_launcher-web.png


--------------------------------------------------------------------------------
/app/src/main/java/com/github/kr328/clash/ApkBrokenActivity.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash
 2 | 
 3 | import android.content.Intent
 4 | import android.net.Uri
 5 | import com.github.kr328.clash.design.ApkBrokenDesign
 6 | import kotlinx.coroutines.isActive
 7 | 
 8 | class ApkBrokenActivity : BaseActivity<ApkBrokenDesign>() {
 9 |     override suspend fun main() {
10 |         val design = ApkBrokenDesign(this)
11 | 
12 |         setContentDesign(design)
13 | 
14 |         while (isActive) {
15 |             val req = design.requests.receive()
16 | 
17 |             startActivity(Intent(Intent.ACTION_VIEW).setData(Uri.parse(req.url)))
18 |         }
19 |     }
20 | }


--------------------------------------------------------------------------------
/app/src/main/java/com/github/kr328/clash/AppCrashedActivity.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash
 2 | 
 3 | import com.github.kr328.clash.common.compat.versionCodeCompat
 4 | import com.github.kr328.clash.common.log.Log
 5 | import com.github.kr328.clash.design.AppCrashedDesign
 6 | import com.github.kr328.clash.log.SystemLogcat
 7 | import kotlinx.coroutines.Dispatchers
 8 | import kotlinx.coroutines.isActive
 9 | import kotlinx.coroutines.withContext
10 | 
11 | class AppCrashedActivity : BaseActivity<AppCrashedDesign>() {
12 |     override suspend fun main() {
13 |         val design = AppCrashedDesign(this)
14 | 
15 |         setContentDesign(design)
16 | 
17 |         val packageInfo = withContext(Dispatchers.IO) {
18 |             packageManager.getPackageInfo(packageName, 0)
19 |         }
20 | 
21 |         Log.i("App version: versionName = ${packageInfo.versionName} versionCode = ${packageInfo.versionCodeCompat}")
22 | 
23 |         val logs = withContext(Dispatchers.IO) {
24 |             SystemLogcat.dumpCrash()
25 |         }
26 | 
27 |         design.setAppLogs(logs)
28 | 
29 |         while (isActive) {
30 |             events.receive()
31 |         }
32 |     }
33 | }


--------------------------------------------------------------------------------
/app/src/main/java/com/github/kr328/clash/DialerReceiver.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash
 2 | 
 3 | import android.content.BroadcastReceiver
 4 | import android.content.Context
 5 | import android.content.Intent
 6 | 
 7 | class DialerReceiver : BroadcastReceiver() {
 8 |     override fun onReceive(context: Context, intent: Intent) {
 9 |         val intent = Intent(context, MainActivity::class.java)
10 |             .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
11 |         context.startActivity(intent)
12 |     }
13 | }


--------------------------------------------------------------------------------
/app/src/main/java/com/github/kr328/clash/HelpActivity.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash
 2 | 
 3 | import android.content.Intent
 4 | import com.github.kr328.clash.design.HelpDesign
 5 | import kotlinx.coroutines.isActive
 6 | 
 7 | class HelpActivity : BaseActivity<HelpDesign>() {
 8 |     override suspend fun main() {
 9 |         val design = HelpDesign(this) {
10 |             startActivity(Intent(Intent.ACTION_VIEW).setData(it))
11 |         }
12 | 
13 |         setContentDesign(design)
14 | 
15 |         while (isActive) {
16 |             events.receive()
17 |         }
18 |     }
19 | }


--------------------------------------------------------------------------------
/app/src/main/java/com/github/kr328/clash/NetworkSettingsActivity.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash
 2 | 
 3 | import com.github.kr328.clash.common.util.intent
 4 | import com.github.kr328.clash.design.NetworkSettingsDesign
 5 | import com.github.kr328.clash.service.store.ServiceStore
 6 | import kotlinx.coroutines.isActive
 7 | import kotlinx.coroutines.selects.select
 8 | 
 9 | class NetworkSettingsActivity : BaseActivity<NetworkSettingsDesign>() {
10 |     override suspend fun main() {
11 |         val design = NetworkSettingsDesign(
12 |             this,
13 |             uiStore,
14 |             ServiceStore(this),
15 |             clashRunning,
16 |         )
17 | 
18 |         setContentDesign(design)
19 | 
20 |         while (isActive) {
21 |             select<Unit> {
22 |                 events.onReceive {
23 |                     when (it) {
24 |                         Event.ClashStart, Event.ClashStop, Event.ServiceRecreated ->
25 |                             recreate()
26 |                         else -> Unit
27 |                     }
28 |                 }
29 |                 design.requests.onReceive {
30 |                     when (it) {
31 |                         NetworkSettingsDesign.Request.StartAccessControlList ->
32 |                             startActivity(AccessControlActivity::class.intent)
33 |                     }
34 |                 }
35 |             }
36 |         }
37 |     }
38 | 
39 | }
40 | 


--------------------------------------------------------------------------------
/app/src/main/java/com/github/kr328/clash/RestartReceiver.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash
 2 | 
 3 | import android.content.BroadcastReceiver
 4 | import android.content.Context
 5 | import android.content.Intent
 6 | import com.github.kr328.clash.service.StatusProvider
 7 | import com.github.kr328.clash.util.startClashService
 8 | 
 9 | class RestartReceiver : BroadcastReceiver() {
10 |     override fun onReceive(context: Context, intent: Intent) {
11 |         when (intent.action) {
12 |             Intent.ACTION_BOOT_COMPLETED, Intent.ACTION_MY_PACKAGE_REPLACED -> {
13 |                 if (StatusProvider.shouldStartClashOnBoot)
14 |                     context.startClashService()
15 |             }
16 |         }
17 |     }
18 | }


--------------------------------------------------------------------------------
/app/src/main/java/com/github/kr328/clash/SettingsActivity.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash
 2 | 
 3 | import com.github.kr328.clash.common.util.intent
 4 | import com.github.kr328.clash.design.SettingsDesign
 5 | import kotlinx.coroutines.isActive
 6 | import kotlinx.coroutines.selects.select
 7 | 
 8 | class SettingsActivity : BaseActivity<SettingsDesign>() {
 9 |     override suspend fun main() {
10 |         val design = SettingsDesign(this)
11 | 
12 |         setContentDesign(design)
13 | 
14 |         while (isActive) {
15 |             select<Unit> {
16 |                 events.onReceive {
17 | 
18 |                 }
19 |                 design.requests.onReceive {
20 |                     when (it) {
21 |                         SettingsDesign.Request.StartApp ->
22 |                             startActivity(AppSettingsActivity::class.intent)
23 |                         SettingsDesign.Request.StartNetwork ->
24 |                             startActivity(NetworkSettingsActivity::class.intent)
25 |                         SettingsDesign.Request.StartOverride ->
26 |                             startActivity(OverrideSettingsActivity::class.intent)
27 |                         SettingsDesign.Request.StartMetaFeature ->
28 |                             startActivity(MetaFeatureSettingsActivity::class.intent)
29 |                     }
30 |                 }
31 |             }
32 |         }
33 |     }
34 | }


--------------------------------------------------------------------------------
/app/src/main/java/com/github/kr328/clash/log/LogcatCache.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.log
 2 | 
 3 | import androidx.collection.CircularArray
 4 | import com.github.kr328.clash.core.model.LogMessage
 5 | import kotlinx.coroutines.sync.Mutex
 6 | import kotlinx.coroutines.sync.withLock
 7 | 
 8 | class LogcatCache {
 9 |     data class Snapshot(val messages: List<LogMessage>, val removed: Int, val appended: Int)
10 | 
11 |     private val array = CircularArray<LogMessage>(CAPACITY)
12 |     private val lock = Mutex()
13 | 
14 |     private var removed: Int = 0
15 |     private var appended: Int = 0
16 | 
17 |     suspend fun append(msg: LogMessage) {
18 |         lock.withLock {
19 |             if (array.size() >= CAPACITY) {
20 |                 array.removeFromStart(1)
21 | 
22 |                 removed++
23 |                 appended--
24 |             }
25 | 
26 |             array.addLast(msg)
27 | 
28 |             appended++
29 |         }
30 |     }
31 | 
32 |     suspend fun snapshot(full: Boolean): Snapshot? {
33 |         return lock.withLock {
34 |             if (!full && removed == 0 && appended == 0) {
35 |                 return@withLock null
36 |             }
37 | 
38 |             Snapshot(
39 |                 List(array.size()) { array[it] },
40 |                 removed,
41 |                 if (full) array.size() + appended else appended
42 |             ).also {
43 |                 removed = 0
44 |                 appended = 0
45 |             }
46 |         }
47 |     }
48 | 
49 |     companion object {
50 |         const val CAPACITY = 128
51 |     }
52 | }
53 | 


--------------------------------------------------------------------------------
/app/src/main/java/com/github/kr328/clash/log/LogcatFilter.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.log
 2 | 
 3 | import android.content.Context
 4 | import com.github.kr328.clash.core.model.LogMessage
 5 | import com.github.kr328.clash.design.util.format
 6 | import java.io.BufferedWriter
 7 | import java.io.Writer
 8 | import java.util.*
 9 | 
10 | class LogcatFilter(output: Writer, private val context: Context) : BufferedWriter(output) {
11 |     fun writeHeader(time: Date) {
12 |         appendLine("# Capture on ${time.format(context)}")
13 |     }
14 | 
15 |     fun writeMessage(message: LogMessage) {
16 |         val time = message.time.format(context, includeDate = false)
17 |         val level = message.level.name
18 | 
19 |         appendLine(FORMAT.format(time, level, message.message))
20 |     }
21 | 
22 |     companion object {
23 |         private const val FORMAT = "%12s %7s: %s"
24 |     }
25 | }


--------------------------------------------------------------------------------
/app/src/main/java/com/github/kr328/clash/log/LogcatWriter.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.log
 2 | 
 3 | import android.content.Context
 4 | import com.github.kr328.clash.core.model.LogMessage
 5 | import com.github.kr328.clash.design.model.LogFile
 6 | import com.github.kr328.clash.util.logsDir
 7 | import java.io.BufferedWriter
 8 | import java.io.FileWriter
 9 | 
10 | class LogcatWriter(context: Context) : AutoCloseable {
11 |     private val file = LogFile.generate()
12 |     private val writer = BufferedWriter(FileWriter(context.logsDir.resolve(file.fileName)))
13 | 
14 |     override fun close() {
15 |         writer.close()
16 |     }
17 | 
18 |     fun appendMessage(message: LogMessage) {
19 |         writer.appendLine(FORMAT.format(message.time.time, message.level.name, message.message))
20 |     }
21 | 
22 |     companion object {
23 |         private const val FORMAT = "%d:%s:%s"
24 |     }
25 | }


--------------------------------------------------------------------------------
/app/src/main/java/com/github/kr328/clash/log/SystemLogcat.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.log
 2 | 
 3 | object SystemLogcat {
 4 |     private val command = arrayOf(
 5 |         "logcat",
 6 |         "-d",
 7 |         "-s",
 8 |         "Go",
 9 |         "DEBUG",
10 |         "AndroidRuntime",
11 |         "ClashMetaForAndroid",
12 |         "LwIP",
13 |     )
14 | 
15 |     fun dumpCrash(): String {
16 |         return try {
17 |             val process = Runtime.getRuntime().exec(command)
18 | 
19 |             val result = process.inputStream.use { stream ->
20 |                 stream.reader().readLines()
21 |                     .filterNot { it.startsWith("------") }
22 |                     .joinToString("\n")
23 |             }
24 | 
25 |             process.waitFor()
26 | 
27 |             result.trim()
28 |         } catch (e: Exception) {
29 |             ""
30 |         }
31 |     }
32 | }
33 | 


--------------------------------------------------------------------------------
/app/src/main/java/com/github/kr328/clash/remote/StatusClient.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.remote
 2 | 
 3 | import android.content.Context
 4 | import android.net.Uri
 5 | import com.github.kr328.clash.common.constants.Authorities
 6 | import com.github.kr328.clash.common.log.Log
 7 | import com.github.kr328.clash.service.StatusProvider
 8 | 
 9 | class StatusClient(private val context: Context) {
10 |     private val uri: Uri
11 |         get() {
12 |             return Uri.Builder()
13 |                 .scheme("content")
14 |                 .authority(Authorities.STATUS_PROVIDER)
15 |                 .build()
16 |         }
17 | 
18 |     fun currentProfile(): String? {
19 |         return try {
20 |             val result = context.contentResolver.call(
21 |                 uri,
22 |                 StatusProvider.METHOD_CURRENT_PROFILE,
23 |                 null,
24 |                 null
25 |             )
26 | 
27 |             result?.getString("name")
28 |         } catch (e: Exception) {
29 |             Log.w("Query current profile: $e", e)
30 | 
31 |             null
32 |         }
33 |     }
34 | }


--------------------------------------------------------------------------------
/app/src/main/java/com/github/kr328/clash/store/AppStore.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.store
 2 | 
 3 | import android.content.Context
 4 | import com.github.kr328.clash.common.store.Store
 5 | import com.github.kr328.clash.common.store.asStoreProvider
 6 | 
 7 | class AppStore(context: Context) {
 8 |     private val store = Store(
 9 |         context
10 |             .getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE)
11 |             .asStoreProvider()
12 |     )
13 | 
14 |     var updatedAt: Long by store.long(
15 |         key = "updated_at",
16 |         defaultValue = -1,
17 |     )
18 | 
19 |     companion object {
20 |         private const val FILE_NAME = "app"
21 |     }
22 | }


--------------------------------------------------------------------------------
/app/src/main/java/com/github/kr328/clash/store/TipsStore.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.store
 2 | 
 3 | import android.content.Context
 4 | import com.github.kr328.clash.common.store.Store
 5 | import com.github.kr328.clash.common.store.asStoreProvider
 6 | 
 7 | class TipsStore(context: Context) {
 8 |     private val store = Store(
 9 |         context
10 |             .getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE)
11 |             .asStoreProvider()
12 |     )
13 | 
14 |     var requestDonate: Boolean by store.boolean(
15 |         key = "request_donate",
16 |         defaultValue = true,
17 |     )
18 | 
19 |     var primaryVersion: Int by store.int(
20 |         key = "primary_version",
21 |         defaultValue = -1,
22 |     )
23 | 
24 |     companion object {
25 |         const val CURRENT_PRIMARY_VERSION = 1
26 | 
27 |         private const val FILE_NAME = "tips"
28 |     }
29 | }


--------------------------------------------------------------------------------
/app/src/main/java/com/github/kr328/clash/util/Activity.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.util
 2 | 
 3 | import androidx.lifecycle.Lifecycle
 4 | import androidx.lifecycle.LifecycleOwner
 5 | import androidx.lifecycle.LifecycleRegistry
 6 | import kotlinx.coroutines.NonCancellable
 7 | import kotlinx.coroutines.withContext
 8 | 
 9 | class ActivityResultLifecycle : LifecycleOwner {
10 |     override val lifecycle = LifecycleRegistry(this)
11 | 
12 |     init {
13 |         lifecycle.currentState = Lifecycle.State.INITIALIZED
14 |     }
15 | 
16 |     suspend fun <T> use(block: suspend (lifecycle: ActivityResultLifecycle, start: () -> Unit) -> T): T {
17 |         return try {
18 |             markCreated()
19 | 
20 |             block(this, this::markStarted)
21 |         } finally {
22 |             withContext(NonCancellable) {
23 |                 markDestroy()
24 |             }
25 |         }
26 |     }
27 | 
28 |     private fun markCreated() {
29 |         lifecycle.currentState = Lifecycle.State.CREATED
30 |     }
31 | 
32 |     private fun markStarted() {
33 |         lifecycle.currentState = Lifecycle.State.STARTED
34 |         lifecycle.currentState = Lifecycle.State.RESUMED
35 |     }
36 | 
37 |     private fun markDestroy() {
38 |         lifecycle.currentState = Lifecycle.State.DESTROYED
39 |     }
40 | }


--------------------------------------------------------------------------------
/app/src/main/java/com/github/kr328/clash/util/Clash.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.util
 2 | 
 3 | import android.content.Context
 4 | import android.content.Intent
 5 | import android.net.VpnService
 6 | import com.github.kr328.clash.common.compat.startForegroundServiceCompat
 7 | import com.github.kr328.clash.common.constants.Intents
 8 | import com.github.kr328.clash.common.util.intent
 9 | import com.github.kr328.clash.design.store.UiStore
10 | import com.github.kr328.clash.service.ClashService
11 | import com.github.kr328.clash.service.TunService
12 | import com.github.kr328.clash.service.util.sendBroadcastSelf
13 | 
14 | fun Context.startClashService(): Intent? {
15 |     val startTun = UiStore(this).enableVpn
16 | 
17 |     if (startTun) {
18 |         val vpnRequest = VpnService.prepare(this)
19 |         if (vpnRequest != null)
20 |             return vpnRequest
21 | 
22 |         startForegroundServiceCompat(TunService::class.intent)
23 |     } else {
24 |         startForegroundServiceCompat(ClashService::class.intent)
25 |     }
26 | 
27 |     return null
28 | }
29 | 
30 | fun Context.stopClashService() {
31 |     sendBroadcastSelf(Intent(Intents.ACTION_CLASH_REQUEST_STOP))
32 | }


--------------------------------------------------------------------------------
/app/src/main/java/com/github/kr328/clash/util/Content.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.util
 2 | 
 3 | import android.content.ContentResolver
 4 | import android.net.Uri
 5 | import kotlinx.coroutines.Dispatchers
 6 | import kotlinx.coroutines.withContext
 7 | import java.io.FileNotFoundException
 8 | 
 9 | private fun fileNotFound(file: Uri): FileNotFoundException {
10 |     return FileNotFoundException("$file not found")
11 | }
12 | 
13 | @Suppress("BlockingMethodInNonBlockingContext")
14 | suspend fun ContentResolver.copyContentTo(
15 |     source: Uri,
16 |     target: Uri
17 | ) {
18 |     withContext(Dispatchers.IO) {
19 |         (openInputStream(source) ?: throw fileNotFound(source)).use { input ->
20 |             (openOutputStream(target, "rwt") ?: throw fileNotFound(target)).use { output ->
21 |                 input.copyTo(output)
22 |             }
23 |         }
24 |     }
25 | }


--------------------------------------------------------------------------------
/app/src/main/java/com/github/kr328/clash/util/Files.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.util
 2 | 
 3 | import android.content.Context
 4 | import java.io.File
 5 | 
 6 | val Context.logsDir: File
 7 |     get() = cacheDir.resolve("logs")
 8 | 
 9 | val Context.clashDir: File
10 |     get() = filesDir.resolve("clash")


--------------------------------------------------------------------------------
/app/src/main/java/com/github/kr328/clash/util/Remote.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.util
 2 | 
 3 | import android.os.DeadObjectException
 4 | import com.github.kr328.clash.common.log.Log
 5 | import com.github.kr328.clash.remote.Remote
 6 | import com.github.kr328.clash.service.remote.IClashManager
 7 | import com.github.kr328.clash.service.remote.IProfileManager
 8 | import kotlinx.coroutines.Dispatchers
 9 | import kotlinx.coroutines.withContext
10 | import kotlin.coroutines.CoroutineContext
11 | 
12 | suspend fun <T> withClash(
13 |     context: CoroutineContext = Dispatchers.IO,
14 |     block: suspend IClashManager.() -> T
15 | ): T {
16 |     while (true) {
17 |         val remote = Remote.service.remote.get()
18 |         val client = remote.clash()
19 | 
20 |         try {
21 |             return withContext(context) { client.block() }
22 |         } catch (e: DeadObjectException) {
23 |             Log.w("Remote services panic")
24 | 
25 |             Remote.service.remote.reset(remote)
26 |         }
27 |     }
28 | }
29 | 
30 | suspend fun <T> withProfile(
31 |     context: CoroutineContext = Dispatchers.IO,
32 |     block: suspend IProfileManager.() -> T
33 | ): T {
34 |     while (true) {
35 |         val remote = Remote.service.remote.get()
36 |         val client = remote.profile()
37 | 
38 |         try {
39 |             return withContext(context) { client.block() }
40 |         } catch (e: DeadObjectException) {
41 |             Log.w("Remote services panic")
42 | 
43 |             Remote.service.remote.reset(remote)
44 |         }
45 |     }
46 | }
47 | 


--------------------------------------------------------------------------------
/app/src/main/java/com/github/kr328/clash/util/Service.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.util
 2 | 
 3 | import android.content.Context
 4 | import android.content.ServiceConnection
 5 | 
 6 | fun Context.unbindServiceSilent(connection: ServiceConnection) {
 7 |     try {
 8 |         unbindService(connection)
 9 |     } catch (e: Exception) {
10 |         // ignore
11 |     }
12 | }


--------------------------------------------------------------------------------
/app/src/main/java/com/github/kr328/clash/util/Uri.kt:
--------------------------------------------------------------------------------
1 | package com.github.kr328.clash.util
2 | 
3 | import android.net.Uri
4 | 
5 | val Uri.fileName: String?
6 |     get() = schemeSpecificPart.split("/").lastOrNull()


--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 | <?xml version="1.0" encoding="utf-8"?>
2 | <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
3 |     <background android:drawable="@color/ic_launcher_background"/>
4 |     <foreground android:drawable="@drawable/ic_launcher_foreground"/>
5 |     <monochrome android:drawable="@drawable/ic_launcher_foreground" />
6 | </adaptive-icon>


--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 | <?xml version="1.0" encoding="utf-8"?>
2 | <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
3 |     <background android:drawable="@color/ic_launcher_background"/>
4 |     <foreground android:drawable="@drawable/ic_launcher_foreground"/>
5 |     <monochrome android:drawable="@drawable/ic_launcher_foreground" />
6 | </adaptive-icon>


--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MetaCubeX/ClashMetaForAndroid/61093eb8d655cbd89dee761bbb79634481c3f5b6/app/src/main/res/mipmap-hdpi/ic_launcher.png


--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MetaCubeX/ClashMetaForAndroid/61093eb8d655cbd89dee761bbb79634481c3f5b6/app/src/main/res/mipmap-hdpi/ic_launcher_round.png


--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MetaCubeX/ClashMetaForAndroid/61093eb8d655cbd89dee761bbb79634481c3f5b6/app/src/main/res/mipmap-mdpi/ic_launcher.png


--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MetaCubeX/ClashMetaForAndroid/61093eb8d655cbd89dee761bbb79634481c3f5b6/app/src/main/res/mipmap-mdpi/ic_launcher_round.png


--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_banner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MetaCubeX/ClashMetaForAndroid/61093eb8d655cbd89dee761bbb79634481c3f5b6/app/src/main/res/mipmap-xhdpi/ic_banner.png


--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MetaCubeX/ClashMetaForAndroid/61093eb8d655cbd89dee761bbb79634481c3f5b6/app/src/main/res/mipmap-xhdpi/ic_launcher.png


--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MetaCubeX/ClashMetaForAndroid/61093eb8d655cbd89dee761bbb79634481c3f5b6/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png


--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MetaCubeX/ClashMetaForAndroid/61093eb8d655cbd89dee761bbb79634481c3f5b6/app/src/main/res/mipmap-xxhdpi/ic_launcher.png


--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MetaCubeX/ClashMetaForAndroid/61093eb8d655cbd89dee761bbb79634481c3f5b6/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png


--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MetaCubeX/ClashMetaForAndroid/61093eb8d655cbd89dee761bbb79634481c3f5b6/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png


--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MetaCubeX/ClashMetaForAndroid/61093eb8d655cbd89dee761bbb79634481c3f5b6/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png


--------------------------------------------------------------------------------
/app/src/main/res/values-night/themes.xml:
--------------------------------------------------------------------------------
1 | <?xml version="1.0" encoding="utf-8"?>
2 | <resources>
3 |     <style name="BootstrapTheme" parent="AppThemeDark" />
4 | </resources>


--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 | <?xml version="1.0" encoding="utf-8"?>
2 | <resources>
3 |     <color name="color_launcher_background">#FFFFFF</color>
4 | </resources>


--------------------------------------------------------------------------------
/app/src/main/res/values/ic_banner_background.xml:
--------------------------------------------------------------------------------
1 | <?xml version="1.0" encoding="utf-8"?>
2 | <resources>
3 |     <color name="ic_banner_background">#FFFFFF</color>
4 | </resources>


--------------------------------------------------------------------------------
/app/src/main/res/values/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 | <?xml version="1.0" encoding="utf-8"?>
2 | <resources>
3 |     <color name="ic_launcher_background">#FFFFFF</color>
4 | </resources>


--------------------------------------------------------------------------------
/app/src/main/res/values/ids.xml:
--------------------------------------------------------------------------------
1 | <?xml version="1.0" encoding="utf-8"?>
2 | <resources>
3 |     <item name="nf_logcat_status" type="id" />
4 | </resources>


--------------------------------------------------------------------------------
/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 | <?xml version="1.0" encoding="utf-8"?>
2 | <resources>
3 |     <style name="BootstrapTheme" parent="AppThemeLight" />
4 | </resources>
5 | 


--------------------------------------------------------------------------------
/app/src/main/res/xml/full_backup_content.xml:
--------------------------------------------------------------------------------
 1 | <?xml version="1.0" encoding="utf-8"?>
 2 | <full-backup-content>
 3 |     <include
 4 |         domain="sharedpref"
 5 |         path="." />
 6 |     <include
 7 |         domain="database"
 8 |         path="." />
 9 |     <include
10 |         domain="file"
11 |         path="imported" />
12 |     <include
13 |         domain="file"
14 |         path="pending" />
15 |     <include
16 |         domain="file"
17 |         path="clash/override.json" />
18 | </full-backup-content>


--------------------------------------------------------------------------------
/app/src/main/res/xml/network_security_config.xml:
--------------------------------------------------------------------------------
 1 | <?xml version="1.0" encoding="utf-8"?>
 2 | <network-security-config xmlns:tools="http://schemas.android.com/tools"
 3 |     tools:ignore="AcceptsUserCertificates">
 4 |     <base-config>
 5 |         <trust-anchors>
 6 |             <certificates src="system" />
 7 |             <certificates src="user" />
 8 |         </trust-anchors>
 9 |     </base-config>
10 | </network-security-config>


--------------------------------------------------------------------------------
/common/build.gradle.kts:
--------------------------------------------------------------------------------
 1 | plugins {
 2 |     kotlin("android")
 3 |     id("com.android.library")
 4 | }
 5 | 
 6 | dependencies {
 7 |     compileOnly(project(":hideapi"))
 8 | 
 9 |     implementation(libs.kotlin.coroutine)
10 |     implementation(libs.androidx.core)
11 | }
12 | 


--------------------------------------------------------------------------------
/common/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MetaCubeX/ClashMetaForAndroid/61093eb8d655cbd89dee761bbb79634481c3f5b6/common/consumer-rules.pro


--------------------------------------------------------------------------------
/common/proguard-rules.pro:
--------------------------------------------------------------------------------
 1 | # Add project specific ProGuard rules here.
 2 | # You can control the set of applied configuration files using the
 3 | # proguardFiles setting in build.gradle.
 4 | #
 5 | # For more details, see
 6 | #   http://developer.android.com/guide/developing/tools/proguard.html
 7 | 
 8 | # If your project uses WebView with JS, uncomment the following
 9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | #   public *;
13 | #}
14 | 
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 | 
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 | 


--------------------------------------------------------------------------------
/common/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
 1 | <manifest xmlns:android="http://schemas.android.com/apk/res/android">
 2 | 
 3 |     <permission
 4 |         android:name="${applicationId}.permission.RECEIVE_BROADCASTS"
 5 |         android:description="@string/receive_broadcasts_of_clash"
 6 |         android:label="@string/receive_clash_broadcasts"
 7 |         android:protectionLevel="privileged|signature" />
 8 |     <uses-permission android:name="${applicationId}.permission.RECEIVE_BROADCASTS" />
 9 | </manifest>
10 | 


--------------------------------------------------------------------------------
/common/src/main/java/com/github/kr328/clash/common/Global.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.common
 2 | 
 3 | import android.app.Application
 4 | import kotlinx.coroutines.CoroutineScope
 5 | import kotlinx.coroutines.Dispatchers
 6 | import kotlinx.coroutines.cancel
 7 | 
 8 | object Global : CoroutineScope by CoroutineScope(Dispatchers.IO) {
 9 |     val application: Application
10 |         get() = application_
11 | 
12 |     private lateinit var application_: Application
13 | 
14 |     fun init(application: Application) {
15 |         this.application_ = application
16 |     }
17 | 
18 |     fun destroy() {
19 |         cancel()
20 |     }
21 | }


--------------------------------------------------------------------------------
/common/src/main/java/com/github/kr328/clash/common/compat/App.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.common.compat
 2 | 
 3 | import android.app.ActivityThread
 4 | import android.app.Application
 5 | import android.graphics.drawable.AdaptiveIconDrawable
 6 | import android.graphics.drawable.Drawable
 7 | import android.os.Build
 8 | import com.github.kr328.clash.common.log.Log
 9 | 
10 | val Application.currentProcessName: String
11 |     get() {
12 |         if (Build.VERSION.SDK_INT >= 28)
13 |             return Application.getProcessName()
14 | 
15 |         return try {
16 |             ActivityThread.currentProcessName()
17 |         } catch (throwable: Throwable) {
18 |             Log.w("Resolve process name: $throwable")
19 | 
20 |             packageName
21 |         }
22 |     }
23 | 
24 | fun Drawable.foreground(): Drawable {
25 |     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O &&
26 |         this is AdaptiveIconDrawable && this.background == null
27 |     ) {
28 |         return this.foreground
29 |     }
30 |     return this
31 | }
32 | 


--------------------------------------------------------------------------------
/common/src/main/java/com/github/kr328/clash/common/compat/Context.kt:
--------------------------------------------------------------------------------
 1 | @file:Suppress("DEPRECATION")
 2 | 
 3 | package com.github.kr328.clash.common.compat
 4 | 
 5 | import android.annotation.SuppressLint
 6 | import android.content.BroadcastReceiver
 7 | import android.content.Context
 8 | import android.content.IntentFilter
 9 | import android.graphics.drawable.Drawable
10 | import android.os.Build
11 | import android.os.Handler
12 | import androidx.annotation.ColorRes
13 | import androidx.annotation.DrawableRes
14 | import androidx.core.content.ContextCompat
15 | 
16 | fun Context.getColorCompat(@ColorRes id: Int): Int {
17 |     return ContextCompat.getColor(this, id)
18 | }
19 | 
20 | fun Context.getDrawableCompat(@DrawableRes id: Int): Drawable? {
21 |     return ContextCompat.getDrawable(this, id)
22 | }
23 | 
24 | @SuppressLint("UnspecifiedRegisterReceiverFlag")
25 | fun Context.registerReceiverCompat(
26 |     receiver: BroadcastReceiver,
27 |     filter: IntentFilter,
28 |     permission: String? = null,
29 |     handler: Handler? = null
30 | ) =
31 |     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
32 |         registerReceiver(receiver, filter, permission, handler,
33 |             if (permission == null) Context.RECEIVER_EXPORTED else Context.RECEIVER_NOT_EXPORTED
34 |         )
35 |     else
36 |         registerReceiver(receiver, filter, permission, handler)
37 | 
38 | 


--------------------------------------------------------------------------------
/common/src/main/java/com/github/kr328/clash/common/compat/Html.kt:
--------------------------------------------------------------------------------
 1 | @file:Suppress("DEPRECATION")
 2 | 
 3 | package com.github.kr328.clash.common.compat
 4 | 
 5 | import android.os.Build
 6 | import android.text.Html
 7 | import android.text.Spanned
 8 | 
 9 | fun fromHtmlCompat(content: String): Spanned {
10 |     return if (Build.VERSION.SDK_INT >= 24) {
11 |         Html.fromHtml(content, Html.FROM_HTML_MODE_COMPACT)
12 |     } else {
13 |         Html.fromHtml(content)
14 |     }
15 | }


--------------------------------------------------------------------------------
/common/src/main/java/com/github/kr328/clash/common/compat/Intents.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.common.compat
 2 | 
 3 | import android.app.PendingIntent
 4 | import android.os.Build
 5 | 
 6 | fun pendingIntentFlags(flags: Int, mutable: Boolean = false): Int {
 7 |     return if (Build.VERSION.SDK_INT >= 24) {
 8 |         if (Build.VERSION.SDK_INT > 30 && mutable) {
 9 |             flags or PendingIntent.FLAG_MUTABLE
10 |         } else {
11 |             flags or PendingIntent.FLAG_IMMUTABLE
12 |         }
13 |     } else {
14 |         flags
15 |     }
16 | }
17 | 


--------------------------------------------------------------------------------
/common/src/main/java/com/github/kr328/clash/common/compat/Package.kt:
--------------------------------------------------------------------------------
 1 | @file:Suppress("DEPRECATION")
 2 | 
 3 | package com.github.kr328.clash.common.compat
 4 | 
 5 | import android.content.pm.PackageInfo
 6 | 
 7 | val PackageInfo.versionCodeCompat: Long
 8 |     get() {
 9 |         return if (android.os.Build.VERSION.SDK_INT >= 28) {
10 |             longVersionCode
11 |         } else {
12 |             versionCode.toLong()
13 |         }
14 |     }
15 | 


--------------------------------------------------------------------------------
/common/src/main/java/com/github/kr328/clash/common/compat/Resource.kt:
--------------------------------------------------------------------------------
 1 | @file:Suppress("DEPRECATION")
 2 | 
 3 | package com.github.kr328.clash.common.compat
 4 | 
 5 | import android.content.res.Configuration
 6 | import android.os.Build
 7 | import java.util.*
 8 | 
 9 | val Configuration.preferredLocale: Locale
10 |     get() {
11 |         return if (Build.VERSION.SDK_INT >= 24) {
12 |             locales[0]
13 |         } else {
14 |             locale
15 |         }
16 |     }


--------------------------------------------------------------------------------
/common/src/main/java/com/github/kr328/clash/common/compat/Services.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.common.compat
 2 | 
 3 | import android.app.Notification
 4 | import android.app.Service
 5 | import android.content.Context
 6 | import android.content.Intent
 7 | import android.content.pm.ServiceInfo
 8 | import android.os.Build
 9 | 
10 | fun Context.startForegroundServiceCompat(intent: Intent) {
11 |     if (Build.VERSION.SDK_INT >= 26) {
12 |         startForegroundService(intent)
13 |     } else {
14 |         startService(intent)
15 |     }
16 | }
17 | 
18 | fun Service.startForegroundCompat(id: Int, notification: Notification) {
19 |     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
20 |         startForeground(id, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE)
21 |     } else {
22 |         startForeground(id, notification)
23 |     }
24 | }
25 | 


--------------------------------------------------------------------------------
/common/src/main/java/com/github/kr328/clash/common/compat/View.kt:
--------------------------------------------------------------------------------
 1 | @file:Suppress("DEPRECATION")
 2 | 
 3 | package com.github.kr328.clash.common.compat
 4 | 
 5 | import android.os.Build
 6 | import android.widget.TextView
 7 | import androidx.annotation.StyleRes
 8 | 
 9 | var TextView.textAppearance: Int
10 |     get() = throw UnsupportedOperationException("set value only")
11 |     set(@StyleRes value) {
12 |         if (Build.VERSION.SDK_INT >= 23) {
13 |             setTextAppearance(value)
14 |         } else {
15 |             setTextAppearance(context, value)
16 |         }
17 |     }


--------------------------------------------------------------------------------
/common/src/main/java/com/github/kr328/clash/common/constants/Authorities.kt:
--------------------------------------------------------------------------------
1 | package com.github.kr328.clash.common.constants
2 | 
3 | import com.github.kr328.clash.common.util.packageName
4 | 
5 | object Authorities {
6 |     val STATUS_PROVIDER = "$packageName.status"
7 |     val SETTINGS_PROVIDER = "$packageName.settings"
8 |     val FILES_PROVIDER = "$packageName.files"
9 | }


--------------------------------------------------------------------------------
/common/src/main/java/com/github/kr328/clash/common/constants/Components.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.common.constants
 2 | 
 3 | import android.content.ComponentName
 4 | import com.github.kr328.clash.common.util.packageName
 5 | 
 6 | object Components {
 7 |     private const val componentsPackageName = "com.github.kr328.clash"
 8 | 
 9 |     val MAIN_ACTIVITY = ComponentName(packageName, "$componentsPackageName.MainActivity")
10 |     val PROPERTIES_ACTIVITY = ComponentName(packageName, "$componentsPackageName.PropertiesActivity")
11 | }


--------------------------------------------------------------------------------
/common/src/main/java/com/github/kr328/clash/common/constants/Metadata.kt:
--------------------------------------------------------------------------------
1 | package com.github.kr328.clash.common.constants
2 | 
3 | import com.github.kr328.clash.common.util.packageName
4 | 
5 | object Metadata {
6 |     val GEOIP_FILE_NAME = "$packageName.GEOIP_FILE_NAME"
7 | }


--------------------------------------------------------------------------------
/common/src/main/java/com/github/kr328/clash/common/constants/Permissions.kt:
--------------------------------------------------------------------------------
1 | package com.github.kr328.clash.common.constants
2 | 
3 | import com.github.kr328.clash.common.util.packageName
4 | 
5 | object Permissions {
6 |     val RECEIVE_SELF_BROADCASTS = "$packageName.permission.RECEIVE_BROADCASTS"
7 | }


--------------------------------------------------------------------------------
/common/src/main/java/com/github/kr328/clash/common/id/UndefinedIds.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.common.id
 2 | 
 3 | object UndefinedIds {
 4 |     private const val PREFIX = 0x14000000
 5 |     private const val MASK = 0x00FFFFFF
 6 | 
 7 |     private var current: Int = 0
 8 | 
 9 |     @Synchronized
10 |     fun next(): Int {
11 |         current = ((current and MASK) + 1 or PREFIX)
12 | 
13 |         return current
14 |     }
15 | }
16 | 


--------------------------------------------------------------------------------
/common/src/main/java/com/github/kr328/clash/common/log/Log.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.common.log
 2 | 
 3 | object Log {
 4 |     private const val TAG = "ClashMetaForAndroid"
 5 | 
 6 |     fun i(message: String, throwable: Throwable? = null) =
 7 |         android.util.Log.i(TAG, message, throwable)
 8 | 
 9 |     fun w(message: String, throwable: Throwable? = null) =
10 |         android.util.Log.w(TAG, message, throwable)
11 | 
12 |     fun e(message: String, throwable: Throwable? = null) =
13 |         android.util.Log.e(TAG, message, throwable)
14 | 
15 |     fun d(message: String, throwable: Throwable? = null) =
16 |         android.util.Log.d(TAG, message, throwable)
17 | 
18 |     fun v(message: String, throwable: Throwable? = null) =
19 |         android.util.Log.v(TAG, message, throwable)
20 | 
21 |     fun f(message: String, throwable: Throwable) =
22 |         android.util.Log.wtf(message, throwable)
23 | }
24 | 


--------------------------------------------------------------------------------
/common/src/main/java/com/github/kr328/clash/common/store/StoreProvider.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.common.store
 2 | 
 3 | interface StoreProvider {
 4 |     fun getInt(key: String, defaultValue: Int): Int
 5 |     fun setInt(key: String, value: Int)
 6 | 
 7 |     fun getLong(key: String, defaultValue: Long): Long
 8 |     fun setLong(key: String, value: Long)
 9 | 
10 |     fun getString(key: String, defaultValue: String): String
11 |     fun setString(key: String, value: String)
12 | 
13 |     fun getStringSet(key: String, defaultValue: Set<String>): Set<String>
14 |     fun setStringSet(key: String, value: Set<String>)
15 | 
16 |     fun getBoolean(key: String, defaultValue: Boolean): Boolean
17 |     fun setBoolean(key: String, value: Boolean)
18 | }


--------------------------------------------------------------------------------
/common/src/main/java/com/github/kr328/clash/common/util/Components.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.common.util
 2 | 
 3 | import android.content.ComponentName
 4 | import android.content.Intent
 5 | import com.github.kr328.clash.common.Global
 6 | import kotlin.reflect.KClass
 7 | 
 8 | val KClass<*>.componentName: ComponentName
 9 |     get() = ComponentName(Global.application.packageName, this.java.name)
10 | 
11 | val KClass<*>.intent: Intent
12 |     get() = Intent(Global.application, this.java)


--------------------------------------------------------------------------------
/common/src/main/java/com/github/kr328/clash/common/util/Global.kt:
--------------------------------------------------------------------------------
1 | package com.github.kr328.clash.common.util
2 | 
3 | import com.github.kr328.clash.common.Global
4 | 
5 | val packageName: String = Global.application.packageName


--------------------------------------------------------------------------------
/common/src/main/java/com/github/kr328/clash/common/util/Intent.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.common.util
 2 | 
 3 | import android.content.Intent
 4 | import android.net.Uri
 5 | import java.util.*
 6 | 
 7 | fun Intent.grantPermissions(read: Boolean = true, write: Boolean = true): Intent {
 8 |     var flags = 0
 9 | 
10 |     if (read)
11 |         flags = flags or Intent.FLAG_GRANT_READ_URI_PERMISSION
12 | 
13 |     if (write)
14 |         flags = flags or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
15 | 
16 |     addFlags(flags)
17 | 
18 |     return this
19 | }
20 | 
21 | var Intent.fileName: String?
22 |     get() {
23 |         return data?.takeIf { it.scheme == "file" }?.schemeSpecificPart
24 |     }
25 |     set(value) {
26 |         data = Uri.fromParts("file", value, null)
27 |     }
28 | 
29 | var Intent.uuid: UUID?
30 |     get() {
31 |         return data?.takeIf { it.scheme == "uuid" }?.schemeSpecificPart?.let(UUID::fromString)
32 |     }
33 |     set(value) {
34 |         data = Uri.fromParts("uuid", value.toString(), null)
35 |     }
36 | 
37 | fun Intent.setUUID(uuid: UUID): Intent {
38 |     this.uuid = uuid
39 | 
40 |     return this
41 | }
42 | 
43 | fun Intent.setFileName(fileName: String): Intent {
44 |     this.fileName = fileName
45 | 
46 |     return this
47 | }


--------------------------------------------------------------------------------
/common/src/main/java/com/github/kr328/clash/common/util/Patterns.kt:
--------------------------------------------------------------------------------
1 | package com.github.kr328.clash.common.util
2 | 
3 | val PatternFileName = Regex("[^*&%\\n\\r/]+")


--------------------------------------------------------------------------------
/common/src/main/java/com/github/kr328/clash/common/util/Ticker.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.common.util
 2 | 
 3 | import kotlinx.coroutines.CoroutineScope
 4 | import kotlinx.coroutines.channels.Channel
 5 | import kotlinx.coroutines.delay
 6 | import kotlinx.coroutines.isActive
 7 | import kotlinx.coroutines.launch
 8 | 
 9 | fun CoroutineScope.ticker(period: Long): Channel<Long> {
10 |     val channel = Channel<Long>(Channel.RENDEZVOUS)
11 | 
12 |     launch {
13 |         try {
14 |             while (isActive) {
15 |                 channel.send(System.currentTimeMillis())
16 | 
17 |                 delay(period)
18 |             }
19 |         } catch (ignored: Exception) {
20 | 
21 |         }
22 |     }
23 | 
24 |     return channel
25 | }


--------------------------------------------------------------------------------
/common/src/main/res/values-ru/strings.xml:
--------------------------------------------------------------------------------
1 | <?xml version="1.0" encoding="utf-8"?>
2 | <resources>
3 |     <string name="receive_clash_broadcasts">Получать оповещения от Clash</string>
4 |     <string name="receive_broadcasts_of_clash">Получать оповещения от сервисов Clash</string>
5 | </resources>
6 | 


--------------------------------------------------------------------------------
/common/src/main/res/values-zh-rTW/strings.xml:
--------------------------------------------------------------------------------
1 | <?xml version="1.0" encoding="utf-8"?>
2 | <resources>
3 |     <string name="receive_clash_broadcasts">接收 Clash 廣播</string>
4 |     <string name="receive_broadcasts_of_clash">接收來自 Clash 內部的廣播</string>
5 | </resources>
6 | 


--------------------------------------------------------------------------------
/common/src/main/res/values-zh/strings.xml:
--------------------------------------------------------------------------------
1 | <?xml version="1.0" encoding="utf-8"?>
2 | <resources>
3 |     <string name="receive_clash_broadcasts">接收 Clash 广播</string>
4 |     <string name="receive_broadcasts_of_clash">接收来自 Clash 内部的广播</string>
5 | </resources>


--------------------------------------------------------------------------------
/common/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 | <?xml version="1.0" encoding="utf-8"?>
2 | <resources>
3 |     <string name="receive_clash_broadcasts">Receive Clash Broadcasts</string>
4 |     <string name="receive_broadcasts_of_clash">Receive broadcasts of clash services</string>
5 | </resources>
6 | 


--------------------------------------------------------------------------------
/core/.gitignore:
--------------------------------------------------------------------------------
1 | /src/main/cpp/version.h
2 | 


--------------------------------------------------------------------------------
/core/consumer-rules.pro:
--------------------------------------------------------------------------------
 1 | -keep class kotlinx.coroutines.CompletableDeferred {
 2 |     *;
 3 | }
 4 | 
 5 | -keep class kotlin.Unit {
 6 |     *;
 7 | }
 8 | 
 9 | -keepattributes *Annotation*, InnerClasses
10 | -dontnote kotlinx.serialization.AnnotationsKt # core serialization annotations
11 | 
12 | # kotlinx-serialization-json specific. Add this if you have java.lang.NoClassDefFoundError kotlinx.serialization.json.JsonObjectSerializer
13 | -keepclassmembers class kotlinx.serialization.json.** {
14 |     *** Companion;
15 | }
16 | -keepclasseswithmembers class kotlinx.serialization.json.** {
17 |     kotlinx.serialization.KSerializer serializer(...);
18 | }
19 | 


--------------------------------------------------------------------------------
/core/proguard-rules.pro:
--------------------------------------------------------------------------------
 1 | # Add project specific ProGuard rules here.
 2 | # You can control the set of applied configuration files using the
 3 | # proguardFiles setting in build.gradle.
 4 | #
 5 | # For more details, see
 6 | #   http://developer.android.com/guide/developing/tools/proguard.html
 7 | 
 8 | # If your project uses WebView with JS, uncomment the following
 9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | #   public *;
13 | #}
14 | 
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 | 
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 | 


--------------------------------------------------------------------------------
/core/src/foss/golang/main.go:
--------------------------------------------------------------------------------
1 | package golang
2 | 
3 | import (
4 | 	_ "cfa/native/all"
5 | )
6 | 


--------------------------------------------------------------------------------
/core/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 | <manifest xmlns:android="http://schemas.android.com/apk/res/android" >
2 | 
3 |     <uses-permission android:name="android.permission.INTERNET" />
4 | </manifest>
5 | 


--------------------------------------------------------------------------------
/core/src/main/cpp/bridge_helper.c:
--------------------------------------------------------------------------------
 1 | #include "bridge_helper.h"
 2 | 
 3 | uint64_t down_scale_traffic(uint64_t value) {
 4 |     if (value > 1042 * 1024 * 1024)
 5 |         return ((value * 100u / 1024u / 1024u / 1024u) & 0x3FFFFFFFu) | (3u << 30u);
 6 |     if (value > 1024 * 1024)
 7 |         return ((value * 100u / 1024u / 1024u) & 0x3FFFFFFFu) | (2u << 30u);
 8 |     if (value > 1024)
 9 |         return ((value * 100u / 1024u) & 0x3FFFFFFFu) | (1u << 30u);
10 |     return value & 0x3FFFFFFFu;
11 | }
12 | 
13 | 


--------------------------------------------------------------------------------
/core/src/main/cpp/bridge_helper.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | 
3 | #include <stdint.h>
4 | 
5 | uint64_t down_scale_traffic(uint64_t value);


--------------------------------------------------------------------------------
/core/src/main/cpp/jni_helper.h:
--------------------------------------------------------------------------------
 1 | #pragma once
 2 | 
 3 | #include <jni.h>
 4 | #include <stdint.h>
 5 | #include <stdlib.h>
 6 | #include <malloc.h>
 7 | #include <android/log.h>
 8 | 
 9 | struct _scoped_jni {
10 |     JNIEnv *env;
11 |     int require_release;
12 | };
13 | 
14 | extern void initialize_jni(JavaVM *vm, JNIEnv *env);
15 | extern jstring jni_new_string(JNIEnv *env, const char *str);
16 | extern char *jni_get_string(JNIEnv *env, jstring str);
17 | extern int jni_catch_exception(JNIEnv *env);
18 | extern void jni_attach_thread(struct _scoped_jni *jni);
19 | extern void jni_detach_thread(struct _scoped_jni *env);
20 | extern void release_string(char **str);
21 | 
22 | #define ATTACH_JNI() __attribute__((unused, cleanup(jni_detach_thread))) \
23 |                     struct _scoped_jni _jni; \
24 |                     jni_attach_thread(&_jni); \
25 |                     JNIEnv *env = _jni.env
26 | 
27 | #define scoped_string __attribute__((cleanup(release_string))) char*
28 | 
29 | #define find_class(name) (*env)->FindClass(env, name)
30 | #define find_method(cls, name, signature) (*env)->GetMethodID(env, cls, name, signature)
31 | #define new_global(obj) (*env)->NewGlobalRef(env, obj)
32 | #define del_global(obj) (*env)->DeleteGlobalRef(env, obj)
33 | #define get_string(jstr) jni_get_string(env, jstr)
34 | #define new_string(cstr) jni_new_string(env, cstr)


--------------------------------------------------------------------------------
/core/src/main/cpp/version.h.in:
--------------------------------------------------------------------------------
 1 | #ifndef VERSION_H_IN
 2 | #define VERSION_H_IN
 3 |  
 4 | /**
 5 |  * 当前编译core版本号
 6 |  */
 7 |  
 8 | #define GIT_VERSION @GIT_VERSION@
 9 | #define make_Str(x) #x 
10 | #define make_String(x) make_Str(x)
11 | 
12 | #endif


--------------------------------------------------------------------------------
/core/src/main/golang/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
 1 | <component name="ProjectCodeStyleConfiguration">
 2 |   <code_scheme name="Project" version="173">
 3 |     <GoCodeStyleSettings>
 4 |       <option name="IMPORT_SORTING" value="GOIMPORTS" />
 5 |       <option name="MOVE_ALL_IMPORTS_IN_ONE_DECLARATION" value="true" />
 6 |       <option name="MOVE_ALL_STDLIB_IMPORTS_IN_ONE_GROUP" value="true" />
 7 |       <option name="GROUP_STDLIB_IMPORTS" value="true" />
 8 |     </GoCodeStyleSettings>
 9 |   </code_scheme>
10 | </component>


--------------------------------------------------------------------------------
/core/src/main/golang/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 | <component name="ProjectCodeStyleConfiguration">
2 |   <state>
3 |     <option name="USE_PER_PROJECT_SETTINGS" value="true" />
4 |   </state>
5 | </component>


--------------------------------------------------------------------------------
/core/src/main/golang/native/all/imports.go:
--------------------------------------------------------------------------------
 1 | package all
 2 | 
 3 | import (
 4 | 	_ "cfa/native/app"
 5 | 	_ "cfa/native/common"
 6 | 	_ "cfa/native/config"
 7 | 	_ "cfa/native/delegate"
 8 | 	_ "cfa/native/platform"
 9 | 	_ "cfa/native/proxy"
10 | 	_ "cfa/native/tun"
11 | 	_ "cfa/native/tunnel"
12 | 
13 | 	_ "golang.org/x/sync/semaphore"
14 | 
15 | 	_ "github.com/metacubex/mihomo/log"
16 | )
17 | 


--------------------------------------------------------------------------------
/core/src/main/golang/native/app.go:
--------------------------------------------------------------------------------
 1 | package main
 2 | 
 3 | //#include "bridge.h"
 4 | import "C"
 5 | 
 6 | import (
 7 | 	"errors"
 8 | 	"unsafe"
 9 | 
10 | 	"cfa/native/app"
11 | 
12 | 	"github.com/metacubex/mihomo/log"
13 | )
14 | 
15 | func openRemoteContent(url string) (int, error) {
16 | 	u := C.CString(url)
17 | 	e := (*C.char)(C.malloc(1024))
18 | 
19 | 	log.Debugln("Open remote url: %s", url)
20 | 
21 | 	defer C.free(unsafe.Pointer(e))
22 | 
23 | 	fd := C.open_content(u, e, 1024)
24 | 
25 | 	if fd < 0 {
26 | 		return -1, errors.New(C.GoString(e))
27 | 	}
28 | 
29 | 	return int(fd), nil
30 | }
31 | 
32 | //export notifyDnsChanged
33 | func notifyDnsChanged(dnsList C.c_string) {
34 | 	d := C.GoString(dnsList)
35 | 
36 | 	app.NotifyDnsChanged(d)
37 | }
38 | 
39 | //export notifyInstalledAppsChanged
40 | func notifyInstalledAppsChanged(uids C.c_string) {
41 | 	u := C.GoString(uids)
42 | 
43 | 	app.NotifyInstallAppsChanged(u)
44 | }
45 | 
46 | //export notifyTimeZoneChanged
47 | func notifyTimeZoneChanged(name C.c_string, offset C.int) {
48 | 	app.NotifyTimeZoneChanged(C.GoString(name), int(offset))
49 | }
50 | 
51 | 
52 | //export queryConfiguration
53 | func queryConfiguration() *C.char {
54 | 	response := &struct{}{}
55 | 
56 | 	return marshalJson(&response)
57 | }
58 | 
59 | func init() {
60 | 	app.ApplyContentContext(openRemoteContent)
61 | }


--------------------------------------------------------------------------------
/core/src/main/golang/native/app/app.go:
--------------------------------------------------------------------------------
 1 | package app
 2 | 
 3 | import (
 4 | 	"strconv"
 5 | 	"strings"
 6 | 	"time"
 7 | )
 8 | 
 9 | var appVersionName string
10 | var platformVersion int
11 | var installedAppsUid = map[int]string{}
12 | 
13 | func ApplyVersionName(versionName string) {
14 | 	appVersionName = versionName
15 | }
16 | 
17 | func ApplyPlatformVersion(version int) {
18 | 	platformVersion = version
19 | }
20 | 
21 | func VersionName() string {
22 | 	return appVersionName
23 | }
24 | 
25 | func PlatformVersion() int {
26 | 	return platformVersion
27 | }
28 | 
29 | func NotifyInstallAppsChanged(uidList string) {
30 | 	uids := map[int]string{}
31 | 
32 | 	for _, item := range strings.Split(uidList, ",") {
33 | 		kv := strings.Split(item, ":")
34 | 		if len(kv) == 2 {
35 | 			uid, err := strconv.Atoi(kv[0])
36 | 			if err != nil {
37 | 				continue
38 | 			}
39 | 
40 | 			uids[uid] = kv[1]
41 | 		}
42 | 	}
43 | 
44 | 	installedAppsUid = uids
45 | }
46 | 
47 | func QueryAppByUid(uid int) string {
48 | 	return installedAppsUid[uid]
49 | }
50 | 
51 | func NotifyTimeZoneChanged(name string, offset int) {
52 | 	time.Local = time.FixedZone(name, offset)
53 | }


--------------------------------------------------------------------------------
/core/src/main/golang/native/app/content.go:
--------------------------------------------------------------------------------
 1 | package app
 2 | 
 3 | import (
 4 | 	"errors"
 5 | 	"os"
 6 | 	"syscall"
 7 | )
 8 | 
 9 | var openContentImpl = func(url string) (int, error) {
10 | 	return -1, errors.New("not implement")
11 | }
12 | 
13 | func OpenContent(url string) (*os.File, error) {
14 | 	fd, err := openContentImpl(url)
15 | 
16 | 	if err != nil {
17 | 		return nil, err
18 | 	}
19 | 
20 | 	_ = syscall.SetNonblock(fd, true)
21 | 
22 | 	return os.NewFile(uintptr(fd), "fd"), nil
23 | }
24 | 
25 | func ApplyContentContext(openContent func(string) (int, error)) {
26 | 	openContentImpl = openContent
27 | }
28 | 


--------------------------------------------------------------------------------
/core/src/main/golang/native/app/dns.go:
--------------------------------------------------------------------------------
 1 | package app
 2 | 
 3 | import (
 4 | 	"strings"
 5 | 
 6 | 	"github.com/metacubex/mihomo/dns"
 7 | )
 8 | 
 9 | func NotifyDnsChanged(dnsList string) {
10 | 	var addr []string
11 | 	if len(dnsList) > 0 {
12 | 		addr = strings.Split(dnsList, ",")
13 | 	}
14 | 	dns.UpdateSystemDNS(addr)
15 | 	dns.FlushCacheWithDefaultResolver()
16 | }
17 | 


--------------------------------------------------------------------------------
/core/src/main/golang/native/app/tun.go:
--------------------------------------------------------------------------------
 1 | package app
 2 | 
 3 | import (
 4 | 	"net"
 5 | 	"syscall"
 6 | 
 7 | 	"cfa/native/platform"
 8 | )
 9 | 
10 | var markSocketImpl func(fd int)
11 | var querySocketUidImpl func(protocol int, source, target string) int
12 | 
13 | func MarkSocket(fd int) {
14 | 	markSocketImpl(fd)
15 | }
16 | 
17 | func QuerySocketUid(source, target net.Addr) int {
18 | 	var protocol int
19 | 
20 | 	switch source.Network() {
21 | 	case "udp", "udp4", "udp6":
22 | 		protocol = syscall.IPPROTO_UDP
23 | 	case "tcp", "tcp4", "tcp6":
24 | 		protocol = syscall.IPPROTO_TCP
25 | 	default:
26 | 		return -1
27 | 	}
28 | 
29 | 	if PlatformVersion() < 29 {
30 | 		return platform.QuerySocketUidFromProcFs(source, target)
31 | 	}
32 | 
33 | 	return querySocketUidImpl(protocol, source.String(), target.String())
34 | }
35 | 
36 | func ApplyTunContext(markSocket func(fd int), querySocketUid func(int, string, string) int) {
37 | 	if markSocket == nil {
38 | 		markSocket = func(fd int) {}
39 | 	}
40 | 
41 | 	if querySocketUid == nil {
42 | 		querySocketUid = func(int, string, string) int { return -1 }
43 | 	}
44 | 
45 | 	markSocketImpl = markSocket
46 | 	querySocketUidImpl = querySocketUid
47 | }
48 | 
49 | func init() {
50 | 	ApplyTunContext(nil, nil)
51 | }
52 | 


--------------------------------------------------------------------------------
/core/src/main/golang/native/app/ui.go:
--------------------------------------------------------------------------------
 1 | package app
 2 | 
 3 | import (
 4 | 	"github.com/dlclark/regexp2"
 5 | 
 6 | 	"github.com/metacubex/mihomo/log"
 7 | )
 8 | 
 9 | var uiSubtitlePattern *regexp2.Regexp
10 | 
11 | func ApplySubtitlePattern(pattern string) {
12 | 	if pattern == "" {
13 | 		uiSubtitlePattern = nil
14 | 
15 | 		return
16 | 	}
17 | 
18 | 	if o := uiSubtitlePattern; o != nil && o.String() == pattern {
19 | 		return
20 | 	}
21 | 
22 | 	reg, err := regexp2.Compile(pattern, regexp2.IgnoreCase|regexp2.Compiled)
23 | 	if err == nil {
24 | 		uiSubtitlePattern = reg
25 | 	} else {
26 | 		uiSubtitlePattern = nil
27 | 
28 | 		log.Warnln("Compile ui-subtitle-pattern: %s", err.Error())
29 | 	}
30 | }
31 | 
32 | func SubtitlePattern() *regexp2.Regexp {
33 | 	return uiSubtitlePattern
34 | }
35 | 


--------------------------------------------------------------------------------
/core/src/main/golang/native/common/path.go:
--------------------------------------------------------------------------------
 1 | package common
 2 | 
 3 | import "strings"
 4 | 
 5 | func ResolveAsRoot(path string) string {
 6 | 	directories := strings.Split(path, "/")
 7 | 	result := make([]string, 0, len(directories))
 8 | 
 9 | 	for _, directory := range directories {
10 | 		switch directory {
11 | 		case "", ".":
12 | 			continue
13 | 		case "..":
14 | 			if len(result) > 0 {
15 | 				result = result[:len(result)-1]
16 | 			}
17 | 		default:
18 | 			result = append(result, directory)
19 | 		}
20 | 	}
21 | 
22 | 	return strings.Join(result, "/")
23 | }
24 | 


--------------------------------------------------------------------------------
/core/src/main/golang/native/config.go:
--------------------------------------------------------------------------------
 1 | package main
 2 | 
 3 | //#include "bridge.h"
 4 | import "C"
 5 | 
 6 | import (
 7 | 	"runtime"
 8 | 	"unsafe"
 9 | 
10 | 	"cfa/native/config"
11 | )
12 | 
13 | type remoteValidCallback struct {
14 | 	callback unsafe.Pointer
15 | }
16 | 
17 | func (r *remoteValidCallback) reportStatus(json string) {
18 | 	C.fetch_report(r.callback, marshalString(json))
19 | }
20 | 
21 | //export fetchAndValid
22 | func fetchAndValid(callback unsafe.Pointer, path, url C.c_string, force C.int) {
23 | 	go func(path, url string, callback unsafe.Pointer) {
24 | 		cb := &remoteValidCallback{callback: callback}
25 | 
26 | 		err := config.FetchAndValid(path, url, force != 0, cb.reportStatus)
27 | 
28 | 		C.fetch_complete(callback, marshalString(err))
29 | 
30 | 		C.release_object(callback)
31 | 
32 | 		runtime.GC()
33 | 	}(C.GoString(path), C.GoString(url), callback)
34 | }
35 | 
36 | //export load
37 | func load(completable unsafe.Pointer, path C.c_string) {
38 | 	go func(path string) {
39 | 		C.complete(completable, marshalString(config.Load(path)))
40 | 
41 | 		C.release_object(completable)
42 | 
43 | 		runtime.GC()
44 | 	}(C.GoString(path))
45 | }
46 | 
47 | //export readOverride
48 | func readOverride(slot C.int) *C.char {
49 | 	return C.CString(config.ReadOverride(config.OverrideSlot(slot)))
50 | }
51 | 
52 | //export writeOverride
53 | func writeOverride(slot C.int, content C.c_string) {
54 | 	c := C.GoString(content)
55 | 
56 | 	config.WriteOverride(config.OverrideSlot(slot), c)
57 | }
58 | 
59 | //export clearOverride
60 | func clearOverride(slot C.int) {
61 | 	config.ClearOverride(config.OverrideSlot(slot))
62 | }


--------------------------------------------------------------------------------
/core/src/main/golang/native/config/defaults.go:
--------------------------------------------------------------------------------
 1 | package config
 2 | 
 3 | var (
 4 | 	defaultNameServers = []string{
 5 | 		"223.5.5.5",
 6 | 		"119.29.29.29",
 7 | 		"8.8.4.4",
 8 | 		"1.0.0.1",
 9 | 	}
10 | 	defaultFakeIPFilter = []string{
11 | 		// Stun Services
12 | 		"+.stun.*.*",
13 | 		"+.stun.*.*.*",
14 | 		"+.stun.*.*.*.*",
15 | 		"+.stun.*.*.*.*.*",
16 | 
17 | 		// Google Voices
18 | 		"lens.l.google.com",
19 | 
20 | 		// Nintendo Switch STUN
21 | 		"*.n.n.srv.nintendo.net",
22 | 
23 | 		// PlayStation STUN
24 | 		"+.stun.playstation.net",
25 | 
26 | 		// XBox
27 | 		"xbox.*.*.microsoft.com",
28 | 		"*.*.xboxlive.com",
29 | 
30 | 		// Microsoft Captive Portal
31 | 		"*.msftncsi.com",
32 | 		"*.msftconnecttest.com",
33 | 
34 | 		// Bilibili CDN
35 | 		"*.mcdn.bilivideo.cn",
36 | 
37 | 		// Windows Default LAN WorkGroup
38 | 		"WORKGROUP",
39 | 	}
40 | 	defaultFakeIPRange = "28.0.0.0/8"
41 | )
42 | 


--------------------------------------------------------------------------------
/core/src/main/golang/native/config/provider.go:
--------------------------------------------------------------------------------
 1 | package config
 2 | 
 3 | import (
 4 | 	"io"
 5 | 
 6 | 	"github.com/metacubex/mihomo/config"
 7 | )
 8 | 
 9 | const (
10 | 	PROXIES = "proxies"
11 | 	RULES   = "rules"
12 | )
13 | 
14 | func forEachProviders(rawCfg *config.RawConfig, fun func(index int, total int, key string, provider map[string]any, prefix string)) {
15 | 	total := len(rawCfg.ProxyProvider) + len(rawCfg.RuleProvider)
16 | 	index := 0
17 | 
18 | 	for k, v := range rawCfg.ProxyProvider {
19 | 		fun(index, total, k, v, PROXIES)
20 | 
21 | 		index++
22 | 	}
23 | 
24 | 	for k, v := range rawCfg.RuleProvider {
25 | 		fun(index, total, k, v, RULES)
26 | 
27 | 		index++
28 | 	}
29 | }
30 | 
31 | func destroyProviders(cfg *config.Config) {
32 | 	for _, p := range cfg.Providers {
33 | 		if p, ok := p.(io.Closer); ok {
34 | 			_ = p.Close()
35 | 		}
36 | 	}
37 | 
38 | 	for _, p := range cfg.RuleProviders {
39 | 		if p, ok := p.(io.Closer); ok {
40 | 			_ = p.Close()
41 | 		}
42 | 	}
43 | }
44 | 


--------------------------------------------------------------------------------
/core/src/main/golang/native/debug.go:
--------------------------------------------------------------------------------
 1 | // +build debug
 2 | 
 3 | package main
 4 | 
 5 | import (
 6 | 	"net/http"
 7 | 	_ "net/http/pprof"
 8 | 
 9 | 	"github.com/metacubex/mihomo/log"
10 | )
11 | 
12 | func init() {
13 | 	go func() {
14 | 		log.Debugln("pprof service listen at: 0.0.0.0:8888")
15 | 
16 | 		_ = http.ListenAndServe("0.0.0.0:8888", nil)
17 | 	}()
18 | }
19 | 


--------------------------------------------------------------------------------
/core/src/main/golang/native/main.go:
--------------------------------------------------------------------------------
 1 | package main
 2 | 
 3 | /*
 4 | #cgo LDFLAGS: -llog
 5 | 
 6 | #include "bridge.h"
 7 | */
 8 | import "C"
 9 | 
10 | import (
11 | 	"runtime"
12 | 	"runtime/debug"
13 | 
14 | 	"cfa/native/config"
15 | 	"cfa/native/delegate"
16 | 	"cfa/native/tunnel"
17 | 
18 | 	"github.com/metacubex/mihomo/log"
19 | )
20 | 
21 | func main() {
22 | 	panic("Stub!")
23 | }
24 | 
25 | //export coreInit
26 | func coreInit(home, versionName, gitVersion C.c_string, sdkVersion C.int) {
27 | 	h := C.GoString(home)
28 | 	v := C.GoString(versionName)
29 | 	g := C.GoString(gitVersion)
30 | 	s := int(sdkVersion)
31 | 
32 | 	delegate.Init(h, v, g, s)
33 | 
34 | 	reset()
35 | }
36 | 
37 | //export reset
38 | func reset() {
39 | 	config.LoadDefault()
40 | 	tunnel.ResetStatistic()
41 | 	tunnel.CloseAllConnections()
42 | 
43 | 	runtime.GC()
44 | 	debug.FreeOSMemory()
45 | }
46 | 
47 | //export forceGc
48 | func forceGc() {
49 | 	go func() {
50 | 		log.Infoln("[APP] request force GC")
51 | 
52 | 		runtime.GC()
53 | 		debug.FreeOSMemory()
54 | 	}()
55 | }
56 | 


--------------------------------------------------------------------------------
/core/src/main/golang/native/platform/limit.go:
--------------------------------------------------------------------------------
 1 | // +build linux
 2 | 
 3 | package platform
 4 | 
 5 | import "syscall"
 6 | 
 7 | var nullFd int
 8 | var maxFdCount int
 9 | 
10 | func init() {
11 | 	fd, err := syscall.Open("/dev/null", syscall.O_WRONLY, 0644)
12 | 	if err != nil {
13 | 		panic(err.Error())
14 | 	}
15 | 
16 | 	nullFd = fd
17 | 
18 | 	var limit syscall.Rlimit
19 | 
20 | 	if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limit); err != nil {
21 | 		maxFdCount = 1024
22 | 	} else {
23 | 		maxFdCount = int(limit.Cur)
24 | 	}
25 | 
26 | 	maxFdCount = maxFdCount / 4 * 3
27 | }
28 | 
29 | func ShouldBlockConnection() bool {
30 | 	fd, err := syscall.Dup(nullFd)
31 | 	if err != nil {
32 | 		return true
33 | 	}
34 | 
35 | 	_ = syscall.Close(fd)
36 | 
37 | 	if fd > maxFdCount {
38 | 		return true
39 | 	}
40 | 
41 | 	return false
42 | }
43 | 


--------------------------------------------------------------------------------
/core/src/main/golang/native/proxy.go:
--------------------------------------------------------------------------------
 1 | package main
 2 | 
 3 | //#include "bridge.h"
 4 | import "C"
 5 | 
 6 | import (
 7 | 	"cfa/native/proxy"
 8 | )
 9 | 
10 | //export startHttp
11 | func startHttp(listenAt C.c_string) *C.char {
12 | 	l := C.GoString(listenAt)
13 | 
14 | 	listen, err := proxy.Start(l)
15 | 	if err != nil {
16 | 		return nil
17 | 	}
18 | 
19 | 	return C.CString(listen)
20 | }
21 | 
22 | //export stopHttp
23 | func stopHttp() {
24 | 	proxy.Stop()
25 | }


--------------------------------------------------------------------------------
/core/src/main/golang/native/proxy/http.go:
--------------------------------------------------------------------------------
 1 | package proxy
 2 | 
 3 | import (
 4 | 	"sync"
 5 | 
 6 | 	"github.com/metacubex/mihomo/listener/http"
 7 | 	"github.com/metacubex/mihomo/tunnel"
 8 | )
 9 | 
10 | var listener *http.Listener
11 | var lock sync.Mutex
12 | 
13 | func Start(listen string) (listenAt string, err error) {
14 | 	lock.Lock()
15 | 	defer lock.Unlock()
16 | 
17 | 	stopLocked()
18 | 
19 | 	listener, err = http.NewWithAuthenticate(listen, tunnel.Tunnel, false)
20 | 	if err == nil {
21 | 		listenAt = listener.Address()
22 | 	}
23 | 
24 | 	return
25 | }
26 | 
27 | func Stop() {
28 | 	lock.Lock()
29 | 	defer lock.Unlock()
30 | 
31 | 	stopLocked()
32 | }
33 | 
34 | func stopLocked() {
35 | 	if listener != nil {
36 | 		listener.Close()
37 | 	}
38 | 
39 | 	listener = nil
40 | }
41 | 


--------------------------------------------------------------------------------
/core/src/main/golang/native/trace.c:
--------------------------------------------------------------------------------
1 | #include "trace.h"
2 | 
3 | #if ENABLE_TRACE
4 | 
5 | void trace_method_exit(const char **name) {
6 |     __android_log_print(ANDROID_LOG_VERBOSE, TAG, "TRACE-OUT %s", *name);
7 | }
8 | 
9 | #endif


--------------------------------------------------------------------------------
/core/src/main/golang/native/trace.h:
--------------------------------------------------------------------------------
 1 | #pragma once
 2 | 
 3 | #include "bridge.h"
 4 | 
 5 | #include <android/log.h>
 6 | 
 7 | #define ENABLE_TRACE 0
 8 | 
 9 | #if ENABLE_TRACE
10 | 
11 | extern void trace_method_exit(const char **name);
12 | 
13 | #define TRACE_METHOD() __attribute__((cleanup(trace_method_exit))) const char *__method_name = __FUNCTION__; __android_log_print(ANDROID_LOG_VERBOSE, TAG, "TRACE-IN  %s", __method_name)
14 | 
15 | #else
16 | 
17 | #define TRACE_METHOD()
18 | 
19 | #endif


--------------------------------------------------------------------------------
/core/src/main/golang/native/tunnel/conn.go:
--------------------------------------------------------------------------------
 1 | package tunnel
 2 | 
 3 | import (
 4 | 	C "github.com/metacubex/mihomo/constant"
 5 | 	"github.com/metacubex/mihomo/tunnel/statistic"
 6 | )
 7 | 
 8 | func CloseAllConnections() {
 9 | 	statistic.DefaultManager.Range(func(c statistic.Tracker) bool {
10 | 		_ = c.Close()
11 | 		return true
12 | 	})
13 | }
14 | 
15 | func closeMatch(filter func(conn C.Connection) bool) {
16 | 	statistic.DefaultManager.Range(func(c statistic.Tracker) bool {
17 | 		if filter(c) {
18 | 			_ = c.Close()
19 | 		}
20 | 		return true
21 | 	})
22 | }
23 | 
24 | func closeConnByGroup(name string) {
25 | 	closeMatch(func(conn C.Connection) bool {
26 | 		for _, c := range conn.Chains() {
27 | 			if c == name {
28 | 				return true
29 | 			}
30 | 		}
31 | 
32 | 		return false
33 | 	})
34 | }
35 | 


--------------------------------------------------------------------------------
/core/src/main/golang/native/tunnel/connectivity.go:
--------------------------------------------------------------------------------
 1 | package tunnel
 2 | 
 3 | import (
 4 | 	"sync"
 5 | 
 6 | 	"github.com/metacubex/mihomo/adapter/outboundgroup"
 7 | 	"github.com/metacubex/mihomo/constant/provider"
 8 | 	"github.com/metacubex/mihomo/log"
 9 | 	"github.com/metacubex/mihomo/tunnel"
10 | )
11 | 
12 | func HealthCheck(name string) {
13 | 	p := tunnel.Proxies()[name]
14 | 
15 | 	if p == nil {
16 | 		log.Warnln("Request health check for `%s`: not found", name)
17 | 
18 | 		return
19 | 	}
20 | 
21 | 	g, ok := p.Adapter().(outboundgroup.ProxyGroup)
22 | 	if !ok {
23 | 		log.Warnln("Request health check for `%s`: invalid type %s", name, p.Type().String())
24 | 
25 | 		return
26 | 	}
27 | 
28 | 	wg := &sync.WaitGroup{}
29 | 
30 | 	for _, pr := range g.Providers() {
31 | 		wg.Add(1)
32 | 
33 | 		go func(provider provider.ProxyProvider) {
34 | 			provider.HealthCheck()
35 | 
36 | 			wg.Done()
37 | 		}(pr)
38 | 	}
39 | 
40 | 	wg.Wait()
41 | }
42 | 
43 | func HealthCheckAll() {
44 | 	for _, g := range QueryProxyGroupNames(false) {
45 | 		go func(group string) {
46 | 			HealthCheck(group)
47 | 		}(g)
48 | 	}
49 | }
50 | 


--------------------------------------------------------------------------------
/core/src/main/golang/native/tunnel/loopback.go:
--------------------------------------------------------------------------------
1 | package tunnel
2 | 
3 | import (
4 | 	"net/netip"
5 | )
6 | 
7 | var loopback = netip.MustParseAddr("127.0.0.1")
8 | 


--------------------------------------------------------------------------------
/core/src/main/golang/native/tunnel/state.go:
--------------------------------------------------------------------------------
 1 | package tunnel
 2 | 
 3 | import (
 4 | 	"github.com/metacubex/mihomo/tunnel"
 5 | )
 6 | 
 7 | func QueryMode() string {
 8 | 	return tunnel.Mode().String()
 9 | }
10 | 


--------------------------------------------------------------------------------
/core/src/main/golang/native/tunnel/statistic.go:
--------------------------------------------------------------------------------
 1 | package tunnel
 2 | 
 3 | import (
 4 | 	"github.com/metacubex/mihomo/tunnel/statistic"
 5 | )
 6 | 
 7 | func ResetStatistic() {
 8 | 	statistic.DefaultManager.ResetStatistic()
 9 | }
10 | 
11 | func Now() (up int64, down int64) {
12 | 	return statistic.DefaultManager.Now()
13 | }
14 | 
15 | func Total() (up int64, down int64) {
16 | 	return statistic.DefaultManager.Total()
17 | }
18 | 


--------------------------------------------------------------------------------
/core/src/main/golang/native/tunnel/suspend.go:
--------------------------------------------------------------------------------
 1 | package tunnel
 2 | 
 3 | func Suspend(s bool) {
 4 | 	// cause by ACTION_SCREEN_OFF/ACTION_SCREEN_ON,
 5 | 	// but we don't know what should do so just ignored.
 6 | 	//
 7 | 	// WARNING: don't call core's Tunnel.OnSuspend/OnRunning at here,
 8 | 	// this will cause the core to stop processing new incoming connections when the screen is locked.
 9 | }
10 | 


--------------------------------------------------------------------------------
/core/src/main/golang/native/utils.go:
--------------------------------------------------------------------------------
 1 | package main
 2 | 
 3 | import "C"
 4 | 
 5 | import (
 6 | 	"encoding/json"
 7 | 	"reflect"
 8 | )
 9 | 
10 | func marshalJson(obj any) *C.char {
11 | 	res, err := json.Marshal(obj)
12 | 	if err != nil {
13 | 		panic(err.Error())
14 | 	}
15 | 
16 | 	return C.CString(string(res))
17 | }
18 | 
19 | func marshalString(obj any) *C.char {
20 | 	if obj == nil {
21 | 		return nil
22 | 	}
23 | 
24 | 	switch o := obj.(type) {
25 | 	case error:
26 | 		return C.CString(o.Error())
27 | 	case string:
28 | 		return C.CString(o)
29 | 	}
30 | 
31 | 	panic("invalid marshal type " + reflect.TypeOf(obj).Name())
32 | }
33 | 


--------------------------------------------------------------------------------
/core/src/main/java/com/github/kr328/clash/core/bridge/ClashException.kt:
--------------------------------------------------------------------------------
1 | package com.github.kr328.clash.core.bridge
2 | 
3 | import androidx.annotation.Keep
4 | 
5 | @Keep
6 | class ClashException(msg: String) : IllegalArgumentException(msg)


--------------------------------------------------------------------------------
/core/src/main/java/com/github/kr328/clash/core/bridge/Content.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.core.bridge
 2 | 
 3 | import android.net.Uri
 4 | import androidx.annotation.Keep
 5 | import com.github.kr328.clash.common.Global
 6 | import java.io.FileNotFoundException
 7 | 
 8 | @Keep
 9 | object Content {
10 |     @JvmStatic
11 |     fun open(url: String): Int {
12 |         val uri = Uri.parse(url)
13 | 
14 |         if (uri.scheme != "content") {
15 |             throw UnsupportedOperationException("Unsupported scheme ${uri.scheme}")
16 |         }
17 | 
18 |         return Global.application.contentResolver.openFileDescriptor(uri, "r")?.detachFd()
19 |             ?: throw FileNotFoundException("$uri not found")
20 |     }
21 | }
22 | 


--------------------------------------------------------------------------------
/core/src/main/java/com/github/kr328/clash/core/bridge/FetchCallback.kt:
--------------------------------------------------------------------------------
1 | package com.github.kr328.clash.core.bridge
2 | 
3 | import androidx.annotation.Keep
4 | 
5 | @Keep
6 | interface FetchCallback {
7 |     fun report(statusJson: String)
8 |     fun complete(error: String?)
9 | }


--------------------------------------------------------------------------------
/core/src/main/java/com/github/kr328/clash/core/bridge/LogcatInterface.kt:
--------------------------------------------------------------------------------
1 | package com.github.kr328.clash.core.bridge
2 | 
3 | import androidx.annotation.Keep
4 | 
5 | @Keep
6 | interface LogcatInterface {
7 |     fun received(jsonPayload: String)
8 | }


--------------------------------------------------------------------------------
/core/src/main/java/com/github/kr328/clash/core/bridge/TunInterface.kt:
--------------------------------------------------------------------------------
1 | package com.github.kr328.clash.core.bridge
2 | 
3 | import androidx.annotation.Keep
4 | 
5 | @Keep
6 | interface TunInterface {
7 |     fun markSocket(fd: Int)
8 |     fun querySocketUid(protocol: Int, source: String, target: String): Int
9 | }


--------------------------------------------------------------------------------
/core/src/main/java/com/github/kr328/clash/core/model/FetchStatus.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.core.model
 2 | 
 3 | import android.os.Parcel
 4 | import android.os.Parcelable
 5 | import com.github.kr328.clash.core.util.Parcelizer
 6 | import kotlinx.serialization.Serializable
 7 | 
 8 | @Serializable
 9 | data class FetchStatus(
10 |     val action: Action,
11 |     val args: List<String>,
12 |     val progress: Int,
13 |     val max: Int
14 | ) : Parcelable {
15 |     enum class Action {
16 |         FetchConfiguration,
17 |         FetchProviders,
18 |         Verifying,
19 |     }
20 | 
21 |     override fun describeContents(): Int {
22 |         return 0
23 |     }
24 | 
25 |     override fun writeToParcel(dest: Parcel, flags: Int) {
26 |         Parcelizer.encodeToParcel(serializer(), dest, this)
27 |     }
28 | 
29 |     companion object CREATOR : Parcelable.Creator<FetchStatus> {
30 |         override fun createFromParcel(parcel: Parcel): FetchStatus {
31 |             return Parcelizer.decodeFromParcel(serializer(), parcel)
32 |         }
33 | 
34 |         override fun newArray(size: Int): Array<FetchStatus?> {
35 |             return arrayOfNulls(size)
36 |         }
37 |     }
38 | }


--------------------------------------------------------------------------------
/core/src/main/java/com/github/kr328/clash/core/model/Provider.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.core.model
 2 | 
 3 | import android.os.Parcel
 4 | import android.os.Parcelable
 5 | import com.github.kr328.clash.core.util.Parcelizer
 6 | import kotlinx.serialization.Serializable
 7 | 
 8 | @Serializable
 9 | data class Provider(
10 |     val name: String,
11 |     val type: Type,
12 |     val vehicleType: VehicleType,
13 |     val updatedAt: Long
14 | ) : Parcelable, Comparable<Provider> {
15 |     enum class Type {
16 |         Proxy, Rule
17 |     }
18 | 
19 |     enum class VehicleType {
20 |         HTTP, File, Inline, Compatible
21 |     }
22 | 
23 |     override fun writeToParcel(parcel: Parcel, flags: Int) {
24 |         Parcelizer.encodeToParcel(serializer(), parcel, this)
25 |     }
26 | 
27 |     override fun describeContents(): Int {
28 |         return 0
29 |     }
30 | 
31 |     override fun compareTo(other: Provider): Int {
32 |         return compareValuesBy(this, other, Provider::type, Provider::name)
33 |     }
34 | 
35 |     companion object CREATOR : Parcelable.Creator<Provider> {
36 |         override fun createFromParcel(parcel: Parcel): Provider {
37 |             return Parcelizer.decodeFromParcel(serializer(), parcel)
38 |         }
39 | 
40 |         override fun newArray(size: Int): Array<Provider?> {
41 |             return arrayOfNulls(size)
42 |         }
43 |     }
44 | }
45 | 


--------------------------------------------------------------------------------
/core/src/main/java/com/github/kr328/clash/core/model/ProviderList.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.core.model
 2 | 
 3 | import android.os.Parcel
 4 | import android.os.Parcelable
 5 | import com.github.kr328.clash.common.util.createListFromParcelSlice
 6 | import com.github.kr328.clash.common.util.writeToParcelSlice
 7 | 
 8 | class ProviderList(data: List<Provider>) : List<Provider> by data, Parcelable {
 9 |     constructor(parcel: Parcel) : this(Provider.createListFromParcelSlice(parcel, 0, 20))
10 | 
11 |     override fun describeContents(): Int {
12 |         return 0
13 |     }
14 | 
15 |     override fun writeToParcel(parcel: Parcel, flags: Int) {
16 |         return writeToParcelSlice(parcel, flags)
17 |     }
18 | 
19 |     companion object CREATOR : Parcelable.Creator<ProviderList> {
20 |         override fun createFromParcel(parcel: Parcel): ProviderList {
21 |             return ProviderList(parcel)
22 |         }
23 | 
24 |         override fun newArray(size: Int): Array<ProviderList?> {
25 |             return arrayOfNulls(size)
26 |         }
27 |     }
28 | }


--------------------------------------------------------------------------------
/core/src/main/java/com/github/kr328/clash/core/model/ProxySort.kt:
--------------------------------------------------------------------------------
1 | package com.github.kr328.clash.core.model
2 | 
3 | enum class ProxySort {
4 |     Default, Title, Delay
5 | }
6 | 


--------------------------------------------------------------------------------
/core/src/main/java/com/github/kr328/clash/core/model/Traffic.kt:
--------------------------------------------------------------------------------
1 | package com.github.kr328.clash.core.model
2 | 
3 | typealias Traffic = Long


--------------------------------------------------------------------------------
/core/src/main/java/com/github/kr328/clash/core/model/TunnelState.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.core.model
 2 | 
 3 | import android.os.Parcel
 4 | import android.os.Parcelable
 5 | import com.github.kr328.clash.core.util.Parcelizer
 6 | import kotlinx.serialization.SerialName
 7 | import kotlinx.serialization.Serializable
 8 | 
 9 | @Serializable
10 | data class TunnelState(
11 |     val mode: Mode,
12 | ) : Parcelable {
13 |     @Serializable
14 |     enum class Mode {
15 |         @SerialName("direct")
16 |         Direct,
17 | 
18 |         @SerialName("global")
19 |         Global,
20 | 
21 |         @SerialName("rule")
22 |         Rule,
23 | 
24 |         @SerialName("script")
25 |         Script,
26 |     }
27 | 
28 |     override fun writeToParcel(parcel: Parcel, flags: Int) {
29 |         Parcelizer.encodeToParcel(serializer(), parcel, this)
30 |     }
31 | 
32 |     override fun describeContents(): Int {
33 |         return 0
34 |     }
35 | 
36 |     companion object CREATOR : Parcelable.Creator<TunnelState> {
37 |         override fun createFromParcel(parcel: Parcel): TunnelState {
38 |             return Parcelizer.decodeFromParcel(serializer(), parcel)
39 |         }
40 | 
41 |         override fun newArray(size: Int): Array<TunnelState?> {
42 |             return arrayOfNulls(size)
43 |         }
44 |     }
45 | }


--------------------------------------------------------------------------------
/core/src/main/java/com/github/kr328/clash/core/model/UiConfiguration.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.core.model
 2 | 
 3 | import android.os.Parcel
 4 | import android.os.Parcelable
 5 | import com.github.kr328.clash.core.util.Parcelizer
 6 | import kotlinx.serialization.Serializable
 7 | 
 8 | @Serializable
 9 | class UiConfiguration : Parcelable {
10 |     override fun writeToParcel(parcel: Parcel, flags: Int) {
11 |         Parcelizer.encodeToParcel(serializer(), parcel, this)
12 |     }
13 | 
14 |     override fun describeContents(): Int {
15 |         return 0
16 |     }
17 | 
18 |     companion object CREATOR : Parcelable.Creator<UiConfiguration> {
19 |         override fun createFromParcel(parcel: Parcel): UiConfiguration {
20 |             return Parcelizer.decodeFromParcel(serializer(), parcel)
21 |         }
22 | 
23 |         override fun newArray(size: Int): Array<UiConfiguration?> {
24 |             return arrayOfNulls(size)
25 |         }
26 |     }
27 | }
28 | 


--------------------------------------------------------------------------------
/core/src/main/java/com/github/kr328/clash/core/util/Net.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.core.util
 2 | 
 3 | import java.net.InetAddress
 4 | import java.net.InetSocketAddress
 5 | import java.net.URL
 6 | 
 7 | fun parseInetSocketAddress(address: String): InetSocketAddress {
 8 |     val url = URL("https://$address")
 9 | 
10 |     return InetSocketAddress(InetAddress.getByName(url.host), url.port)
11 | }


--------------------------------------------------------------------------------
/core/src/main/java/com/github/kr328/clash/core/util/Serializers.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.core.util
 2 | 
 3 | import kotlinx.serialization.KSerializer
 4 | import kotlinx.serialization.descriptors.PrimitiveKind
 5 | import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
 6 | import kotlinx.serialization.descriptors.SerialDescriptor
 7 | import kotlinx.serialization.encoding.Decoder
 8 | import kotlinx.serialization.encoding.Encoder
 9 | import java.util.*
10 | 
11 | object DateSerializer : KSerializer<Date> {
12 |     override val descriptor: SerialDescriptor
13 |         get() = PrimitiveSerialDescriptor("Date", PrimitiveKind.LONG)
14 | 
15 |     override fun deserialize(decoder: Decoder): Date {
16 |         return Date(decoder.decodeLong())
17 |     }
18 | 
19 |     override fun serialize(encoder: Encoder, value: Date) {
20 |         encoder.encodeLong(value.time)
21 |     }
22 | }


--------------------------------------------------------------------------------
/design/build.gradle.kts:
--------------------------------------------------------------------------------
 1 | plugins {
 2 |     kotlin("android")
 3 |     kotlin("kapt")
 4 |     id("com.android.library")
 5 | }
 6 | 
 7 | dependencies {
 8 |     implementation(project(":common"))
 9 |     implementation(project(":core"))
10 |     implementation(project(":service"))
11 | 
12 |     implementation(libs.kotlin.coroutine)
13 |     implementation(libs.androidx.core)
14 |     implementation(libs.androidx.appcompat)
15 |     implementation(libs.androidx.activity)
16 |     implementation(libs.androidx.coordinator)
17 |     implementation(libs.androidx.recyclerview)
18 |     implementation(libs.androidx.fragment)
19 |     implementation(libs.androidx.viewpager)
20 |     implementation(libs.google.material)
21 | }
22 | 


--------------------------------------------------------------------------------
/design/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MetaCubeX/ClashMetaForAndroid/61093eb8d655cbd89dee761bbb79634481c3f5b6/design/consumer-rules.pro


--------------------------------------------------------------------------------
/design/proguard-rules.pro:
--------------------------------------------------------------------------------
 1 | # Add project specific ProGuard rules here.
 2 | # You can control the set of applied configuration files using the
 3 | # proguardFiles setting in build.gradle.
 4 | #
 5 | # For more details, see
 6 | #   http://developer.android.com/guide/developing/tools/proguard.html
 7 | 
 8 | # If your project uses WebView with JS, uncomment the following
 9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | #   public *;
13 | #}
14 | 
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 | 
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 | 


--------------------------------------------------------------------------------
/design/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 | <manifest />
2 | 


--------------------------------------------------------------------------------
/design/src/main/java/com/github/kr328/clash/design/AppCrashedDesign.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.design
 2 | 
 3 | import android.content.Context
 4 | import android.view.View
 5 | import com.github.kr328.clash.design.databinding.DesignAppCrashedBinding
 6 | import com.github.kr328.clash.design.util.applyFrom
 7 | import com.github.kr328.clash.design.util.bindAppBarElevation
 8 | import com.github.kr328.clash.design.util.layoutInflater
 9 | import com.github.kr328.clash.design.util.root
10 | 
11 | class AppCrashedDesign(context: Context) : Design<Unit>(context) {
12 |     private val binding = DesignAppCrashedBinding
13 |         .inflate(context.layoutInflater, context.root, false)
14 | 
15 |     override val root: View
16 |         get() = binding.root
17 | 
18 |     fun setAppLogs(logs: String) {
19 |         binding.logsView.text = logs
20 |     }
21 | 
22 |     init {
23 |         binding.self = this
24 | 
25 |         binding.activityBarLayout.applyFrom(context)
26 | 
27 |         binding.scrollRoot.bindAppBarElevation(binding.activityBarLayout)
28 |     }
29 | }


--------------------------------------------------------------------------------
/design/src/main/java/com/github/kr328/clash/design/SettingsDesign.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.design
 2 | 
 3 | import android.content.Context
 4 | import android.view.View
 5 | import com.github.kr328.clash.design.databinding.DesignSettingsBinding
 6 | import com.github.kr328.clash.design.util.applyFrom
 7 | import com.github.kr328.clash.design.util.bindAppBarElevation
 8 | import com.github.kr328.clash.design.util.layoutInflater
 9 | import com.github.kr328.clash.design.util.root
10 | 
11 | class SettingsDesign(context: Context) : Design<SettingsDesign.Request>(context) {
12 |     enum class Request {
13 |         StartApp, StartNetwork, StartOverride, StartMetaFeature,
14 |     }
15 | 
16 |     private val binding = DesignSettingsBinding
17 |         .inflate(context.layoutInflater, context.root, false)
18 | 
19 |     override val root: View
20 |         get() = binding.root
21 | 
22 |     init {
23 |         binding.self = this
24 | 
25 |         binding.activityBarLayout.applyFrom(context)
26 | 
27 |         binding.scrollRoot.bindAppBarElevation(binding.activityBarLayout)
28 |     }
29 | 
30 |     fun request(request: Request) {
31 |         requests.trySend(request)
32 |     }
33 | }


--------------------------------------------------------------------------------
/design/src/main/java/com/github/kr328/clash/design/adapter/LogFileAdapter.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.design.adapter
 2 | 
 3 | import android.content.Context
 4 | import android.view.ViewGroup
 5 | import android.view.ViewGroup.LayoutParams
 6 | import androidx.recyclerview.widget.RecyclerView
 7 | import com.github.kr328.clash.design.model.LogFile
 8 | import com.github.kr328.clash.design.util.format
 9 | import com.github.kr328.clash.design.view.ActionLabel
10 | 
11 | class LogFileAdapter(
12 |     private val context: Context,
13 |     private val open: (LogFile) -> Unit,
14 | ) : RecyclerView.Adapter<LogFileAdapter.Holder>() {
15 |     class Holder(val label: ActionLabel) : RecyclerView.ViewHolder(label)
16 | 
17 |     var logs: List<LogFile> = emptyList()
18 | 
19 |     override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
20 |         return Holder(ActionLabel(context).apply {
21 |             layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)
22 |         })
23 |     }
24 | 
25 |     override fun onBindViewHolder(holder: Holder, position: Int) {
26 |         val current = logs[position]
27 | 
28 |         holder.label.text = current.fileName
29 |         holder.label.subtext = current.date.format(context)
30 |         holder.label.setOnClickListener {
31 |             open(current)
32 |         }
33 |     }
34 | 
35 |     override fun getItemCount(): Int {
36 |         return logs.size
37 |     }
38 | }


--------------------------------------------------------------------------------
/design/src/main/java/com/github/kr328/clash/design/adapter/LogMessageAdapter.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.design.adapter
 2 | 
 3 | import android.content.Context
 4 | import android.view.ViewGroup
 5 | import androidx.recyclerview.widget.RecyclerView
 6 | import com.github.kr328.clash.core.model.LogMessage
 7 | import com.github.kr328.clash.design.databinding.AdapterLogMessageBinding
 8 | import com.github.kr328.clash.design.util.layoutInflater
 9 | 
10 | class LogMessageAdapter(
11 |     private val context: Context,
12 |     private val copy: (LogMessage) -> Unit,
13 | ) :
14 |     RecyclerView.Adapter<LogMessageAdapter.Holder>() {
15 |     class Holder(val binding: AdapterLogMessageBinding) : RecyclerView.ViewHolder(binding.root)
16 | 
17 |     var messages: List<LogMessage> = emptyList()
18 | 
19 |     override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
20 |         return Holder(
21 |             AdapterLogMessageBinding
22 |                 .inflate(context.layoutInflater, parent, false)
23 |         )
24 |     }
25 | 
26 |     override fun onBindViewHolder(holder: Holder, position: Int) {
27 |         val current = messages[position]
28 | 
29 |         holder.binding.message = current
30 |         holder.binding.root.setOnLongClickListener {
31 |             copy(current)
32 | 
33 |             true
34 |         }
35 |     }
36 | 
37 |     override fun getItemCount(): Int {
38 |         return messages.size
39 |     }
40 | }


--------------------------------------------------------------------------------
/design/src/main/java/com/github/kr328/clash/design/adapter/ProxyAdapter.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.design.adapter
 2 | 
 3 | import android.view.ViewGroup
 4 | import androidx.recyclerview.widget.RecyclerView
 5 | import com.github.kr328.clash.design.component.ProxyView
 6 | import com.github.kr328.clash.design.component.ProxyViewConfig
 7 | import com.github.kr328.clash.design.component.ProxyViewState
 8 | 
 9 | class ProxyAdapter(
10 |     private val config: ProxyViewConfig,
11 |     private val clicked: (String) -> Unit,
12 | ) : RecyclerView.Adapter<ProxyAdapter.Holder>() {
13 |     class Holder(val view: ProxyView) : RecyclerView.ViewHolder(view)
14 | 
15 |     var selectable: Boolean = false
16 |     var states: List<ProxyViewState> = emptyList()
17 | 
18 |     override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
19 |         return Holder(ProxyView(config.context, config))
20 |     }
21 | 
22 |     override fun onBindViewHolder(holder: Holder, position: Int) {
23 |         val current = states[position]
24 | 
25 |         holder.view.apply {
26 |             state = current
27 | 
28 |             setOnClickListener {
29 |                 clicked(current.proxy.name)
30 |             }
31 | 
32 |             val isSelector = selectable
33 | 
34 |             isFocusable = isSelector
35 |             isClickable = isSelector
36 | 
37 |             current.update(true)
38 |         }
39 |     }
40 | 
41 |     override fun getItemCount(): Int {
42 |         return states.size
43 |     }
44 | }
45 | 


--------------------------------------------------------------------------------
/design/src/main/java/com/github/kr328/clash/design/model/AppInfo.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.design.model
 2 | 
 3 | import android.graphics.drawable.Drawable
 4 | 
 5 | data class AppInfo(
 6 |     val packageName: String,
 7 |     val label: String,
 8 |     val icon: Drawable,
 9 |     val installTime: Long,
10 |     val updateDate: Long,
11 | )


--------------------------------------------------------------------------------
/design/src/main/java/com/github/kr328/clash/design/model/AppInfoSort.kt:
--------------------------------------------------------------------------------
1 | package com.github.kr328.clash.design.model
2 | 
3 | enum class AppInfoSort(comparator: Comparator<AppInfo>) : Comparator<AppInfo> by comparator {
4 |     Label(compareBy(AppInfo::label)),
5 |     PackageName(compareBy(AppInfo::packageName)),
6 |     InstallTime(compareBy(AppInfo::installTime)),
7 |     UpdateTime(compareBy(AppInfo::updateDate)),
8 | }
9 | 


--------------------------------------------------------------------------------
/design/src/main/java/com/github/kr328/clash/design/model/Behavior.kt:
--------------------------------------------------------------------------------
1 | package com.github.kr328.clash.design.model
2 | 
3 | interface Behavior {
4 |     var autoRestart: Boolean
5 | }


--------------------------------------------------------------------------------
/design/src/main/java/com/github/kr328/clash/design/model/DarkMode.kt:
--------------------------------------------------------------------------------
1 | package com.github.kr328.clash.design.model
2 | 
3 | enum class DarkMode {
4 |     Auto, ForceLight, ForceDark
5 | }
6 | 


--------------------------------------------------------------------------------
/design/src/main/java/com/github/kr328/clash/design/model/File.kt:
--------------------------------------------------------------------------------
1 | package com.github.kr328.clash.design.model
2 | 
3 | data class File(
4 |     val id: String,
5 |     val name: String,
6 |     val size: Long,
7 |     val lastModified: Long,
8 |     val isDirectory: Boolean
9 | )


--------------------------------------------------------------------------------
/design/src/main/java/com/github/kr328/clash/design/model/LogFile.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.design.model
 2 | 
 3 | import java.util.*
 4 | 
 5 | data class LogFile(val fileName: String, val date: Date) {
 6 |     companion object {
 7 |         private val REGEX_FILE = Regex("clash-(\\d+).log")
 8 |         private const val FORMAT_FILE_NAME = "clash-%d.log"
 9 | 
10 |         fun parseFromFileName(fileName: String): LogFile? {
11 |             return REGEX_FILE.matchEntire(fileName)?.run {
12 |                 LogFile(fileName, Date(groupValues[1].toLong()))
13 |             }
14 |         }
15 | 
16 |         fun generate(): LogFile {
17 |             val current = Date()
18 |             val fileName = FORMAT_FILE_NAME.format(current.time)
19 | 
20 |             return LogFile(fileName, current)
21 |         }
22 |     }
23 | }


--------------------------------------------------------------------------------
/design/src/main/java/com/github/kr328/clash/design/model/ProfilePageState.kt:
--------------------------------------------------------------------------------
1 | package com.github.kr328.clash.design.model
2 | 
3 | class ProfilePageState {
4 |     var allUpdating = false
5 | }


--------------------------------------------------------------------------------
/design/src/main/java/com/github/kr328/clash/design/model/ProfileProvider.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.design.model
 2 | 
 3 | import android.content.Context
 4 | import android.content.Intent
 5 | import android.graphics.drawable.Drawable
 6 | import com.github.kr328.clash.common.compat.getDrawableCompat
 7 | import com.github.kr328.clash.design.R
 8 | 
 9 | sealed class ProfileProvider {
10 |     class File(private val context: Context) : ProfileProvider() {
11 |         override val name: String
12 |             get() = context.getString(R.string.file)
13 |         override val summary: String
14 |             get() = context.getString(R.string.import_from_file)
15 |         override val icon: Drawable?
16 |             get() = context.getDrawableCompat(R.drawable.ic_baseline_attach_file)
17 |     }
18 | 
19 |     class Url(private val context: Context) : ProfileProvider() {
20 |         override val name: String
21 |             get() = context.getString(R.string.url)
22 |         override val summary: String
23 |             get() = context.getString(R.string.import_from_url)
24 |         override val icon: Drawable?
25 |             get() = context.getDrawableCompat(R.drawable.ic_baseline_cloud_download)
26 |     }
27 | 
28 |     class External(
29 |         override val name: String,
30 |         override val summary: String,
31 |         override val icon: Drawable?,
32 |         val intent: Intent,
33 |     ) : ProfileProvider()
34 | 
35 |     abstract val name: String
36 |     abstract val summary: String
37 |     abstract val icon: Drawable?
38 | }
39 | 


--------------------------------------------------------------------------------
/design/src/main/java/com/github/kr328/clash/design/model/ProviderState.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.design.model
 2 | 
 3 | import androidx.databinding.BaseObservable
 4 | import androidx.databinding.Bindable
 5 | import com.github.kr328.clash.core.model.Provider
 6 | import com.github.kr328.clash.design.BR
 7 | 
 8 | class ProviderState(
 9 |     val provider: Provider,
10 |     updatedAt: Long,
11 |     updating: Boolean,
12 | ) : BaseObservable() {
13 |     var updatedAt: Long = updatedAt
14 |         @Bindable get
15 |         set(value) {
16 |             field = value
17 | 
18 |             notifyPropertyChanged(BR.updatedAt)
19 |         }
20 | 
21 |     var updating: Boolean = updating
22 |         @Bindable get
23 |         set(value) {
24 |             field = value
25 | 
26 |             notifyPropertyChanged(BR.updating)
27 |         }
28 | }


--------------------------------------------------------------------------------
/design/src/main/java/com/github/kr328/clash/design/model/ProxyPageState.kt:
--------------------------------------------------------------------------------
1 | package com.github.kr328.clash.design.model
2 | 
3 | class ProxyPageState {
4 |     var bottom = false
5 |     var urlTesting = false
6 | }


--------------------------------------------------------------------------------
/design/src/main/java/com/github/kr328/clash/design/model/ProxyState.kt:
--------------------------------------------------------------------------------
1 | package com.github.kr328.clash.design.model
2 | 
3 | data class ProxyState(var now: String)


--------------------------------------------------------------------------------
/design/src/main/java/com/github/kr328/clash/design/preference/Category.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.design.preference
 2 | 
 3 | import android.view.View
 4 | import androidx.annotation.StringRes
 5 | import com.github.kr328.clash.design.databinding.PreferenceCategoryBinding
 6 | import com.github.kr328.clash.design.util.layoutInflater
 7 | 
 8 | fun PreferenceScreen.category(
 9 |     @StringRes text: Int,
10 | ) {
11 |     val binding = PreferenceCategoryBinding
12 |         .inflate(context.layoutInflater, root, false)
13 | 
14 |     binding.textView.text = context.getString(text)
15 | 
16 |     addElement(object : Preference {
17 |         override val view: View
18 |             get() = binding.root
19 |         override var enabled: Boolean
20 |             get() = binding.root.isEnabled
21 |             set(value) {
22 |                 binding.root.isEnabled = value
23 |             }
24 |     })
25 | }


--------------------------------------------------------------------------------
/design/src/main/java/com/github/kr328/clash/design/preference/Preference.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.design.preference
 2 | 
 3 | import android.view.View
 4 | 
 5 | fun interface OnChangedListener {
 6 |     fun onChanged()
 7 | }
 8 | 
 9 | interface Preference {
10 |     val view: View
11 | 
12 |     var enabled: Boolean
13 |         get() = view.isEnabled
14 |         set(value) {
15 |             view.isEnabled = value
16 |             view.isClickable = value
17 |             view.isFocusable = value
18 |             view.alpha = if (value) 1.0f else 0.33f
19 |         }
20 | }


--------------------------------------------------------------------------------
/design/src/main/java/com/github/kr328/clash/design/preference/Screen.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.design.preference
 2 | 
 3 | import android.content.Context
 4 | import android.view.ViewGroup
 5 | import android.widget.LinearLayout
 6 | import android.widget.LinearLayout.LayoutParams
 7 | import android.widget.LinearLayout.LayoutParams.MATCH_PARENT
 8 | import android.widget.LinearLayout.LayoutParams.WRAP_CONTENT
 9 | import kotlinx.coroutines.CoroutineScope
10 | 
11 | interface PreferenceScreen : CoroutineScope {
12 |     val context: Context
13 |     val root: ViewGroup
14 | }
15 | 
16 | fun CoroutineScope.preferenceScreen(
17 |     context: Context,
18 |     configure: PreferenceScreen.() -> Unit
19 | ): PreferenceScreen {
20 |     val root = LinearLayout(context).apply {
21 |         orientation = LinearLayout.VERTICAL
22 |     }
23 | 
24 |     val impl = object : PreferenceScreen, CoroutineScope by this {
25 |         override val context: Context
26 |             get() = context
27 |         override val root: ViewGroup
28 |             get() = root
29 |     }
30 | 
31 |     impl.configure()
32 | 
33 |     return impl
34 | }
35 | 
36 | fun PreferenceScreen.addElement(preference: Preference) {
37 |     root.addView(preference.view, LayoutParams(MATCH_PARENT, WRAP_CONTENT))
38 | }


--------------------------------------------------------------------------------
/design/src/main/java/com/github/kr328/clash/design/preference/Tips.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.design.preference
 2 | 
 3 | import android.view.View
 4 | import androidx.annotation.StringRes
 5 | import com.github.kr328.clash.design.databinding.PreferenceTipsBinding
 6 | import com.github.kr328.clash.design.util.getHtml
 7 | import com.github.kr328.clash.design.util.layoutInflater
 8 | import com.github.kr328.clash.design.util.root
 9 | 
10 | interface TipsPreference : Preference {
11 |     var text: CharSequence?
12 | }
13 | 
14 | fun PreferenceScreen.tips(
15 |     @StringRes text: Int,
16 |     configure: TipsPreference.() -> Unit = {},
17 | ): TipsPreference {
18 |     val binding = PreferenceTipsBinding
19 |         .inflate(context.layoutInflater, context.root, false)
20 |     val impl = object : TipsPreference {
21 |         override var text: CharSequence?
22 |             get() = binding.tips.text
23 |             set(value) {
24 |                 binding.tips.text = value
25 |             }
26 |         override val view: View
27 |             get() = binding.root
28 |         override var enabled: Boolean
29 |             get() = binding.root.isEnabled
30 |             set(value) {
31 |                 binding.root.isEnabled = value
32 |             }
33 |     }
34 | 
35 |     binding.tips.text = context.getHtml(text)
36 | 
37 |     impl.configure()
38 | 
39 |     addElement(impl)
40 | 
41 |     return impl
42 | }


--------------------------------------------------------------------------------
/design/src/main/java/com/github/kr328/clash/design/preference/Value.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.design.preference
 2 | 
 3 | interface NullableTextAdapter<T> {
 4 |     fun from(value: T): String?
 5 |     fun to(text: String?): T
 6 | 
 7 |     companion object {
 8 |         val Port = object : NullableTextAdapter<Int?> {
 9 |             override fun from(value: Int?): String? {
10 |                 if (value == null) return null
11 | 
12 |                 return if (value > 0) value.toString() else ""
13 |             }
14 | 
15 |             override fun to(text: String?): Int? {
16 |                 if (text == null) return null
17 | 
18 |                 return text.toIntOrNull() ?: 0
19 |             }
20 |         }
21 | 
22 |         val String = object : NullableTextAdapter<String?> {
23 |             override fun from(value: String?): String? {
24 |                 return value
25 |             }
26 | 
27 |             override fun to(text: String?): String? {
28 |                 return text
29 |             }
30 |         }
31 |     }
32 | }
33 | 
34 | interface TextAdapter<T> {
35 |     fun from(value: T): String
36 |     fun to(text: String): T
37 | 
38 |     companion object {
39 |         val String = object : TextAdapter<String> {
40 |             override fun from(value: String): String {
41 |                 return value
42 |             }
43 | 
44 |             override fun to(text: String): String {
45 |                 return text
46 |             }
47 |         }
48 |     }
49 | }


--------------------------------------------------------------------------------
/design/src/main/java/com/github/kr328/clash/design/ui/DayNight.kt:
--------------------------------------------------------------------------------
1 | package com.github.kr328.clash.design.ui
2 | 
3 | enum class DayNight {
4 |     Day, Night
5 | }


--------------------------------------------------------------------------------
/design/src/main/java/com/github/kr328/clash/design/ui/Insets.kt:
--------------------------------------------------------------------------------
1 | package com.github.kr328.clash.design.ui
2 | 
3 | data class Insets(val start: Int, val top: Int, val end: Int, val bottom: Int) {
4 |     companion object {
5 |         val EMPTY = Insets(0, 0, 0, 0)
6 |     }
7 | }


--------------------------------------------------------------------------------
/design/src/main/java/com/github/kr328/clash/design/ui/ObservableCurrentTime.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.design.ui
 2 | 
 3 | import androidx.databinding.BaseObservable
 4 | import androidx.databinding.Bindable
 5 | import androidx.databinding.library.baseAdapters.BR
 6 | 
 7 | class ObservableCurrentTime : BaseObservable() {
 8 |     var value: Long = System.currentTimeMillis()
 9 |         @Bindable get
10 |         private set(value) {
11 |             field = value
12 | 
13 |             notifyPropertyChanged(BR.value)
14 |         }
15 | 
16 |     fun update() {
17 |         value = System.currentTimeMillis()
18 |     }
19 | }


--------------------------------------------------------------------------------
/design/src/main/java/com/github/kr328/clash/design/ui/Surface.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.design.ui
 2 | 
 3 | import androidx.databinding.BaseObservable
 4 | import androidx.databinding.Bindable
 5 | import com.github.kr328.clash.design.BR
 6 | 
 7 | class Surface : BaseObservable() {
 8 |     var insets: Insets = Insets.EMPTY
 9 |         @Bindable get
10 |         set(value) {
11 |             field = value
12 | 
13 |             notifyPropertyChanged(BR.insets)
14 |         }
15 | }


--------------------------------------------------------------------------------
/design/src/main/java/com/github/kr328/clash/design/ui/ToastDuration.kt:
--------------------------------------------------------------------------------
1 | package com.github.kr328.clash.design.ui
2 | 
3 | enum class ToastDuration {
4 |     Short, Long, Indefinite
5 | }


--------------------------------------------------------------------------------
/design/src/main/java/com/github/kr328/clash/design/util/ActivityBar.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.design.util
 2 | 
 3 | import android.app.Activity
 4 | import android.content.Context
 5 | import android.widget.ImageView
 6 | import android.widget.TextView
 7 | import com.github.kr328.clash.design.R
 8 | import com.github.kr328.clash.design.view.ActivityBarLayout
 9 | 
10 | fun ActivityBarLayout.applyFrom(context: Context) {
11 |     if (context is Activity) {
12 |         findViewById<ImageView>(R.id.activity_bar_close_view)?.apply {
13 |             setOnClickListener {
14 |                 context.onBackPressed()
15 |             }
16 |         }
17 |         findViewById<TextView>(R.id.activity_bar_title_view)?.apply {
18 |             text = context.title
19 |         }
20 |     }
21 | }


--------------------------------------------------------------------------------
/design/src/main/java/com/github/kr328/clash/design/util/App.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.design.util
 2 | 
 3 | import android.content.pm.PackageInfo
 4 | import android.content.pm.PackageManager
 5 | import com.github.kr328.clash.common.compat.foreground
 6 | import com.github.kr328.clash.design.model.AppInfo
 7 | 
 8 | fun PackageInfo.toAppInfo(pm: PackageManager): AppInfo {
 9 |     return AppInfo(
10 |         packageName = packageName,
11 |         icon = applicationInfo!!.loadIcon(pm).foreground(),
12 |         label = applicationInfo!!.loadLabel(pm).toString(),
13 |         installTime = firstInstallTime,
14 |         updateDate = lastUpdateTime,
15 |     )
16 | }
17 | 


--------------------------------------------------------------------------------
/design/src/main/java/com/github/kr328/clash/design/util/Binding.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.design.util
 2 | 
 3 | import android.view.View
 4 | import androidx.databinding.BindingAdapter
 5 | 
 6 | @BindingAdapter("android:minHeight")
 7 | fun bindMinHeight(view: View, value: Float) {
 8 |     view.minimumHeight = value.toInt()
 9 | }
10 | 


--------------------------------------------------------------------------------
/design/src/main/java/com/github/kr328/clash/design/util/Context.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.design.util
 2 | 
 3 | import android.app.Activity
 4 | import android.content.Context
 5 | import android.text.Spanned
 6 | import android.view.LayoutInflater
 7 | import android.view.ViewGroup
 8 | import androidx.annotation.DimenRes
 9 | import androidx.annotation.StringRes
10 | import com.github.kr328.clash.common.compat.fromHtmlCompat
11 | 
12 | val Context.layoutInflater: LayoutInflater
13 |     get() = LayoutInflater.from(this)
14 | 
15 | val Context.root: ViewGroup?
16 |     get() {
17 |         return when (this) {
18 |             is Activity -> {
19 |                 findViewById(android.R.id.content)
20 |             }
21 |             else -> {
22 |                 null
23 |             }
24 |         }
25 |     }
26 | 
27 | fun Context.getPixels(@DimenRes resId: Int): Int {
28 |     return resources.getDimensionPixelSize(resId)
29 | }
30 | 
31 | fun Context.getHtml(@StringRes resId: Int): Spanned {
32 |     return fromHtmlCompat(getString(resId))
33 | }
34 | 


--------------------------------------------------------------------------------
/design/src/main/java/com/github/kr328/clash/design/util/Diff.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.design.util
 2 | 
 3 | import androidx.recyclerview.widget.DiffUtil
 4 | 
 5 | fun <T> List<T>.diffWith(
 6 |     newList: List<T>,
 7 |     detectMove: Boolean = false,
 8 |     id: (T) -> Any? = { it }
 9 | ): DiffUtil.DiffResult {
10 |     val oldList = this
11 | 
12 |     return DiffUtil.calculateDiff(object : DiffUtil.Callback() {
13 |         override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
14 |             return id(oldList[oldItemPosition]) == id(newList[newItemPosition])
15 |         }
16 | 
17 |         override fun getOldListSize(): Int {
18 |             return oldList.size
19 |         }
20 | 
21 |         override fun getNewListSize(): Int {
22 |             return newList.size
23 |         }
24 | 
25 |         override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
26 |             return oldList[oldItemPosition] == newList[newItemPosition]
27 |         }
28 |     }, detectMove)
29 | }
30 | 


--------------------------------------------------------------------------------
/design/src/main/java/com/github/kr328/clash/design/util/Inserts.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.design.util
 2 | 
 3 | import android.view.View
 4 | import androidx.core.view.ViewCompat
 5 | import androidx.core.view.WindowInsetsCompat
 6 | import com.github.kr328.clash.design.ui.Insets
 7 | 
 8 | fun View.setOnInsertsChangedListener(adaptLandscape: Boolean = true, listener: (Insets) -> Unit) {
 9 |     setOnApplyWindowInsetsListener { v, ins ->
10 |         val compat = WindowInsetsCompat.toWindowInsetsCompat(ins)
11 |         val insets = compat.getInsets(WindowInsetsCompat.Type.systemBars())
12 | 
13 |         val rInsets = if (ViewCompat.getLayoutDirection(v) == ViewCompat.LAYOUT_DIRECTION_LTR) {
14 |             Insets(
15 |                 insets.left,
16 |                 insets.top,
17 |                 insets.right,
18 |                 insets.bottom,
19 |             )
20 |         } else {
21 |             Insets(
22 |                 insets.right,
23 |                 insets.top,
24 |                 insets.left,
25 |                 insets.bottom,
26 |             )
27 |         }
28 | 
29 |         listener(if (adaptLandscape) rInsets.landscape(v.context) else rInsets)
30 | 
31 |         compat.toWindowInsets()!!
32 |     }
33 | 
34 |     requestApplyInsets()
35 | }
36 | 


--------------------------------------------------------------------------------
/design/src/main/java/com/github/kr328/clash/design/util/Interval.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.design.util
 2 | 
 3 | import android.content.Context
 4 | import com.github.kr328.clash.design.R
 5 | import java.util.concurrent.TimeUnit
 6 | 
 7 | fun Long.elapsedIntervalString(context: Context): String {
 8 |     val day = TimeUnit.MILLISECONDS.toDays(this)
 9 |     val hour = TimeUnit.MILLISECONDS.toHours(this)
10 |     val minute = TimeUnit.MILLISECONDS.toMinutes(this)
11 | 
12 |     return when {
13 |         day > 0 -> context.getString(R.string.format_days_ago, day)
14 |         hour > 0 -> context.getString(R.string.format_hours_ago, hour)
15 |         minute > 0 -> context.getString(R.string.format_minutes_ago, minute)
16 |         else -> context.getString(R.string.recently)
17 |     }
18 | }


--------------------------------------------------------------------------------
/design/src/main/java/com/github/kr328/clash/design/util/Landscape.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.design.util
 2 | 
 3 | import android.content.Context
 4 | import com.github.kr328.clash.design.R
 5 | import com.github.kr328.clash.design.ui.Insets
 6 | 
 7 | fun Insets.landscape(context: Context): Insets {
 8 |     val displayMetrics = context.resources.displayMetrics
 9 |     val minWidth = context.getPixels(R.dimen.surface_landscape_min_width)
10 | 
11 |     val width = displayMetrics.widthPixels
12 |     val height = displayMetrics.heightPixels
13 | 
14 |     return if (width > height && width > minWidth) {
15 |         val expectedWidth = width.coerceAtMost(height.coerceAtLeast(minWidth))
16 | 
17 |         val padding = (width - expectedWidth).coerceAtLeast(start + end) / 2
18 | 
19 |         copy(start = padding.coerceAtLeast(start), end = padding.coerceAtLeast(end))
20 |     } else {
21 |         this
22 |     }
23 | }


--------------------------------------------------------------------------------
/design/src/main/java/com/github/kr328/clash/design/util/ListView.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.design.util
 2 | 
 3 | import android.content.Context
 4 | import android.view.View
 5 | import android.view.View.MeasureSpec
 6 | import android.widget.FrameLayout
 7 | import android.widget.ListAdapter
 8 | 
 9 | fun ListAdapter.measureWidth(context: Context): Int {
10 |     val parent = FrameLayout(context)
11 | 
12 |     val widthMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)
13 |     val heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)
14 | 
15 |     var itemView: View? = null
16 |     var maxWidth = 0
17 |     var itemType = 0
18 | 
19 |     for (i in 0 until count) {
20 |         val positionType = getItemViewType(i)
21 |         if (positionType != itemType) {
22 |             itemType = positionType
23 |             itemView = null
24 |         }
25 | 
26 |         itemView = getView(i, itemView, parent)
27 |         itemView.measure(widthMeasureSpec, heightMeasureSpec)
28 | 
29 |         val itemWidth: Int = itemView.measuredWidth
30 | 
31 |         if (itemWidth > maxWidth) {
32 |             maxWidth = itemWidth
33 |         }
34 |     }
35 | 
36 |     return maxWidth
37 | }


--------------------------------------------------------------------------------
/design/src/main/java/com/github/kr328/clash/design/util/ScrollView.kt:
--------------------------------------------------------------------------------
1 | package com.github.kr328.clash.design.util
2 | 
3 | import com.github.kr328.clash.design.view.ObservableScrollView
4 | 
5 | val ObservableScrollView.isTop: Boolean
6 |     get() = scrollX == 0 && scrollY == 0
7 | 


--------------------------------------------------------------------------------
/design/src/main/java/com/github/kr328/clash/design/util/Toast.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.design.util
 2 | 
 3 | import com.github.kr328.clash.design.Design
 4 | import com.github.kr328.clash.design.R
 5 | import com.github.kr328.clash.design.ui.ToastDuration
 6 | import com.google.android.material.dialog.MaterialAlertDialogBuilder
 7 | 
 8 | suspend fun Design<*>.showExceptionToast(message: CharSequence) {
 9 |     showToast(message, ToastDuration.Long) {
10 |         setAction(R.string.detail) {
11 |             MaterialAlertDialogBuilder(it.context)
12 |                 .setTitle(R.string.error)
13 |                 .setMessage(message)
14 |                 .setCancelable(true)
15 |                 .setPositiveButton(R.string.ok) { _, _ -> }
16 |                 .show()
17 |         }
18 |     }
19 | }
20 | 
21 | suspend fun Design<*>.showExceptionToast(exception: Exception) {
22 |     showExceptionToast(exception.message ?: "Unknown")
23 | }


--------------------------------------------------------------------------------
/design/src/main/java/com/github/kr328/clash/design/util/Validator.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.design.util
 2 | 
 3 | import com.github.kr328.clash.common.util.PatternFileName
 4 | 
 5 | typealias Validator = (String) -> Boolean
 6 | 
 7 | val ValidatorAcceptAll: Validator = {
 8 |     true
 9 | }
10 | 
11 | val ValidatorFileName: Validator = {
12 |     PatternFileName.matches(it) && it.isNotBlank()
13 | }
14 | 
15 | val ValidatorNotBlank: Validator = {
16 |     it.isNotBlank()
17 | }
18 | 
19 | val ValidatorHttpUrl: Validator = {
20 |     it.startsWith("https://", ignoreCase = true) || it.startsWith("http://", ignoreCase = true)
21 | }
22 | 
23 | val ValidatorAutoUpdateInterval: Validator = {
24 |     it.isEmpty() || (it.toLongOrNull() ?: 0) >= 15
25 | }


--------------------------------------------------------------------------------
/design/src/main/java/com/github/kr328/clash/design/util/View.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.design.util
 2 | 
 3 | import android.view.View
 4 | import android.view.inputmethod.InputMethodManager
 5 | import androidx.core.content.getSystemService
 6 | 
 7 | fun View.requestTextInput() {
 8 |     post {
 9 |         requestFocus()
10 | 
11 |         postDelayed({
12 |             context.getSystemService<InputMethodManager>()
13 |                 ?.showSoftInput(this, 0)
14 |         }, 300)
15 |     }
16 | }
17 | 


--------------------------------------------------------------------------------
/design/src/main/java/com/github/kr328/clash/design/view/ActivityBarLayout.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.design.view
 2 | 
 3 | import android.content.Context
 4 | import android.util.AttributeSet
 5 | import android.view.MotionEvent
 6 | import android.widget.FrameLayout
 7 | import androidx.annotation.AttrRes
 8 | import androidx.annotation.StyleRes
 9 | import com.github.kr328.clash.design.util.resolveThemedColor
10 | 
11 | class ActivityBarLayout @JvmOverloads constructor(
12 |     context: Context,
13 |     attributeSet: AttributeSet? = null,
14 |     @AttrRes defStyleAttr: Int = 0,
15 |     @StyleRes defStyleRes: Int = 0
16 | ) : FrameLayout(context, attributeSet, defStyleAttr, defStyleRes) {
17 |     init {
18 |         alpha = 0.96f
19 | 
20 |         setBackgroundColor(context.resolveThemedColor(android.R.attr.windowBackground))
21 |     }
22 | 
23 |     override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
24 |         super.dispatchTouchEvent(ev)
25 | 
26 |         return true
27 |     }
28 | }


--------------------------------------------------------------------------------
/design/src/main/java/com/github/kr328/clash/design/view/AppRecyclerView.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.design.view
 2 | 
 3 | import android.content.Context
 4 | import android.graphics.Canvas
 5 | import android.util.AttributeSet
 6 | import androidx.annotation.AttrRes
 7 | import androidx.recyclerview.widget.RecyclerView
 8 | 
 9 | class AppRecyclerView @JvmOverloads constructor(
10 |     context: Context,
11 |     attributeSet: AttributeSet? = null,
12 |     @AttrRes defStyleAttr: Int = 0
13 | ) : RecyclerView(context, attributeSet, defStyleAttr) {
14 |     init {
15 |         isFocusable = false
16 |     }
17 | }
18 | 


--------------------------------------------------------------------------------
/design/src/main/java/com/github/kr328/clash/design/view/ObservableScrollView.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.design.view
 2 | 
 3 | import android.content.Context
 4 | import android.util.AttributeSet
 5 | import android.widget.ScrollView
 6 | import androidx.annotation.AttrRes
 7 | import androidx.annotation.StyleRes
 8 | 
 9 | class ObservableScrollView @JvmOverloads constructor(
10 |     context: Context,
11 |     attributeSet: AttributeSet? = null,
12 |     @AttrRes defStyleAttr: Int = 0,
13 |     @StyleRes defStyleRes: Int = 0
14 | ) : ScrollView(context, attributeSet, defStyleAttr, defStyleRes) {
15 |     fun interface OnScrollChangedListener {
16 |         fun onChanged(scrollView: ObservableScrollView, x: Int, y: Int, oldl: Int, oldt: Int)
17 |     }
18 | 
19 |     private val scrollChangedListeners: MutableSet<OnScrollChangedListener> = mutableSetOf()
20 | 
21 |     override fun onScrollChanged(l: Int, t: Int, oldl: Int, oldt: Int) {
22 |         super.onScrollChanged(l, t, oldl, oldt)
23 | 
24 |         scrollChangedListeners.forEach {
25 |             it.onChanged(this, l, t, oldl, oldt)
26 |         }
27 |     }
28 | 
29 |     fun addOnScrollChangedListener(listener: OnScrollChangedListener) {
30 |         scrollChangedListeners.add(listener)
31 |     }
32 | }


--------------------------------------------------------------------------------
/design/src/main/java/com/github/kr328/clash/design/view/VerticalScrollableHost.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.design.view
 2 | 
 3 | import android.content.Context
 4 | import android.util.AttributeSet
 5 | import android.view.MotionEvent
 6 | import android.widget.FrameLayout
 7 | import kotlin.math.absoluteValue
 8 | import kotlin.math.tan
 9 | 
10 | class VerticalScrollableHost @JvmOverloads constructor(
11 |     context: Context,
12 |     attributeSet: AttributeSet? = null,
13 |     defStyleAttr: Int = 0,
14 |     defStyleRes: Int = 0
15 | ) : FrameLayout(context, attributeSet, defStyleAttr, defStyleRes) {
16 |     private var initialX = 0f
17 |     private var initialY = 0f
18 | 
19 |     private val degree = tan(Math.toRadians(15.0))
20 | 
21 |     override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
22 |         val parentView = parent ?: return super.onInterceptTouchEvent(ev)
23 | 
24 |         if (ev.action == MotionEvent.ACTION_DOWN) {
25 |             initialX = ev.x
26 |             initialY = ev.y
27 |             parentView.requestDisallowInterceptTouchEvent(true)
28 |         } else if (ev.action == MotionEvent.ACTION_MOVE) {
29 |             val dx = ev.x - initialX
30 |             val dy = ev.y - initialY
31 | 
32 |             val t = dy.absoluteValue / dx.absoluteValue
33 | 
34 |             if (t < degree) {
35 |                 parentView.requestDisallowInterceptTouchEvent(false)
36 |             }
37 |         }
38 | 
39 |         return super.onInterceptTouchEvent(ev)
40 |     }
41 | }


--------------------------------------------------------------------------------
/design/src/main/res/anim/rotate_infinite.xml:
--------------------------------------------------------------------------------
 1 | <?xml version="1.0" encoding="utf-8"?>
 2 | <set xmlns:android="http://schemas.android.com/apk/res/android">
 3 |     <rotate xmlns:android="http://schemas.android.com/apk/res/android"
 4 |         android:duration="1000"
 5 |         android:fromDegrees="360"
 6 |         android:pivotX="50%"
 7 |         android:pivotY="50%"
 8 |         android:toDegrees="0"
 9 |         android:repeatCount="infinite"
10 |         android:interpolator="@android:anim/cycle_interpolator" />
11 | </set>


--------------------------------------------------------------------------------
/design/src/main/res/drawable/bg_b.xml:
--------------------------------------------------------------------------------
 1 | <?xml version="1.0" encoding="utf-8"?>
 2 | <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
 3 |     <item>
 4 |         <shape android:shape="rectangle">
 5 |             <solid android:color="?attr/colorSurface" />
 6 |             <corners
 7 |                 android:radius="8dp"
 8 |                 />
 9 |         </shape>
10 |     </item>
11 | 
12 | </layer-list>


--------------------------------------------------------------------------------
/design/src/main/res/drawable/bg_bottom_sheet.xml:
--------------------------------------------------------------------------------
 1 | <?xml version="1.0" encoding="utf-8"?>
 2 | <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
 3 |     <item>
 4 |         <shape android:shape="rectangle">
 5 |             <solid android:color="?attr/colorSurface" />
 6 |             <corners
 7 |                 android:topLeftRadius="16dp"
 8 |                 android:topRightRadius="16dp" />
 9 |         </shape>
10 |     </item>
11 |     <item
12 |         android:gravity="top|center_horizontal"
13 |         android:top="@dimen/bottom_sheet_background_padding_top">
14 |         <shape android:shape="rectangle">
15 |             <size
16 |                 android:width="40dp"
17 |                 android:height="@dimen/bottom_sheet_header_height" />
18 |             <solid android:color="?attr/colorControlHighlight" />
19 |             <corners android:radius="5dp" />
20 |         </shape>
21 |     </item>
22 | </layer-list>


--------------------------------------------------------------------------------
/design/src/main/res/drawable/ic_baseline_adb.xml:
--------------------------------------------------------------------------------
 1 | <vector xmlns:android="http://schemas.android.com/apk/res/android"
 2 |     android:width="24dp"
 3 |     android:height="24dp"
 4 |     android:tint="?attr/colorControlNormal"
 5 |     android:viewportWidth="24"
 6 |     android:viewportHeight="24">
 7 |     <path
 8 |         android:fillColor="@android:color/white"
 9 |         android:pathData="M5,16c0,3.87 3.13,7 7,7s7,-3.13 7,-7v-4L5,12v4zM16.12,4.37l2.1,-2.1 -0.82,-0.83 -2.3,2.31C14.16,3.28 13.12,3 12,3s-2.16,0.28 -3.09,0.75L6.6,1.44l-0.82,0.83 2.1,2.1C6.14,5.64 5,7.68 5,10v1h14v-1c0,-2.32 -1.14,-4.36 -2.88,-5.63zM9,9c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1zM15,9c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1z" />
10 | </vector>
11 | 


--------------------------------------------------------------------------------
/design/src/main/res/drawable/ic_baseline_add.xml:
--------------------------------------------------------------------------------
 1 | <vector xmlns:android="http://schemas.android.com/apk/res/android"
 2 |     android:width="24dp"
 3 |     android:height="24dp"
 4 |     android:tint="?attr/colorControlNormal"
 5 |     android:viewportWidth="24"
 6 |     android:viewportHeight="24">
 7 |     <path
 8 |         android:fillColor="@android:color/white"
 9 |         android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z" />
10 | </vector>
11 | 


--------------------------------------------------------------------------------
/design/src/main/res/drawable/ic_baseline_apps.xml:
--------------------------------------------------------------------------------
 1 | <vector xmlns:android="http://schemas.android.com/apk/res/android"
 2 |     android:width="24dp"
 3 |     android:height="24dp"
 4 |     android:tint="?attr/colorControlNormal"
 5 |     android:viewportWidth="24"
 6 |     android:viewportHeight="24">
 7 |     <path
 8 |         android:fillColor="@android:color/white"
 9 |         android:pathData="M4,8h4L8,4L4,4v4zM10,20h4v-4h-4v4zM4,20h4v-4L4,16v4zM4,14h4v-4L4,10v4zM10,14h4v-4h-4v4zM16,4v4h4L20,4h-4zM10,8h4L14,4h-4v4zM16,14h4v-4h-4v4zM16,20h4v-4h-4v4z" />
10 | </vector>
11 | 


--------------------------------------------------------------------------------
/design/src/main/res/drawable/ic_baseline_arrow_back.xml:
--------------------------------------------------------------------------------
 1 | <vector xmlns:android="http://schemas.android.com/apk/res/android"
 2 |     android:width="24dp"
 3 |     android:height="24dp"
 4 |     android:tint="?attr/colorControlNormal"
 5 |     android:viewportWidth="24"
 6 |     android:viewportHeight="24">
 7 |     <path
 8 |         android:fillColor="@android:color/white"
 9 |         android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z" />
10 | </vector>
11 | 


--------------------------------------------------------------------------------
/design/src/main/res/drawable/ic_baseline_assignment.xml:
--------------------------------------------------------------------------------
 1 | <vector xmlns:android="http://schemas.android.com/apk/res/android"
 2 |     android:width="24dp"
 3 |     android:height="24dp"
 4 |     android:tint="?attr/colorControlNormal"
 5 |     android:viewportWidth="24"
 6 |     android:viewportHeight="24">
 7 |     <path
 8 |         android:fillColor="@android:color/white"
 9 |         android:pathData="M19,3h-4.18C14.4,1.84 13.3,1 12,1c-1.3,0 -2.4,0.84 -2.82,2L5,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2L21,5c0,-1.1 -0.9,-2 -2,-2zM12,3c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zM14,17L7,17v-2h7v2zM17,13L7,13v-2h10v2zM17,9L7,9L7,7h10v2z" />
10 | </vector>
11 | 


--------------------------------------------------------------------------------
/design/src/main/res/drawable/ic_baseline_attach_file.xml:
--------------------------------------------------------------------------------
 1 | <vector xmlns:android="http://schemas.android.com/apk/res/android"
 2 |     android:width="24dp"
 3 |     android:height="24dp"
 4 |     android:tint="?attr/colorControlNormal"
 5 |     android:viewportWidth="24"
 6 |     android:viewportHeight="24">
 7 |     <path
 8 |         android:fillColor="@android:color/white"
 9 |         android:pathData="M16.5,6v11.5c0,2.21 -1.79,4 -4,4s-4,-1.79 -4,-4V5c0,-1.38 1.12,-2.5 2.5,-2.5s2.5,1.12 2.5,2.5v10.5c0,0.55 -0.45,1 -1,1s-1,-0.45 -1,-1V6H10v9.5c0,1.38 1.12,2.5 2.5,2.5s2.5,-1.12 2.5,-2.5V5c0,-2.21 -1.79,-4 -4,-4S7,2.79 7,5v12.5c0,3.04 2.46,5.5 5.5,5.5s5.5,-2.46 5.5,-5.5V6h-1.5z" />
10 | </vector>
11 | 


--------------------------------------------------------------------------------
/design/src/main/res/drawable/ic_baseline_brightness_4.xml:
--------------------------------------------------------------------------------
 1 | <vector xmlns:android="http://schemas.android.com/apk/res/android"
 2 |     android:width="24dp"
 3 |     android:height="24dp"
 4 |     android:tint="?attr/colorControlNormal"
 5 |     android:viewportWidth="24"
 6 |     android:viewportHeight="24">
 7 |     <path
 8 |         android:fillColor="@android:color/white"
 9 |         android:pathData="M20,8.69V4h-4.69L12,0.69 8.69,4H4v4.69L0.69,12 4,15.31V20h4.69L12,23.31 15.31,20H20v-4.69L23.31,12 20,8.69zM12,18c-0.89,0 -1.74,-0.2 -2.5,-0.55C11.56,16.5 13,14.42 13,12s-1.44,-4.5 -3.5,-5.45C10.26,6.2 11.11,6 12,6c3.31,0 6,2.69 6,6s-2.69,6 -6,6z" />
10 | </vector>
11 | 


--------------------------------------------------------------------------------
/design/src/main/res/drawable/ic_baseline_clear_all.xml:
--------------------------------------------------------------------------------
 1 | <vector xmlns:android="http://schemas.android.com/apk/res/android"
 2 |     android:width="24dp"
 3 |     android:height="24dp"
 4 |     android:tint="?attr/colorControlNormal"
 5 |     android:viewportWidth="24"
 6 |     android:viewportHeight="24">
 7 |     <path
 8 |         android:fillColor="@android:color/white"
 9 |         android:pathData="M5,13h14v-2L5,11v2zM3,17h14v-2L3,15v2zM7,7v2h14L21,7L7,7z" />
10 | </vector>
11 | 


--------------------------------------------------------------------------------
/design/src/main/res/drawable/ic_baseline_close.xml:
--------------------------------------------------------------------------------
 1 | <vector xmlns:android="http://schemas.android.com/apk/res/android"
 2 |     android:width="24dp"
 3 |     android:height="24dp"
 4 |     android:tint="?attr/colorControlNormal"
 5 |     android:viewportWidth="24"
 6 |     android:viewportHeight="24">
 7 |     <path
 8 |         android:fillColor="@android:color/white"
 9 |         android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z" />
10 | </vector>
11 | 


--------------------------------------------------------------------------------
/design/src/main/res/drawable/ic_baseline_cloud_download.xml:
--------------------------------------------------------------------------------
 1 | <vector xmlns:android="http://schemas.android.com/apk/res/android"
 2 |     android:width="24dp"
 3 |     android:height="24dp"
 4 |     android:tint="?attr/colorControlNormal"
 5 |     android:viewportWidth="24"
 6 |     android:viewportHeight="24">
 7 |     <path
 8 |         android:fillColor="@android:color/white"
 9 |         android:pathData="M19.35,10.04C18.67,6.59 15.64,4 12,4 9.11,4 6.6,5.64 5.35,8.04 2.34,8.36 0,10.91 0,14c0,3.31 2.69,6 6,6h13c2.76,0 5,-2.24 5,-5 0,-2.64 -2.05,-4.78 -4.65,-4.96zM17,13l-5,5 -5,-5h3V9h4v4h3z" />
10 | </vector>
11 | 


--------------------------------------------------------------------------------
/design/src/main/res/drawable/ic_baseline_content_copy.xml:
--------------------------------------------------------------------------------
 1 | <vector xmlns:android="http://schemas.android.com/apk/res/android"
 2 |     android:width="24dp"
 3 |     android:height="24dp"
 4 |     android:tint="?attr/colorControlNormal"
 5 |     android:viewportWidth="24"
 6 |     android:viewportHeight="24">
 7 |     <path
 8 |         android:fillColor="@android:color/white"
 9 |         android:pathData="M16,1L4,1c-1.1,0 -2,0.9 -2,2v14h2L4,3h12L16,1zM19,5L8,5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h11c1.1,0 2,-0.9 2,-2L21,7c0,-1.1 -0.9,-2 -2,-2zM19,21L8,21L8,7h11v14z" />
10 | </vector>
11 | 


--------------------------------------------------------------------------------
/design/src/main/res/drawable/ic_baseline_delete.xml:
--------------------------------------------------------------------------------
 1 | <vector xmlns:android="http://schemas.android.com/apk/res/android"
 2 |     android:width="24dp"
 3 |     android:height="24dp"
 4 |     android:tint="?attr/colorControlNormal"
 5 |     android:viewportWidth="24"
 6 |     android:viewportHeight="24">
 7 |     <path
 8 |         android:fillColor="@android:color/white"
 9 |         android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z" />
10 | </vector>
11 | 


--------------------------------------------------------------------------------
/design/src/main/res/drawable/ic_baseline_dns.xml:
--------------------------------------------------------------------------------
 1 | <vector xmlns:android="http://schemas.android.com/apk/res/android"
 2 |     android:width="24dp"
 3 |     android:height="24dp"
 4 |     android:tint="?attr/colorControlNormal"
 5 |     android:viewportWidth="24"
 6 |     android:viewportHeight="24">
 7 |     <path
 8 |         android:fillColor="@android:color/white"
 9 |         android:pathData="M20,13H4c-0.55,0 -1,0.45 -1,1v6c0,0.55 0.45,1 1,1h16c0.55,0 1,-0.45 1,-1v-6c0,-0.55 -0.45,-1 -1,-1zM7,19c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2zM20,3H4c-0.55,0 -1,0.45 -1,1v6c0,0.55 0.45,1 1,1h16c0.55,0 1,-0.45 1,-1V4c0,-0.55 -0.45,-1 -1,-1zM7,9c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2z" />
10 | </vector>
11 | 


--------------------------------------------------------------------------------
/design/src/main/res/drawable/ic_baseline_domain.xml:
--------------------------------------------------------------------------------
 1 | <vector xmlns:android="http://schemas.android.com/apk/res/android"
 2 |     android:width="24dp"
 3 |     android:height="24dp"
 4 |     android:tint="?attr/colorControlNormal"
 5 |     android:viewportWidth="24"
 6 |     android:viewportHeight="24">
 7 |     <path
 8 |         android:fillColor="@android:color/white"
 9 |         android:pathData="M12,7V3H2v18h20V7H12zM6,19H4v-2h2V19zM6,15H4v-2h2V15zM6,11H4V9h2V11zM6,7H4V5h2V7zM10,19H8v-2h2V19zM10,15H8v-2h2V15zM10,11H8V9h2V11zM10,7H8V5h2V7zM20,19h-8v-2h2v-2h-2v-2h2v-2h-2V9h8V19zM18,11h-2v2h2V11zM18,15h-2v2h2V15z" />
10 | </vector>
11 | 


--------------------------------------------------------------------------------
/design/src/main/res/drawable/ic_baseline_edit.xml:
--------------------------------------------------------------------------------
 1 | <vector xmlns:android="http://schemas.android.com/apk/res/android"
 2 |     android:width="24dp"
 3 |     android:height="24dp"
 4 |     android:tint="?attr/colorControlNormal"
 5 |     android:viewportWidth="24"
 6 |     android:viewportHeight="24">
 7 |     <path
 8 |         android:fillColor="@android:color/white"
 9 |         android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z" />
10 | </vector>
11 | 


--------------------------------------------------------------------------------
/design/src/main/res/drawable/ic_baseline_extension.xml:
--------------------------------------------------------------------------------
 1 | <vector xmlns:android="http://schemas.android.com/apk/res/android"
 2 |     android:width="24dp"
 3 |     android:height="24dp"
 4 |     android:tint="?attr/colorControlNormal"
 5 |     android:viewportWidth="24"
 6 |     android:viewportHeight="24">
 7 |     <path
 8 |         android:fillColor="@android:color/white"
 9 |         android:pathData="M20.5,11H19V7c0,-1.1 -0.9,-2 -2,-2h-4V3.5C13,2.12 11.88,1 10.5,1S8,2.12 8,3.5V5H4c-1.1,0 -1.99,0.9 -1.99,2v3.8H3.5c1.49,0 2.7,1.21 2.7,2.7s-1.21,2.7 -2.7,2.7H2V20c0,1.1 0.9,2 2,2h3.8v-1.5c0,-1.49 1.21,-2.7 2.7,-2.7 1.49,0 2.7,1.21 2.7,2.7V22H17c1.1,0 2,-0.9 2,-2v-4h1.5c1.38,0 2.5,-1.12 2.5,-2.5S21.88,11 20.5,11z" />
10 | </vector>
11 | 


--------------------------------------------------------------------------------
/design/src/main/res/drawable/ic_baseline_flash_on.xml:
--------------------------------------------------------------------------------
 1 | <vector xmlns:android="http://schemas.android.com/apk/res/android"
 2 |     android:width="24dp"
 3 |     android:height="24dp"
 4 |     android:tint="?attr/colorControlNormal"
 5 |     android:viewportWidth="24"
 6 |     android:viewportHeight="24">
 7 |     <path
 8 |         android:fillColor="@android:color/white"
 9 |         android:pathData="M7,2v11h3v9l7,-12h-4l4,-8z" />
10 | </vector>
11 | 


--------------------------------------------------------------------------------
/design/src/main/res/drawable/ic_baseline_get_app.xml:
--------------------------------------------------------------------------------
 1 | <vector xmlns:android="http://schemas.android.com/apk/res/android"
 2 |     android:width="24dp"
 3 |     android:height="24dp"
 4 |     android:viewportWidth="24"
 5 |     android:viewportHeight="24"
 6 |     android:tint="?attr/colorControlNormal">
 7 |   <path
 8 |       android:fillColor="@android:color/white"
 9 |       android:pathData="M19,9h-4V3H9v6H5l7,7 7,-7zM5,18v2h14v-2H5z"/>
10 | </vector>
11 | 


--------------------------------------------------------------------------------
/design/src/main/res/drawable/ic_baseline_help_center.xml:
--------------------------------------------------------------------------------
 1 | <vector xmlns:android="http://schemas.android.com/apk/res/android"
 2 |     android:width="24dp"
 3 |     android:height="24dp"
 4 |     android:tint="?attr/colorControlNormal"
 5 |     android:viewportWidth="24"
 6 |     android:viewportHeight="24">
 7 |     <path
 8 |         android:fillColor="@android:color/white"
 9 |         android:pathData="M19,3H5C3.9,3 3,3.9 3,5v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5C21,3.9 20.1,3 19,3zM12.01,18c-0.7,0 -1.26,-0.56 -1.26,-1.26c0,-0.71 0.56,-1.25 1.26,-1.25c0.71,0 1.25,0.54 1.25,1.25C13.25,17.43 12.72,18 12.01,18zM15.02,10.6c-0.76,1.11 -1.48,1.46 -1.87,2.17c-0.16,0.29 -0.22,0.48 -0.22,1.41h-1.82c0,-0.49 -0.08,-1.29 0.31,-1.98c0.49,-0.87 1.42,-1.39 1.96,-2.16c0.57,-0.81 0.25,-2.33 -1.37,-2.33c-1.06,0 -1.58,0.8 -1.8,1.48L8.56,8.49C9.01,7.15 10.22,6 11.99,6c1.48,0 2.49,0.67 3.01,1.52C15.44,8.24 15.7,9.59 15.02,10.6z" />
10 | </vector>
11 | 


--------------------------------------------------------------------------------
/design/src/main/res/drawable/ic_baseline_hide.xml:
--------------------------------------------------------------------------------
 1 | <vector xmlns:android="http://schemas.android.com/apk/res/android"
 2 |     android:width="24dp"
 3 |     android:height="24dp"
 4 |     android:tint="?attr/colorControlNormal"
 5 |     android:viewportWidth="1024"
 6 |     android:viewportHeight="1024">
 7 | 
 8 |     <path
 9 |         android:fillColor="@android:color/white"
10 |         android:pathData="M825.9,134.2l51.7,51.7 -655.1,655.1 -51.7,-51.7 655.1,-655.1zM804.4,325.8c41.3,39.5 81.3,87.9 120,145.3a73.1,73.1 0,0 1,2.8 77.4l-2.8,4.4 -6.9,10.1C795.2,740.3 660,829 512,829c-58.4,0 -114.9,-13.8 -169.3,-41.4l55.1,-55.1c37.4,15.7 75.5,23.4 114.2,23.4 120.9,0 235.5,-75.1 345.1,-234l6.7,-9.8 -6.7,-9.8c-34.3,-49.7 -69,-91.2 -104.4,-124.7l51.7,-51.7zM512,195c51.4,0 101.3,10.7 149.7,32.1l-56.5,56.5A289.4,289.4 0,0 0,512 268.2c-120.9,0 -235.5,75.1 -345.1,234L160.2,512l6.7,9.8c29.5,42.8 59.4,79.5 89.7,110.3l-51.7,51.7c-36.1,-36.7 -71.3,-80.3 -105.4,-130.9a73.1,73.1 0,0 1,-2.8 -77.4l2.8,-4.4 6.9,-10.1C228.8,283.7 364,195 512,195zM664.8,465.4a161.7,161.7 0,0 1,-205.8 205.8l65.1,-65.1a88.6,88.6 0,0 0,75.6 -75.6l65.1,-65.1zM512,356.7c6.4,0 12.8,0.4 19,1.1l-179.5,179.6A161.7,161.7 0,0 1,512 356.7z" />
11 | 
12 | </vector>
13 | 


--------------------------------------------------------------------------------
/design/src/main/res/drawable/ic_baseline_info.xml:
--------------------------------------------------------------------------------
 1 | <vector xmlns:android="http://schemas.android.com/apk/res/android"
 2 |     android:width="24dp"
 3 |     android:height="24dp"
 4 |     android:tint="?attr/colorControlNormal"
 5 |     android:viewportWidth="24"
 6 |     android:viewportHeight="24">
 7 |     <path
 8 |         android:fillColor="@android:color/white"
 9 |         android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,17h-2v-6h2v6zM13,9h-2L11,7h2v2z" />
10 | </vector>
11 | 


--------------------------------------------------------------------------------
/design/src/main/res/drawable/ic_baseline_more_vert.xml:
--------------------------------------------------------------------------------
 1 | <vector xmlns:android="http://schemas.android.com/apk/res/android"
 2 |     android:width="24dp"
 3 |     android:height="24dp"
 4 |     android:tint="?attr/colorControlNormal"
 5 |     android:viewportWidth="24"
 6 |     android:viewportHeight="24">
 7 |     <path
 8 |         android:fillColor="@android:color/white"
 9 |         android:pathData="M12,8c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM12,16c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z" />
10 | </vector>
11 | 


--------------------------------------------------------------------------------
/design/src/main/res/drawable/ic_baseline_publish.xml:
--------------------------------------------------------------------------------
 1 | <vector xmlns:android="http://schemas.android.com/apk/res/android"
 2 |     android:width="24dp"
 3 |     android:height="24dp"
 4 |     android:tint="?attr/colorControlNormal"
 5 |     android:viewportWidth="24"
 6 |     android:viewportHeight="24">
 7 |     <path
 8 |         android:fillColor="@android:color/white"
 9 |         android:pathData="M5,4v2h14L19,4L5,4zM5,14h4v6h6v-6h4l-7,-7 -7,7z" />
10 | </vector>
11 | 


--------------------------------------------------------------------------------
/design/src/main/res/drawable/ic_baseline_replay.xml:
--------------------------------------------------------------------------------
 1 | <vector xmlns:android="http://schemas.android.com/apk/res/android"
 2 |     android:width="24dp"
 3 |     android:height="24dp"
 4 |     android:tint="?attr/colorControlNormal"
 5 |     android:viewportWidth="24"
 6 |     android:viewportHeight="24">
 7 |     <path
 8 |         android:fillColor="@android:color/white"
 9 |         android:pathData="M12,5V1L7,6l5,5V7c3.31,0 6,2.69 6,6s-2.69,6 -6,6 -6,-2.69 -6,-6H4c0,4.42 3.58,8 8,8s8,-3.58 8,-8 -3.58,-8 -8,-8z" />
10 | </vector>
11 | 


--------------------------------------------------------------------------------
/design/src/main/res/drawable/ic_baseline_restore.xml:
--------------------------------------------------------------------------------
 1 | <vector xmlns:android="http://schemas.android.com/apk/res/android"
 2 |     android:width="24dp"
 3 |     android:height="24dp"
 4 |     android:tint="?attr/colorControlNormal"
 5 |     android:viewportWidth="24"
 6 |     android:viewportHeight="24">
 7 |     <path
 8 |         android:fillColor="@android:color/white"
 9 |         android:pathData="M13,3c-4.97,0 -9,4.03 -9,9L1,12l3.89,3.89 0.07,0.14L9,12L6,12c0,-3.87 3.13,-7 7,-7s7,3.13 7,7 -3.13,7 -7,7c-1.93,0 -3.68,-0.79 -4.94,-2.06l-1.42,1.42C8.27,19.99 10.51,21 13,21c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zM12,8v5l4.28,2.54 0.72,-1.21 -3.5,-2.08L13.5,8L12,8z" />
10 | </vector>
11 | 


--------------------------------------------------------------------------------
/design/src/main/res/drawable/ic_baseline_save.xml:
--------------------------------------------------------------------------------
 1 | <vector xmlns:android="http://schemas.android.com/apk/res/android"
 2 |     android:width="24dp"
 3 |     android:height="24dp"
 4 |     android:tint="?attr/colorControlNormal"
 5 |     android:viewportWidth="24"
 6 |     android:viewportHeight="24">
 7 |     <path
 8 |         android:fillColor="@android:color/white"
 9 |         android:pathData="M17,3L5,3c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2L21,7l-4,-4zM12,19c-1.66,0 -3,-1.34 -3,-3s1.34,-3 3,-3 3,1.34 3,3 -1.34,3 -3,3zM15,9L5,9L5,5h10v4z" />
10 | </vector>
11 | 


--------------------------------------------------------------------------------
/design/src/main/res/drawable/ic_baseline_search.xml:
--------------------------------------------------------------------------------
 1 | <vector xmlns:android="http://schemas.android.com/apk/res/android"
 2 |     android:width="24dp"
 3 |     android:height="24dp"
 4 |     android:tint="?attr/colorControlNormal"
 5 |     android:viewportWidth="24"
 6 |     android:viewportHeight="24">
 7 |     <path
 8 |         android:fillColor="@android:color/white"
 9 |         android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z" />
10 | </vector>
11 | 


--------------------------------------------------------------------------------
/design/src/main/res/drawable/ic_baseline_settings.xml:
--------------------------------------------------------------------------------
 1 | <vector xmlns:android="http://schemas.android.com/apk/res/android"
 2 |     android:width="24dp"
 3 |     android:height="24dp"
 4 |     android:tint="?attr/colorControlNormal"
 5 |     android:viewportWidth="24"
 6 |     android:viewportHeight="24">
 7 |     <path
 8 |         android:fillColor="@android:color/white"
 9 |         android:pathData="M19.14,12.94c0.04,-0.3 0.06,-0.61 0.06,-0.94c0,-0.32 -0.02,-0.64 -0.07,-0.94l2.03,-1.58c0.18,-0.14 0.23,-0.41 0.12,-0.61l-1.92,-3.32c-0.12,-0.22 -0.37,-0.29 -0.59,-0.22l-2.39,0.96c-0.5,-0.38 -1.03,-0.7 -1.62,-0.94L14.4,2.81c-0.04,-0.24 -0.24,-0.41 -0.48,-0.41h-3.84c-0.24,0 -0.43,0.17 -0.47,0.41L9.25,5.35C8.66,5.59 8.12,5.92 7.63,6.29L5.24,5.33c-0.22,-0.08 -0.47,0 -0.59,0.22L2.74,8.87C2.62,9.08 2.66,9.34 2.86,9.48l2.03,1.58C4.84,11.36 4.8,11.69 4.8,12s0.02,0.64 0.07,0.94l-2.03,1.58c-0.18,0.14 -0.23,0.41 -0.12,0.61l1.92,3.32c0.12,0.22 0.37,0.29 0.59,0.22l2.39,-0.96c0.5,0.38 1.03,0.7 1.62,0.94l0.36,2.54c0.05,0.24 0.24,0.41 0.48,0.41h3.84c0.24,0 0.44,-0.17 0.47,-0.41l0.36,-2.54c0.59,-0.24 1.13,-0.56 1.62,-0.94l2.39,0.96c0.22,0.08 0.47,0 0.59,-0.22l1.92,-3.32c0.12,-0.22 0.07,-0.47 -0.12,-0.61L19.14,12.94zM12,15.6c-1.98,0 -3.6,-1.62 -3.6,-3.6s1.62,-3.6 3.6,-3.6s3.6,1.62 3.6,3.6S13.98,15.6 12,15.6z" />
10 | </vector>
11 | 


--------------------------------------------------------------------------------
/design/src/main/res/drawable/ic_baseline_stop.xml:
--------------------------------------------------------------------------------
 1 | <vector xmlns:android="http://schemas.android.com/apk/res/android"
 2 |     android:width="24dp"
 3 |     android:height="24dp"
 4 |     android:tint="?attr/colorControlNormal"
 5 |     android:viewportWidth="24"
 6 |     android:viewportHeight="24">
 7 |     <path
 8 |         android:fillColor="@android:color/white"
 9 |         android:pathData="M6,6h12v12H6z" />
10 | </vector>
11 | 


--------------------------------------------------------------------------------
/design/src/main/res/drawable/ic_baseline_swap_vert.xml:
--------------------------------------------------------------------------------
 1 | <vector xmlns:android="http://schemas.android.com/apk/res/android"
 2 |     android:width="24dp"
 3 |     android:height="24dp"
 4 |     android:tint="?attr/colorControlNormal"
 5 |     android:viewportWidth="24"
 6 |     android:viewportHeight="24">
 7 |     <path
 8 |         android:fillColor="@android:color/white"
 9 |         android:pathData="M16,17.01V10h-2v7.01h-3L15,21l4,-3.99h-3zM9,3L5,6.99h3V14h2V6.99h3L9,3z" />
10 | </vector>
11 | 


--------------------------------------------------------------------------------
/design/src/main/res/drawable/ic_baseline_swap_vertical_circle.xml:
--------------------------------------------------------------------------------
 1 | <vector xmlns:android="http://schemas.android.com/apk/res/android"
 2 |     android:width="24dp"
 3 |     android:height="24dp"
 4 |     android:tint="?attr/colorControlNormal"
 5 |     android:viewportWidth="24"
 6 |     android:viewportHeight="24">
 7 |     <path
 8 |         android:fillColor="@android:color/white"
 9 |         android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM6.5,9L10,5.5 13.5,9L11,9v4L9,13L9,9L6.5,9zM17.5,15L14,18.5 10.5,15L13,15v-4h2v4h2.5z" />
10 | </vector>
11 | 


--------------------------------------------------------------------------------
/design/src/main/res/drawable/ic_baseline_sync.xml:
--------------------------------------------------------------------------------
 1 | <vector xmlns:android="http://schemas.android.com/apk/res/android"
 2 |     android:width="24dp"
 3 |     android:height="24dp"
 4 |     android:tint="?attr/colorControlNormal"
 5 |     android:viewportWidth="24"
 6 |     android:viewportHeight="24">
 7 |     <path
 8 |         android:fillColor="@android:color/white"
 9 |         android:pathData="M12,4L12,1L8,5l4,4L12,6c3.31,0 6,2.69 6,6 0,1.01 -0.25,1.97 -0.7,2.8l1.46,1.46C19.54,15.03 20,13.57 20,12c0,-4.42 -3.58,-8 -8,-8zM12,18c-3.31,0 -6,-2.69 -6,-6 0,-1.01 0.25,-1.97 0.7,-2.8L5.24,7.74C4.46,8.97 4,10.43 4,12c0,4.42 3.58,8 8,8v3l4,-4 -4,-4v3z" />
10 | </vector>
11 | 


--------------------------------------------------------------------------------
/design/src/main/res/drawable/ic_baseline_update.xml:
--------------------------------------------------------------------------------
 1 | <vector xmlns:android="http://schemas.android.com/apk/res/android"
 2 |     android:width="24dp"
 3 |     android:height="24dp"
 4 |     android:tint="?attr/colorControlNormal"
 5 |     android:viewportWidth="24"
 6 |     android:viewportHeight="24">
 7 |     <path
 8 |         android:fillColor="@android:color/white"
 9 |         android:pathData="M21,10.12h-6.78l2.74,-2.82c-2.73,-2.7 -7.15,-2.8 -9.88,-0.1c-2.73,2.71 -2.73,7.08 0,9.79s7.15,2.71 9.88,0C18.32,15.65 19,14.08 19,12.1h2c0,1.98 -0.88,4.55 -2.64,6.29c-3.51,3.48 -9.21,3.48 -12.72,0c-3.5,-3.47 -3.53,-9.11 -0.02,-12.58s9.14,-3.47 12.65,0L21,3V10.12zM12.5,8v4.25l3.5,2.08l-0.72,1.21L11,13V8H12.5z" />
10 | </vector>
11 | 


--------------------------------------------------------------------------------
/design/src/main/res/drawable/ic_baseline_view_list.xml:
--------------------------------------------------------------------------------
 1 | <vector xmlns:android="http://schemas.android.com/apk/res/android"
 2 |     android:width="24dp"
 3 |     android:height="24dp"
 4 |     android:tint="?attr/colorControlNormal"
 5 |     android:viewportWidth="24"
 6 |     android:viewportHeight="24">
 7 |     <path
 8 |         android:fillColor="@android:color/white"
 9 |         android:pathData="M4,14h4v-4L4,10v4zM4,19h4v-4L4,15v4zM4,9h4L8,5L4,5v4zM9,14h12v-4L9,10v4zM9,19h12v-4L9,15v4zM9,5v4h12L21,5L9,5z" />
10 | </vector>
11 | 


--------------------------------------------------------------------------------
/design/src/main/res/drawable/ic_baseline_vpn_lock.xml:
--------------------------------------------------------------------------------
 1 | <vector xmlns:android="http://schemas.android.com/apk/res/android"
 2 |     android:width="24dp"
 3 |     android:height="24dp"
 4 |     android:tint="?attr/colorControlNormal"
 5 |     android:viewportWidth="24"
 6 |     android:viewportHeight="24">
 7 |     <path
 8 |         android:fillColor="@android:color/white"
 9 |         android:pathData="M22,4v-0.5C22,2.12 20.88,1 19.5,1S17,2.12 17,3.5L17,4c-0.55,0 -1,0.45 -1,1v4c0,0.55 0.45,1 1,1h5c0.55,0 1,-0.45 1,-1L23,5c0,-0.55 -0.45,-1 -1,-1zM21.2,4h-3.4v-0.5c0,-0.94 0.76,-1.7 1.7,-1.7s1.7,0.76 1.7,1.7L21.2,4zM18.92,12c0.04,0.33 0.08,0.66 0.08,1 0,2.08 -0.8,3.97 -2.1,5.39 -0.26,-0.81 -1,-1.39 -1.9,-1.39h-1v-3c0,-0.55 -0.45,-1 -1,-1L7,13v-2h2c0.55,0 1,-0.45 1,-1L10,8h2c1.1,0 2,-0.9 2,-2L14,3.46c-0.95,-0.3 -1.95,-0.46 -3,-0.46C5.48,3 1,7.48 1,13s4.48,10 10,10 10,-4.48 10,-10c0,-0.34 -0.02,-0.67 -0.05,-1h-2.03zM10,20.93c-3.95,-0.49 -7,-3.85 -7,-7.93 0,-0.62 0.08,-1.21 0.21,-1.79L8,16v1c0,1.1 0.9,2 2,2v1.93z" />
10 | </vector>
11 | 


--------------------------------------------------------------------------------
/design/src/main/res/drawable/ic_baseline_work.xml:
--------------------------------------------------------------------------------
 1 | <vector xmlns:android="http://schemas.android.com/apk/res/android"
 2 |     android:width="24dp"
 3 |     android:height="24dp"
 4 |     android:tint="?attr/colorControlNormal"
 5 |     android:viewportWidth="24"
 6 |     android:viewportHeight="24">
 7 |     <path
 8 |         android:fillColor="@android:color/white"
 9 |         android:pathData="M20,6h-4L16,4c0,-1.11 -0.89,-2 -2,-2h-4c-1.11,0 -2,0.89 -2,2v2L4,6c-1.11,0 -1.99,0.89 -1.99,2L2,19c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2L22,8c0,-1.11 -0.89,-2 -2,-2zM14,6h-4L10,4h4v2z" />
10 | </vector>
11 | 


--------------------------------------------------------------------------------
/design/src/main/res/drawable/ic_outline_article.xml:
--------------------------------------------------------------------------------
 1 | <vector xmlns:android="http://schemas.android.com/apk/res/android"
 2 |     android:width="24dp"
 3 |     android:height="24dp"
 4 |     android:tint="?attr/colorControlNormal"
 5 |     android:viewportWidth="24"
 6 |     android:viewportHeight="24">
 7 |     <path
 8 |         android:fillColor="@android:color/white"
 9 |         android:pathData="M19,5v14H5V5H19M19,3H5C3.9,3 3,3.9 3,5v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5C21,3.9 20.1,3 19,3L19,3z" />
10 |     <path
11 |         android:fillColor="@android:color/white"
12 |         android:pathData="M14,17H7v-2h7V17zM17,13H7v-2h10V13zM17,9H7V7h10V9z" />
13 | </vector>
14 | 


--------------------------------------------------------------------------------
/design/src/main/res/drawable/ic_outline_check_circle.xml:
--------------------------------------------------------------------------------
 1 | <vector xmlns:android="http://schemas.android.com/apk/res/android"
 2 |     android:width="24dp"
 3 |     android:height="24dp"
 4 |     android:tint="?attr/colorControlNormal"
 5 |     android:viewportWidth="24"
 6 |     android:viewportHeight="24">
 7 |     <path
 8 |         android:fillColor="@android:color/white"
 9 |         android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM16.59,7.58L10,14.17l-2.59,-2.58L6,13l4,4 8,-8z" />
10 | </vector>
11 | 


--------------------------------------------------------------------------------
/design/src/main/res/drawable/ic_outline_delete.xml:
--------------------------------------------------------------------------------
 1 | <vector xmlns:android="http://schemas.android.com/apk/res/android"
 2 |     android:width="24dp"
 3 |     android:height="24dp"
 4 |     android:tint="?attr/colorControlNormal"
 5 |     android:viewportWidth="24"
 6 |     android:viewportHeight="24">
 7 |     <path
 8 |         android:fillColor="@android:color/white"
 9 |         android:pathData="M16,9v10H8V9h8m-1.5,-6h-5l-1,1H5v2h14V4h-3.5l-1,-1zM18,7H6v12c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7z" />
10 | </vector>
11 | 


--------------------------------------------------------------------------------
/design/src/main/res/drawable/ic_outline_folder.xml:
--------------------------------------------------------------------------------
 1 | <vector xmlns:android="http://schemas.android.com/apk/res/android"
 2 |     android:width="24dp"
 3 |     android:height="24dp"
 4 |     android:tint="?attr/colorControlNormal"
 5 |     android:viewportWidth="24"
 6 |     android:viewportHeight="24">
 7 |     <path
 8 |         android:fillColor="@android:color/white"
 9 |         android:pathData="M9.17,6l2,2H20v10H4V6h5.17M10,4H4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V8c0,-1.1 -0.9,-2 -2,-2h-8l-2,-2z" />
10 | </vector>
11 | 


--------------------------------------------------------------------------------
/design/src/main/res/drawable/ic_outline_inbox.xml:
--------------------------------------------------------------------------------
 1 | <vector xmlns:android="http://schemas.android.com/apk/res/android"
 2 |     android:width="24dp"
 3 |     android:height="24dp"
 4 |     android:tint="?attr/colorControlNormal"
 5 |     android:viewportWidth="24"
 6 |     android:viewportHeight="24">
 7 |     <path
 8 |         android:fillColor="@android:color/white"
 9 |         android:pathData="M19,3L5,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2L21,5c0,-1.1 -0.9,-2 -2,-2zM19,19L5,19v-3h3.56c0.69,1.19 1.97,2 3.45,2s2.75,-0.81 3.45,-2L19,16v3zM19,14h-4.99c0,1.1 -0.9,2 -2,2s-2,-0.9 -2,-2L5,14L5,5h14v9z" />
10 | </vector>
11 | 


--------------------------------------------------------------------------------
/design/src/main/res/drawable/ic_outline_info.xml:
--------------------------------------------------------------------------------
 1 | <vector xmlns:android="http://schemas.android.com/apk/res/android"
 2 |     android:width="24dp"
 3 |     android:height="24dp"
 4 |     android:tint="?attr/colorControlNormal"
 5 |     android:viewportWidth="24"
 6 |     android:viewportHeight="24">
 7 |     <path
 8 |         android:fillColor="@android:color/white"
 9 |         android:pathData="M11,7h2v2h-2zM11,11h2v6h-2zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8z" />
10 | </vector>
11 | 


--------------------------------------------------------------------------------
/design/src/main/res/drawable/ic_outline_label.xml:
--------------------------------------------------------------------------------
 1 | <vector xmlns:android="http://schemas.android.com/apk/res/android"
 2 |     android:width="24dp"
 3 |     android:height="24dp"
 4 |     android:tint="?attr/colorControlNormal"
 5 |     android:viewportWidth="24"
 6 |     android:viewportHeight="24">
 7 |     <path
 8 |         android:fillColor="@android:color/white"
 9 |         android:pathData="M17.63,5.84C17.27,5.33 16.67,5 16,5L5,5.01C3.9,5.01 3,5.9 3,7v10c0,1.1 0.9,1.99 2,1.99L16,19c0.67,0 1.27,-0.33 1.63,-0.84L22,12l-4.37,-6.16zM16,17H5V7h11l3.55,5L16,17z" />
10 | </vector>
11 | 


--------------------------------------------------------------------------------
/design/src/main/res/drawable/ic_outline_not_interested.xml:
--------------------------------------------------------------------------------
 1 | <vector xmlns:android="http://schemas.android.com/apk/res/android"
 2 |     android:width="24dp"
 3 |     android:height="24dp"
 4 |     android:tint="?attr/colorControlNormal"
 5 |     android:viewportWidth="24"
 6 |     android:viewportHeight="24">
 7 |     <path
 8 |         android:fillColor="@android:color/white"
 9 |         android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.42,0 -8,-3.58 -8,-8 0,-1.85 0.63,-3.55 1.69,-4.9L16.9,18.31C15.55,19.37 13.85,20 12,20zM18.31,16.9L7.1,5.69C8.45,4.63 10.15,4 12,4c4.42,0 8,3.58 8,8 0,1.85 -0.63,3.55 -1.69,4.9z" />
10 | </vector>
11 | 


--------------------------------------------------------------------------------
/design/src/main/res/drawable/ic_outline_update.xml:
--------------------------------------------------------------------------------
 1 | <vector xmlns:android="http://schemas.android.com/apk/res/android"
 2 |     android:width="24dp"
 3 |     android:height="24dp"
 4 |     android:tint="?attr/colorControlNormal"
 5 |     android:viewportWidth="24"
 6 |     android:viewportHeight="24">
 7 |     <path
 8 |         android:fillColor="@android:color/white"
 9 |         android:pathData="M11,8v5l4.25,2.52l0.77,-1.28l-3.52,-2.09V8H11zM21,10V3l-2.64,2.64C16.74,4.01 14.49,3 12,3c-4.97,0 -9,4.03 -9,9s4.03,9 9,9s9,-4.03 9,-9h-2c0,3.86 -3.14,7 -7,7s-7,-3.14 -7,-7s3.14,-7 7,-7c1.93,0 3.68,0.79 4.95,2.05L14,10H21z" />
10 | </vector>
11 | 


--------------------------------------------------------------------------------
/design/src/main/res/layout/adapter_editable_text_list.xml:
--------------------------------------------------------------------------------
 1 | <?xml version="1.0" encoding="utf-8"?>
 2 | <layout xmlns:android="http://schemas.android.com/apk/res/android">
 3 |     <RelativeLayout
 4 |         android:layout_width="match_parent"
 5 |         android:layout_height="wrap_content"
 6 |         android:paddingVertical="@dimen/item_padding_vertical"
 7 |         android:paddingStart="@dimen/item_header_margin"
 8 |         android:paddingEnd="@dimen/item_tailing_margin">
 9 | 
10 |         <TextView
11 |             android:id="@+id/text_view"
12 |             android:layout_width="wrap_content"
13 |             android:layout_height="wrap_content"
14 |             android:layout_alignParentStart="true"
15 |             android:layout_centerVertical="true"
16 |             android:layout_toStartOf="@id/delete_view"
17 |             android:textAppearance="@style/TextAppearance.MaterialComponents.Body1" />
18 | 
19 |         <ImageView
20 |             android:id="@+id/delete_view"
21 |             android:layout_width="@dimen/item_tailing_component_size"
22 |             android:layout_height="@dimen/item_tailing_component_size"
23 |             android:layout_alignParentEnd="true"
24 |             android:layout_centerVertical="true"
25 |             android:background="?attr/selectableItemBackgroundBorderless"
26 |             android:clickable="true"
27 |             android:contentDescription="@string/delete"
28 |             android:focusable="true"
29 |             android:padding="@dimen/toolbar_image_action_padding"
30 |             android:src="@drawable/ic_baseline_close" />
31 |     </RelativeLayout>
32 | </layout>


--------------------------------------------------------------------------------
/design/src/main/res/layout/common_activity_bar.xml:
--------------------------------------------------------------------------------
 1 | <?xml version="1.0" encoding="utf-8"?>
 2 | <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3 |     android:layout_width="wrap_content"
 4 |     android:layout_height="wrap_content"
 5 |     android:gravity="center_vertical"
 6 |     android:minHeight="@dimen/toolbar_height"
 7 |     android:orientation="horizontal">
 8 | 
 9 |     <ImageView
10 |         android:id="@+id/activity_bar_close_view"
11 |         android:layout_width="@dimen/item_header_component_size"
12 |         android:layout_height="@dimen/item_header_component_size"
13 |         android:layout_marginHorizontal="@dimen/item_header_margin"
14 |         android:background="?attr/selectableItemBackgroundBorderless"
15 |         android:clickable="true"
16 |         android:contentDescription="@string/close"
17 |         android:focusable="true"
18 |         android:paddingHorizontal="@dimen/toolbar_image_action_padding"
19 |         android:src="@drawable/ic_baseline_arrow_back" />
20 | 
21 |     <TextView
22 |         android:id="@+id/activity_bar_title_view"
23 |         android:layout_width="wrap_content"
24 |         android:layout_height="wrap_content"
25 |         android:ellipsize="end"
26 |         android:maxLines="1"
27 |         android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6" />
28 | </LinearLayout>


--------------------------------------------------------------------------------
/design/src/main/res/layout/common_recycler_list.xml:
--------------------------------------------------------------------------------
 1 | <?xml version="1.0" encoding="utf-8"?>
 2 | <layout xmlns:android="http://schemas.android.com/apk/res/android">
 3 |     <data>
 4 |         <variable
 5 |             name="insets"
 6 |             type="com.github.kr328.clash.design.ui.Insets" />
 7 |     </data>
 8 | 
 9 |     <com.github.kr328.clash.design.view.AppRecyclerView
10 |         android:id="@+id/recycler_list"
11 |         android:layout_width="match_parent"
12 |         android:layout_height="match_parent"
13 |         android:clipToPadding="false"
14 |         android:paddingTop="@{(float) insets.top + @dimen/toolbar_height}"
15 |         android:paddingBottom="@{insets.bottom}" />
16 | </layout>


--------------------------------------------------------------------------------
/design/src/main/res/layout/design_new_profile.xml:
--------------------------------------------------------------------------------
 1 | <?xml version="1.0" encoding="utf-8"?>
 2 | <layout xmlns:android="http://schemas.android.com/apk/res/android"
 3 |     xmlns:app="http://schemas.android.com/apk/res-auto">
 4 |     <data>
 5 |         <variable
 6 |             name="self"
 7 |             type="com.github.kr328.clash.design.NewProfileDesign" />
 8 |     </data>
 9 | 
10 |     <androidx.coordinatorlayout.widget.CoordinatorLayout
11 |         android:layout_width="match_parent"
12 |         android:layout_height="match_parent"
13 |         android:paddingStart="@{self.surface.insets.start}"
14 |         android:paddingEnd="@{self.surface.insets.end}">
15 | 
16 |         <include
17 |             android:id="@+id/main_list"
18 |             layout="@layout/common_recycler_list"
19 |             app:insets="@{self.surface.insets}" />
20 | 
21 |         <com.github.kr328.clash.design.view.ActivityBarLayout
22 |             android:id="@+id/activity_bar_layout"
23 |             android:layout_width="match_parent"
24 |             android:layout_height="wrap_content"
25 |             android:paddingTop="@{self.surface.insets.top}"
26 |             android:paddingEnd="@dimen/item_tailing_margin">
27 | 
28 |             <include layout="@layout/common_activity_bar" />
29 |         </com.github.kr328.clash.design.view.ActivityBarLayout>
30 |     </androidx.coordinatorlayout.widget.CoordinatorLayout>
31 | </layout>


--------------------------------------------------------------------------------
/design/src/main/res/layout/dialog_fetch_status.xml:
--------------------------------------------------------------------------------
 1 | <?xml version="1.0" encoding="utf-8"?>
 2 | <layout xmlns:android="http://schemas.android.com/apk/res/android">
 3 |     <LinearLayout
 4 |         android:layout_width="match_parent"
 5 |         android:layout_height="match_parent"
 6 |         android:gravity="center_vertical"
 7 |         android:minHeight="@dimen/item_min_height"
 8 |         android:orientation="vertical"
 9 |         android:padding="@dimen/dialog_padding">
10 | 
11 |         <com.google.android.material.progressindicator.LinearProgressIndicator
12 |             android:id="@+id/progress_indicator"
13 |             android:layout_width="match_parent"
14 |             android:layout_height="wrap_content" />
15 | 
16 |         <TextView
17 |             android:id="@+id/text"
18 |             android:layout_width="match_parent"
19 |             android:layout_height="wrap_content"
20 |             android:layout_marginTop="@dimen/item_text_margin" />
21 |     </LinearLayout>
22 | </layout>


--------------------------------------------------------------------------------
/design/src/main/res/layout/dialog_text_field.xml:
--------------------------------------------------------------------------------
 1 | <?xml version="1.0" encoding="utf-8"?>
 2 | <layout xmlns:android="http://schemas.android.com/apk/res/android"
 3 |     xmlns:app="http://schemas.android.com/apk/res-auto">
 4 |     <LinearLayout
 5 |         android:layout_width="match_parent"
 6 |         android:layout_height="wrap_content"
 7 |         android:paddingHorizontal="@dimen/dialog_padding"
 8 |         android:paddingTop="@dimen/dialog_padding"
 9 |         android:paddingBottom="@dimen/dialog_button_margin">
10 | 
11 |         <com.google.android.material.textfield.TextInputLayout
12 |             android:id="@+id/text_layout"
13 |             style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
14 |             android:layout_width="match_parent"
15 |             android:layout_height="wrap_content"
16 |             app:errorEnabled="true">
17 | 
18 |             <com.google.android.material.textfield.TextInputEditText
19 |                 android:id="@+id/text_field"
20 |                 android:layout_width="match_parent"
21 |                 android:layout_height="wrap_content" />
22 |         </com.google.android.material.textfield.TextInputLayout>
23 |     </LinearLayout>
24 | </layout>


--------------------------------------------------------------------------------
/design/src/main/res/layout/preference_category.xml:
--------------------------------------------------------------------------------
 1 | <?xml version="1.0" encoding="utf-8"?>
 2 | <layout xmlns:android="http://schemas.android.com/apk/res/android">
 3 |     <TextView
 4 |         android:id="@+id/text_view"
 5 |         android:layout_width="match_parent"
 6 |         android:layout_height="wrap_content"
 7 |         android:paddingVertical="@dimen/item_padding_vertical"
 8 |         android:paddingStart="@{@dimen/item_header_component_size + @dimen/item_header_margin * 2}"
 9 |         android:paddingEnd="@dimen/item_tailing_margin"
10 |         android:textColor="?attr/colorControlActivated" />
11 | </layout>


--------------------------------------------------------------------------------
/design/src/main/res/layout/preference_tips.xml:
--------------------------------------------------------------------------------
 1 | <?xml version="1.0" encoding="utf-8"?>
 2 | <layout xmlns:android="http://schemas.android.com/apk/res/android">
 3 |     <LinearLayout
 4 |         android:layout_width="wrap_content"
 5 |         android:layout_height="wrap_content"
 6 |         android:gravity="center_vertical"
 7 |         android:orientation="horizontal"
 8 |         android:paddingVertical="@dimen/item_padding_vertical">
 9 | 
10 |         <View
11 |             android:layout_width="@dimen/tips_icon_size"
12 |             android:layout_height="@dimen/tips_icon_size"
13 |             android:layout_marginHorizontal="@dimen/tips_icon_margin"
14 |             android:background="@drawable/ic_outline_info" />
15 | 
16 |         <TextView
17 |             android:id="@+id/tips"
18 |             android:layout_width="wrap_content"
19 |             android:layout_height="wrap_content"
20 |             android:layout_marginEnd="@dimen/item_tailing_margin"
21 |             android:textAppearance="@style/TextAppearance.MaterialComponents.Body2" />
22 |     </LinearLayout>
23 | </layout>


--------------------------------------------------------------------------------
/design/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
 1 | <?xml version="1.0" encoding="utf-8"?>
 2 | <resources>
 3 |     <color name="color_clash_light">#1e4376</color>
 4 |     <color name="color_clash_dark">#1976d2</color>
 5 |     <color name="color_system_ui_overlay">#50000000</color>
 6 |     <color name="color_light_background">#FFFAFAFA</color>
 7 |     <color name="color_dark_background">#FF121212</color>
 8 |     <color name="color_dark_surface">#FF202020</color>
 9 |     <color name="color_light_clash_stopped">#FF808080</color>
10 |     <color name="color_light_control_disabled">#FFD3D3D3</color>
11 |     <color name="color_dark_control_disabled">#FF808080</color>
12 |     <color name="color_error">#FFB00020</color>
13 | </resources>
14 | 


--------------------------------------------------------------------------------
/design/src/main/res/values/ids.xml:
--------------------------------------------------------------------------------
1 | <?xml version="1.0" encoding="utf-8"?>
2 | <resources>
3 |     <item name="close_button" type="id" />
4 | </resources>


--------------------------------------------------------------------------------
/design/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
 1 | <?xml version="1.0" encoding="utf-8"?>
 2 | <resources>
 3 | 
 4 |     <style name="ThemeOverlay" />
 5 | 
 6 |     <style name="ThemeOverlay.ControlColorError">
 7 |         <item name="colorControlNormal">@color/color_error</item>
 8 |         <item name="android:textColor">@color/color_error</item>
 9 |     </style>
10 | </resources>


--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/full_description.txt:
--------------------------------------------------------------------------------
 1 | A Graphical user interface of Clash.Meta for Android.
 2 | 
 3 | <h3>Features</h3>
 4 | <ul>
 5 | <li>Local HTTP/HTTPS/SOCKS server</li>
 6 | <li>VMess, Shadowsocks, Trojan, Snell protocol support for remote connections</li>
 7 | <li>Built-in DNS server that aims to minimize DNS pollution attack impact, supports DoH/DoT upstream and fake IP</li>
 8 | <li>Rules based off domains, GEOIP, IPCIDR or Process to forward packets to different nodes</li>
 9 | <li>Remote groups allow users to implement powerful rules. Supports automatic fallback, load balancing or auto select node based off latency</li>
10 | <li>Remote providers, allowing users to get node lists remotely instead of hardcoding in config</li>
11 | </ul>


--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/short_description.txt:
--------------------------------------------------------------------------------
1 | A rule-based tunnel


--------------------------------------------------------------------------------
/fastlane/metadata/android/zh-CN/full_description.txt:
--------------------------------------------------------------------------------
 1 | Clash.Meta 的 Android 图形界面。
 2 | 
 3 | <h3>功能</h3>
 4 | <ul>
 5 | <li>本地 HTTP/HTTPS/SOCKS 服务器</li>
 6 | <li>支持 VMess, Shadowsocks, Trojan, Snell 协议远程连接</li>
 7 | <li>内置 DNS 服务器,旨在最小化 DNS 污染攻击影响,支持 DoH/DoT 上游和 fake IP</li>
 8 | <li>基于规则根据域名,GEOIP, IPCIDR 或进程转发数据包到不同节点</li>
 9 | <li>远程分组允许用户实现强大的规则。支持自动回退,负载均衡或基于延迟自动选择节点</li>
10 | <li>远程提供者,允许用户远程获取节点列表而不是在配置中硬编码</li>
11 | </ul>


--------------------------------------------------------------------------------
/fastlane/metadata/android/zh-CN/short_description.txt:
--------------------------------------------------------------------------------
1 | 基于规则的隧道


--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
 1 | # Project-wide Gradle settings.
 2 | # IDE (e.g. Android Studio) users:
 3 | # Gradle settings configured through the IDE *will override*
 4 | # any settings specified in this file.
 5 | # For more details on how to configure your build environment visit
 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
 7 | # Specifies the JVM arguments used for the daemon process.
 8 | # The setting is particularly useful for tweaking memory settings.
 9 | org.gradle.jvmargs=-Xmx4g -XX:+UseZGC -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app's APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
20 | # Kotlin code style for this project: "official" or "obsolete":
21 | kotlin.code.style=official
22 | # Gradle parallel build
23 | org.gradle.parallel=true


--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MetaCubeX/ClashMetaForAndroid/61093eb8d655cbd89dee761bbb79634481c3f5b6/gradle/wrapper/gradle-wrapper.jar


--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Tue Jan 14 14:06:42 CST 2025
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
5 | zipStoreBase=GRADLE_USER_HOME
6 | zipStorePath=wrapper/dists
7 | 


--------------------------------------------------------------------------------
/hideapi/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 |     id("com.android.library")
3 | }
4 | 


--------------------------------------------------------------------------------
/hideapi/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MetaCubeX/ClashMetaForAndroid/61093eb8d655cbd89dee761bbb79634481c3f5b6/hideapi/consumer-rules.pro


--------------------------------------------------------------------------------
/hideapi/proguard-rules.pro:
--------------------------------------------------------------------------------
 1 | # Add project specific ProGuard rules here.
 2 | # You can control the set of applied configuration files using the
 3 | # proguardFiles setting in build.gradle.kts.
 4 | #
 5 | # For more details, see
 6 | #   http://developer.android.com/guide/developing/tools/proguard.html
 7 | 
 8 | # If your project uses WebView with JS, uncomment the following
 9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | #   public *;
13 | #}
14 | 
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 | 
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile


--------------------------------------------------------------------------------
/hideapi/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 | <?xml version="1.0" encoding="utf-8"?>
2 | <manifest />


--------------------------------------------------------------------------------
/hideapi/src/main/java/android/app/ActivityThread.java:
--------------------------------------------------------------------------------
1 | package android.app;
2 | 
3 | public class ActivityThread {
4 |     public static String currentProcessName() {
5 |         throw new IllegalArgumentException("Stub!");
6 |     }
7 | }
8 | 


--------------------------------------------------------------------------------
/release.keystore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MetaCubeX/ClashMetaForAndroid/61093eb8d655cbd89dee761bbb79634481c3f5b6/release.keystore


--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 |   "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3 |   "extends": [
4 |     "config:recommended"
5 |   ]
6 | }
7 | 


--------------------------------------------------------------------------------
/service/build.gradle.kts:
--------------------------------------------------------------------------------
 1 | plugins {
 2 |     kotlin("android")
 3 |     id("kotlinx-serialization")
 4 |     id("com.android.library")
 5 |     id("com.google.devtools.ksp")
 6 | }
 7 | 
 8 | dependencies {
 9 |     implementation(project(":core"))
10 |     implementation(project(":common"))
11 | 
12 |     ksp(libs.kaidl.compiler)
13 |     ksp(libs.androidx.room.compiler)
14 | 
15 |     implementation(libs.kotlin.coroutine)
16 |     implementation(libs.kotlin.serialization.json)
17 |     implementation(libs.androidx.core)
18 |     implementation(libs.androidx.room.runtime)
19 |     implementation(libs.androidx.room.ktx)
20 |     implementation(libs.kaidl.runtime)
21 |     implementation(libs.rikkax.multiprocess)
22 |     implementation(platform("com.squareup.okhttp3:okhttp-bom:4.12.0"))
23 | 
24 |     // define any required OkHttp artifacts without version
25 |     implementation("com.squareup.okhttp3:okhttp")
26 |     implementation("com.squareup.okhttp3:logging-interceptor")
27 | }
28 | 
29 | afterEvaluate {
30 |     android {
31 |         libraryVariants.forEach {
32 |             sourceSets[it.name].kotlin.srcDir(buildDir.resolve("generated/ksp/${it.name}/kotlin"))
33 |             sourceSets[it.name].java.srcDir(buildDir.resolve("generated/ksp/${it.name}/java"))
34 |         }
35 |     }
36 | }


--------------------------------------------------------------------------------
/service/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MetaCubeX/ClashMetaForAndroid/61093eb8d655cbd89dee761bbb79634481c3f5b6/service/consumer-rules.pro


--------------------------------------------------------------------------------
/service/proguard-rules.pro:
--------------------------------------------------------------------------------
 1 | # Add project specific ProGuard rules here.
 2 | # You can control the set of applied configuration files using the
 3 | # proguardFiles setting in build.gradle.
 4 | #
 5 | # For more details, see
 6 | #   http://developer.android.com/guide/developing/tools/proguard.html
 7 | 
 8 | # If your project uses WebView with JS, uncomment the following
 9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | #   public *;
13 | #}
14 | 
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 | 
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 | 


--------------------------------------------------------------------------------
/service/src/main/java/com/github/kr328/clash/service/BaseService.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.service
 2 | 
 3 | import android.app.Service
 4 | import com.github.kr328.clash.service.util.cancelAndJoinBlocking
 5 | import kotlinx.coroutines.CoroutineScope
 6 | import kotlinx.coroutines.Dispatchers
 7 | 
 8 | abstract class BaseService : Service(), CoroutineScope by CoroutineScope(Dispatchers.Default) {
 9 |     override fun onDestroy() {
10 |         super.onDestroy()
11 | 
12 |         cancelAndJoinBlocking()
13 |     }
14 | }


--------------------------------------------------------------------------------
/service/src/main/java/com/github/kr328/clash/service/PreferenceProvider.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.service
 2 | 
 3 | import android.content.Context
 4 | import android.content.SharedPreferences
 5 | import com.github.kr328.clash.common.constants.Authorities
 6 | import rikka.preference.MultiProcessPreference
 7 | import rikka.preference.PreferenceProvider
 8 | 
 9 | class PreferenceProvider : PreferenceProvider() {
10 |     override fun onCreatePreference(context: Context): SharedPreferences {
11 |         return context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE)
12 |     }
13 | 
14 |     companion object {
15 |         private const val FILE_NAME = "service"
16 | 
17 |         fun createSharedPreferencesFromContext(context: Context): SharedPreferences {
18 |             return when (context) {
19 |                 is BaseService, is TunService ->
20 |                     context.getSharedPreferences(
21 |                         FILE_NAME,
22 |                         Context.MODE_PRIVATE
23 |                     )
24 |                 else ->
25 |                     MultiProcessPreference(
26 |                         context,
27 |                         Authorities.SETTINGS_PROVIDER
28 |                     )
29 |             }
30 |         }
31 |     }
32 | }


--------------------------------------------------------------------------------
/service/src/main/java/com/github/kr328/clash/service/RemoteService.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.service
 2 | 
 3 | import android.content.Intent
 4 | import android.os.IBinder
 5 | import com.github.kr328.clash.service.remote.IClashManager
 6 | import com.github.kr328.clash.service.remote.IRemoteService
 7 | import com.github.kr328.clash.service.remote.IProfileManager
 8 | import com.github.kr328.clash.service.remote.wrap
 9 | import com.github.kr328.clash.service.util.cancelAndJoinBlocking
10 | 
11 | class RemoteService : BaseService(), IRemoteService {
12 |     private val binder = this.wrap()
13 | 
14 |     private var clash: ClashManager? = null
15 |     private var profile: ProfileManager? = null
16 |     private var clashBinder: IClashManager? = null
17 |     private var profileBinder: IProfileManager? = null
18 | 
19 |     override fun onCreate() {
20 |         super.onCreate()
21 | 
22 |         clash = ClashManager(this)
23 |         profile = ProfileManager(this)
24 |         clashBinder = clash?.wrap() as IClashManager?
25 |         profileBinder = profile?.wrap() as IProfileManager?
26 |     }
27 | 
28 |     override fun onDestroy() {
29 |         super.onDestroy()
30 | 
31 |         clash?.cancelAndJoinBlocking()
32 |         profile?.cancelAndJoinBlocking()
33 |     }
34 | 
35 |     override fun onBind(intent: Intent?): IBinder {
36 |         return binder
37 |     }
38 | 
39 |     override fun clash(): IClashManager {
40 |         return clashBinder!!
41 |     }
42 | 
43 |     override fun profile(): IProfileManager {
44 |         return profileBinder!!
45 |     }
46 | }


--------------------------------------------------------------------------------
/service/src/main/java/com/github/kr328/clash/service/clash/module/CloseModule.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.service.clash.module
 2 | 
 3 | import android.app.Service
 4 | import com.github.kr328.clash.common.constants.Intents
 5 | import com.github.kr328.clash.common.log.Log
 6 | 
 7 | class CloseModule(service: Service) : Module<CloseModule.RequestClose>(service) {
 8 |     object RequestClose
 9 | 
10 |     override suspend fun run() {
11 |         val broadcasts = receiveBroadcast {
12 |             addAction(Intents.ACTION_CLASH_REQUEST_STOP)
13 |         }
14 | 
15 |         broadcasts.receive()
16 | 
17 |         Log.d("User request close")
18 | 
19 |         return enqueueEvent(RequestClose)
20 |     }
21 | }


--------------------------------------------------------------------------------
/service/src/main/java/com/github/kr328/clash/service/clash/module/TimeZoneModule.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.service.clash.module
 2 | 
 3 | import android.app.Service
 4 | import android.content.Intent
 5 | import com.github.kr328.clash.core.Clash
 6 | import java.util.*
 7 | 
 8 | class TimeZoneModule(service: Service) : Module<Unit>(service) {
 9 |     override suspend fun run() {
10 |         val timeZones = receiveBroadcast {
11 |             addAction(Intent.ACTION_TIMEZONE_CHANGED)
12 |         }
13 | 
14 |         while (true) {
15 |             val timeZone = TimeZone.getDefault()
16 | 
17 |             Clash.notifyTimeZoneChanged(timeZone.id, timeZone.rawOffset / 1000)
18 | 
19 |             timeZones.receive()
20 |         }
21 |     }
22 | }


--------------------------------------------------------------------------------
/service/src/main/java/com/github/kr328/clash/service/data/Converters.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.service.data
 2 | 
 3 | import androidx.room.TypeConverter
 4 | import com.github.kr328.clash.service.model.Profile
 5 | import java.util.*
 6 | 
 7 | class Converters {
 8 |     @TypeConverter
 9 |     fun fromUUID(uuid: UUID): String {
10 |         return uuid.toString()
11 |     }
12 | 
13 |     @TypeConverter
14 |     fun toUUID(uuid: String): UUID {
15 |         return UUID.fromString(uuid)
16 |     }
17 | 
18 |     @TypeConverter
19 |     fun fromProfileType(type: Profile.Type): String {
20 |         return type.name
21 |     }
22 | 
23 |     @TypeConverter
24 |     fun toProfileType(type: String): Profile.Type {
25 |         return Profile.Type.valueOf(type)
26 |     }
27 | }


--------------------------------------------------------------------------------
/service/src/main/java/com/github/kr328/clash/service/data/Daos.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.service.data
 2 | 
 3 | fun ImportedDao(): ImportedDao {
 4 |     return Database.database.openImportedDao()
 5 | }
 6 | 
 7 | fun PendingDao(): PendingDao {
 8 |     return Database.database.openPendingDao()
 9 | }
10 | 
11 | fun SelectionDao(): SelectionDao {
12 |     return Database.database.openSelectionProxyDao()
13 | }


--------------------------------------------------------------------------------
/service/src/main/java/com/github/kr328/clash/service/data/Imported.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.service.data
 2 | 
 3 | import androidx.room.ColumnInfo
 4 | import androidx.room.Entity
 5 | import androidx.room.TypeConverters
 6 | import com.github.kr328.clash.service.model.Profile
 7 | import java.util.*
 8 | 
 9 | @Entity(tableName = "imported", primaryKeys = ["uuid"])
10 | @TypeConverters(Converters::class)
11 | data class Imported(
12 |     @ColumnInfo(name = "uuid") val uuid: UUID,
13 |     @ColumnInfo(name = "name") val name: String,
14 |     @ColumnInfo(name = "type") val type: Profile.Type,
15 |     @ColumnInfo(name = "source") val source: String,
16 |     @ColumnInfo(name = "interval") val interval: Long,
17 |     @ColumnInfo(name = "upload") val upload: Long,
18 |     @ColumnInfo(name = "download") val download: Long,
19 |     @ColumnInfo(name = "total") val total: Long,
20 |     @ColumnInfo(name = "expire") val expire: Long,
21 |     @ColumnInfo(name = "createdAt") val createdAt: Long,
22 | )


--------------------------------------------------------------------------------
/service/src/main/java/com/github/kr328/clash/service/data/ImportedDao.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.service.data
 2 | 
 3 | import androidx.room.*
 4 | import java.util.*
 5 | 
 6 | @Dao
 7 | @TypeConverters(Converters::class)
 8 | interface ImportedDao {
 9 |     @Query("SELECT * FROM imported WHERE uuid = :uuid")
10 |     suspend fun queryByUUID(uuid: UUID): Imported?
11 | 
12 |     @Query("SELECT uuid FROM imported ORDER BY createdAt")
13 |     suspend fun queryAllUUIDs(): List<UUID>
14 | 
15 |     @Insert(onConflict = OnConflictStrategy.ABORT)
16 |     suspend fun insert(imported: Imported): Long
17 | 
18 |     @Update(onConflict = OnConflictStrategy.ABORT)
19 |     suspend fun update(imported: Imported)
20 | 
21 |     @Query("DELETE FROM imported WHERE uuid = :uuid")
22 |     suspend fun remove(uuid: UUID)
23 | 
24 |     @Query("SELECT EXISTS(SELECT 1 FROM imported WHERE uuid = :uuid)")
25 |     suspend fun exists(uuid: UUID): Boolean
26 | }


--------------------------------------------------------------------------------
/service/src/main/java/com/github/kr328/clash/service/data/Pending.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.service.data
 2 | 
 3 | import androidx.room.ColumnInfo
 4 | import androidx.room.Entity
 5 | import androidx.room.TypeConverters
 6 | import com.github.kr328.clash.service.model.Profile
 7 | import java.util.*
 8 | 
 9 | @Entity(tableName = "pending", primaryKeys = ["uuid"])
10 | @TypeConverters(Converters::class)
11 | data class Pending(
12 |     @ColumnInfo(name = "uuid") val uuid: UUID,
13 |     @ColumnInfo(name = "name") val name: String,
14 |     @ColumnInfo(name = "type") val type: Profile.Type,
15 |     @ColumnInfo(name = "source") val source: String,
16 |     @ColumnInfo(name = "interval") val interval: Long,
17 |     @ColumnInfo(name = "upload") val upload: Long,
18 |     @ColumnInfo(name = "download") val download: Long,
19 |     @ColumnInfo(name = "total") val total: Long,
20 |     @ColumnInfo(name = "expire") val expire: Long,
21 |     @ColumnInfo(name = "createdAt") val createdAt: Long = System.currentTimeMillis(),
22 | )


--------------------------------------------------------------------------------
/service/src/main/java/com/github/kr328/clash/service/data/PendingDao.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.service.data
 2 | 
 3 | import androidx.room.*
 4 | import java.util.*
 5 | 
 6 | @Dao
 7 | @TypeConverters(Converters::class)
 8 | interface PendingDao {
 9 |     @Query("SELECT * FROM pending WHERE uuid = :uuid")
10 |     suspend fun queryByUUID(uuid: UUID): Pending?
11 | 
12 |     @Query("DELETE FROM pending WHERE uuid = :uuid")
13 |     suspend fun remove(uuid: UUID)
14 | 
15 |     @Query("SELECT EXISTS(SELECT 1 FROM pending WHERE uuid = :uuid)")
16 |     suspend fun exists(uuid: UUID): Boolean
17 | 
18 |     @Query("SELECT uuid FROM pending ORDER BY createdAt")
19 |     suspend fun queryAllUUIDs(): List<UUID>
20 | 
21 |     @Insert(onConflict = OnConflictStrategy.REPLACE)
22 |     suspend fun insert(pending: Pending)
23 | 
24 |     @Update(onConflict = OnConflictStrategy.REPLACE)
25 |     suspend fun update(pending: Pending)
26 | }
27 | 


--------------------------------------------------------------------------------
/service/src/main/java/com/github/kr328/clash/service/data/Selection.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.service.data
 2 | 
 3 | import androidx.room.ColumnInfo
 4 | import androidx.room.Entity
 5 | import androidx.room.ForeignKey
 6 | import androidx.room.TypeConverters
 7 | import java.util.*
 8 | 
 9 | @Entity(
10 |     tableName = "selections",
11 |     foreignKeys = [ForeignKey(
12 |         entity = Imported::class,
13 |         childColumns = ["uuid"],
14 |         parentColumns = ["uuid"],
15 |         onDelete = ForeignKey.CASCADE,
16 |         onUpdate = ForeignKey.CASCADE
17 |     )],
18 |     primaryKeys = ["uuid", "proxy"]
19 | )
20 | @TypeConverters(Converters::class)
21 | data class Selection(
22 |     @ColumnInfo(name = "uuid") val uuid: UUID,
23 |     @ColumnInfo(name = "proxy") val proxy: String,
24 |     @ColumnInfo(name = "selected") val selected: String,
25 | )


--------------------------------------------------------------------------------
/service/src/main/java/com/github/kr328/clash/service/data/SelectionDao.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.service.data
 2 | 
 3 | import androidx.room.*
 4 | import java.util.*
 5 | 
 6 | @Dao
 7 | @TypeConverters(Converters::class)
 8 | interface SelectionDao {
 9 |     @Insert(onConflict = OnConflictStrategy.REPLACE)
10 |     fun setSelected(selection: Selection)
11 | 
12 |     @Query("DELETE FROM selections WHERE uuid = :uuid AND proxy = :proxy")
13 |     fun removeSelected(uuid: UUID, proxy: String)
14 | 
15 |     @Query("SELECT * FROM selections WHERE uuid = :uuid")
16 |     suspend fun querySelections(uuid: UUID): List<Selection>
17 | 
18 |     @Query("DELETE FROM selections WHERE uuid = :uuid AND proxy in (:proxies)")
19 |     suspend fun removeSelections(uuid: UUID, proxies: List<String>)
20 | }
21 | 


--------------------------------------------------------------------------------
/service/src/main/java/com/github/kr328/clash/service/data/migrations/Migrations.kt:
--------------------------------------------------------------------------------
1 | package com.github.kr328.clash.service.data.migrations
2 | 
3 | import androidx.room.migration.Migration
4 | 
5 | val MIGRATIONS: Array<Migration> = arrayOf()
6 | 
7 | val LEGACY_MIGRATION = ::migrationFromLegacy


--------------------------------------------------------------------------------
/service/src/main/java/com/github/kr328/clash/service/document/Document.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.service.document
 2 | 
 3 | interface Document {
 4 |     val id: String
 5 |     val name: String
 6 |     val mimeType: String
 7 |     val size: Long
 8 |     val updatedAt: Long
 9 |     val flags: Set<Flag>
10 | }


--------------------------------------------------------------------------------
/service/src/main/java/com/github/kr328/clash/service/document/FileDocument.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.service.document
 2 | 
 3 | import android.provider.DocumentsContract
 4 | import java.io.File
 5 | 
 6 | class FileDocument(
 7 |     val file: File,
 8 |     override val flags: Set<Flag>,
 9 |     private val idOverride: String? = null,
10 |     private val nameOverride: String? = null,
11 | ) : Document {
12 |     override val id: String
13 |         get() = idOverride ?: file.name
14 |     override val name: String
15 |         get() = nameOverride ?: file.name
16 |     override val mimeType: String
17 |         get() = if (file.isDirectory) DocumentsContract.Document.MIME_TYPE_DIR else "text/plain"
18 |     override val size: Long
19 |         get() = file.length()
20 |     override val updatedAt: Long
21 |         get() = file.lastModified()
22 | }


--------------------------------------------------------------------------------
/service/src/main/java/com/github/kr328/clash/service/document/Flag.kt:
--------------------------------------------------------------------------------
1 | package com.github.kr328.clash.service.document
2 | 
3 | enum class Flag {
4 |     Writable, Deletable, Virtual
5 | }


--------------------------------------------------------------------------------
/service/src/main/java/com/github/kr328/clash/service/document/Path.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.service.document
 2 | 
 3 | import java.util.*
 4 | 
 5 | data class Path(
 6 |     val uuid: UUID?,
 7 |     val scope: Scope?,
 8 |     val relative: List<String>?
 9 | ) {
10 |     enum class Scope {
11 |         Configuration, Providers
12 |     }
13 | 
14 |     override fun toString(): String {
15 |         if (uuid == null)
16 |             return "/"
17 | 
18 |         if (scope == null)
19 |             return "/$uuid"
20 | 
21 |         val sc = when (scope) {
22 |             Scope.Configuration -> Paths.CONFIGURATION_ID
23 |             Scope.Providers -> Paths.PROVIDERS_ID
24 |         }
25 | 
26 |         if (relative == null)
27 |             return "/$uuid/$sc"
28 | 
29 |         return "/$uuid/$sc/${relative.joinToString(separator = "/")}"
30 |     }
31 | }


--------------------------------------------------------------------------------
/service/src/main/java/com/github/kr328/clash/service/document/VirtualDocument.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.service.document
 2 | 
 3 | class VirtualDocument(
 4 |     override val id: String,
 5 |     override val name: String,
 6 |     override val mimeType: String,
 7 |     override val size: Long,
 8 |     override val updatedAt: Long,
 9 |     override val flags: Set<Flag>,
10 | ) : Document
11 | 


--------------------------------------------------------------------------------
/service/src/main/java/com/github/kr328/clash/service/model/AccessControlMode.kt:
--------------------------------------------------------------------------------
1 | package com.github.kr328.clash.service.model
2 | 
3 | enum class AccessControlMode {
4 |     AcceptAll, AcceptSelected, DenySelected
5 | }
6 | 


--------------------------------------------------------------------------------
/service/src/main/java/com/github/kr328/clash/service/model/Profile.kt:
--------------------------------------------------------------------------------
 1 | @file:UseSerializers(UUIDSerializer::class)
 2 | 
 3 | package com.github.kr328.clash.service.model
 4 | 
 5 | import android.os.Parcel
 6 | import android.os.Parcelable
 7 | import com.github.kr328.clash.core.util.Parcelizer
 8 | import com.github.kr328.clash.service.util.UUIDSerializer
 9 | import kotlinx.serialization.Serializable
10 | import kotlinx.serialization.UseSerializers
11 | import java.util.*
12 | 
13 | @Serializable
14 | data class Profile(
15 |     val uuid: UUID,
16 |     val name: String,
17 |     val type: Type,
18 |     val source: String,
19 |     val active: Boolean,
20 |     val interval: Long,
21 |     val upload: Long,
22 |     var download: Long,
23 |     val total: Long,
24 |     val expire: Long,
25 | 
26 | 
27 |     val updatedAt: Long,
28 |     val imported: Boolean,
29 |     val pending: Boolean,
30 | ) : Parcelable {
31 |     enum class Type {
32 |         File, Url, External
33 |     }
34 | 
35 |     override fun writeToParcel(parcel: Parcel, flags: Int) {
36 |         Parcelizer.encodeToParcel(serializer(), parcel, this)
37 |     }
38 | 
39 |     override fun describeContents(): Int {
40 |         return 0
41 |     }
42 | 
43 |     companion object CREATOR : Parcelable.Creator<Profile> {
44 |         override fun createFromParcel(parcel: Parcel): Profile {
45 |             return Parcelizer.decodeFromParcel(serializer(), parcel)
46 |         }
47 | 
48 |         override fun newArray(size: Int): Array<Profile?> {
49 |             return arrayOfNulls(size)
50 |         }
51 |     }
52 | }


--------------------------------------------------------------------------------
/service/src/main/java/com/github/kr328/clash/service/remote/IClashManager.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.service.remote
 2 | 
 3 | import com.github.kr328.clash.core.Clash
 4 | import com.github.kr328.clash.core.model.*
 5 | import com.github.kr328.kaidl.BinderInterface
 6 | 
 7 | @BinderInterface
 8 | interface IClashManager {
 9 |     fun queryTunnelState(): TunnelState
10 |     fun queryTrafficTotal(): Long
11 |     fun queryProxyGroupNames(excludeNotSelectable: Boolean): List<String>
12 |     fun queryProxyGroup(name: String, proxySort: ProxySort): ProxyGroup
13 |     fun queryConfiguration(): UiConfiguration
14 |     fun queryProviders(): ProviderList
15 | 
16 |     fun patchSelector(group: String, name: String): Boolean
17 | 
18 |     suspend fun healthCheck(group: String)
19 |     suspend fun updateProvider(type: Provider.Type, name: String)
20 | 
21 |     fun queryOverride(slot: Clash.OverrideSlot): ConfigurationOverride
22 |     fun patchOverride(slot: Clash.OverrideSlot, configuration: ConfigurationOverride)
23 |     fun clearOverride(slot: Clash.OverrideSlot)
24 | 
25 |     fun setLogObserver(observer: ILogObserver?)
26 | }


--------------------------------------------------------------------------------
/service/src/main/java/com/github/kr328/clash/service/remote/IFetchObserver.kt:
--------------------------------------------------------------------------------
1 | package com.github.kr328.clash.service.remote
2 | 
3 | import com.github.kr328.clash.core.model.FetchStatus
4 | import com.github.kr328.kaidl.BinderInterface
5 | 
6 | @BinderInterface
7 | fun interface IFetchObserver {
8 |     fun updateStatus(status: FetchStatus)
9 | }


--------------------------------------------------------------------------------
/service/src/main/java/com/github/kr328/clash/service/remote/ILogObserver.kt:
--------------------------------------------------------------------------------
1 | package com.github.kr328.clash.service.remote
2 | 
3 | import com.github.kr328.clash.core.model.LogMessage
4 | import com.github.kr328.kaidl.BinderInterface
5 | 
6 | @BinderInterface
7 | interface ILogObserver {
8 |     fun newItem(log: LogMessage)
9 | }


--------------------------------------------------------------------------------
/service/src/main/java/com/github/kr328/clash/service/remote/IProfileManager.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.service.remote
 2 | 
 3 | import com.github.kr328.clash.service.model.Profile
 4 | import com.github.kr328.kaidl.BinderInterface
 5 | import java.util.*
 6 | 
 7 | @BinderInterface
 8 | interface IProfileManager {
 9 |     suspend fun create(type: Profile.Type, name: String, source: String = ""): UUID
10 |     suspend fun clone(uuid: UUID): UUID
11 |     suspend fun commit(uuid: UUID, callback: IFetchObserver? = null)
12 |     suspend fun release(uuid: UUID)
13 |     suspend fun delete(uuid: UUID)
14 |     suspend fun patch(uuid: UUID, name: String, source: String, interval: Long)
15 |     suspend fun update(uuid: UUID)
16 |     suspend fun queryByUUID(uuid: UUID): Profile?
17 |     suspend fun queryAll(): List<Profile>
18 |     suspend fun queryActive(): Profile?
19 |     suspend fun setActive(profile: Profile)
20 | }


--------------------------------------------------------------------------------
/service/src/main/java/com/github/kr328/clash/service/remote/IRemoteService.kt:
--------------------------------------------------------------------------------
1 | package com.github.kr328.clash.service.remote
2 | 
3 | import com.github.kr328.kaidl.BinderInterface
4 | 
5 | @BinderInterface
6 | interface IRemoteService {
7 |     fun clash(): IClashManager
8 |     fun profile(): IProfileManager
9 | }


--------------------------------------------------------------------------------
/service/src/main/java/com/github/kr328/clash/service/util/Connectivity.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.service.util
 2 | 
 3 | import android.net.ConnectivityManager
 4 | import android.net.Network
 5 | 
 6 | fun ConnectivityManager.resolveDns(network: Network?): List<String> {
 7 |     val properties = getLinkProperties(network) ?: return listOf()
 8 |     return properties.dnsServers.map { it.asSocketAddressText(53) }
 9 | }
10 | 


--------------------------------------------------------------------------------
/service/src/main/java/com/github/kr328/clash/service/util/Coroutine.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.service.util
 2 | 
 3 | import kotlinx.coroutines.CoroutineScope
 4 | import kotlinx.coroutines.job
 5 | import kotlinx.coroutines.runBlocking
 6 | 
 7 | fun CoroutineScope.cancelAndJoinBlocking() {
 8 |     val scope = this
 9 | 
10 |     runBlocking {
11 |         scope.coroutineContext.job.cancel()
12 |         scope.coroutineContext.job.join()
13 |     }
14 | }


--------------------------------------------------------------------------------
/service/src/main/java/com/github/kr328/clash/service/util/Database.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.service.util
 2 | 
 3 | import com.github.kr328.clash.service.data.ImportedDao
 4 | import com.github.kr328.clash.service.data.PendingDao
 5 | import java.util.*
 6 | 
 7 | suspend fun generateProfileUUID(): UUID {
 8 |     var result = UUID.randomUUID()
 9 | 
10 |     while (ImportedDao().exists(result) || PendingDao().exists(result)) {
11 |         result = UUID.randomUUID()
12 |     }
13 | 
14 |     return result
15 | }
16 | 


--------------------------------------------------------------------------------
/service/src/main/java/com/github/kr328/clash/service/util/Files.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.service.util
 2 | 
 3 | import android.content.Context
 4 | import java.io.File
 5 | 
 6 | val Context.importedDir: File
 7 |     get() = filesDir.resolve("imported")
 8 | 
 9 | val Context.pendingDir: File
10 |     get() = filesDir.resolve("pending")
11 | 
12 | val Context.processingDir: File
13 |     get() = filesDir.resolve("processing")
14 | 
15 | val File.directoryLastModified: Long?
16 |     get() {
17 |         return walk().map { it.lastModified() }.maxOrNull()
18 |     }


--------------------------------------------------------------------------------
/service/src/main/java/com/github/kr328/clash/service/util/Intent.kt:
--------------------------------------------------------------------------------
1 | package com.github.kr328.clash.service.util
2 | 
3 | import android.content.Intent
4 | 
5 | val Intent.packageName: String?
6 |     get() {
7 |         return data?.takeIf { it.scheme == "package" }?.schemeSpecificPart
8 |     }


--------------------------------------------------------------------------------
/service/src/main/java/com/github/kr328/clash/service/util/Net.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.service.util
 2 | 
 3 | data class IPNet(val ip: String, val prefix: Int)
 4 | 
 5 | fun parseCIDR(cidr: String): IPNet {
 6 |     val s = cidr.split("/", limit = 2)
 7 | 
 8 |     if (s.size != 2)
 9 |         throw IllegalArgumentException("Invalid address")
10 | 
11 |     val address = s[0]
12 |     val prefix = s[1].toInt()
13 | 
14 |     return IPNet(address, prefix)
15 | }


--------------------------------------------------------------------------------
/service/src/main/java/com/github/kr328/clash/service/util/Serializers.kt:
--------------------------------------------------------------------------------
 1 | package com.github.kr328.clash.service.util
 2 | 
 3 | import kotlinx.serialization.KSerializer
 4 | import kotlinx.serialization.descriptors.PrimitiveKind
 5 | import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
 6 | import kotlinx.serialization.descriptors.SerialDescriptor
 7 | import kotlinx.serialization.encoding.Decoder
 8 | import kotlinx.serialization.encoding.Encoder
 9 | import java.util.*
10 | 
11 | class UUIDSerializer : KSerializer<UUID> {
12 |     override val descriptor: SerialDescriptor =
13 |         PrimitiveSerialDescriptor("UUID", PrimitiveKind.STRING)
14 | 
15 |     override fun deserialize(decoder: Decoder): UUID {
16 |         return UUID.fromString(decoder.decodeString())
17 |     }
18 | 
19 |     override fun serialize(encoder: Encoder, value: UUID) {
20 |         encoder.encodeString(value.toString())
21 |     }
22 | }


--------------------------------------------------------------------------------
/service/src/main/res/values-ja-rJP/strings.xml:
--------------------------------------------------------------------------------
 1 | <?xml version="1.0" encoding="utf-8"?>
 2 | <resources>
 3 |     <string name="clash_service_status_channel">Clashステータス</string>
 4 |     <string name="profile_service_status">プロファイルサービスのステータス</string>
 5 |     <string name="profile_process_status">プロファイルの処理状況</string>
 6 |     <string name="profile_process_result">プロファイルプロセスの結果</string>
 7 |     <string name="update_successfully">正常に更新されました</string>
 8 |     <string name="update_failure">更新に失敗しました</string>
 9 |     <string name="format_update_complete">%sを更新しました</string>
10 |     <string name="format_update_failure">更新 %1$s: %2$s </string>
11 |     <string name="running">実行中</string>
12 |     <string name="loading">読み込み中</string>
13 |     <string name="clash_meta_for_android">Clash Meta for Android</string>
14 |     <string name="profiles_and_providers">プロファイルと外部リソース</string>
15 |     <string name="configuration_yaml">コンフィグ.yaml</string>
16 |     <string name="provider_files">外部リソースファイル</string>
17 |     <string name="profile_updater">プロファイルの更新</string>
18 |     <string name="profile_updating">プロファイルを更新中</string>
19 | </resources>


--------------------------------------------------------------------------------
/service/src/main/res/values-ko-rKR/strings.xml:
--------------------------------------------------------------------------------
 1 | <?xml version="1.0" encoding="utf-8"?>
 2 | <resources>
 3 |     <string name="clash_service_status_channel">Clash 상태</string>
 4 |     <string name="profile_service_status">구성 파일 서비스 상태</string>
 5 |     <string name="profile_process_status">구성 파일 처리 상태</string>
 6 |     <string name="profile_process_result">구성 파일 처리 결과</string>
 7 |     <string name="update_successfully">업데이트 성공</string>
 8 |     <string name="update_failure">업데이트 실패</string>
 9 |     <string name="format_update_complete">%s 업데이트 성공</string>
10 |     <string name="format_update_failure">업데이트 %1$s: %2$s</string>
11 |     <string name="running">연결됨</string>
12 |     <string name="loading">로딩중</string>
13 |     <string name="clash_meta_for_android">Clash Meta for Android</string>
14 |     <string name="profiles_and_providers">구성 파일과 외부 리소스</string>
15 |     <string name="configuration_yaml">구성 파일.yaml</string>
16 |     <string name="provider_files">외부 리소스 파일</string>
17 |     <string name="profile_updater">구성 파일 업데이트</string>
18 |     <string name="profile_updating">구성 파일 업데이트 중</string>
19 | </resources>


--------------------------------------------------------------------------------
/service/src/main/res/values-ru/strings.xml:
--------------------------------------------------------------------------------
 1 | <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="PluralsCandidate">
 2 |     <!-- from https://github.com/shadowsocks/shadowsocks-android/blob/master/core/src/main/res/values/strings.xml -->
 3 |     <string name="clash_notification_content" translatable="false">"%1$s↑\t%2$s↓"</string>
 4 | 
 5 |     <string name="clash_service_status_channel">Статус Clash</string>
 6 |     <string name="profile_service_status">Статус сервиса профиля</string>
 7 |     <string name="profile_process_status">Статус обработки профиля</string>
 8 |     <string name="profile_process_result">Результат обработки профиля</string>
 9 |     <string name="update_successfully">Успешно обновлено</string>
10 |     <string name="update_failure">Не удалось обновить</string>
11 |     <string name="format_update_complete">Обновление %s завершено</string>
12 |     <string name="format_update_failure">Обновление %1$s: %2$s</string>
13 |     <string name="running">Работает</string>
14 |     <string name="loading">Загружается</string>
15 |     <string name="clash_meta_for_android">Clash Meta для Android</string>
16 |     <string name="profiles_and_providers">Профили и провайдеры</string>
17 |     <string name="configuration_yaml">Configuration.yaml</string>
18 |     <string name="provider_files">Файлы провайдера</string>
19 |     <string name="profile_updater">Обновление профиля</string>
20 |     <string name="profile_updating">Профиль обновляется</string>
21 | </resources>
22 | 


--------------------------------------------------------------------------------
/service/src/main/res/values-zh-rHK/strings.xml:
--------------------------------------------------------------------------------
 1 | <?xml version="1.0" encoding="utf-8"?>
 2 | <resources>
 3 |     <string name="clash_service_status_channel">Clash 狀態</string>
 4 |     <string name="running">正在運行</string>
 5 |     <string name="format_update_complete">更新 %s 成功</string>
 6 |     <string name="format_update_failure">"更新 %1$s: %2$s "</string>
 7 |     <string name="clash_meta_for_android">Clash Meta for Android</string>
 8 |     <string name="profiles_and_providers">配置文件和外部資源</string>
 9 |     <string name="configuration_yaml">配置文件.yaml</string>
10 |     <string name="provider_files">外部資源文件列表</string>
11 |     <string name="loading">載入中</string>
12 |     <string name="profile_process_status">配置文件處理狀態</string>
13 |     <string name="update_successfully">更新成功</string>
14 |     <string name="update_failure">更新失敗</string>
15 |     <string name="profile_updater">配置更新服務</string>
16 |     <string name="profile_updating">配置更新中</string>
17 |     <string name="profile_service_status">配置文件服務狀態</string>
18 |     <string name="profile_process_result">配置文件處理結果</string>
19 | </resources>


--------------------------------------------------------------------------------
/service/src/main/res/values-zh-rTW/strings.xml:
--------------------------------------------------------------------------------
 1 | <?xml version="1.0" encoding="utf-8"?>
 2 | <resources>
 3 |     <string name="clash_service_status_channel">Clash 狀態</string>
 4 |     <string name="running">正在運作</string>
 5 |     <string name="format_update_complete">更新 %s 成功</string>
 6 |     <string name="format_update_failure">"更新 %1$s: %2$s "</string>
 7 |     <string name="clash_meta_for_android">Clash Meta for Android</string>
 8 |     <string name="profiles_and_providers">設定檔和外部資源</string>
 9 |     <string name="configuration_yaml">設定檔.yaml</string>
10 |     <string name="provider_files">外部資源文件列表</string>
11 |     <string name="loading">載入中</string>
12 |     <string name="profile_process_status">設定檔處理狀態</string>
13 |     <string name="update_successfully">更新成功</string>
14 |     <string name="update_failure">更新失敗</string>
15 |     <string name="profile_updater">設定檔更新服務</string>
16 |     <string name="profile_updating">設定檔更新中</string>
17 |     <string name="profile_service_status">設定檔服務狀態</string>
18 |     <string name="profile_process_result">設定檔處理結果</string>
19 | </resources>
20 | 


--------------------------------------------------------------------------------
/service/src/main/res/values-zh/strings.xml:
--------------------------------------------------------------------------------
 1 | <?xml version="1.0" encoding="utf-8"?>
 2 | <resources>
 3 |     <string name="clash_service_status_channel">Clash 状态</string>
 4 |     <string name="running">正在运行</string>
 5 |     <string name="format_update_complete">更新 %s 成功</string>
 6 |     <string name="format_update_failure">"更新 %1$s: %2$s "</string>
 7 |     <string name="clash_meta_for_android">Clash Meta for Android</string>
 8 |     <string name="profiles_and_providers">配置文件和外部资源</string>
 9 |     <string name="configuration_yaml">配置文件.yaml</string>
10 |     <string name="provider_files">外部资源文件列表</string>
11 |     <string name="loading">载入中</string>
12 |     <string name="profile_process_status">配置文件处理状态</string>
13 |     <string name="update_successfully">更新成功</string>
14 |     <string name="update_failure">更新失败</string>
15 |     <string name="profile_updater">配置更新服务</string>
16 |     <string name="profile_updating">配置更新中</string>
17 |     <string name="profile_service_status">配置文件服务状态</string>
18 |     <string name="profile_process_result">配置文件处理结果</string>
19 | </resources>


--------------------------------------------------------------------------------
/service/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 | <?xml version="1.0" encoding="utf-8"?>
2 | <resources>
3 |     <color name="color_clash">#1E4376</color>
4 | </resources>


--------------------------------------------------------------------------------
/service/src/main/res/values/ids.xml:
--------------------------------------------------------------------------------
1 | <?xml version="1.0" encoding="utf-8"?>
2 | <resources>
3 |     <item name="nf_clash_status" type="id" />
4 |     <item name="nf_vpn_status" type="id" />
5 |     <item name="nf_profile_worker" type="id" />
6 | </resources>


--------------------------------------------------------------------------------
/service/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
 1 | <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="PluralsCandidate">
 2 |     <!-- from https://github.com/shadowsocks/shadowsocks-android/blob/master/core/src/main/res/values/strings.xml -->
 3 |     <string name="clash_notification_content" translatable="false">"%1$s↑\t%2$s↓"</string>
 4 | 
 5 |     <string name="clash_service_status_channel">Clash Status</string>
 6 |     <string name="profile_service_status">Profile Service Status</string>
 7 |     <string name="profile_process_status">Profile Processing Status</string>
 8 |     <string name="profile_process_result">Profile Process Result</string>
 9 |     <string name="update_successfully">Update Successfully</string>
10 |     <string name="update_failure">Update Failure</string>
11 |     <string name="format_update_complete">Update %s completed</string>
12 |     <string name="format_update_failure">Update %1$s: %2$s</string>
13 |     <string name="running">Running</string>
14 |     <string name="loading">Loading</string>
15 |     <string name="clash_meta_for_android">Clash Meta for Android</string>
16 |     <string name="profiles_and_providers">Profiles and Providers</string>
17 |     <string name="configuration_yaml">Configuration.yaml</string>
18 |     <string name="provider_files">Provider Files</string>
19 |     <string name="profile_updater">Profile Updater</string>
20 |     <string name="profile_updating">Profile Updating</string>
21 | </resources>
22 | 


--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
 1 | rootProject.name = "ClashMetaForAndroid"
 2 | 
 3 | include(":app")
 4 | include(":core")
 5 | include(":service")
 6 | include(":design")
 7 | include(":common")
 8 | include(":hideapi")
 9 | 
10 | pluginManagement {
11 |     repositories {
12 |         mavenLocal()
13 |         mavenCentral()
14 |         gradlePluginPortal()
15 |     }
16 | }
17 | 


--------------------------------------------------------------------------------