From 402d63bd9af87a225fe20258ff2ef55b55b4648c Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Thu, 12 Jan 2023 14:35:38 +0800 Subject: [PATCH] . --- .github/ISSUE_TEMPLATE/bug_cn.md | 34 + .github/ISSUE_TEMPLATE/config.yml | 5 + .gitignore | 5 + AndroidLibV2rayLite/README.md | 20 + CR.md | 29 + LICENSE | 674 ++++++++++++++++ README.md | 36 + V2rayNG/.gitignore | 10 + V2rayNG/app/.gitignore | 2 + V2rayNG/app/build.gradle | 151 ++++ V2rayNG/app/libs/arm64-v8a/libtun2socks.so | Bin 0 -> 152232 bytes V2rayNG/app/libs/armeabi-v7a/libtun2socks.so | Bin 0 -> 102532 bytes V2rayNG/app/libs/x86/libtun2socks.so | Bin 0 -> 154864 bytes V2rayNG/app/libs/x86_64/libtun2socks.so | Bin 0 -> 158960 bytes V2rayNG/app/proguard-rules.pro | 0 .../java/com/v2ray/ang/ApplicationTest.java | 13 + V2rayNG/app/src/main/AndroidManifest.xml | 187 +++++ .../app/src/main/assets/custom_routing_block | 1 + .../app/src/main/assets/custom_routing_direct | 132 +++ .../app/src/main/assets/custom_routing_proxy | 33 + .../app/src/main/assets/proxy_packagename.txt | 241 ++++++ V2rayNG/app/src/main/assets/v2ray_config.json | 105 +++ V2rayNG/app/src/main/ic_launcher-web.png | Bin 0 -> 11854 bytes .../ang/helper/ItemTouchHelperAdapter.java | 60 ++ .../ang/helper/ItemTouchHelperViewHolder.java | 41 + .../v2ray/ang/helper/OnStartDragListener.java | 33 + .../helper/SimpleItemTouchHelperCallback.java | 128 +++ .../kotlin/com/v2ray/ang/AngApplication.kt | 28 + .../main/kotlin/com/v2ray/ang/AppConfig.kt | 89 +++ .../kotlin/com/v2ray/ang/dto/AngConfig.kt | 32 + .../main/kotlin/com/v2ray/ang/dto/AppInfo.kt | 9 + .../kotlin/com/v2ray/ang/dto/EConfigType.kt | 15 + .../kotlin/com/v2ray/ang/dto/ERoutingMode.kt | 9 + .../v2ray/ang/dto/ServerAffiliationInfo.kt | 10 + .../kotlin/com/v2ray/ang/dto/ServerConfig.kt | 69 ++ .../kotlin/com/v2ray/ang/dto/ServersCache.kt | 4 + .../com/v2ray/ang/dto/SubscriptionItem.kt | 8 + .../kotlin/com/v2ray/ang/dto/V2rayConfig.kt | 462 +++++++++++ .../kotlin/com/v2ray/ang/dto/VmessQRCode.kt | 16 + .../kotlin/com/v2ray/ang/extension/_Ext.kt | 80 ++ .../com/v2ray/ang/receiver/TaskerReceiver.kt | 41 + .../com/v2ray/ang/receiver/WidgetProvider.kt | 85 ++ .../com/v2ray/ang/service/QSTileService.kt | 87 ++ .../com/v2ray/ang/service/ServiceControl.kt | 13 + .../ang/service/V2RayProxyOnlyService.kt | 56 ++ .../v2ray/ang/service/V2RayServiceManager.kt | 399 ++++++++++ .../com/v2ray/ang/service/V2RayTestService.kt | 45 ++ .../com/v2ray/ang/service/V2RayVpnService.kt | 333 ++++++++ .../kotlin/com/v2ray/ang/ui/BaseActivity.kt | 53 ++ .../com/v2ray/ang/ui/FragmentAdapter.kt | 17 + .../kotlin/com/v2ray/ang/ui/LogcatActivity.kt | 92 +++ .../kotlin/com/v2ray/ang/ui/MainActivity.kt | 658 +++++++++++++++ .../com/v2ray/ang/ui/MainRecyclerAdapter.kt | 260 ++++++ .../com/v2ray/ang/ui/PerAppProxyActivity.kt | 355 +++++++++ .../com/v2ray/ang/ui/PerAppProxyAdapter.kt | 87 ++ .../v2ray/ang/ui/RoutingSettingsActivity.kt | 38 + .../v2ray/ang/ui/RoutingSettingsFragment.kt | 158 ++++ .../com/v2ray/ang/ui/ScScannerActivity.kt | 45 ++ .../com/v2ray/ang/ui/ScSwitchActivity.kt | 22 + .../com/v2ray/ang/ui/ScannerActivity.kt | 116 +++ .../kotlin/com/v2ray/ang/ui/ServerActivity.kt | 412 ++++++++++ .../ang/ui/ServerCustomConfigActivity.kt | 144 ++++ .../com/v2ray/ang/ui/SettingsActivity.kt | 182 +++++ .../com/v2ray/ang/ui/SubEditActivity.kt | 133 ++++ .../com/v2ray/ang/ui/SubSettingActivity.kt | 55 ++ .../v2ray/ang/ui/SubSettingRecyclerAdapter.kt | 50 ++ .../kotlin/com/v2ray/ang/ui/TaskerActivity.kt | 117 +++ .../com/v2ray/ang/ui/UrlSchemeActivity.kt | 51 ++ .../com/v2ray/ang/ui/UserAssetActivity.kt | 216 +++++ .../com/v2ray/ang/util/AngConfigManager.kt | 750 ++++++++++++++++++ .../com/v2ray/ang/util/AppManagerUtil.kt | 43 + .../kotlin/com/v2ray/ang/util/MessageUtil.kt | 45 ++ .../kotlin/com/v2ray/ang/util/MmkvManager.kt | 179 +++++ .../com/v2ray/ang/util/MyContextWrapper.kt | 33 + .../com/v2ray/ang/util/QRCodeDecoder.kt | 100 +++ .../com/v2ray/ang/util/SpeedtestUtil.kt | 142 ++++ .../main/kotlin/com/v2ray/ang/util/Utils.kt | 439 ++++++++++ .../com/v2ray/ang/util/V2rayConfigUtil.kt | 432 ++++++++++ .../com/v2ray/ang/viewmodel/MainViewModel.kt | 235 ++++++ .../v2ray/ang/viewmodel/SettingsViewModel.kt | 64 ++ V2rayNG/app/src/main/res/anim/fade_in.xml | 6 + V2rayNG/app/src/main/res/anim/fade_out.xml | 6 + .../res/color/color_highlight_material.xml | 5 + .../main/res/drawable-hdpi/ic_stat_direct.png | Bin 0 -> 964 bytes .../main/res/drawable-hdpi/ic_stat_name.png | Bin 0 -> 389 bytes .../main/res/drawable-hdpi/ic_stat_proxy.png | Bin 0 -> 1216 bytes .../main/res/drawable-mdpi/ic_stat_direct.png | Bin 0 -> 739 bytes .../main/res/drawable-mdpi/ic_stat_name.png | Bin 0 -> 279 bytes .../main/res/drawable-mdpi/ic_stat_proxy.png | Bin 0 -> 882 bytes .../res/drawable-xhdpi/ic_stat_direct.png | Bin 0 -> 1144 bytes .../main/res/drawable-xhdpi/ic_stat_name.png | Bin 0 -> 494 bytes .../main/res/drawable-xhdpi/ic_stat_proxy.png | Bin 0 -> 1535 bytes .../src/main/res/drawable-xxhdpi/donate.png | Bin 0 -> 3237 bytes .../res/drawable-xxhdpi/ic_stat_direct.png | Bin 0 -> 1507 bytes .../main/res/drawable-xxhdpi/ic_stat_name.png | Bin 0 -> 698 bytes .../res/drawable-xxhdpi/ic_stat_proxy.png | Bin 0 -> 2333 bytes .../main/res/drawable-xxhdpi/side_nav_bar.xml | 9 + .../res/drawable-xxxhdpi/ic_stat_direct.png | Bin 0 -> 1915 bytes .../res/drawable-xxxhdpi/ic_stat_name.png | Bin 0 -> 867 bytes .../res/drawable-xxxhdpi/ic_stat_proxy.png | Bin 0 -> 3199 bytes .../src/main/res/drawable/ic_action_done.xml | 9 + .../main/res/drawable/ic_add_white_24dp.xml | 9 + .../drawable/ic_attach_money_black_24dp.xml | 9 + .../drawable/ic_attach_money_white_24dp.xml | 9 + .../res/drawable/ic_close_grey_800_24dp.xml | 10 + .../drawable/ic_cloud_download_white_24dp.xml | 9 + .../src/main/res/drawable/ic_copy_white.xml | 9 + .../res/drawable/ic_delete_black_24dp.xml | 9 + .../res/drawable/ic_delete_white_24dp.xml | 9 + .../drawable/ic_description_black_24dp.xml | 9 + .../drawable/ic_description_white_24dp.xml | 9 + .../main/res/drawable/ic_edit_black_24dp.xml | 9 + .../src/main/res/drawable/ic_fab_check.xml | 9 + .../res/drawable/ic_feedback_white_24dp.xml | 9 + .../main/res/drawable/ic_file_white_24dp.xml | 34 + .../src/main/res/drawable/ic_image_photo.xml | 9 + .../main/res/drawable/ic_info_black_24dp.xml | 9 + .../res/drawable/ic_logcat_white_24dp.xml | 9 + .../ic_outline_filter_alt_white_24.xml | 5 + .../res/drawable/ic_qu_scan_black_24dp.xml | 19 + .../drawable/ic_qu_settings_black_24dp.xml | 13 + .../res/drawable/ic_qu_switch_black_24dp.xml | 18 + .../res/drawable/ic_rounded_corner_grey.xml | 6 + .../res/drawable/ic_rounded_corner_theme.xml | 6 + .../main/res/drawable/ic_save_white_24dp.xml | 9 + .../main/res/drawable/ic_scan_black_24dp.xml | 19 + .../res/drawable/ic_select_all_white_24dp.xml | 10 + .../res/drawable/ic_settings_white_24dp.xml | 9 + .../main/res/drawable/ic_share_black_24dp.xml | 9 + .../main/res/drawable/ic_share_white_24dp.xml | 9 + .../res/drawable/ic_shortcut_background.xml | 7 + .../drawable/ic_subscriptions_black_24dp.xml | 9 + .../drawable/ic_subscriptions_white_24dp.xml | 9 + .../res/drawable/ic_whatshot_black_24dp.xml | 9 + .../res/drawable/ic_whatshot_white_24dp.xml | 9 + .../src/main/res/drawable/nav_header_bg.png | Bin 0 -> 11934 bytes .../main/res/layout/activity_bypass_list.xml | 92 +++ .../src/main/res/layout/activity_logcat.xml | 38 + .../app/src/main/res/layout/activity_main.xml | 126 +++ .../app/src/main/res/layout/activity_none.xml | 5 + .../res/layout/activity_routing_settings.xml | 17 + .../layout/activity_server_custom_config.xml | 80 ++ .../layout/activity_server_shadowsocks.xml | 129 +++ .../main/res/layout/activity_server_socks.xml | 130 +++ .../res/layout/activity_server_trojan.xml | 215 +++++ .../main/res/layout/activity_server_vless.xml | 236 ++++++ .../main/res/layout/activity_server_vmess.xml | 235 ++++++ .../src/main/res/layout/activity_settings.xml | 8 + .../src/main/res/layout/activity_sub_edit.xml | 107 +++ .../main/res/layout/activity_sub_setting.xml | 22 + .../src/main/res/layout/activity_tasker.xml | 48 ++ .../main/res/layout/dialog_config_filter.xml | 44 + .../res/layout/fragment_routing_settings.xml | 39 + .../app/src/main/res/layout/item_qrcode.xml | 15 + .../res/layout/item_recycler_bypass_list.xml | 52 ++ .../main/res/layout/item_recycler_footer.xml | 32 + .../main/res/layout/item_recycler_main.xml | 189 +++++ .../res/layout/item_recycler_sub_setting.xml | 109 +++ .../res/layout/item_recycler_user_asset.xml | 60 ++ .../app/src/main/res/layout/nav_header.xml | 25 + .../app/src/main/res/layout/nav_toolbar.xml | 13 + V2rayNG/app/src/main/res/layout/nav_view.xml | 9 + .../res/layout/preference_with_help_link.xml | 11 + .../app/src/main/res/layout/tls_layout.xml | 108 +++ .../app/src/main/res/layout/widget_switch.xml | 15 + .../app/src/main/res/menu/action_server.xml | 14 + .../src/main/res/menu/action_sub_setting.xml | 19 + V2rayNG/app/src/main/res/menu/menu_asset.xml | 14 + .../src/main/res/menu/menu_bypass_list.xml | 34 + V2rayNG/app/src/main/res/menu/menu_drawer.xml | 41 + V2rayNG/app/src/main/res/menu/menu_logcat.xml | 14 + V2rayNG/app/src/main/res/menu/menu_main.xml | 102 +++ .../app/src/main/res/menu/menu_routing.xml | 31 + .../app/src/main/res/menu/menu_scanner.xml | 9 + .../res/mipmap-anydpi-v26/ic_launcher.xml | 5 + .../mipmap-anydpi-v26/ic_launcher_round.xml | 5 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 1236 bytes .../mipmap-hdpi/ic_launcher_foreground.png | Bin 0 -> 877 bytes .../res/mipmap-hdpi/ic_launcher_round.png | Bin 0 -> 2968 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 910 bytes .../mipmap-mdpi/ic_launcher_foreground.png | Bin 0 -> 582 bytes .../res/mipmap-mdpi/ic_launcher_round.png | Bin 0 -> 1913 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 1667 bytes .../mipmap-xhdpi/ic_launcher_foreground.png | Bin 0 -> 1159 bytes .../res/mipmap-xhdpi/ic_launcher_round.png | Bin 0 -> 4248 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 2548 bytes .../mipmap-xxhdpi/ic_launcher_foreground.png | Bin 0 -> 1887 bytes .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin 0 -> 6689 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 3595 bytes .../mipmap-xxxhdpi/ic_launcher_foreground.png | Bin 0 -> 2715 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin 0 -> 9693 bytes V2rayNG/app/src/main/res/raw/licenses.xml | 49 ++ .../app/src/main/res/values-fa/strings.xml | 229 ++++++ .../app/src/main/res/values-ru/strings.xml | 229 ++++++ .../values-sw360dp-v13/values-preference.xml | 5 + .../app/src/main/res/values-vi/strings.xml | 228 ++++++ .../src/main/res/values-zh-rCN/strings.xml | 226 ++++++ .../src/main/res/values-zh-rTW/strings.xml | 226 ++++++ V2rayNG/app/src/main/res/values/arrays.xml | 180 +++++ V2rayNG/app/src/main/res/values/attrs.xml | 5 + V2rayNG/app/src/main/res/values/colors.xml | 24 + V2rayNG/app/src/main/res/values/dimens.xml | 16 + .../res/values/ic_launcher_background.xml | 4 + V2rayNG/app/src/main/res/values/strings.xml | 231 ++++++ V2rayNG/app/src/main/res/values/styles.xml | 52 ++ .../src/main/res/xml/app_widget_provider.xml | 6 + .../app/src/main/res/xml/pref_settings.xml | 141 ++++ V2rayNG/app/src/main/res/xml/shortcuts.xml | 45 ++ .../java/com/v2ray/ang/ExampleUnitTest.java | 15 + .../kotlin/com/v2ray/ang/ExampleUnitTest.kt | 37 + V2rayNG/build.gradle | 31 + V2rayNG/gradle.properties | 22 + V2rayNG/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 53636 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + V2rayNG/gradlew | 160 ++++ V2rayNG/gradlew.bat | 90 +++ V2rayNG/settings.gradle | 1 + 217 files changed, 14697 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_cn.md create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .gitignore create mode 100644 AndroidLibV2rayLite/README.md create mode 100644 CR.md create mode 100644 LICENSE create mode 100644 README.md create mode 100644 V2rayNG/.gitignore create mode 100644 V2rayNG/app/.gitignore create mode 100644 V2rayNG/app/build.gradle create mode 100644 V2rayNG/app/libs/arm64-v8a/libtun2socks.so create mode 100644 V2rayNG/app/libs/armeabi-v7a/libtun2socks.so create mode 100644 V2rayNG/app/libs/x86/libtun2socks.so create mode 100644 V2rayNG/app/libs/x86_64/libtun2socks.so create mode 100644 V2rayNG/app/proguard-rules.pro create mode 100644 V2rayNG/app/src/androidTest/java/com/v2ray/ang/ApplicationTest.java create mode 100644 V2rayNG/app/src/main/AndroidManifest.xml create mode 100644 V2rayNG/app/src/main/assets/custom_routing_block create mode 100644 V2rayNG/app/src/main/assets/custom_routing_direct create mode 100644 V2rayNG/app/src/main/assets/custom_routing_proxy create mode 100644 V2rayNG/app/src/main/assets/proxy_packagename.txt create mode 100644 V2rayNG/app/src/main/assets/v2ray_config.json create mode 100644 V2rayNG/app/src/main/ic_launcher-web.png create mode 100644 V2rayNG/app/src/main/java/com/v2ray/ang/helper/ItemTouchHelperAdapter.java create mode 100644 V2rayNG/app/src/main/java/com/v2ray/ang/helper/ItemTouchHelperViewHolder.java create mode 100644 V2rayNG/app/src/main/java/com/v2ray/ang/helper/OnStartDragListener.java create mode 100644 V2rayNG/app/src/main/java/com/v2ray/ang/helper/SimpleItemTouchHelperCallback.java create mode 100644 V2rayNG/app/src/main/kotlin/com/v2ray/ang/AngApplication.kt create mode 100644 V2rayNG/app/src/main/kotlin/com/v2ray/ang/AppConfig.kt create mode 100644 V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/AngConfig.kt create mode 100644 V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/AppInfo.kt create mode 100644 V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/EConfigType.kt create mode 100644 V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/ERoutingMode.kt create mode 100644 V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/ServerAffiliationInfo.kt create mode 100644 V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/ServerConfig.kt create mode 100644 V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/ServersCache.kt create mode 100644 V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/SubscriptionItem.kt create mode 100644 V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/V2rayConfig.kt create mode 100644 V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/VmessQRCode.kt create mode 100644 V2rayNG/app/src/main/kotlin/com/v2ray/ang/extension/_Ext.kt create mode 100644 V2rayNG/app/src/main/kotlin/com/v2ray/ang/receiver/TaskerReceiver.kt create mode 100644 V2rayNG/app/src/main/kotlin/com/v2ray/ang/receiver/WidgetProvider.kt create mode 100644 V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/QSTileService.kt create mode 100644 V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/ServiceControl.kt create mode 100644 V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayProxyOnlyService.kt create mode 100644 V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayServiceManager.kt create mode 100644 V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayTestService.kt create mode 100644 V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayVpnService.kt create mode 100644 V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/BaseActivity.kt create mode 100644 V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/FragmentAdapter.kt create mode 100644 V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/LogcatActivity.kt create mode 100644 V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt create mode 100644 V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainRecyclerAdapter.kt create mode 100644 V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/PerAppProxyActivity.kt create mode 100644 V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/PerAppProxyAdapter.kt create mode 100644 V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/RoutingSettingsActivity.kt create mode 100644 V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/RoutingSettingsFragment.kt create mode 100644 V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ScScannerActivity.kt create mode 100644 V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ScSwitchActivity.kt create mode 100644 V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ScannerActivity.kt create mode 100644 V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ServerActivity.kt create mode 100644 V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ServerCustomConfigActivity.kt create mode 100644 V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/SettingsActivity.kt create mode 100644 V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/SubEditActivity.kt create mode 100644 V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/SubSettingActivity.kt create mode 100644 V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/SubSettingRecyclerAdapter.kt create mode 100644 V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/TaskerActivity.kt create mode 100644 V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/UrlSchemeActivity.kt create mode 100644 V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/UserAssetActivity.kt create mode 100644 V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/AngConfigManager.kt create mode 100644 V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/AppManagerUtil.kt create mode 100644 V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/MessageUtil.kt create mode 100644 V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/MmkvManager.kt create mode 100644 V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/MyContextWrapper.kt create mode 100644 V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/QRCodeDecoder.kt create mode 100644 V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/SpeedtestUtil.kt create mode 100644 V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/Utils.kt create mode 100644 V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/V2rayConfigUtil.kt create mode 100644 V2rayNG/app/src/main/kotlin/com/v2ray/ang/viewmodel/MainViewModel.kt create mode 100644 V2rayNG/app/src/main/kotlin/com/v2ray/ang/viewmodel/SettingsViewModel.kt create mode 100644 V2rayNG/app/src/main/res/anim/fade_in.xml create mode 100644 V2rayNG/app/src/main/res/anim/fade_out.xml create mode 100644 V2rayNG/app/src/main/res/color/color_highlight_material.xml create mode 100644 V2rayNG/app/src/main/res/drawable-hdpi/ic_stat_direct.png create mode 100644 V2rayNG/app/src/main/res/drawable-hdpi/ic_stat_name.png create mode 100644 V2rayNG/app/src/main/res/drawable-hdpi/ic_stat_proxy.png create mode 100644 V2rayNG/app/src/main/res/drawable-mdpi/ic_stat_direct.png create mode 100644 V2rayNG/app/src/main/res/drawable-mdpi/ic_stat_name.png create mode 100644 V2rayNG/app/src/main/res/drawable-mdpi/ic_stat_proxy.png create mode 100644 V2rayNG/app/src/main/res/drawable-xhdpi/ic_stat_direct.png create mode 100644 V2rayNG/app/src/main/res/drawable-xhdpi/ic_stat_name.png create mode 100644 V2rayNG/app/src/main/res/drawable-xhdpi/ic_stat_proxy.png create mode 100644 V2rayNG/app/src/main/res/drawable-xxhdpi/donate.png create mode 100644 V2rayNG/app/src/main/res/drawable-xxhdpi/ic_stat_direct.png create mode 100644 V2rayNG/app/src/main/res/drawable-xxhdpi/ic_stat_name.png create mode 100644 V2rayNG/app/src/main/res/drawable-xxhdpi/ic_stat_proxy.png create mode 100644 V2rayNG/app/src/main/res/drawable-xxhdpi/side_nav_bar.xml create mode 100644 V2rayNG/app/src/main/res/drawable-xxxhdpi/ic_stat_direct.png create mode 100644 V2rayNG/app/src/main/res/drawable-xxxhdpi/ic_stat_name.png create mode 100644 V2rayNG/app/src/main/res/drawable-xxxhdpi/ic_stat_proxy.png create mode 100644 V2rayNG/app/src/main/res/drawable/ic_action_done.xml create mode 100644 V2rayNG/app/src/main/res/drawable/ic_add_white_24dp.xml create mode 100644 V2rayNG/app/src/main/res/drawable/ic_attach_money_black_24dp.xml create mode 100644 V2rayNG/app/src/main/res/drawable/ic_attach_money_white_24dp.xml create mode 100644 V2rayNG/app/src/main/res/drawable/ic_close_grey_800_24dp.xml create mode 100644 V2rayNG/app/src/main/res/drawable/ic_cloud_download_white_24dp.xml create mode 100644 V2rayNG/app/src/main/res/drawable/ic_copy_white.xml create mode 100644 V2rayNG/app/src/main/res/drawable/ic_delete_black_24dp.xml create mode 100644 V2rayNG/app/src/main/res/drawable/ic_delete_white_24dp.xml create mode 100644 V2rayNG/app/src/main/res/drawable/ic_description_black_24dp.xml create mode 100644 V2rayNG/app/src/main/res/drawable/ic_description_white_24dp.xml create mode 100644 V2rayNG/app/src/main/res/drawable/ic_edit_black_24dp.xml create mode 100644 V2rayNG/app/src/main/res/drawable/ic_fab_check.xml create mode 100644 V2rayNG/app/src/main/res/drawable/ic_feedback_white_24dp.xml create mode 100644 V2rayNG/app/src/main/res/drawable/ic_file_white_24dp.xml create mode 100644 V2rayNG/app/src/main/res/drawable/ic_image_photo.xml create mode 100644 V2rayNG/app/src/main/res/drawable/ic_info_black_24dp.xml create mode 100644 V2rayNG/app/src/main/res/drawable/ic_logcat_white_24dp.xml create mode 100644 V2rayNG/app/src/main/res/drawable/ic_outline_filter_alt_white_24.xml create mode 100644 V2rayNG/app/src/main/res/drawable/ic_qu_scan_black_24dp.xml create mode 100644 V2rayNG/app/src/main/res/drawable/ic_qu_settings_black_24dp.xml create mode 100644 V2rayNG/app/src/main/res/drawable/ic_qu_switch_black_24dp.xml create mode 100644 V2rayNG/app/src/main/res/drawable/ic_rounded_corner_grey.xml create mode 100644 V2rayNG/app/src/main/res/drawable/ic_rounded_corner_theme.xml create mode 100644 V2rayNG/app/src/main/res/drawable/ic_save_white_24dp.xml create mode 100644 V2rayNG/app/src/main/res/drawable/ic_scan_black_24dp.xml create mode 100644 V2rayNG/app/src/main/res/drawable/ic_select_all_white_24dp.xml create mode 100644 V2rayNG/app/src/main/res/drawable/ic_settings_white_24dp.xml create mode 100644 V2rayNG/app/src/main/res/drawable/ic_share_black_24dp.xml create mode 100644 V2rayNG/app/src/main/res/drawable/ic_share_white_24dp.xml create mode 100644 V2rayNG/app/src/main/res/drawable/ic_shortcut_background.xml create mode 100644 V2rayNG/app/src/main/res/drawable/ic_subscriptions_black_24dp.xml create mode 100644 V2rayNG/app/src/main/res/drawable/ic_subscriptions_white_24dp.xml create mode 100644 V2rayNG/app/src/main/res/drawable/ic_whatshot_black_24dp.xml create mode 100644 V2rayNG/app/src/main/res/drawable/ic_whatshot_white_24dp.xml create mode 100644 V2rayNG/app/src/main/res/drawable/nav_header_bg.png create mode 100644 V2rayNG/app/src/main/res/layout/activity_bypass_list.xml create mode 100644 V2rayNG/app/src/main/res/layout/activity_logcat.xml create mode 100644 V2rayNG/app/src/main/res/layout/activity_main.xml create mode 100644 V2rayNG/app/src/main/res/layout/activity_none.xml create mode 100644 V2rayNG/app/src/main/res/layout/activity_routing_settings.xml create mode 100644 V2rayNG/app/src/main/res/layout/activity_server_custom_config.xml create mode 100644 V2rayNG/app/src/main/res/layout/activity_server_shadowsocks.xml create mode 100644 V2rayNG/app/src/main/res/layout/activity_server_socks.xml create mode 100644 V2rayNG/app/src/main/res/layout/activity_server_trojan.xml create mode 100644 V2rayNG/app/src/main/res/layout/activity_server_vless.xml create mode 100644 V2rayNG/app/src/main/res/layout/activity_server_vmess.xml create mode 100644 V2rayNG/app/src/main/res/layout/activity_settings.xml create mode 100644 V2rayNG/app/src/main/res/layout/activity_sub_edit.xml create mode 100644 V2rayNG/app/src/main/res/layout/activity_sub_setting.xml create mode 100644 V2rayNG/app/src/main/res/layout/activity_tasker.xml create mode 100644 V2rayNG/app/src/main/res/layout/dialog_config_filter.xml create mode 100644 V2rayNG/app/src/main/res/layout/fragment_routing_settings.xml create mode 100644 V2rayNG/app/src/main/res/layout/item_qrcode.xml create mode 100644 V2rayNG/app/src/main/res/layout/item_recycler_bypass_list.xml create mode 100644 V2rayNG/app/src/main/res/layout/item_recycler_footer.xml create mode 100644 V2rayNG/app/src/main/res/layout/item_recycler_main.xml create mode 100644 V2rayNG/app/src/main/res/layout/item_recycler_sub_setting.xml create mode 100644 V2rayNG/app/src/main/res/layout/item_recycler_user_asset.xml create mode 100644 V2rayNG/app/src/main/res/layout/nav_header.xml create mode 100644 V2rayNG/app/src/main/res/layout/nav_toolbar.xml create mode 100644 V2rayNG/app/src/main/res/layout/nav_view.xml create mode 100644 V2rayNG/app/src/main/res/layout/preference_with_help_link.xml create mode 100644 V2rayNG/app/src/main/res/layout/tls_layout.xml create mode 100644 V2rayNG/app/src/main/res/layout/widget_switch.xml create mode 100644 V2rayNG/app/src/main/res/menu/action_server.xml create mode 100644 V2rayNG/app/src/main/res/menu/action_sub_setting.xml create mode 100644 V2rayNG/app/src/main/res/menu/menu_asset.xml create mode 100644 V2rayNG/app/src/main/res/menu/menu_bypass_list.xml create mode 100644 V2rayNG/app/src/main/res/menu/menu_drawer.xml create mode 100644 V2rayNG/app/src/main/res/menu/menu_logcat.xml create mode 100644 V2rayNG/app/src/main/res/menu/menu_main.xml create mode 100644 V2rayNG/app/src/main/res/menu/menu_routing.xml create mode 100644 V2rayNG/app/src/main/res/menu/menu_scanner.xml create mode 100644 V2rayNG/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml create mode 100644 V2rayNG/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml create mode 100644 V2rayNG/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 V2rayNG/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png create mode 100644 V2rayNG/app/src/main/res/mipmap-hdpi/ic_launcher_round.png create mode 100644 V2rayNG/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 V2rayNG/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png create mode 100644 V2rayNG/app/src/main/res/mipmap-mdpi/ic_launcher_round.png create mode 100644 V2rayNG/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 V2rayNG/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png create mode 100644 V2rayNG/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png create mode 100644 V2rayNG/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 V2rayNG/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png create mode 100644 V2rayNG/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png create mode 100644 V2rayNG/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 V2rayNG/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png create mode 100644 V2rayNG/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png create mode 100644 V2rayNG/app/src/main/res/raw/licenses.xml create mode 100644 V2rayNG/app/src/main/res/values-fa/strings.xml create mode 100644 V2rayNG/app/src/main/res/values-ru/strings.xml create mode 100644 V2rayNG/app/src/main/res/values-sw360dp-v13/values-preference.xml create mode 100644 V2rayNG/app/src/main/res/values-vi/strings.xml create mode 100644 V2rayNG/app/src/main/res/values-zh-rCN/strings.xml create mode 100644 V2rayNG/app/src/main/res/values-zh-rTW/strings.xml create mode 100644 V2rayNG/app/src/main/res/values/arrays.xml create mode 100644 V2rayNG/app/src/main/res/values/attrs.xml create mode 100644 V2rayNG/app/src/main/res/values/colors.xml create mode 100644 V2rayNG/app/src/main/res/values/dimens.xml create mode 100644 V2rayNG/app/src/main/res/values/ic_launcher_background.xml create mode 100644 V2rayNG/app/src/main/res/values/strings.xml create mode 100644 V2rayNG/app/src/main/res/values/styles.xml create mode 100644 V2rayNG/app/src/main/res/xml/app_widget_provider.xml create mode 100644 V2rayNG/app/src/main/res/xml/pref_settings.xml create mode 100644 V2rayNG/app/src/main/res/xml/shortcuts.xml create mode 100644 V2rayNG/app/src/test/java/com/v2ray/ang/ExampleUnitTest.java create mode 100644 V2rayNG/app/src/test/kotlin/com/v2ray/ang/ExampleUnitTest.kt create mode 100644 V2rayNG/build.gradle create mode 100644 V2rayNG/gradle.properties create mode 100644 V2rayNG/gradle/wrapper/gradle-wrapper.jar create mode 100644 V2rayNG/gradle/wrapper/gradle-wrapper.properties create mode 100644 V2rayNG/gradlew create mode 100644 V2rayNG/gradlew.bat create mode 100644 V2rayNG/settings.gradle diff --git a/.github/ISSUE_TEMPLATE/bug_cn.md b/.github/ISSUE_TEMPLATE/bug_cn.md new file mode 100644 index 00000000..eaa1bc38 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_cn.md @@ -0,0 +1,34 @@ +--- +name: v2rayNG程序问题 +about: 创建一个报告来帮助我们改进 +--- + +在提出问题前请先自行排除服务器端问题,同时也请通过搜索确认是否有人提出过相同问题。 + + +### 预期行为 +描述你认为应该发生什么 + +### 实际行为 +描述实际发生了什么 + +### 复现方法 +1. +2. +3. + +### 日志信息 +
+ +通过`adb logcat -s com.v2ray.ang GoLog V2rayConfigUtilGoLog Main`获取日志。请自行删减日志中可能出现的敏感信息。 + +如果问题可重现,建议先执行`adb logcat -c`清空系统日志再执行上述命令,再操作重现问题。 +``` +在这里粘贴日志 +``` +
+ +### 环境信息 + +### 额外信息(可选) + diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000..3cd47d28 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: V2Ray程序问题 + url: https://github.com/v2fly/v2ray-core/ + about: 如果您有V2Ray而非v2rayNG的问题,请至这个链接讨论。 \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..0963c509 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +*.dat +*.jks +V2rayNG/app/release/output.json +.idea/ +.gradle/ diff --git a/AndroidLibV2rayLite/README.md b/AndroidLibV2rayLite/README.md new file mode 100644 index 00000000..118eefcf --- /dev/null +++ b/AndroidLibV2rayLite/README.md @@ -0,0 +1,20 @@ +# AndroidLibV2rayLite + +### Preparation +- latest Ubuntu environment +- At lease 30G free space +- Get Repo [AndroidLibV2rayLite](https://github.com/2dust/AndroidLibV2rayLite) or [AndroidLibXrayLite](https://github.com/2dust/AndroidLibXrayLite) +### Prepare Go +- Go to https://golang.org/doc/install and install latest go +- Make sure `go version` works as expected +### Prepare gomobile +- Go to https://pkg.go.dev/golang.org/x/mobile/cmd/gomobile and install gomobile +- export PATH=$PATH:~/go/bin +- Make sure `gomobile init` works as expected +### Prepare NDK +- Go to https://developer.android.com/ndk/downloads and install latest NDK +- export PATH=$PATH: +- Make sure `ndk-build -v` works as expected +### Make +- sudo apt install make +- Read and understand [build script](https://github.com/2dust/AndroidLibV2rayLite/blob/master/Makefile) diff --git a/CR.md b/CR.md new file mode 100644 index 00000000..0ec148b6 --- /dev/null +++ b/CR.md @@ -0,0 +1,29 @@ +v2rayNG 隐私条款 + +最后更新 2017-11-22 + +v2rayNG 尊重并保护所有用户的个人隐私权,为此我们向大众公开这份隐私条款。**您使用 v2rayNG 即代表您以阅读并同意了这份条款,如果您不同意这份条款请立即停止使用并卸载 v2rayNG。** + +1. 信息收集 + + v2rayNG 软件自身不会发送任何信息到开发者,但是您下载软件的应用市场(如 Google Play)可能会收集关于应用运行状态的相关信息并提供给 v2rayNG 开发者。有关这些信息,请阅读您使用的应用市场所提供的隐私条款。 + + v2rayNG 软件中可能包含需要通过 IAP 支付解锁的功能,您的支付信息将由相关的 IAP 渠道进行处理,而我们对支付信息没有访问权。 + + 当您向 v2rayNG 开发者反馈软件运行中的错误时,开发者可能会要求您提供软件以及系统的日志以帮助确认问题的原因。因日志中可能包括敏感信息,此类信息只能由您自己操作发送。**我们不对任何传输服务的安全性和隐私性做任何明示或暗示的担保,请您在传送相关信息时选择可以您自身可以接受的方式。** + +2. 信息共享 + + 我们不会向任何第三方出售收集到的用户数据。我们可能向外部开发者提供信息以协助软件的开发,但是在提供信息之前我们会传达相关保密义务并确定其可以遵守。 + +3. 信息存留 + + 除非有相关法律规定,我们会在 30 天内清除不需要继续使用的用户数据,或将统计数据整合为无法识别单个用户的综合报告。 + +4. 信息泄露 + + 我们会使用合理的技术和安全手段尽力保护用户的数据,但是无法保证数据的绝对安全。如果我们确认数据发生了泄露,我们会在 7 天内通过可用的渠道通知用户。**您同意不向我们追责任何因不可抗力而造成的损失。** + +5. 条款修改 + + 我们保留修改这份隐私条款的权利,但是会确保在更新条款前至少 30 天通过我们的可用渠道和应用内提示来通知用户。**在新条款生效后继续使用软件即表示您同意修改后的隐私条款。** diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..94a9ed02 --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/README.md b/README.md new file mode 100644 index 00000000..34c837dc --- /dev/null +++ b/README.md @@ -0,0 +1,36 @@ +# v2rayNG + +A V2Ray client for Android, support [Xray core](https://github.com/XTLS/Xray-core) and [v2fly core](https://github.com/v2fly/v2ray-core) + +[![API](https://img.shields.io/badge/API-21%2B-yellow.svg?style=flat)](https://developer.android.com/about/versions/lollipop) +[![Kotlin Version](https://img.shields.io/badge/Kotlin-1.6.21-blue.svg)](https://kotlinlang.org) +[![GitHub commit activity](https://img.shields.io/github/commit-activity/m/2dust/v2rayNG)](https://github.com/2dust/v2rayNG/commits/master) +[![CodeFactor](https://www.codefactor.io/repository/github/2dust/v2rayng/badge)](https://www.codefactor.io/repository/github/2dust/v2rayng) +[![GitHub Releases](https://img.shields.io/github/downloads/2dust/v2rayNG/latest/total?logo=github)](https://github.com/2dust/v2rayNG/releases) +[![Chat on Telegram](https://img.shields.io/badge/Chat%20on-Telegram-brightgreen.svg)](https://t.me/v2rayn) + + +Get it on Google Play + + +### Telegram Channel +[github_2dust](https://t.me/github_2dust) + +### Usage + +#### Geoip and Geosite +- geoip.dat and geosite.dat files are in `Android/data/com.v2ray.ang/files/assets` (path may differ on some Android device) +- download feature will get enhanced version in this [repo](https://github.com/Loyalsoldier/v2ray-rules-dat) (Note it need a working proxy) +- latest official [domain list](https://github.com/v2fly/domain-list-community) and [ip list](https://github.com/v2fly/geoip) can be imported manually +- possible to use third party dat file in the same folder, like [h2y](https://guide.v2fly.org/routing/sitedata.html#%E5%A4%96%E7%BD%AE%E7%9A%84%E5%9F%9F%E5%90%8D%E6%96%87%E4%BB%B6) + +### More in our [wiki](https://github.com/2dust/v2rayNG/wiki) + +### Development guide + +Android project under V2rayNG folder can be compiled directly in Android Studio, or using Gradle wrapper. But the v2ray core inside the aar is (probably) outdated. +The aar can be compiled from the Golang project [AndroidLibV2rayLite](https://github.com/2dust/AndroidLibV2rayLite) or [AndroidLibXrayLite](https://github.com/2dust/AndroidLibXrayLite). +For a quick start, read guide for [Go Mobile](https://github.com/golang/go/wiki/Mobile) and [Makefiles for Go Developers](https://tutorialedge.net/golang/makefiles-for-go-developers/) + +v2rayNG can run on Android Emulators. For WSA, VPN permission need to be granted via +`appops set [package name] ACTIVATE_VPN allow` diff --git a/V2rayNG/.gitignore b/V2rayNG/.gitignore new file mode 100644 index 00000000..16eb6ec8 --- /dev/null +++ b/V2rayNG/.gitignore @@ -0,0 +1,10 @@ +*.iml +.gradle +/local.properties +/.idea +.DS_Store +/build +/captures +*.apk +signing.properties +*.aar diff --git a/V2rayNG/app/.gitignore b/V2rayNG/app/.gitignore new file mode 100644 index 00000000..2abde4aa --- /dev/null +++ b/V2rayNG/app/.gitignore @@ -0,0 +1,2 @@ +/build +/google-services.json diff --git a/V2rayNG/app/build.gradle b/V2rayNG/app/build.gradle new file mode 100644 index 00000000..3600fe3b --- /dev/null +++ b/V2rayNG/app/build.gradle @@ -0,0 +1,151 @@ +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' + +Properties props = new Properties() +props.load(new FileInputStream(new File('local.properties'))) + +android { + compileSdkVersion Integer.parseInt("$compileSdkVer") + buildToolsVersion "$buildToolsVer" + + compileOptions { + targetCompatibility = "8" + sourceCompatibility = "8" + } + + defaultConfig { + applicationId "com.v2ray.ang" + minSdkVersion 21 + targetSdkVersion Integer.parseInt("$targetSdkVer") + multiDexEnabled true + versionCode 495 + versionName "1.7.33" + } + + if (props["sign"]) { + signingConfigs { + release { + storeFile file("../key.jks") + keyAlias 'ang' + keyPassword '123456' + storePassword '123456' + } + debug { + storeFile file("../key.jks") + keyAlias 'ang' + keyPassword '123456' + storePassword '123456' + } + } + } + + buildTypes { + release { + minifyEnabled false + zipAlignEnabled false + shrinkResources false + if (props["sign"]) { + signingConfig signingConfigs.release + } + ndk.abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a' +// proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + debug { + minifyEnabled false + zipAlignEnabled false + shrinkResources false + if (props["sign"]) { + signingConfig signingConfigs.release + } + ndk.abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a' + } + } + + sourceSets { + main { + jniLibs.srcDirs = ['libs'] + java.srcDirs += 'src/main/kotlin' + } + } + + kotlinOptions { + jvmTarget = JavaVersion.VERSION_1_8 + } + + splits { + abi { + enable true + reset() + include 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a' //select ABIs to build APKs for + universalApk true //generate an additional APK that contains all the ABIs + } + } + + // map for the version code + project.ext.versionCodes = ['armeabi-v7a': 1, 'arm64-v8a': 2, 'x86': 3, 'x86_64': 4] + + android.applicationVariants.all { variant -> + // assign different version code for each output + variant.outputs.each { output -> + output.outputFileName = "v2rayNG_" + variant.versionName + "_" + output.getFilter(com.android.build.OutputFile.ABI) + ".apk" + + output.versionCodeOverride = + project.ext.versionCodes.get(output.getFilter(com.android.build.OutputFile.ABI), 0) * + 1000000 + android.defaultConfig.versionCode + } + } + + buildFeatures { + viewBinding true + } +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.aar', '*.jar'], exclude: []) + testImplementation 'junit:junit:4.13.2' + + // Androidx + implementation 'androidx.constraintlayout:constraintlayout:2.1.4' + implementation 'androidx.legacy:legacy-support-v4:1.0.0' + implementation 'androidx.appcompat:appcompat:1.4.2' + implementation 'com.google.android.material:material:1.6.1' + implementation 'androidx.cardview:cardview:1.0.0' + implementation 'androidx.preference:preference-ktx:1.2.0' + implementation 'androidx.recyclerview:recyclerview:1.2.1' + implementation 'androidx.fragment:fragment-ktx:1.5.2' + implementation 'androidx.multidex:multidex:2.0.1' + implementation 'androidx.viewpager2:viewpager2:1.1.0-beta01' + + // Androidx ktx + implementation 'androidx.activity:activity-ktx:1.5.1' + implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1' + implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.5.1' + implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.1' + + //kotlin + implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2" + + implementation 'com.tencent:mmkv-static:1.2.12' + implementation 'com.google.code.gson:gson:2.8.9' + implementation 'io.reactivex:rxjava:1.3.4' + implementation 'io.reactivex:rxandroid:1.2.1' + implementation 'com.tbruyelle.rxpermissions:rxpermissions:0.9.4@aar' + implementation 'me.dm7.barcodescanner:core:1.9.8' + implementation 'me.dm7.barcodescanner:zxing:1.9.8' + implementation 'com.github.jorgecastilloprz:fabprogresscircle:1.01@aar' + implementation 'me.drakeet.support:toastcompat:1.1.0' + implementation 'com.blacksquircle.ui:editorkit:2.1.1' + implementation 'com.blacksquircle.ui:language-base:2.1.1' + implementation 'com.blacksquircle.ui:language-json:2.1.1' +} + +//buildscript { +// repositories { +// google() +// mavenCentral() +// maven { url 'https://maven.google.com' } +// maven { url 'https://jitpack.io' } +// } +//} diff --git a/V2rayNG/app/libs/arm64-v8a/libtun2socks.so b/V2rayNG/app/libs/arm64-v8a/libtun2socks.so new file mode 100644 index 0000000000000000000000000000000000000000..3dbdde7ad453ddc30c3e5e6414f0771a6a19d856 GIT binary patch literal 152232 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 vL-8!*nB|JWh-g{!}_#V-d9oUGaMwgza?j_5| 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

}#diz+ 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

0Z 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 PcIwf?i|7V+0)77tINEWN literal 0 HcmV?d00001 diff --git a/V2rayNG/app/proguard-rules.pro b/V2rayNG/app/proguard-rules.pro new file mode 100644 index 00000000..e69de29b diff --git a/V2rayNG/app/src/androidTest/java/com/v2ray/ang/ApplicationTest.java b/V2rayNG/app/src/androidTest/java/com/v2ray/ang/ApplicationTest.java new file mode 100644 index 00000000..e221e714 --- /dev/null +++ b/V2rayNG/app/src/androidTest/java/com/v2ray/ang/ApplicationTest.java @@ -0,0 +1,13 @@ +package com.v2ray.ang; + +import android.app.Application; +import android.test.ApplicationTestCase; + +/** + * Testing Fundamentals + */ +public class ApplicationTest extends ApplicationTestCase { + public ApplicationTest() { + super(Application.class); + } +} \ No newline at end of file diff --git a/V2rayNG/app/src/main/AndroidManifest.xml b/V2rayNG/app/src/main/AndroidManifest.xml new file mode 100644 index 00000000..f90f1274 --- /dev/null +++ b/V2rayNG/app/src/main/AndroidManifest.xml @@ -0,0 +1,187 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/V2rayNG/app/src/main/assets/custom_routing_block b/V2rayNG/app/src/main/assets/custom_routing_block new file mode 100644 index 00000000..a6ebf394 --- /dev/null +++ b/V2rayNG/app/src/main/assets/custom_routing_block @@ -0,0 +1 @@ +geosite:category-ads-all, \ No newline at end of file diff --git a/V2rayNG/app/src/main/assets/custom_routing_direct b/V2rayNG/app/src/main/assets/custom_routing_direct new file mode 100644 index 00000000..5408992c --- /dev/null +++ b/V2rayNG/app/src/main/assets/custom_routing_direct @@ -0,0 +1,132 @@ +domain:12306.com, +domain:51ym.me, +domain:52pojie.cn, +domain:8686c.com, +domain:abercrombie.com, +domain:adobesc.com, +domain:air-matters.com, +domain:air-matters.io, +domain:airtable.com, +domain:akadns.net, +domain:apache.org, +domain:api.crisp.chat, +domain:api.termius.com, +domain:appshike.com, +domain:appstore.com, +domain:aweme.snssdk.com, +domain:bababian.com, +domain:battle.net, +domain:beatsbydre.com, +domain:bet365.com, +domain:bilibili.cn, +domain:ccgslb.com, +domain:ccgslb.net, +domain:chunbo.com, +domain:chunboimg.com, +domain:clashroyaleapp.com, +domain:cloudsigma.com, +domain:cloudxns.net, +domain:cmfu.com, +domain:culturedcode.com, +domain:dct-cloud.com, +domain:didialift.com, +domain:douyutv.com, +domain:duokan.com, +domain:dytt8.net, +domain:easou.com, +domain:ecitic.net, +domain:eclipse.org, +domain:eudic.net, +domain:ewqcxz.com, +domain:fir.im, +domain:frdic.com, +domain:fresh-ideas.cc, +domain:godic.net, +domain:goodread.com, +domain:haibian.com, +domain:hdslb.net, +domain:hollisterco.com, +domain:hongxiu.com, +domain:hxcdn.net, +domain:images.unsplash.com, +domain:img4me.com, +domain:ipify.org, +domain:ixdzs.com, +domain:jd.hk, +domain:jianshuapi.com, +domain:jomodns.com, +domain:jsboxbbs.com, +domain:knewone.com, +domain:kuaidi100.com, +domain:lemicp.com, +domain:letvcloud.com, +domain:lizhi.io, +domain:localizecdn.com, +domain:lucifr.com, +domain:luoo.net, +domain:mai.tn, +domain:maven.org, +domain:miwifi.com, +domain:moji.com, +domain:moke.com, +domain:mtalk.google.com, +domain:mxhichina.com, +domain:myqcloud.com, +domain:myunlu.com, +domain:netease.com, +domain:nfoservers.com, +domain:nssurge.com, +domain:nuomi.com, +domain:ourdvs.com, +domain:overcast.fm, +domain:paypal.com, +domain:paypalobjects.com, +domain:pgyer.com, +domain:qdaily.com, +domain:qdmm.com, +domain:qin.io, +domain:qingmang.me, +domain:qingmang.mobi, +domain:qqurl.com, +domain:rarbg.to, +domain:rrmj.tv, +domain:ruguoapp.com, +domain:sm.ms, +domain:snwx.com, +domain:soku.com, +domain:startssl.com, +domain:store.steampowered.com, +domain:symcd.com, +domain:teamviewer.com, +domain:tmzvps.com, +domain:trello.com, +domain:trellocdn.com, +domain:ttmeiju.com, +domain:udache.com, +domain:uxengine.net, +domain:weather.bjango.com, +domain:weather.com, +domain:webqxs.com, +domain:weico.cc, +domain:wenku8.net, +domain:werewolf.53site.com, +domain:windowsupdate.com, +domain:wkcdn.com, +domain:workflowy.com, +domain:xdrig.com, +domain:xiaojukeji.com, +domain:xiaomi.net, +domain:xiaomicp.com, +domain:ximalaya.com, +domain:xitek.com, +domain:xmcdn.com, +domain:xslb.net, +domain:xteko.com, +domain:yach.me, +domain:yixia.com, +domain:yunjiasu-cdn.net, +domain:zealer.com, +domain:zgslb.net, +domain:zimuzu.tv, +domain:zmz002.com, +domain:samsungdm.com, \ No newline at end of file diff --git a/V2rayNG/app/src/main/assets/custom_routing_proxy b/V2rayNG/app/src/main/assets/custom_routing_proxy new file mode 100644 index 00000000..d67a6237 --- /dev/null +++ b/V2rayNG/app/src/main/assets/custom_routing_proxy @@ -0,0 +1,33 @@ +geosite:google, +geosite:github, +geosite:netflix, +geosite:steam, +geosite:telegram, +geosite:tumblr, +geosite:speedtest, +geosite:bbc, +domain:gvt1.com, +domain:textnow.com, +domain:twitch.tv, +domain:wikileaks.org, +domain:naver.com, +91.108.4.0/22, +91.108.8.0/22, +91.108.12.0/22, +91.108.20.0/22, +91.108.36.0/23, +91.108.38.0/23, +91.108.56.0/22, +149.154.160.0/20, +149.154.164.0/22, +149.154.172.0/22, +74.125.0.0/16, +173.194.0.0/16, +172.217.0.0/16, +216.58.200.0/24, +216.58.220.0/24, +91.108.56.116, +91.108.56.0/24, +109.239.140.0/24, +149.154.167.0/24, +149.154.175.0/24, \ No newline at end of file diff --git a/V2rayNG/app/src/main/assets/proxy_packagename.txt b/V2rayNG/app/src/main/assets/proxy_packagename.txt new file mode 100644 index 00000000..ecac8251 --- /dev/null +++ b/V2rayNG/app/src/main/assets/proxy_packagename.txt @@ -0,0 +1,241 @@ +amanita_design.samorost3.gp +android +au.com.shiftyjelly.pocketcasts +bbc.mobile.news.ww +be.mygod.vpnhotspot +ch.protonmail.android +co.wanqu.android +com.alphainventor.filemanager +com.amazon.kindle +com.amazon.mshop.android.shopping +com.android.chrome +com.android.providers.downloads +com.android.providers.downloads.ui +com.android.providers.telephony +com.android.settings +com.android.vending +com.android6park.m6park +com.apkpure.aegon +com.apkupdater +com.app.pornhub +com.arthurivanets.owly +com.asahi.tida.tablet +com.authy.authy +com.avmovie +com.ballistiq.artstation +com.binance.dev +com.bitly.app +com.brave.browser +com.brave.browser_beta +com.breel.wallpapers18 +com.bvanced.android.youtube +com.chrome.beta +com.chrome.canary +com.chrome.dev +com.cl.newt66y +com.cradle.iitc_mobile +com.cygames.shadowverse +com.devhd.feedly +com.devolver.reigns2 +com.discord +com.downloader.video.tumblr +com.driverbrowser +com.dropbox.android +com.duolingo +com.duckduckgo.mobile.android +com.dv.adm +com.estrongs.android.pop +com.estrongs.android.pop.pro +com.evernote +com.facebook.katana +com.facebook.lite +com.facebook.mlite +com.facebook.orca +com.facebook.services +com.facebook.system +com.fastaccess.github +com.felixfilip.scpae +com.fireproofstudios.theroom4 +com.firstrowria.pushnotificationtester +com.flyersoft.moonreaderp +com.fooview.android.fooview +com.fvd.eversync +com.gameloft.android.anmp.glofta8hm +com.gameloft.android.anmp.glofta9hm +com.gianlu.aria2app +com.github.yeriomin.yalpstore +com.google.android.apps.adm +com.google.android.apps.books +com.google.android.apps.docs +com.google.android.apps.docs.editors.sheets +com.google.android.apps.fitness +com.google.android.apps.googleassistant +com.google.android.apps.googlevoice +com.google.android.apps.hangoutsdialer +com.google.android.apps.inbox +com.google.android.apps.magazines +com.google.android.apps.maps +com.google.android.apps.nbu.files +com.google.android.apps.paidtasks +com.google.android.apps.pdfviewer +com.google.android.apps.photos +com.google.android.apps.plus +com.google.android.apps.translate +com.google.android.gm +com.google.android.gms +com.google.android.gms.setup +com.google.android.googlequicksearchbox +com.google.android.gsf +com.google.android.gsf.login +com.google.android.ims +com.google.android.inputmethod.latin +com.google.android.instantapps.supervisor +com.google.android.keep +com.google.android.music +com.google.android.ogyoutube +com.google.android.partnersetup +com.google.android.play.games +com.google.android.street +com.google.android.syncadapters.calendar +com.google.android.syncadapters.contacts +com.google.android.talk +com.google.android.tts +com.google.android.videos +com.google.android.youtube +com.google.ar.lens +com.hochan.coldsoup +com.ifttt.ifttt +com.imgur.mobile +com.innologica.inoreader +com.instagram.android +com.instapaper.android +com.jarvanh.vpntether +com.kapp.youtube.final +com.klinker.android.twitter_l +com.lastpass.lpandroid +com.linecorp.linelite +com.lingodeer +com.mediapods.tumbpods +com.mgoogle.android.gms +com.microsoft.emmx +com.microsoft.office.powerpoint +com.microsoft.skydrive +com.mixplorer +com.msd.consumerchinese +com.msd.professionalchinese +com.mss2011c.sharehelper +com.netflix.mediaclient +com.newin.nplayer.pro +com.nianticlabs.ingress.prime.qa +com.nianticproject.ingress +com.ninefolders.hd3 +com.ninegag.android.app +com.nintendo.zara +com.nytimes.cn +com.oasisfeng.island +com.ocnt.liveapp.hw +com.orekie.search +com.patreon.android +com.paypal.android.p2pmobile +com.perol.asdpl.pixivez +com.pinterest +com.popularapp.periodcalendar +com.popularapp.videodownloaderforinstagram +com.pushbullet.android +com.quoord.tapatalkpro.activity +com.quora.android +com.rayark.cytus2 +com.rayark.implosion +com.rayark.pluto +com.reddit.frontpage +com.resilio.sync +com.rhmsoft.edit +com.rubenmayayo.reddit +com.sec.android.app.sbrowser +com.sec.android.app.sbrowser.beta +com.shanga.walli +com.simplehabit.simplehabitapp +com.slack +com.snaptube.premium +com.sololearn +com.sonelli.juicessh +com.spotify.music +com.tencent.huatuo +com.termux +com.teslacoilsw.launcher +com.theinitium.news +com.thomsonreuters.reuters +com.thunkable.android.hritvik00.freenom +com.topjohnwu.magisk +com.tripadvisor.tripadvisor +com.tumblr +com.twitter.android +com.u91porn +com.u9porn +com.ubisoft.dance.justdance2015companion +com.utopia.pxview +com.valvesoftware.android.steam.communimunity +com.valvesoftware.android.steam.community +com.vanced.android.youtube +com.vimeo.android.videoapp +com.vivaldi.browser +com.vivaldi.browser.snapshot +com.vkontakte.android +com.whatsapp +com.wire +com.wuxiangai.refactor +com.xda.labs +com.xvideos.app +com.yandex.browser +com.yandex.browser.beta +com.yandex.browser.alpha +com.z28j.feel +con.medium.reader +de.apkgrabber +de.robv.android.xposed.installer +dk.tacit.android.foldersync.full +es.rafalense.telegram.themes +es.rafalense.themes +flipboard.app +fm.moon.app +fr.gouv.etalab.mastodon +github.tornaco.xposedmoduletest +idm.internet.download.manager +idm.internet.download.manager.plus +io.github.javiewer +io.github.skyhacker2.magnetsearch +io.va.exposed +it.mvilla.android.fenix2 +jp.bokete.app.android +jp.naver.line.android +jp.pxv.android +luo.speedometergpspro +mark.via.gp +me.tshine.easymark +net.teeha.android.url_shortener +net.tsapps.appsales +onion.fire +org.fdroid.fdroid +org.freedownloadmanager.fdm +org.kustom.widget +org.mozilla.fennec_aurora +org.mozilla.fenix +org.mozilla.fenix.nightly +org.mozilla.firefox +org.mozilla.firefox_beta +org.mozilla.focus +org.schabi.newpipe +org.telegram.messenger +org.telegram.multi +org.telegram.plus +org.thunderdog.challegram +org.torproject.android +org.torproject.torbrowser_alpha +org.wikipedia +org.xbmc.kodi +pl.zdunex25.updater +tv.twitch.android.app +tw.com.gamer.android.activecenter +videodownloader.downloadvideo.downloader +uk.co.bbc.learningenglish +com.ted.android diff --git a/V2rayNG/app/src/main/assets/v2ray_config.json b/V2rayNG/app/src/main/assets/v2ray_config.json new file mode 100644 index 00000000..57b1bd8a --- /dev/null +++ b/V2rayNG/app/src/main/assets/v2ray_config.json @@ -0,0 +1,105 @@ +{ + "stats":{}, + "log": { + "loglevel": "warning" + }, + "policy":{ + "levels": { + "8": { + "handshake": 4, + "connIdle": 300, + "uplinkOnly": 1, + "downlinkOnly": 1 + } + }, + "system": { + "statsOutboundUplink": true, + "statsOutboundDownlink": true + } + }, + "inbounds": [{ + "tag": "socks", + "port": 10808, + "protocol": "socks", + "settings": { + "auth": "noauth", + "udp": true, + "userLevel": 8 + }, + "sniffing": { + "enabled": true, + "destOverride": [ + "http", + "tls" + ] + } + }, + { + "tag": "http", + "port": 10809, + "protocol": "http", + "settings": { + "userLevel": 8 + } + } +], + "outbounds": [{ + "tag": "proxy", + "protocol": "vmess", + "settings": { + "vnext": [ + { + "address": "v2ray.cool", + "port": 10086, + "users": [ + { + "id": "a3482e88-686a-4a58-8126-99c9df64b7bf", + "alterId": 0, + "security": "auto", + "level": 8 + } + ] + } + ], + "servers": [ + { + "address": "v2ray.cool", + "method": "chacha20", + "ota": false, + "password": "123456", + "port": 10086, + "level": 8 + } + ] + }, + "streamSettings": { + "network": "tcp" + }, + "mux": { + "enabled": false + } + }, + { + "protocol": "freedom", + "settings": {}, + "tag": "direct" + }, + { + "protocol": "blackhole", + "tag": "block", + "settings": { + "response": { + "type": "http" + } + } + } + ], + "routing": { + "domainStrategy": "IPIfNonMatch", + "rules": [] + }, + "dns": { + "hosts": {}, + "servers": [] + } +} diff --git a/V2rayNG/app/src/main/ic_launcher-web.png b/V2rayNG/app/src/main/ic_launcher-web.png new file mode 100644 index 0000000000000000000000000000000000000000..03a4ce8a4a25a07162674955dd9fd6a3834d3b8f GIT binary patch literal 11854 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 at the end of a "drop" event.
+ *
+ * Implementations should call {@link RecyclerView.Adapter#notifyItemMoved(int, int)} after + * adjusting the underlying data to reflect this move. + * + * @param fromPosition The start position of the moved item. + * @param toPosition Then resolved position of the moved item. + * @return True if the item was moved to the new adapter position. + * + * @see RecyclerView#getAdapterPositionFor(RecyclerView.ViewHolder) + * @see RecyclerView.ViewHolder#getAdapterPosition() + */ + boolean onItemMove(int fromPosition, int toPosition); + + + void onItemMoveCompleted(); + + /** + * Called when an item has been dismissed by a swipe.
+ *
+ * Implementations should call {@link RecyclerView.Adapter#notifyItemRemoved(int)} after + * adjusting the underlying data to reflect this removal. + * + * @param position The position of the item dismissed. + * + * @see RecyclerView#getAdapterPositionFor(RecyclerView.ViewHolder) + * @see RecyclerView.ViewHolder#getAdapterPosition() + */ + void onItemDismiss(int position); +} diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/helper/ItemTouchHelperViewHolder.java b/V2rayNG/app/src/main/java/com/v2ray/ang/helper/ItemTouchHelperViewHolder.java new file mode 100644 index 00000000..149768fc --- /dev/null +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/helper/ItemTouchHelperViewHolder.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2015 Paul Burke + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.v2ray.ang.helper; + +import androidx.recyclerview.widget.ItemTouchHelper; + +/** + * Interface to notify an item ViewHolder of relevant callbacks from {@link + * ItemTouchHelper.Callback}. + * + * @author Paul Burke (ipaulpro) + */ +public interface ItemTouchHelperViewHolder { + + /** + * Called when the {@link ItemTouchHelper} first registers an item as being moved or swiped. + * Implementations should update the item view to indicate it's active state. + */ + void onItemSelected(); + + + /** + * Called when the {@link ItemTouchHelper} has completed the move or swipe, and the active item + * state should be cleared. + */ + void onItemClear(); +} diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/helper/OnStartDragListener.java b/V2rayNG/app/src/main/java/com/v2ray/ang/helper/OnStartDragListener.java new file mode 100644 index 00000000..a6407b6f --- /dev/null +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/helper/OnStartDragListener.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2015 Paul Burke + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.v2ray.ang.helper; + +import androidx.recyclerview.widget.RecyclerView; + +/** + * Listener for manual initiation of a drag. + */ +public interface OnStartDragListener { + + /** + * Called when a view is requesting a start of a drag. + * + * @param viewHolder The holder of the view to drag. + */ + void onStartDrag(RecyclerView.ViewHolder viewHolder); + +} diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/helper/SimpleItemTouchHelperCallback.java b/V2rayNG/app/src/main/java/com/v2ray/ang/helper/SimpleItemTouchHelperCallback.java new file mode 100644 index 00000000..97fceab7 --- /dev/null +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/helper/SimpleItemTouchHelperCallback.java @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2015 Paul Burke + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.v2ray.ang.helper; + +import android.graphics.Canvas; +import androidx.recyclerview.widget.GridLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import androidx.recyclerview.widget.ItemTouchHelper; + +import org.jetbrains.annotations.NotNull; + +/** + * An implementation of {@link ItemTouchHelper.Callback} that enables basic drag & drop and + * swipe-to-dismiss. Drag events are automatically started by an item long-press.
+ *
+ * Expects the RecyclerView.Adapter to listen for {@link + * ItemTouchHelperAdapter} callbacks and the RecyclerView.ViewHolder to implement + * {@link ItemTouchHelperViewHolder}. + * + * @author Paul Burke (ipaulpro) + */ +public class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback { + + public static final float ALPHA_FULL = 1.0f; + + private final ItemTouchHelperAdapter mAdapter; + + public SimpleItemTouchHelperCallback(ItemTouchHelperAdapter adapter) { + mAdapter = adapter; + } + + @Override + public boolean isLongPressDragEnabled() { + return true; + } + + @Override + public boolean isItemViewSwipeEnabled() { + return false; + } + + @Override + public int getMovementFlags(RecyclerView recyclerView, @NotNull RecyclerView.ViewHolder viewHolder) { + // Set movement flags based on the layout manager + if (recyclerView.getLayoutManager() instanceof GridLayoutManager) { + final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT; + final int swipeFlags = 0; + return makeMovementFlags(dragFlags, swipeFlags); + } else { + final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN; + final int swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END; + return makeMovementFlags(dragFlags, swipeFlags); + } + } + + @Override + public boolean onMove(@NotNull RecyclerView recyclerView, RecyclerView.ViewHolder source, RecyclerView.ViewHolder target) { + if (source.getItemViewType() != target.getItemViewType()) { + return false; + } + + // Notify the adapter of the move + mAdapter.onItemMove(source.getBindingAdapterPosition(), target.getBindingAdapterPosition()); + return true; + } + + @Override + public void onSwiped(RecyclerView.ViewHolder viewHolder, int i) { + // Notify the adapter of the dismissal + mAdapter.onItemDismiss(viewHolder.getBindingAdapterPosition()); + } + + @Override + public void onChildDraw(@NotNull Canvas c, @NotNull RecyclerView recyclerView, @NotNull RecyclerView.ViewHolder viewHolder, float dX, + float dY, int actionState, boolean isCurrentlyActive) { + if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) { + // Fade out the view as it is swiped out of the parent's bounds + final float alpha = ALPHA_FULL - Math.abs(dX) / (float) viewHolder.itemView.getWidth(); + viewHolder.itemView.setAlpha(alpha); + viewHolder.itemView.setTranslationX(dX); + } else { + super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive); + } + } + + @Override + public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) { + // We only want the active item to change + if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) { + if (viewHolder instanceof ItemTouchHelperViewHolder) { + // Let the view holder know that this item is being moved or dragged + ItemTouchHelperViewHolder itemViewHolder = (ItemTouchHelperViewHolder) viewHolder; + itemViewHolder.onItemSelected(); + } + } + + super.onSelectedChanged(viewHolder, actionState); + } + + @Override + public void clearView(@NotNull RecyclerView recyclerView, @NotNull RecyclerView.ViewHolder viewHolder) { + super.clearView(recyclerView, viewHolder); + + mAdapter.onItemMoveCompleted(); + + viewHolder.itemView.setAlpha(ALPHA_FULL); + + if (viewHolder instanceof ItemTouchHelperViewHolder) { + // Tell the view holder it's time to restore the idle state + ItemTouchHelperViewHolder itemViewHolder = (ItemTouchHelperViewHolder) viewHolder; + itemViewHolder.onItemClear(); + } + } +} diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/AngApplication.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/AngApplication.kt new file mode 100644 index 00000000..aa1ef7d4 --- /dev/null +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/AngApplication.kt @@ -0,0 +1,28 @@ +package com.v2ray.ang + +import androidx.multidex.MultiDexApplication +import androidx.preference.PreferenceManager +import com.tencent.mmkv.MMKV + +class AngApplication : MultiDexApplication() { + companion object { + const val PREF_LAST_VERSION = "pref_last_version" + } + + var firstRun = false + private set + + override fun onCreate() { + super.onCreate() + +// LeakCanary.install(this) + + val defaultSharedPreferences = PreferenceManager.getDefaultSharedPreferences(this) + firstRun = defaultSharedPreferences.getInt(PREF_LAST_VERSION, 0) != BuildConfig.VERSION_CODE + if (firstRun) + defaultSharedPreferences.edit().putInt(PREF_LAST_VERSION, BuildConfig.VERSION_CODE).apply() + + //Logger.init().logLevel(if (BuildConfig.DEBUG) LogLevel.FULL else LogLevel.NONE) + MMKV.initialize(this) + } +} diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/AppConfig.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/AppConfig.kt new file mode 100644 index 00000000..3793e583 --- /dev/null +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/AppConfig.kt @@ -0,0 +1,89 @@ +package com.v2ray.ang + +/** + * + * App Config Const + */ +object AppConfig { + const val ANG_PACKAGE = "com.v2ray.ang" + const val DIR_ASSETS = "assets" + + // legacy + const val ANG_CONFIG = "ang_config" + const val PREF_INAPP_BUY_IS_PREMIUM = "pref_inapp_buy_is_premium" + const val PREF_ROUTING_CUSTOM = "pref_routing_custom" + + // Preferences mapped to MMKV + const val PREF_MODE = "pref_mode" + const val PREF_SPEED_ENABLED = "pref_speed_enabled" + const val PREF_SNIFFING_ENABLED = "pref_sniffing_enabled" + const val PREF_PROXY_SHARING = "pref_proxy_sharing_enabled" + const val PREF_LOCAL_DNS_ENABLED = "pref_local_dns_enabled" + const val PREF_FAKE_DNS_ENABLED = "pref_fake_dns_enabled" + const val PREF_VPN_DNS = "pref_vpn_dns" + const val PREF_REMOTE_DNS = "pref_remote_dns" + const val PREF_DOMESTIC_DNS = "pref_domestic_dns" + const val PREF_LOCAL_DNS_PORT = "pref_local_dns_port" + const val PREF_ALLOW_INSECURE = "pref_allow_insecure" + const val PREF_SOCKS_PORT = "pref_socks_port" + const val PREF_HTTP_PORT = "pref_http_port" + const val PREF_LOGLEVEL = "pref_core_loglevel" + const val PREF_LANGUAGE = "pref_language" + const val PREF_PREFER_IPV6 = "pref_prefer_ipv6" + const val PREF_ROUTING_DOMAIN_STRATEGY = "pref_routing_domain_strategy" + const val PREF_ROUTING_MODE = "pref_routing_mode" + const val PREF_V2RAY_ROUTING_AGENT = "pref_v2ray_routing_agent" + const val PREF_V2RAY_ROUTING_DIRECT = "pref_v2ray_routing_direct" + const val PREF_V2RAY_ROUTING_BLOCKED = "pref_v2ray_routing_blocked" + const val PREF_PER_APP_PROXY = "pref_per_app_proxy" + const val PREF_PER_APP_PROXY_SET = "pref_per_app_proxy_set" + const val PREF_BYPASS_APPS = "pref_bypass_apps" + const val PREF_CONFIRM_REMOVE = "pref_confirm_remove" + + const val HTTP_PROTOCOL: String = "http://" + const val HTTPS_PROTOCOL: String = "https://" + + const val BROADCAST_ACTION_SERVICE = "com.v2ray.ang.action.service" + const val BROADCAST_ACTION_ACTIVITY = "com.v2ray.ang.action.activity" + const val BROADCAST_ACTION_WIDGET_CLICK = "com.v2ray.ang.action.widget.click" + + const val TASKER_EXTRA_BUNDLE = "com.twofortyfouram.locale.intent.extra.BUNDLE" + const val TASKER_EXTRA_STRING_BLURB = "com.twofortyfouram.locale.intent.extra.BLURB" + const val TASKER_EXTRA_BUNDLE_SWITCH = "tasker_extra_bundle_switch" + const val TASKER_EXTRA_BUNDLE_GUID = "tasker_extra_bundle_guid" + const val TASKER_DEFAULT_GUID = "Default" + + const val TAG_AGENT = "proxy" + const val TAG_DIRECT = "direct" + const val TAG_BLOCKED = "block" + + const val androidpackagenamelistUrl = "https://raw.githubusercontent.com/2dust/androidpackagenamelist/master/proxy.txt" + const val v2rayCustomRoutingListUrl = "https://raw.githubusercontent.com/2dust/v2rayCustomRoutingList/master/" + const val v2rayNGIssues = "https://github.com/2dust/v2rayNG/issues" + const val v2rayNGWikiMode = "https://github.com/2dust/v2rayNG/wiki/Mode" + const val promotionUrl = "aHR0cHM6Ly85LjIzNDQ1Ni54eXovYWJjLmh0bWw=" + const val geoUrl = "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/"; + + const val DNS_AGENT = "1.1.1.1" + const val DNS_DIRECT = "223.5.5.5" + + const val PORT_LOCAL_DNS = "10853" + const val PORT_SOCKS = "10808" + const val PORT_HTTP = "10809" + + const val MSG_REGISTER_CLIENT = 1 + const val MSG_STATE_RUNNING = 11 + const val MSG_STATE_NOT_RUNNING = 12 + const val MSG_UNREGISTER_CLIENT = 2 + const val MSG_STATE_START = 3 + const val MSG_STATE_START_SUCCESS = 31 + const val MSG_STATE_START_FAILURE = 32 + const val MSG_STATE_STOP = 4 + const val MSG_STATE_STOP_SUCCESS = 41 + const val MSG_STATE_RESTART = 5 + const val MSG_MEASURE_DELAY = 6 + const val MSG_MEASURE_DELAY_SUCCESS = 61 + const val MSG_MEASURE_CONFIG = 7 + const val MSG_MEASURE_CONFIG_SUCCESS = 71 + const val MSG_MEASURE_CONFIG_CANCEL = 72 +} diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/AngConfig.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/AngConfig.kt new file mode 100644 index 00000000..01f2bdd8 --- /dev/null +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/AngConfig.kt @@ -0,0 +1,32 @@ +package com.v2ray.ang.dto + +data class AngConfig( + var index: Int, + var vmess: ArrayList, + var subItem: ArrayList +) { + data class VmessBean(var guid: String = "123456", + var address: String = "v2ray.cool", + var port: Int = 10086, + var id: String = "a3482e88-686a-4a58-8126-99c9df64b7bf", + var alterId: Int = 64, + var security: String = "aes-128-cfb", + var network: String = "tcp", + var remarks: String = "def", + var headerType: String = "", + var requestHost: String = "", + var path: String = "", + var streamSecurity: String = "", + var allowInsecure: String = "", + var configType: Int = 1, + var configVersion: Int = 1, + var testResult: String = "", + var subid: String = "", + var flow: String = "", + var sni: String = "") + + data class SubItemBean(var id: String = "", + var remarks: String = "", + var url: String = "", + var enabled: Boolean = true) +} diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/AppInfo.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/AppInfo.kt new file mode 100644 index 00000000..f99655a8 --- /dev/null +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/AppInfo.kt @@ -0,0 +1,9 @@ +package com.v2ray.ang.dto + +import android.graphics.drawable.Drawable + +data class AppInfo(val appName: String, + val packageName: String, + val appIcon: Drawable, + val isSystemApp: Boolean, + var isSelected: Int) \ No newline at end of file diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/EConfigType.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/EConfigType.kt new file mode 100644 index 00000000..5204c0ca --- /dev/null +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/EConfigType.kt @@ -0,0 +1,15 @@ +package com.v2ray.ang.dto + +enum class EConfigType(val value: Int, val protocolScheme: String) { + VMESS(1, "vmess://"), + CUSTOM(2, ""), + SHADOWSOCKS(3, "ss://"), + SOCKS(4, "socks://"), + VLESS(5, "vless://"), + TROJAN(6, "trojan://"), + WIREGUARD(7, "wireguard://"); + + companion object { + fun fromInt(value: Int) = values().firstOrNull { it.value == value } + } +} diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/ERoutingMode.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/ERoutingMode.kt new file mode 100644 index 00000000..96d77397 --- /dev/null +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/ERoutingMode.kt @@ -0,0 +1,9 @@ +package com.v2ray.ang.dto + +enum class ERoutingMode(val value: String ) { + GLOBAL_PROXY("0"), + BYPASS_LAN("1"), + BYPASS_MAINLAND("2"), + BYPASS_LAN_MAINLAND("3"), + GLOBAL_DIRECT("4"); +} diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/ServerAffiliationInfo.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/ServerAffiliationInfo.kt new file mode 100644 index 00000000..bb4d20be --- /dev/null +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/ServerAffiliationInfo.kt @@ -0,0 +1,10 @@ +package com.v2ray.ang.dto + +data class ServerAffiliationInfo(var testDelayMillis: Long = 0L) { + fun getTestDelayString(): String { + if (testDelayMillis == 0L) { + return "" + } + return testDelayMillis.toString() + "ms" + } +} diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/ServerConfig.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/ServerConfig.kt new file mode 100644 index 00000000..88a9a7ec --- /dev/null +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/ServerConfig.kt @@ -0,0 +1,69 @@ +package com.v2ray.ang.dto + +import com.v2ray.ang.AppConfig.TAG_AGENT +import com.v2ray.ang.AppConfig.TAG_BLOCKED +import com.v2ray.ang.AppConfig.TAG_DIRECT +import com.v2ray.ang.util.Utils + +data class ServerConfig( + val configVersion: Int = 3, + val configType: EConfigType, + var subscriptionId: String = "", + val addedTime: Long = System.currentTimeMillis(), + var remarks: String = "", + val outboundBean: V2rayConfig.OutboundBean? = null, + var fullConfig: V2rayConfig? = null +) { + companion object { + fun create(configType: EConfigType): ServerConfig { + when(configType) { + EConfigType.VMESS, EConfigType.VLESS -> + return ServerConfig( + configType = configType, + outboundBean = V2rayConfig.OutboundBean( + protocol = configType.name.lowercase(), + settings = V2rayConfig.OutboundBean.OutSettingsBean( + vnext = listOf(V2rayConfig.OutboundBean.OutSettingsBean.VnextBean( + users = listOf(V2rayConfig.OutboundBean.OutSettingsBean.VnextBean.UsersBean())))), + streamSettings = V2rayConfig.OutboundBean.StreamSettingsBean())) + EConfigType.CUSTOM, EConfigType.WIREGUARD -> + return ServerConfig(configType = configType) + EConfigType.SHADOWSOCKS, EConfigType.SOCKS, EConfigType.TROJAN -> + return ServerConfig( + configType = configType, + outboundBean = V2rayConfig.OutboundBean( + protocol = configType.name.lowercase(), + settings = V2rayConfig.OutboundBean.OutSettingsBean( + servers = listOf(V2rayConfig.OutboundBean.OutSettingsBean.ServersBean())), + streamSettings = V2rayConfig.OutboundBean.StreamSettingsBean())) + } + } + } + + fun getProxyOutbound(): V2rayConfig.OutboundBean? { + if (configType != EConfigType.CUSTOM) { + return outboundBean + } + return fullConfig?.getProxyOutbound() + } + + fun getAllOutboundTags(): MutableList { + if (configType != EConfigType.CUSTOM) { + return mutableListOf(TAG_AGENT, TAG_DIRECT, TAG_BLOCKED) + } + fullConfig?.let { config -> + return config.outbounds.map { it.tag }.toMutableList() + } + return mutableListOf() + } + + fun getV2rayPointDomainAndPort(): String { + val address = getProxyOutbound()?.getServerAddress().orEmpty() + val port = getProxyOutbound()?.getServerPort() + return if (Utils.isIpv6Address(address)) { + String.format("[%s]:%s", address, port) + } else { + String.format("%s:%s", address, port) + } + } +} diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/ServersCache.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/ServersCache.kt new file mode 100644 index 00000000..e686b469 --- /dev/null +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/ServersCache.kt @@ -0,0 +1,4 @@ +package com.v2ray.ang.dto + +data class ServersCache(val guid: String, + val config: ServerConfig) \ No newline at end of file diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/SubscriptionItem.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/SubscriptionItem.kt new file mode 100644 index 00000000..b2195148 --- /dev/null +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/SubscriptionItem.kt @@ -0,0 +1,8 @@ +package com.v2ray.ang.dto + +data class SubscriptionItem( + var remarks: String = "", + var url: String = "", + var enabled: Boolean = true, + val addedTime: Long = System.currentTimeMillis()) { +} diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/V2rayConfig.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/V2rayConfig.kt new file mode 100644 index 00000000..cfc771b1 --- /dev/null +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/V2rayConfig.kt @@ -0,0 +1,462 @@ +package com.v2ray.ang.dto + +import android.text.TextUtils +import com.google.gson.GsonBuilder +import com.google.gson.JsonPrimitive +import com.google.gson.JsonSerializationContext +import com.google.gson.JsonSerializer +import com.google.gson.annotations.SerializedName +import com.google.gson.reflect.TypeToken +import java.lang.reflect.Type + +data class V2rayConfig( + var stats: Any? = null, + val log: LogBean, + var policy: PolicyBean?, + val inbounds: ArrayList, + var outbounds: ArrayList, + var dns: DnsBean, + val routing: RoutingBean, + val api: Any? = null, + val transport: Any? = null, + val reverse: Any? = null, + var fakedns: Any? = null, + val browserForwarder: Any? = null) { + companion object { + const val DEFAULT_PORT = 443 + const val DEFAULT_SECURITY = "auto" + const val DEFAULT_LEVEL = 8 + const val DEFAULT_NETWORK = "tcp" + + const val TLS = "tls" + const val XTLS = "xtls" + const val HTTP = "http" + } + + data class LogBean(val access: String, + val error: String, + var loglevel: String?, + val dnsLog: Boolean? = null) + + data class InboundBean( + var tag: String, + var port: Int, + var protocol: String, + var listen: String? = null, + val settings: Any? = null, + val sniffing: SniffingBean?, + val streamSettings: Any? = null, + val allocate: Any? = null) { + + data class InSettingsBean(val auth: String? = null, + val udp: Boolean? = null, + val userLevel: Int? = null, + val address: String? = null, + val port: Int? = null, + val network: String? = null) + + data class SniffingBean(var enabled: Boolean, + val destOverride: ArrayList, + val metadataOnly: Boolean? = null) + } + + data class OutboundBean(val tag: String = "proxy", + var protocol: String, + var settings: OutSettingsBean? = null, + var streamSettings: StreamSettingsBean? = null, + val proxySettings: Any? = null, + val sendThrough: String? = null, + val mux: MuxBean? = MuxBean(false)) { + + data class OutSettingsBean(var vnext: List? = null, + var servers: List? = null, + /*Blackhole*/ + var response: Response? = null, + /*DNS*/ + val network: String? = null, + val address: Any? = null, + val port: Int? = null, + /*Freedom*/ + var domainStrategy: String? = null, + val redirect: String? = null, + val userLevel: Int? = null, + /*Loopback*/ + val inboundTag: String? = null, + /*Wireguard*/ + val secretKey: String? = null, + val peers: List? = null, + ) { + + data class VnextBean(var address: String = "", + var port: Int = DEFAULT_PORT, + var users: List) { + + data class UsersBean(var id: String = "", + var alterId: Int? = null, + var security: String = DEFAULT_SECURITY, + var level: Int = DEFAULT_LEVEL, + var encryption: String = "", + var flow: String = "") + } + + data class ServersBean(var address: String = "", + var method: String = "chacha20-poly1305", + var ota: Boolean = false, + var password: String = "", + var port: Int = DEFAULT_PORT, + var level: Int = DEFAULT_LEVEL, + val email: String? = null, + var flow: String? = null, + val ivCheck: Boolean? = null, + var users: List? = null) { + + + data class SocksUsersBean(var user: String = "", + var pass: String = "", + var level: Int = DEFAULT_LEVEL) + } + + data class Response(var type: String) + + data class WireGuardBean(var publicKey: String = "", + var endpoint: String = "") + } + + data class StreamSettingsBean(var network: String = DEFAULT_NETWORK, + var security: String = "", + var tcpSettings: TcpSettingsBean? = null, + var kcpSettings: KcpSettingsBean? = null, + var wsSettings: WsSettingsBean? = null, + var httpSettings: HttpSettingsBean? = null, + var tlsSettings: TlsSettingsBean? = null, + var quicSettings: QuicSettingBean? = null, + var xtlsSettings: TlsSettingsBean? = null, + var grpcSettings: GrpcSettingsBean? = null, + val dsSettings: Any? = null, + val sockopt: Any? = null + ) { + + data class TcpSettingsBean(var header: HeaderBean = HeaderBean(), + val acceptProxyProtocol: Boolean? = null) { + data class HeaderBean(var type: String = "none", + var request: RequestBean? = null, + var response: Any? = null) { + data class RequestBean(var path: List = ArrayList(), + var headers: HeadersBean = HeadersBean(), + val version: String? = null, + val method: String? = null) { + data class HeadersBean(var Host: List = ArrayList(), + @SerializedName("User-Agent") + val userAgent: List? = null, + @SerializedName("Accept-Encoding") + val acceptEncoding: List? = null, + val Connection: List? = null, + val Pragma: String? = null) + } + } + } + + data class KcpSettingsBean(var mtu: Int = 1350, + var tti: Int = 50, + var uplinkCapacity: Int = 12, + var downlinkCapacity: Int = 100, + var congestion: Boolean = false, + var readBufferSize: Int = 1, + var writeBufferSize: Int = 1, + var header: HeaderBean = HeaderBean(), + var seed: String? = null) { + data class HeaderBean(var type: String = "none") + } + + data class WsSettingsBean(var path: String = "", + var headers: HeadersBean = HeadersBean(), + val maxEarlyData: Int? = null, + val useBrowserForwarding: Boolean? = null, + val acceptProxyProtocol: Boolean? = null) { + data class HeadersBean(var Host: String = "") + } + + data class HttpSettingsBean(var host: List = ArrayList(), + var path: String = "") + + data class TlsSettingsBean(var allowInsecure: Boolean = false, + var serverName: String = "", + val alpn: List? = null, + val minVersion: String? = null, + val maxVersion: String? = null, + val preferServerCipherSuites: Boolean? = null, + val cipherSuites: String? = null, + val fingerprint: String? = null, + val certificates: List? = null, + val disableSystemRoot: Boolean? = null, + val enableSessionResumption: Boolean? = null) + + data class QuicSettingBean(var security: String = "none", + var key: String = "", + var header: HeaderBean = HeaderBean()) { + data class HeaderBean(var type: String = "none") + } + + data class GrpcSettingsBean(var serviceName: String = "", + var multiMode: Boolean? = null) + + fun populateTransportSettings(transport: String, headerType: String?, host: String?, path: String?, seed: String?, + quicSecurity: String?, key: String?, mode: String?, serviceName: String?): String { + var sni = "" + network = transport + when (network) { + "tcp" -> { + val tcpSetting = TcpSettingsBean() + if (headerType == HTTP) { + tcpSetting.header.type = HTTP + if (!TextUtils.isEmpty(host) || !TextUtils.isEmpty(path)) { + val requestObj = TcpSettingsBean.HeaderBean.RequestBean() + requestObj.headers.Host = (host ?: "").split(",").map { it.trim() }.filter { it.isNotEmpty() } + requestObj.path = (path ?: "").split(",").map { it.trim() }.filter { it.isNotEmpty() } + tcpSetting.header.request = requestObj + sni = requestObj.headers.Host.getOrNull(0) ?: sni + } + } else { + tcpSetting.header.type = "none" + sni = host ?: "" + } + tcpSettings = tcpSetting + } + "kcp" -> { + val kcpsetting = KcpSettingsBean() + kcpsetting.header.type = headerType ?: "none" + if (seed.isNullOrEmpty()) { + kcpsetting.seed = null + } else { + kcpsetting.seed = seed + } + kcpSettings = kcpsetting + } + "ws" -> { + val wssetting = WsSettingsBean() + wssetting.headers.Host = host ?: "" + sni = wssetting.headers.Host + wssetting.path = path ?: "/" + wsSettings = wssetting + } + "h2", "http" -> { + network = "h2" + val h2Setting = HttpSettingsBean() + h2Setting.host = (host ?: "").split(",").map { it.trim() }.filter { it.isNotEmpty() } + sni = h2Setting.host.getOrNull(0) ?: sni + h2Setting.path = path ?: "/" + httpSettings = h2Setting + } + "quic" -> { + val quicsetting = QuicSettingBean() + quicsetting.security = quicSecurity ?: "none" + quicsetting.key = key ?: "" + quicsetting.header.type = headerType ?: "none" + quicSettings = quicsetting + } + "grpc" -> { + val grpcSetting = GrpcSettingsBean() + grpcSetting.multiMode = mode == "multi" + grpcSetting.serviceName = serviceName ?: "" + sni = host ?: "" + grpcSettings = grpcSetting + } + } + return sni + } + + fun populateTlsSettings(streamSecurity: String, allowInsecure: Boolean, sni: String, fingerprint: String?, alpns: String?) { + security = streamSecurity + val tlsSetting = TlsSettingsBean( + allowInsecure = allowInsecure, + serverName = sni, + fingerprint = fingerprint, + alpn = if (alpns.isNullOrEmpty()) null else alpns.split(",").map { it.trim() }.filter { it.isNotEmpty() } + ) + if (security == TLS) { + tlsSettings = tlsSetting + xtlsSettings = null + } else if (security == XTLS) { + tlsSettings = null + xtlsSettings = tlsSetting + } + } + } + + data class MuxBean(var enabled: Boolean, var concurrency: Int = 8) + + fun getServerAddress(): String? { + if (protocol.equals(EConfigType.VMESS.name, true) + || protocol.equals(EConfigType.VLESS.name, true)) { + return settings?.vnext?.get(0)?.address + } else if (protocol.equals(EConfigType.SHADOWSOCKS.name, true) + || protocol.equals(EConfigType.SOCKS.name, true) + || protocol.equals(EConfigType.TROJAN.name, true)) { + return settings?.servers?.get(0)?.address + } else if (protocol.equals(EConfigType.WIREGUARD.name, true)) { + return settings?.peers?.get(0)?.endpoint?.substringBeforeLast(":") + } + return null + } + + fun getServerPort(): Int? { + if (protocol.equals(EConfigType.VMESS.name, true) + || protocol.equals(EConfigType.VLESS.name, true)) { + return settings?.vnext?.get(0)?.port + } else if (protocol.equals(EConfigType.SHADOWSOCKS.name, true) + || protocol.equals(EConfigType.SOCKS.name, true) + || protocol.equals(EConfigType.TROJAN.name, true)) { + return settings?.servers?.get(0)?.port + } else if (protocol.equals(EConfigType.WIREGUARD.name, true)) { + return settings?.peers?.get(0)?.endpoint?.substringAfterLast(":")?.toInt() + } + return null + } + + fun getPassword(): String? { + if (protocol.equals(EConfigType.VMESS.name, true) + || protocol.equals(EConfigType.VLESS.name, true)) { + return settings?.vnext?.get(0)?.users?.get(0)?.id + } else if (protocol.equals(EConfigType.SHADOWSOCKS.name, true) + || protocol.equals(EConfigType.TROJAN.name, true)) { + return settings?.servers?.get(0)?.password + } else if (protocol.equals(EConfigType.SOCKS.name, true)) { + return settings?.servers?.get(0)?.users?.get(0)?.pass + } else if (protocol.equals(EConfigType.WIREGUARD.name, true)) { + return settings?.secretKey + } + return null + } + + fun getSecurityEncryption(): String? { + return when { + protocol.equals(EConfigType.VMESS.name, true) -> settings?.vnext?.get(0)?.users?.get(0)?.security + protocol.equals(EConfigType.VLESS.name, true) -> settings?.vnext?.get(0)?.users?.get(0)?.encryption + protocol.equals(EConfigType.SHADOWSOCKS.name, true) -> settings?.servers?.get(0)?.method + else -> null + } + } + + fun getTransportSettingDetails(): List? { + if (protocol.equals(EConfigType.VMESS.name, true) + || protocol.equals(EConfigType.VLESS.name, true) + || protocol.equals(EConfigType.TROJAN.name, true)) { + val transport = streamSettings?.network ?: return null + return when (transport) { + "tcp" -> { + val tcpSetting = streamSettings?.tcpSettings ?: return null + listOf(tcpSetting.header.type, + tcpSetting.header.request?.headers?.Host?.joinToString().orEmpty(), + tcpSetting.header.request?.path?.joinToString().orEmpty()) + } + "kcp" -> { + val kcpSetting = streamSettings?.kcpSettings ?: return null + listOf(kcpSetting.header.type, + "", + kcpSetting.seed.orEmpty()) + } + "ws" -> { + val wsSetting = streamSettings?.wsSettings ?: return null + listOf("", + wsSetting.headers.Host, + wsSetting.path) + } + "h2" -> { + val h2Setting = streamSettings?.httpSettings ?: return null + listOf("", + h2Setting.host.joinToString(), + h2Setting.path) + } + "quic" -> { + val quicSetting = streamSettings?.quicSettings ?: return null + listOf(quicSetting.header.type, + quicSetting.security, + quicSetting.key) + } + "grpc" -> { + val grpcSetting = streamSettings?.grpcSettings ?: return null + listOf(if (grpcSetting.multiMode == true) "multi" else "gun", + "", + grpcSetting.serviceName) + } + else -> null + } + } + return null + } + } + + data class DnsBean(var servers: ArrayList? = null, + var hosts: Map? = null, + val clientIp: String? = null, + val disableCache: Boolean? = null, + val queryStrategy: String? = null, + val tag: String? = null + ) { + data class ServersBean(var address: String = "", + var port: Int? = null, + var domains: List? = null, + var expectIPs: List? = null, + val clientIp: String? = null) + } + + data class RoutingBean(var domainStrategy: String, + var domainMatcher: String? = null, + var rules: ArrayList, + val balancers: List? = null) { + + data class RulesBean(var type: String = "", + var ip: ArrayList? = null, + var domain: ArrayList? = null, + var outboundTag: String = "", + var balancerTag: String? = null, + var port: String? = null, + val sourcePort: String? = null, + val network: String? = null, + val source: List? = null, + val user: List? = null, + var inboundTag: List? = null, + val protocol: List? = null, + val attrs: String? = null, + val domainMatcher: String? = null + ) + } + + data class PolicyBean(var levels: Map, + var system: Any? = null) { + data class LevelBean( + var handshake: Int? = null, + var connIdle: Int? = null, + var uplinkOnly: Int? = null, + var downlinkOnly: Int? = null, + val statsUserUplink: Boolean? = null, + val statsUserDownlink: Boolean? = null, + var bufferSize: Int? = null) + } + + data class FakednsBean(var ipPool: String = "198.18.0.0/15", + var poolSize: Int = 10000) // roughly 10 times smaller than total ip pool + + fun getProxyOutbound(): OutboundBean? { + outbounds.forEach { outbound -> + EConfigType.values().forEach { + if (outbound.protocol.equals(it.name, true)) { + return outbound + } + } + } + return null + } + + fun toPrettyPrinting(): String { + return GsonBuilder() + .setPrettyPrinting() + .disableHtmlEscaping() + .registerTypeAdapter( // custom serialiser is needed here since JSON by default parse number as Double, core will fail to start + object : TypeToken() {}.type, + JsonSerializer { src: Double?, _: Type?, _: JsonSerializationContext? -> JsonPrimitive(src?.toInt()) } + ) + .create() + .toJson(this) + } +} diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/VmessQRCode.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/VmessQRCode.kt new file mode 100644 index 00000000..48f3a341 --- /dev/null +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/VmessQRCode.kt @@ -0,0 +1,16 @@ +package com.v2ray.ang.dto + +data class VmessQRCode(var v: String = "", + var ps: String = "", + var add: String = "", + var port: String = "", + var id: String = "", + var aid: String = "0", + var scy: String = "", + var net: String = "", + var type: String = "", + var host: String = "", + var path: String = "", + var tls: String = "", + var sni: String = "", + var alpn: String = "") \ No newline at end of file diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/extension/_Ext.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/extension/_Ext.kt new file mode 100644 index 00000000..fb0bff63 --- /dev/null +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/extension/_Ext.kt @@ -0,0 +1,80 @@ +package com.v2ray.ang.extension + +import android.content.Context +import android.os.Build +import android.widget.Toast +import com.v2ray.ang.AngApplication +import me.drakeet.support.toast.ToastCompat +import org.json.JSONObject +import java.net.URI +import java.net.URLConnection + +/** + * Some extensions + */ + +val Context.v2RayApplication: AngApplication + get() = applicationContext as AngApplication + +fun Context.toast(message: Int): Toast = ToastCompat + .makeText(this, message, Toast.LENGTH_SHORT) + .apply { + show() + } + +fun Context.toast(message: CharSequence): Toast = ToastCompat + .makeText(this, message, Toast.LENGTH_SHORT) + .apply { + show() + } + +fun JSONObject.putOpt(pair: Pair) = putOpt(pair.first, pair.second) +fun JSONObject.putOpt(pairs: Map) = pairs.forEach { putOpt(it.key to it.value) } + +const val threshold = 1000 +const val divisor = 1024F + +fun Long.toSpeedString() = toTrafficString() + "/s" + +fun Long.toTrafficString(): String { + if (this == 0L) + return "\t\t\t0\t B" + + if (this < threshold) + return "${this.toFloat().toShortString()}\t B" + + val kib = this / divisor + if (kib < threshold) + return "${kib.toShortString()}\t KB" + + val mib = kib / divisor + if (mib < threshold) + return "${mib.toShortString()}\t MB" + + val gib = mib / divisor + if (gib < threshold) + return "${gib.toShortString()}\t GB" + + val tib = gib / divisor + if (tib < threshold) + return "${tib.toShortString()}\t TB" + + val pib = tib / divisor + if (pib < threshold) + return "${pib.toShortString()}\t PB" + + return "∞" +} + +private fun Float.toShortString(): String { + val s = "%.2f".format(this) + if (s.length <= 4) + return s + return s.substring(0, 4).removeSuffix(".") +} + +val URLConnection.responseLength: Long + get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) contentLengthLong else contentLength.toLong() + +val URI.idnHost: String + get() = (host!!).replace("[", "").replace("]", "") \ No newline at end of file diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/receiver/TaskerReceiver.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/receiver/TaskerReceiver.kt new file mode 100644 index 00000000..08a3184a --- /dev/null +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/receiver/TaskerReceiver.kt @@ -0,0 +1,41 @@ +package com.v2ray.ang.receiver + +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.text.TextUtils +import com.google.zxing.WriterException +import com.tencent.mmkv.MMKV +import com.v2ray.ang.AppConfig +import com.v2ray.ang.service.V2RayServiceManager +import com.v2ray.ang.util.MmkvManager + +import com.v2ray.ang.util.Utils + +class TaskerReceiver : BroadcastReceiver() { + private val mainStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_MAIN, MMKV.MULTI_PROCESS_MODE) } + + override fun onReceive(context: Context, intent: Intent?) { + + try { + val bundle = intent?.getBundleExtra(AppConfig.TASKER_EXTRA_BUNDLE) + val switch = bundle?.getBoolean(AppConfig.TASKER_EXTRA_BUNDLE_SWITCH, false) + val guid = bundle?.getString(AppConfig.TASKER_EXTRA_BUNDLE_GUID, "") + + if (switch == null || guid == null || TextUtils.isEmpty(guid)) { + return + } else if (switch) { + if (guid == AppConfig.TASKER_DEFAULT_GUID) { + Utils.startVServiceFromToggle(context) + } else { + mainStorage?.encode(MmkvManager.KEY_SELECTED_SERVER, guid) + V2RayServiceManager.startV2Ray(context) + } + } else { + Utils.stopVService(context) + } + } catch (e: WriterException) { + e.printStackTrace() + } + } +} diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/receiver/WidgetProvider.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/receiver/WidgetProvider.kt new file mode 100644 index 00000000..826bf24f --- /dev/null +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/receiver/WidgetProvider.kt @@ -0,0 +1,85 @@ +package com.v2ray.ang.receiver + +import android.app.PendingIntent +import android.appwidget.AppWidgetManager +import android.appwidget.AppWidgetProvider +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import android.os.Build +import android.widget.RemoteViews +import com.v2ray.ang.R +import com.v2ray.ang.AppConfig +import com.v2ray.ang.service.V2RayServiceManager +import com.v2ray.ang.util.Utils + +class WidgetProvider : AppWidgetProvider() { + /** + * 每次窗口小部件被更新都调用一次该方法 + */ + override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) { + super.onUpdate(context, appWidgetManager, appWidgetIds) + updateWidgetBackground(context, appWidgetManager, appWidgetIds, V2RayServiceManager.v2rayPoint.isRunning) + } + + + private fun updateWidgetBackground(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray, isRunning: Boolean) { + val remoteViews = RemoteViews(context.packageName, R.layout.widget_switch) + val intent = Intent(context, WidgetProvider::class.java) + intent.action = AppConfig.BROADCAST_ACTION_WIDGET_CLICK + val pendingIntent = PendingIntent.getBroadcast( + context, + R.id.layout_switch, + intent, + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT + } else { + PendingIntent.FLAG_UPDATE_CURRENT + }) + remoteViews.setOnClickPendingIntent(R.id.layout_switch, pendingIntent) + if (isRunning) { + remoteViews.setInt( + R.id.layout_switch, + "setBackgroundResource", + R.drawable.ic_rounded_corner_theme + ) + } else { + remoteViews.setInt( + R.id.layout_switch, + "setBackgroundResource", + R.drawable.ic_rounded_corner_grey + ) + } + + for (appWidgetId in appWidgetIds) { + appWidgetManager.updateAppWidget(appWidgetId, remoteViews) + } + } + + /** + * 接收窗口小部件发送的广播 + */ + override fun onReceive(context: Context, intent: Intent) { + super.onReceive(context, intent) + if (AppConfig.BROADCAST_ACTION_WIDGET_CLICK == intent.action) { + if (V2RayServiceManager.v2rayPoint.isRunning) { + Utils.stopVService(context) + } else { + Utils.startVServiceFromToggle(context) + } + } else if (AppConfig.BROADCAST_ACTION_ACTIVITY == intent.action) { + AppWidgetManager.getInstance(context)?.let { manager -> + when (intent.getIntExtra("key", 0)) { + AppConfig.MSG_STATE_RUNNING, AppConfig.MSG_STATE_START_SUCCESS -> { + updateWidgetBackground(context, manager, manager.getAppWidgetIds(ComponentName(context, WidgetProvider::class.java)), + true) + } + AppConfig.MSG_STATE_NOT_RUNNING, AppConfig.MSG_STATE_START_FAILURE, AppConfig.MSG_STATE_STOP_SUCCESS -> { + updateWidgetBackground(context, manager, manager.getAppWidgetIds(ComponentName(context, WidgetProvider::class.java)), + false) + } + } + } + } + } +} diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/QSTileService.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/QSTileService.kt new file mode 100644 index 00000000..176b0fbe --- /dev/null +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/QSTileService.kt @@ -0,0 +1,87 @@ +package com.v2ray.ang.service + +import android.annotation.TargetApi +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.graphics.drawable.Icon +import android.os.Build +import android.service.quicksettings.Tile +import android.service.quicksettings.TileService +import com.v2ray.ang.AppConfig +import com.v2ray.ang.R +import com.v2ray.ang.util.MessageUtil +import com.v2ray.ang.util.Utils +import java.lang.ref.SoftReference + +@TargetApi(Build.VERSION_CODES.N) +class QSTileService : TileService() { + + fun setState(state: Int) { + if (state == Tile.STATE_INACTIVE) { + qsTile?.state = Tile.STATE_INACTIVE + qsTile?.label = getString(R.string.app_name) + qsTile?.icon = Icon.createWithResource(applicationContext, R.drawable.ic_stat_name) + } else if (state == Tile.STATE_ACTIVE) { + qsTile?.state = Tile.STATE_ACTIVE + qsTile?.label = V2RayServiceManager.currentConfig?.remarks + qsTile?.icon = Icon.createWithResource(applicationContext, R.drawable.ic_stat_name) + } + + qsTile?.updateTile() + } + + override fun onStartListening() { + super.onStartListening() + setState(Tile.STATE_INACTIVE) + mMsgReceive = ReceiveMessageHandler(this) + registerReceiver(mMsgReceive, IntentFilter(AppConfig.BROADCAST_ACTION_ACTIVITY)) + MessageUtil.sendMsg2Service(this, AppConfig.MSG_REGISTER_CLIENT, "") + } + + override fun onStopListening() { + super.onStopListening() + + unregisterReceiver(mMsgReceive) + mMsgReceive = null + } + + override fun onClick() { + super.onClick() + when (qsTile.state) { + Tile.STATE_INACTIVE -> { + Utils.startVServiceFromToggle(this) + } + Tile.STATE_ACTIVE -> { + Utils.stopVService(this) + } + } + } + + private var mMsgReceive: BroadcastReceiver? = null + + private class ReceiveMessageHandler(context: QSTileService) : BroadcastReceiver() { + internal var mReference: SoftReference = SoftReference(context) + override fun onReceive(ctx: Context?, intent: Intent?) { + val context = mReference.get() + when (intent?.getIntExtra("key", 0)) { + AppConfig.MSG_STATE_RUNNING -> { + context?.setState(Tile.STATE_ACTIVE) + } + AppConfig.MSG_STATE_NOT_RUNNING -> { + context?.setState(Tile.STATE_INACTIVE) + } + AppConfig.MSG_STATE_START_SUCCESS -> { + context?.setState(Tile.STATE_ACTIVE) + } + AppConfig.MSG_STATE_START_FAILURE -> { + context?.setState(Tile.STATE_INACTIVE) + } + AppConfig.MSG_STATE_STOP_SUCCESS -> { + context?.setState(Tile.STATE_INACTIVE) + } + } + } + } +} diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/ServiceControl.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/ServiceControl.kt new file mode 100644 index 00000000..36a26b88 --- /dev/null +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/ServiceControl.kt @@ -0,0 +1,13 @@ +package com.v2ray.ang.service + +import android.app.Service + +interface ServiceControl { + fun getService(): Service + + fun startService() + + fun stopService() + + fun vpnProtect(socket: Int): Boolean +} diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayProxyOnlyService.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayProxyOnlyService.kt new file mode 100644 index 00000000..3403af61 --- /dev/null +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayProxyOnlyService.kt @@ -0,0 +1,56 @@ +package com.v2ray.ang.service + +import android.app.Service +import android.content.Context +import android.content.Intent +import android.os.Build +import android.os.IBinder +import androidx.annotation.RequiresApi +import com.v2ray.ang.util.MyContextWrapper +import com.v2ray.ang.util.Utils +import java.lang.ref.SoftReference + +class V2RayProxyOnlyService : Service(), ServiceControl { + override fun onCreate() { + super.onCreate() + V2RayServiceManager.serviceControl = SoftReference(this) + } + + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + V2RayServiceManager.startV2rayPoint() + return START_STICKY + } + + override fun onDestroy() { + super.onDestroy() + V2RayServiceManager.stopV2rayPoint() + } + + override fun getService(): Service { + return this + } + + override fun startService() { + // do nothing + } + + override fun stopService() { + stopSelf() + } + + override fun vpnProtect(socket: Int): Boolean { + return true + } + + override fun onBind(intent: Intent?): IBinder? { + return null + } + + @RequiresApi(Build.VERSION_CODES.N) + override fun attachBaseContext(newBase: Context?) { + val context = newBase?.let { + MyContextWrapper.wrap(newBase, Utils.getLocale(newBase)) + } + super.attachBaseContext(context) + } +} diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayServiceManager.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayServiceManager.kt new file mode 100644 index 00000000..15f4a1e8 --- /dev/null +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayServiceManager.kt @@ -0,0 +1,399 @@ +package com.v2ray.ang.service + +import android.app.* +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.graphics.Color +import android.os.Build +import android.util.Log +import androidx.annotation.RequiresApi +import androidx.core.app.NotificationCompat +import com.tencent.mmkv.MMKV +import com.v2ray.ang.AppConfig +import com.v2ray.ang.AppConfig.ANG_PACKAGE +import com.v2ray.ang.AppConfig.TAG_DIRECT +import com.v2ray.ang.R +import com.v2ray.ang.dto.ServerConfig +import com.v2ray.ang.extension.toSpeedString +import com.v2ray.ang.extension.toast +import com.v2ray.ang.ui.MainActivity +import com.v2ray.ang.util.MessageUtil +import com.v2ray.ang.util.MmkvManager +import com.v2ray.ang.util.Utils +import com.v2ray.ang.util.V2rayConfigUtil +import go.Seq +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch +import libv2ray.Libv2ray +import libv2ray.V2RayPoint +import libv2ray.V2RayVPNServiceSupportsSet +import rx.Observable +import rx.Subscription +import java.lang.ref.SoftReference +import kotlin.math.min + +object V2RayServiceManager { + private const val NOTIFICATION_ID = 1 + private const val NOTIFICATION_PENDING_INTENT_CONTENT = 0 + private const val NOTIFICATION_PENDING_INTENT_STOP_V2RAY = 1 + private const val NOTIFICATION_ICON_THRESHOLD = 3000 + + val v2rayPoint: V2RayPoint = Libv2ray.newV2RayPoint(V2RayCallback(), Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) + private val mMsgReceive = ReceiveMessageHandler() + private val mainStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_MAIN, MMKV.MULTI_PROCESS_MODE) } + private val settingsStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SETTING, MMKV.MULTI_PROCESS_MODE) } + + var serviceControl: SoftReference? = null + set(value) { + field = value + Seq.setContext(value?.get()?.getService()?.applicationContext) + Libv2ray.initV2Env(Utils.userAssetPath(value?.get()?.getService())) + } + var currentConfig: ServerConfig? = null + + private var lastQueryTime = 0L + private var mBuilder: NotificationCompat.Builder? = null + private var mSubscription: Subscription? = null + private var mNotificationManager: NotificationManager? = null + + fun startV2Ray(context: Context) { + if (settingsStorage?.decodeBool(AppConfig.PREF_PROXY_SHARING) == true) { + context.toast(R.string.toast_warning_pref_proxysharing_short) + } else { + context.toast(R.string.toast_services_start) + } + val intent = if (settingsStorage?.decodeString(AppConfig.PREF_MODE) ?: "VPN" == "VPN") { + Intent(context.applicationContext, V2RayVpnService::class.java) + } else { + Intent(context.applicationContext, V2RayProxyOnlyService::class.java) + } + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) { + context.startForegroundService(intent) + } else { + context.startService(intent) + } + } + + private class V2RayCallback : V2RayVPNServiceSupportsSet { + override fun shutdown(): Long { + val serviceControl = serviceControl?.get() ?: return -1 + // called by go + return try { + serviceControl.stopService() + 0 + } catch (e: Exception) { + Log.d(ANG_PACKAGE, e.toString()) + -1 + } + } + + override fun prepare(): Long { + return 0 + } + + override fun protect(l: Long): Boolean { + val serviceControl = serviceControl?.get() ?: return true + return serviceControl.vpnProtect(l.toInt()) + } + + override fun onEmitStatus(l: Long, s: String?): Long { + //Logger.d(s) + return 0 + } + + override fun setup(s: String): Long { + val serviceControl = serviceControl?.get() ?: return -1 + //Logger.d(s) + return try { + serviceControl.startService() + lastQueryTime = System.currentTimeMillis() + startSpeedNotification() + 0 + } catch (e: Exception) { + Log.d(ANG_PACKAGE, e.toString()) + -1 + } + } + } + + fun startV2rayPoint() { + val service = serviceControl?.get()?.getService() ?: return + val guid = mainStorage?.decodeString(MmkvManager.KEY_SELECTED_SERVER) ?: return + val config = MmkvManager.decodeServerConfig(guid) ?: return + if (!v2rayPoint.isRunning) { + val result = V2rayConfigUtil.getV2rayConfig(service, guid) + if (!result.status) + return + + try { + val mFilter = IntentFilter(AppConfig.BROADCAST_ACTION_SERVICE) + mFilter.addAction(Intent.ACTION_SCREEN_ON) + mFilter.addAction(Intent.ACTION_SCREEN_OFF) + mFilter.addAction(Intent.ACTION_USER_PRESENT) + service.registerReceiver(mMsgReceive, mFilter) + } catch (e: Exception) { + Log.d(ANG_PACKAGE, e.toString()) + } + + v2rayPoint.configureFileContent = result.content + v2rayPoint.domainName = config.getV2rayPointDomainAndPort() + currentConfig = config + + try { + v2rayPoint.runLoop(settingsStorage?.decodeBool(AppConfig.PREF_PREFER_IPV6) ?: false) + } catch (e: Exception) { + Log.d(ANG_PACKAGE, e.toString()) + } + + if (v2rayPoint.isRunning) { + MessageUtil.sendMsg2UI(service, AppConfig.MSG_STATE_START_SUCCESS, "") + showNotification() + } else { + MessageUtil.sendMsg2UI(service, AppConfig.MSG_STATE_START_FAILURE, "") + cancelNotification() + } + } + } + + fun stopV2rayPoint() { + val service = serviceControl?.get()?.getService() ?: return + + if (v2rayPoint.isRunning) { + GlobalScope.launch(Dispatchers.Default) { + try { + v2rayPoint.stopLoop() + } catch (e: Exception) { + Log.d(ANG_PACKAGE, e.toString()) + } + } + } + + MessageUtil.sendMsg2UI(service, AppConfig.MSG_STATE_STOP_SUCCESS, "") + cancelNotification() + + try { + service.unregisterReceiver(mMsgReceive) + } catch (e: Exception) { + Log.d(ANG_PACKAGE, e.toString()) + } + } + + private class ReceiveMessageHandler : BroadcastReceiver() { + override fun onReceive(ctx: Context?, intent: Intent?) { + val serviceControl = serviceControl?.get() ?: return + when (intent?.getIntExtra("key", 0)) { + AppConfig.MSG_REGISTER_CLIENT -> { + //Logger.e("ReceiveMessageHandler", intent?.getIntExtra("key", 0).toString()) + if (v2rayPoint.isRunning) { + MessageUtil.sendMsg2UI(serviceControl.getService(), AppConfig.MSG_STATE_RUNNING, "") + } else { + MessageUtil.sendMsg2UI(serviceControl.getService(), AppConfig.MSG_STATE_NOT_RUNNING, "") + } + } + AppConfig.MSG_UNREGISTER_CLIENT -> { + // nothing to do + } + AppConfig.MSG_STATE_START -> { + // nothing to do + } + AppConfig.MSG_STATE_STOP -> { + serviceControl.stopService() + } + AppConfig.MSG_STATE_RESTART -> { + startV2rayPoint() + } + AppConfig.MSG_MEASURE_DELAY -> { + measureV2rayDelay() + } + } + + when (intent?.action) { + Intent.ACTION_SCREEN_OFF -> { + Log.d(ANG_PACKAGE, "SCREEN_OFF, stop querying stats") + stopSpeedNotification() + } + Intent.ACTION_SCREEN_ON -> { + Log.d(ANG_PACKAGE, "SCREEN_ON, start querying stats") + startSpeedNotification() + } + } + } + } + + private fun measureV2rayDelay() { + GlobalScope.launch(Dispatchers.IO) { + val service = serviceControl?.get()?.getService() ?: return@launch + var time = -1L + var errstr = "" + if (v2rayPoint.isRunning) { + try { + time = v2rayPoint.measureDelay() + } catch (e: Exception) { + Log.d(ANG_PACKAGE, "measureV2rayDelay: $e") + errstr = e.message?.substringAfter("\":") ?: "empty message" + } + } + val result = if (time == -1L) { + service.getString(R.string.connection_test_error, errstr) + } else { + service.getString(R.string.connection_test_available, time) + } + + MessageUtil.sendMsg2UI(service, AppConfig.MSG_MEASURE_DELAY_SUCCESS, result) + } + } + + private fun showNotification() { + val service = serviceControl?.get()?.getService() ?: return + val startMainIntent = Intent(service, MainActivity::class.java) + val contentPendingIntent = PendingIntent.getActivity(service, + NOTIFICATION_PENDING_INTENT_CONTENT, startMainIntent, + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT + } else { + PendingIntent.FLAG_UPDATE_CURRENT + }) + + val stopV2RayIntent = Intent(AppConfig.BROADCAST_ACTION_SERVICE) + stopV2RayIntent.`package` = ANG_PACKAGE + stopV2RayIntent.putExtra("key", AppConfig.MSG_STATE_STOP) + + val stopV2RayPendingIntent = PendingIntent.getBroadcast(service, + NOTIFICATION_PENDING_INTENT_STOP_V2RAY, stopV2RayIntent, + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT + } else { + PendingIntent.FLAG_UPDATE_CURRENT + }) + + val channelId = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + createNotificationChannel() + } else { + // If earlier version channel ID is not used + // https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context) + "" + } + + mBuilder = NotificationCompat.Builder(service, channelId) + .setSmallIcon(R.drawable.ic_stat_name) + .setContentTitle(currentConfig?.remarks) + .setPriority(NotificationCompat.PRIORITY_MIN) + .setOngoing(true) + .setShowWhen(false) + .setOnlyAlertOnce(true) + .setContentIntent(contentPendingIntent) + .addAction(R.drawable.ic_close_grey_800_24dp, + service.getString(R.string.notification_action_stop_v2ray), + stopV2RayPendingIntent) + //.build() + + //mBuilder?.setDefaults(NotificationCompat.FLAG_ONLY_ALERT_ONCE) //取消震动,铃声其他都不好使 + + service.startForeground(NOTIFICATION_ID, mBuilder?.build()) + } + + @RequiresApi(Build.VERSION_CODES.O) + private fun createNotificationChannel(): String { + val channelId = "RAY_NG_M_CH_ID" + val channelName = "V2rayNG Background Service" + val chan = NotificationChannel(channelId, + channelName, NotificationManager.IMPORTANCE_HIGH) + chan.lightColor = Color.DKGRAY + chan.importance = NotificationManager.IMPORTANCE_NONE + chan.lockscreenVisibility = Notification.VISIBILITY_PRIVATE + getNotificationManager()?.createNotificationChannel(chan) + return channelId + } + + fun cancelNotification() { + val service = serviceControl?.get()?.getService() ?: return + service.stopForeground(true) + mBuilder = null + mSubscription?.unsubscribe() + mSubscription = null + } + + private fun updateNotification(contentText: String?, proxyTraffic: Long, directTraffic: Long) { + if (mBuilder != null) { + if (proxyTraffic < NOTIFICATION_ICON_THRESHOLD && directTraffic < NOTIFICATION_ICON_THRESHOLD) { + mBuilder?.setSmallIcon(R.drawable.ic_stat_name) + } else if (proxyTraffic > directTraffic) { + mBuilder?.setSmallIcon(R.drawable.ic_stat_proxy) + } else { + mBuilder?.setSmallIcon(R.drawable.ic_stat_direct) + } + mBuilder?.setStyle(NotificationCompat.BigTextStyle().bigText(contentText)) + mBuilder?.setContentText(contentText) // Emui4.1 need content text even if style is set as BigTextStyle + getNotificationManager()?.notify(NOTIFICATION_ID, mBuilder?.build()) + } + } + + private fun getNotificationManager(): NotificationManager? { + if (mNotificationManager == null) { + val service = serviceControl?.get()?.getService() ?: return null + mNotificationManager = service.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + } + return mNotificationManager + } + + private fun startSpeedNotification() { + if (mSubscription == null && + v2rayPoint.isRunning && + settingsStorage?.decodeBool(AppConfig.PREF_SPEED_ENABLED) == true) { + var lastZeroSpeed = false + val outboundTags = currentConfig?.getAllOutboundTags() + outboundTags?.remove(TAG_DIRECT) + + mSubscription = Observable.interval(3, java.util.concurrent.TimeUnit.SECONDS) + .subscribe { + val queryTime = System.currentTimeMillis() + val sinceLastQueryInSeconds = (queryTime - lastQueryTime) / 1000.0 + var proxyTotal = 0L + val text = StringBuilder() + outboundTags?.forEach { + val up = v2rayPoint.queryStats(it, "uplink") + val down = v2rayPoint.queryStats(it, "downlink") + if (up + down > 0) { + appendSpeedString(text, it, up / sinceLastQueryInSeconds, down / sinceLastQueryInSeconds) + proxyTotal += up + down + } + } + val directUplink = v2rayPoint.queryStats(TAG_DIRECT, "uplink") + val directDownlink = v2rayPoint.queryStats(TAG_DIRECT, "downlink") + val zeroSpeed = (proxyTotal == 0L && directUplink == 0L && directDownlink == 0L) + if (!zeroSpeed || !lastZeroSpeed) { + if (proxyTotal == 0L) { + appendSpeedString(text, outboundTags?.firstOrNull(), 0.0, 0.0) + } + appendSpeedString(text, TAG_DIRECT, directUplink / sinceLastQueryInSeconds, + directDownlink / sinceLastQueryInSeconds) + updateNotification(text.toString(), proxyTotal, directDownlink + directUplink) + } + lastZeroSpeed = zeroSpeed + lastQueryTime = queryTime + } + } + } + + private fun appendSpeedString(text: StringBuilder, name: String?, up: Double, down: Double) { + var n = name ?: "no tag" + n = n.substring(0, min(n.length, 6)) + text.append(n) + for (i in n.length..6 step 2) { + text.append("\t") + } + text.append("• ${up.toLong().toSpeedString()}↑ ${down.toLong().toSpeedString()}↓\n") + } + + private fun stopSpeedNotification() { + if (mSubscription != null) { + mSubscription?.unsubscribe() //stop queryStats + mSubscription = null + updateNotification(currentConfig?.remarks, 0, 0) + } + } +} diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayTestService.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayTestService.kt new file mode 100644 index 00000000..362b7196 --- /dev/null +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayTestService.kt @@ -0,0 +1,45 @@ +package com.v2ray.ang.service + +import android.app.Service +import android.content.Intent +import android.os.IBinder +import com.v2ray.ang.AppConfig.MSG_MEASURE_CONFIG +import com.v2ray.ang.AppConfig.MSG_MEASURE_CONFIG_CANCEL +import com.v2ray.ang.AppConfig.MSG_MEASURE_CONFIG_SUCCESS +import com.v2ray.ang.util.MessageUtil +import com.v2ray.ang.util.SpeedtestUtil +import com.v2ray.ang.util.Utils +import go.Seq +import kotlinx.coroutines.* +import libv2ray.Libv2ray +import java.util.concurrent.Executors + +class V2RayTestService : Service() { + private val realTestScope by lazy { CoroutineScope(Executors.newFixedThreadPool(10).asCoroutineDispatcher()) } + + override fun onCreate() { + super.onCreate() + Seq.setContext(this) + Libv2ray.initV2Env(Utils.userAssetPath(this)) + } + + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + when (intent?.getIntExtra("key", 0)) { + MSG_MEASURE_CONFIG -> { + val contentPair = intent.getSerializableExtra("content") as Pair + realTestScope.launch { + val result = SpeedtestUtil.realPing(contentPair.second) + MessageUtil.sendMsg2UI(this@V2RayTestService, MSG_MEASURE_CONFIG_SUCCESS, Pair(contentPair.first, result)) + } + } + MSG_MEASURE_CONFIG_CANCEL -> { + realTestScope.coroutineContext[Job]?.cancelChildren() + } + } + return super.onStartCommand(intent, flags, startId) + } + + override fun onBind(intent: Intent?): IBinder? { + return null + } +} diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayVpnService.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayVpnService.kt new file mode 100644 index 00000000..af17b466 --- /dev/null +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayVpnService.kt @@ -0,0 +1,333 @@ +package com.v2ray.ang.service + +import android.app.Service +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.net.* +import android.os.Build +import android.os.ParcelFileDescriptor +import android.os.StrictMode +import android.util.Log +import androidx.annotation.RequiresApi +import com.tencent.mmkv.MMKV +import com.v2ray.ang.AppConfig +import com.v2ray.ang.R +import com.v2ray.ang.dto.ERoutingMode +import com.v2ray.ang.util.MmkvManager +import com.v2ray.ang.util.MyContextWrapper +import com.v2ray.ang.util.Utils +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch +import java.io.File +import java.lang.ref.SoftReference + +class V2RayVpnService : VpnService(), ServiceControl { + companion object { + private const val VPN_MTU = 1500 + private const val PRIVATE_VLAN4_CLIENT = "26.26.26.1" + private const val PRIVATE_VLAN4_ROUTER = "26.26.26.2" + private const val PRIVATE_VLAN6_CLIENT = "da26:2626::1" + private const val PRIVATE_VLAN6_ROUTER = "da26:2626::2" + private const val TUN2SOCKS = "libtun2socks.so" + } + + private val settingsStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SETTING, MMKV.MULTI_PROCESS_MODE) } + + private lateinit var mInterface: ParcelFileDescriptor + private var isRunning = false + + //val fd: Int get() = mInterface.fd + private lateinit var process: Process + + /**destroy + * Unfortunately registerDefaultNetworkCallback is going to return our VPN interface: https://android.googlesource.com/platform/frameworks/base/+/dda156ab0c5d66ad82bdcf76cda07cbc0a9c8a2e + * + * This makes doing a requestNetwork with REQUEST necessary so that we don't get ALL possible networks that + * satisfies default network capabilities but only THE default network. Unfortunately we need to have + * android.permission.CHANGE_NETWORK_STATE to be able to call requestNetwork. + * + * Source: https://android.googlesource.com/platform/frameworks/base/+/2df4c7d/services/core/java/com/android/server/ConnectivityService.java#887 + */ + @delegate:RequiresApi(Build.VERSION_CODES.P) + private val defaultNetworkRequest by lazy { + NetworkRequest.Builder() + .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) + .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) + .build() + } + + private val connectivity by lazy { getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager } + + @delegate:RequiresApi(Build.VERSION_CODES.P) + private val defaultNetworkCallback by lazy { + object : ConnectivityManager.NetworkCallback() { + override fun onAvailable(network: Network) { + setUnderlyingNetworks(arrayOf(network)) + } + + override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) { + // it's a good idea to refresh capabilities + setUnderlyingNetworks(arrayOf(network)) + } + + override fun onLost(network: Network) { + setUnderlyingNetworks(null) + } + } + } + + override fun onCreate() { + super.onCreate() + + val policy = StrictMode.ThreadPolicy.Builder().permitAll().build() + StrictMode.setThreadPolicy(policy) + V2RayServiceManager.serviceControl = SoftReference(this) + } + + override fun onRevoke() { + stopV2Ray() + } + +// override fun onLowMemory() { +// stopV2Ray() +// super.onLowMemory() +// } + + override fun onDestroy() { + super.onDestroy() + V2RayServiceManager.cancelNotification() + } + + private fun setup() { + val prepare = prepare(this) + if (prepare != null) { + return + } + + // If the old interface has exactly the same parameters, use it! + // Configure a builder while parsing the parameters. + val builder = Builder() + //val enableLocalDns = defaultDPreference.getPrefBoolean(AppConfig.PREF_LOCAL_DNS_ENABLED, false) + + val routingMode = settingsStorage?.decodeString(AppConfig.PREF_ROUTING_MODE) ?: ERoutingMode.GLOBAL_PROXY.value + + builder.setMtu(VPN_MTU) + builder.addAddress(PRIVATE_VLAN4_CLIENT, 30) + //builder.addDnsServer(PRIVATE_VLAN4_ROUTER) + if (routingMode == ERoutingMode.BYPASS_LAN.value || routingMode == ERoutingMode.BYPASS_LAN_MAINLAND.value) { + resources.getStringArray(R.array.bypass_private_ip_address).forEach { + val addr = it.split('/') + builder.addRoute(addr[0], addr[1].toInt()) + } + } else { + builder.addRoute("0.0.0.0", 0) + } + + if (settingsStorage?.decodeBool(AppConfig.PREF_PREFER_IPV6) == true) { + builder.addAddress(PRIVATE_VLAN6_CLIENT, 126) + if (routingMode == ERoutingMode.BYPASS_LAN.value || routingMode == ERoutingMode.BYPASS_LAN_MAINLAND.value) { + builder.addRoute("2000::", 3) //currently only 1/8 of total ipV6 is in use + } else { + builder.addRoute("::", 0) + } + } + + if (settingsStorage?.decodeBool(AppConfig.PREF_LOCAL_DNS_ENABLED) == true) { + builder.addDnsServer(PRIVATE_VLAN4_ROUTER) + } else { + Utils.getVpnDnsServers() + .forEach { + if (Utils.isPureIpAddress(it)) { + builder.addDnsServer(it) + } + } + } + + builder.setSession(V2RayServiceManager.currentConfig?.remarks.orEmpty()) + + if (settingsStorage?.decodeBool(AppConfig.PREF_PER_APP_PROXY) == true) { + val apps = settingsStorage?.decodeStringSet(AppConfig.PREF_PER_APP_PROXY_SET) + val bypassApps = settingsStorage?.decodeBool(AppConfig.PREF_BYPASS_APPS) ?: false + apps?.forEach { + try { + if (bypassApps) + builder.addDisallowedApplication(it) + else + builder.addAllowedApplication(it) + } catch (e: PackageManager.NameNotFoundException) { + //Logger.d(e) + } + } + } + + // Close the old interface since the parameters have been changed. + try { + mInterface.close() + } catch (ignored: Exception) { + // ignored + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + try { + connectivity.requestNetwork(defaultNetworkRequest, defaultNetworkCallback) + } catch (e: Exception) { + e.printStackTrace() + } + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + builder.setMetered(false) + } + + // Create a new interface using the builder and save the parameters. + try { + mInterface = builder.establish()!! + isRunning = true + runTun2socks() + } catch (e: Exception) { + // non-nullable lateinit var + e.printStackTrace() + stopV2Ray() + } + } + + private fun runTun2socks() { + val socksPort = Utils.parseInt(settingsStorage?.decodeString(AppConfig.PREF_SOCKS_PORT), AppConfig.PORT_SOCKS.toInt()) + val cmd = arrayListOf(File(applicationContext.applicationInfo.nativeLibraryDir, TUN2SOCKS).absolutePath, + "--netif-ipaddr", PRIVATE_VLAN4_ROUTER, + "--netif-netmask", "255.255.255.252", + "--socks-server-addr", "127.0.0.1:${socksPort}", + "--tunmtu", VPN_MTU.toString(), + "--sock-path", "sock_path",//File(applicationContext.filesDir, "sock_path").absolutePath, + "--enable-udprelay", + "--loglevel", "notice") + + if (settingsStorage?.decodeBool(AppConfig.PREF_PREFER_IPV6) == true) { + cmd.add("--netif-ip6addr") + cmd.add(PRIVATE_VLAN6_ROUTER) + } + if (settingsStorage?.decodeBool(AppConfig.PREF_LOCAL_DNS_ENABLED) == true) { + val localDnsPort = Utils.parseInt(settingsStorage?.decodeString(AppConfig.PREF_LOCAL_DNS_PORT), AppConfig.PORT_LOCAL_DNS.toInt()) + cmd.add("--dnsgw") + cmd.add("127.0.0.1:${localDnsPort}") + } + Log.d(packageName, cmd.toString()) + + try { + val proBuilder = ProcessBuilder(cmd) + proBuilder.redirectErrorStream(true) + process = proBuilder + .directory(applicationContext.filesDir) + .start() + Thread(Runnable { + Log.d(packageName,"$TUN2SOCKS check") + process.waitFor() + Log.d(packageName,"$TUN2SOCKS exited") + if (isRunning) { + Log.d(packageName,"$TUN2SOCKS restart") + runTun2socks() + } + }).start() + Log.d(packageName, process.toString()) + + sendFd() + } catch (e: Exception) { + Log.d(packageName, e.toString()) + } + } + + private fun sendFd() { + val fd = mInterface.fileDescriptor + val path = File(applicationContext.filesDir, "sock_path").absolutePath + Log.d(packageName, path) + + GlobalScope.launch(Dispatchers.IO) { + var tries = 0 + while (true) try { + Thread.sleep(50L shl tries) + Log.d(packageName, "sendFd tries: $tries") + LocalSocket().use { localSocket -> + localSocket.connect(LocalSocketAddress(path, LocalSocketAddress.Namespace.FILESYSTEM)) + localSocket.setFileDescriptorsForSend(arrayOf(fd)) + localSocket.outputStream.write(42) + } + break + } catch (e: Exception) { + Log.d(packageName, e.toString()) + if (tries > 5) break + tries += 1 + } + } + } + + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + V2RayServiceManager.startV2rayPoint() + return START_STICKY + //return super.onStartCommand(intent, flags, startId) + } + + private fun stopV2Ray(isForced: Boolean = true) { +// val configName = defaultDPreference.getPrefString(PREF_CURR_CONFIG_GUID, "") +// val emptyInfo = VpnNetworkInfo() +// val info = loadVpnNetworkInfo(configName, emptyInfo)!! + (lastNetworkInfo ?: emptyInfo) +// saveVpnNetworkInfo(configName, info) + isRunning = false; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + try { + connectivity.unregisterNetworkCallback(defaultNetworkCallback) + } catch (ignored: Exception) { + // ignored + } + } + + try { + Log.d(packageName, "tun2socks destroy") + process.destroy() + } catch (e: Exception) { + Log.d(packageName, e.toString()) + } + + V2RayServiceManager.stopV2rayPoint() + + if (isForced) { + //stopSelf has to be called ahead of mInterface.close(). otherwise v2ray core cannot be stooped + //It's strage but true. + //This can be verified by putting stopself() behind and call stopLoop and startLoop + //in a row for several times. You will find that later created v2ray core report port in use + //which means the first v2ray core somehow failed to stop and release the port. + stopSelf() + + try { + mInterface.close() + } catch (ignored: Exception) { + // ignored + } + } + } + + override fun getService(): Service { + return this + } + + override fun startService() { + setup() + } + + override fun stopService() { + stopV2Ray(true) + } + + override fun vpnProtect(socket: Int): Boolean { + return protect(socket) + } + + @RequiresApi(Build.VERSION_CODES.N) + override fun attachBaseContext(newBase: Context?) { + val context = newBase?.let { + MyContextWrapper.wrap(newBase, Utils.getLocale(newBase)) + } + super.attachBaseContext(context) + } +} diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/BaseActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/BaseActivity.kt new file mode 100644 index 00000000..66d8feb6 --- /dev/null +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/BaseActivity.kt @@ -0,0 +1,53 @@ +package com.v2ray.ang.ui + +import android.content.Context +import android.os.Build +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import android.view.MenuItem +import androidx.annotation.RequiresApi +import com.v2ray.ang.util.MyContextWrapper +import com.v2ray.ang.R +import com.v2ray.ang.util.Utils + +abstract class BaseActivity : AppCompatActivity() { + override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) { + android.R.id.home -> { + onBackPressed() + true + } + else -> super.onOptionsItemSelected(item) + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + checkDarkMode() + } + + private fun checkDarkMode() { + if (Utils.getDarkModeStatus(this)) { + if (this.javaClass.simpleName == "MainActivity") { + setTheme(R.style.AppThemeDark_NoActionBar) + } else { + setTheme(R.style.AppThemeDark) + } + } else { + if (this.javaClass.simpleName == "MainActivity") { + setTheme(R.style.AppThemeLight_NoActionBar) + } else { + setTheme(R.style.AppThemeLight) + } + } + } + + @RequiresApi(Build.VERSION_CODES.N) + override fun attachBaseContext(newBase: Context?) { + val context = newBase?.let { + MyContextWrapper.wrap(newBase, Utils.getLocale(newBase)) + } + super.attachBaseContext(context) + } + + + +} \ No newline at end of file diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/FragmentAdapter.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/FragmentAdapter.kt new file mode 100644 index 00000000..d9d12992 --- /dev/null +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/FragmentAdapter.kt @@ -0,0 +1,17 @@ +package com.v2ray.ang.ui + +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentActivity +import androidx.viewpager2.adapter.FragmentStateAdapter + +class FragmentAdapter(fragmentActivity: FragmentActivity, private val mFragments: List) : + FragmentStateAdapter(fragmentActivity) { + + override fun createFragment(position: Int): Fragment { + return mFragments[position] + } + + override fun getItemCount(): Int { + return mFragments.size + } +} diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/LogcatActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/LogcatActivity.kt new file mode 100644 index 00000000..08d76490 --- /dev/null +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/LogcatActivity.kt @@ -0,0 +1,92 @@ +package com.v2ray.ang.ui + +import android.os.Handler +import android.os.Looper +import android.os.Bundle +import android.text.method.ScrollingMovementMethod +import android.view.Menu +import android.view.MenuItem +import android.view.View +import androidx.lifecycle.lifecycleScope +import com.v2ray.ang.AppConfig.ANG_PACKAGE +import com.v2ray.ang.R +import com.v2ray.ang.databinding.ActivityLogcatBinding +import com.v2ray.ang.extension.toast +import com.v2ray.ang.util.Utils +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch + +import java.io.IOException +import java.util.LinkedHashSet + +class LogcatActivity : BaseActivity() { + private lateinit var binding: ActivityLogcatBinding + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityLogcatBinding.inflate(layoutInflater) + val view = binding.root + setContentView(view) + + title = getString(R.string.title_logcat) + + supportActionBar?.setDisplayHomeAsUpEnabled(true) + logcat(false) + } + + private fun logcat(shouldFlushLog: Boolean) { + + try { + binding.pbWaiting.visibility = View.VISIBLE + + lifecycleScope.launch(Dispatchers.Default) { + if (shouldFlushLog) { + val lst = LinkedHashSet() + lst.add("logcat") + lst.add("-c") + val process = Runtime.getRuntime().exec(lst.toTypedArray()) + process.waitFor() + } + val lst = LinkedHashSet() + lst.add("logcat") + lst.add("-d") + lst.add("-v") + lst.add("time") + lst.add("-s") + lst.add("GoLog,tun2socks,${ANG_PACKAGE},AndroidRuntime,System.err") + val process = Runtime.getRuntime().exec(lst.toTypedArray()) +// val bufferedReader = BufferedReader( +// InputStreamReader(process.inputStream)) +// val allText = bufferedReader.use(BufferedReader::readText) + val allText = process.inputStream.bufferedReader().use { it.readText() } + launch(Dispatchers.Main) { + binding.tvLogcat.text = allText + binding.tvLogcat.movementMethod = ScrollingMovementMethod() + binding.pbWaiting.visibility = View.GONE + Handler(Looper.getMainLooper()).post { binding.svLogcat.fullScroll(View.FOCUS_DOWN) } + } + } + } catch (e: IOException) { + e.printStackTrace() + } + } + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + menuInflater.inflate(R.menu.menu_logcat, menu) + return super.onCreateOptionsMenu(menu) + } + + override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) { + R.id.copy_all -> { + Utils.setClipboard(this, binding.tvLogcat.text.toString()) + toast(R.string.toast_success) + true + } + R.id.clear_all -> { + logcat(true) + true + } + else -> super.onOptionsItemSelected(item) + } +} diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt new file mode 100644 index 00000000..d7caf53e --- /dev/null +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt @@ -0,0 +1,658 @@ +package com.v2ray.ang.ui + +import android.Manifest +import android.content.* +import android.net.Uri +import android.net.VpnService +import androidx.recyclerview.widget.LinearLayoutManager +import android.view.Menu +import android.view.MenuItem +import com.tbruyelle.rxpermissions.RxPermissions +import com.v2ray.ang.R +import android.os.Bundle +import android.text.TextUtils +import android.view.KeyEvent +import com.v2ray.ang.AppConfig +import android.content.res.ColorStateList +import com.google.android.material.navigation.NavigationView +import androidx.core.content.ContextCompat +import androidx.core.view.GravityCompat +import androidx.appcompat.app.ActionBarDrawerToggle +import androidx.recyclerview.widget.ItemTouchHelper +import android.util.Log +import android.widget.Toast +import androidx.activity.result.contract.ActivityResultContracts +import androidx.activity.viewModels +import androidx.appcompat.app.AlertDialog +import androidx.lifecycle.lifecycleScope +import com.tencent.mmkv.MMKV +import com.v2ray.ang.AppConfig.ANG_PACKAGE +import com.v2ray.ang.BuildConfig +import com.v2ray.ang.databinding.ActivityMainBinding +import com.v2ray.ang.dto.EConfigType +import com.v2ray.ang.extension.toast +import rx.Observable +import rx.android.schedulers.AndroidSchedulers +import java.util.concurrent.TimeUnit +import com.v2ray.ang.helper.SimpleItemTouchHelperCallback +import com.v2ray.ang.service.V2RayServiceManager +import com.v2ray.ang.util.* +import com.v2ray.ang.viewmodel.MainViewModel +import kotlinx.coroutines.* +import me.drakeet.support.toast.ToastCompat +import java.io.File +import java.io.FileOutputStream + +class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedListener { + private lateinit var binding: ActivityMainBinding + + private val adapter by lazy { MainRecyclerAdapter(this) } + private val mainStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_MAIN, MMKV.MULTI_PROCESS_MODE) } + private val settingsStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SETTING, MMKV.MULTI_PROCESS_MODE) } + private val requestVpnPermission = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { + if (it.resultCode == RESULT_OK) { + startV2Ray() + } + } + private var mItemTouchHelper: ItemTouchHelper? = null + val mainViewModel: MainViewModel by viewModels() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityMainBinding.inflate(layoutInflater) + val view = binding.root + setContentView(view) + title = getString(R.string.title_server) + setSupportActionBar(binding.toolbar) + + binding.fab.setOnClickListener { + if (mainViewModel.isRunning.value == true) { + Utils.stopVService(this) + } else if (settingsStorage?.decodeString(AppConfig.PREF_MODE) ?: "VPN" == "VPN") { + val intent = VpnService.prepare(this) + if (intent == null) { + startV2Ray() + } else { + requestVpnPermission.launch(intent) + } + } else { + startV2Ray() + } + } + binding.layoutTest.setOnClickListener { + if (mainViewModel.isRunning.value == true) { + setTestState(getString(R.string.connection_test_testing)) + mainViewModel.testCurrentServerRealPing() + } else { +// tv_test_state.text = getString(R.string.connection_test_fail) + } + } + + binding.recyclerView.setHasFixedSize(true) + binding.recyclerView.layoutManager = LinearLayoutManager(this) + binding.recyclerView.adapter = adapter + + val callback = SimpleItemTouchHelperCallback(adapter) + mItemTouchHelper = ItemTouchHelper(callback) + mItemTouchHelper?.attachToRecyclerView(binding.recyclerView) + + + val toggle = ActionBarDrawerToggle( + this, binding.drawerLayout, binding.toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close) + binding.drawerLayout.addDrawerListener(toggle) + toggle.syncState() + binding.navView.setNavigationItemSelectedListener(this) + binding.version.text = "v${BuildConfig.VERSION_NAME} (${SpeedtestUtil.getLibVersion()})" + + setupViewModel() + copyAssets() + migrateLegacy() + } + + private fun setupViewModel() { + mainViewModel.updateListAction.observe(this) { index -> + if (index >= 0) { + adapter.notifyItemChanged(index) + } else { + adapter.notifyDataSetChanged() + } + } + mainViewModel.updateTestResultAction.observe(this) { setTestState(it) } + mainViewModel.isRunning.observe(this) { isRunning -> + adapter.isRunning = isRunning + if (isRunning) { + binding.fab.backgroundTintList = ColorStateList.valueOf(ContextCompat.getColor(this, R.color.colorSelected)) + setTestState(getString(R.string.connection_connected)) + binding.layoutTest.isFocusable = true + } else { + binding.fab.backgroundTintList = ColorStateList.valueOf(ContextCompat.getColor(this, R.color.colorUnselected)) + setTestState(getString(R.string.connection_not_connected)) + binding.layoutTest.isFocusable = false + } + hideCircle() + } + mainViewModel.startListenBroadcast() + } + + private fun copyAssets() { + val extFolder = Utils.userAssetPath(this) + lifecycleScope.launch(Dispatchers.IO) { + try { + val geo = arrayOf("geosite.dat", "geoip.dat") + assets.list("") + ?.filter { geo.contains(it) } + ?.filter { !File(extFolder, it).exists() } + ?.forEach { + val target = File(extFolder, it) + assets.open(it).use { input -> + FileOutputStream(target).use { output -> + input.copyTo(output) + } + } + Log.i(ANG_PACKAGE, "Copied from apk assets folder to ${target.absolutePath}") + } + } catch (e: Exception) { + Log.e(ANG_PACKAGE, "asset copy failed", e) + } + } + } + + private fun migrateLegacy() { + lifecycleScope.launch(Dispatchers.IO) { + val result = AngConfigManager.migrateLegacyConfig(this@MainActivity) + if (result != null) { + launch(Dispatchers.Main) { + if (result) { + toast(getString(R.string.migration_success)) + mainViewModel.reloadServerList() + } else { + toast(getString(R.string.migration_fail)) + } + } + } + } + } + + fun startV2Ray() { + if (mainStorage?.decodeString(MmkvManager.KEY_SELECTED_SERVER).isNullOrEmpty()) { + return + } + showCircle() +// toast(R.string.toast_services_start) + V2RayServiceManager.startV2Ray(this) + hideCircle() + } + + fun restartV2Ray() { + if (mainViewModel.isRunning.value == true) { + Utils.stopVService(this) + } + Observable.timer(500, TimeUnit.MILLISECONDS) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe { + startV2Ray() + } + } + + public override fun onResume() { + super.onResume() + mainViewModel.reloadServerList() + } + + public override fun onPause() { + super.onPause() + } + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + menuInflater.inflate(R.menu.menu_main, menu) + return true + } + + override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) { + R.id.import_qrcode -> { + importQRcode(true) + true + } + R.id.import_clipboard -> { + importClipboard() + true + } + R.id.import_manually_vmess -> { + importManually(EConfigType.VMESS.value) + true + } + R.id.import_manually_vless -> { + importManually(EConfigType.VLESS.value) + true + } + R.id.import_manually_ss -> { + importManually(EConfigType.SHADOWSOCKS.value) + true + } + R.id.import_manually_socks -> { + importManually(EConfigType.SOCKS.value) + true + } + R.id.import_manually_trojan -> { + importManually(EConfigType.TROJAN.value) + true + } + R.id.import_config_custom_clipboard -> { + importConfigCustomClipboard() + true + } + R.id.import_config_custom_local -> { + importConfigCustomLocal() + true + } + R.id.import_config_custom_url -> { + importConfigCustomUrlClipboard() + true + } + R.id.import_config_custom_url_scan -> { + importQRcode(false) + true + } + +// R.id.sub_setting -> { +// startActivity() +// true +// } + + R.id.sub_update -> { + importConfigViaSub() + true + } + + R.id.export_all -> { + if (AngConfigManager.shareNonCustomConfigsToClipboard(this, mainViewModel.serverList) == 0) { + toast(R.string.toast_success) + } else { + toast(R.string.toast_failure) + } + true + } + + R.id.ping_all -> { + mainViewModel.testAllTcping() + true + } + + R.id.real_ping_all -> { + mainViewModel.testAllRealPing() + true + } + + R.id.service_restart -> { + restartV2Ray() + true + } + + R.id.del_all_config -> { + AlertDialog.Builder(this).setMessage(R.string.del_config_comfirm) + .setPositiveButton(android.R.string.ok) { _, _ -> + MmkvManager.removeAllServer() + mainViewModel.reloadServerList() + } + .show() + true + } + + R.id.del_invalid_config -> { + AlertDialog.Builder(this).setMessage(R.string.del_config_comfirm) + .setPositiveButton(android.R.string.ok) { _, _ -> + MmkvManager.removeInvalidServer() + mainViewModel.reloadServerList() + } + .show() + true + } + R.id.sort_by_test_results -> { + MmkvManager.sortByTestResults() + mainViewModel.reloadServerList() + true + } + R.id.filter_config -> { + mainViewModel.filterConfig(this) + true + } + + else -> super.onOptionsItemSelected(item) + } + + private fun importManually(createConfigType : Int) { + startActivity( + Intent() + .putExtra("createConfigType", createConfigType) + .putExtra("subscriptionId", mainViewModel.subscriptionId) + .setClass(this, ServerActivity::class.java) + ) + } + + /** + * import config from qrcode + */ + fun importQRcode(forConfig: Boolean): Boolean { +// try { +// startActivityForResult(Intent("com.google.zxing.client.android.SCAN") +// .addCategory(Intent.CATEGORY_DEFAULT) +// .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP), requestCode) +// } catch (e: Exception) { + RxPermissions(this) + .request(Manifest.permission.CAMERA) + .subscribe { + if (it) + if (forConfig) + scanQRCodeForConfig.launch(Intent(this, ScannerActivity::class.java)) + else + scanQRCodeForUrlToCustomConfig.launch(Intent(this, ScannerActivity::class.java)) + else + toast(R.string.toast_permission_denied) + } +// } + return true + } + + private val scanQRCodeForConfig = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { + if (it.resultCode == RESULT_OK) { + importBatchConfig(it.data?.getStringExtra("SCAN_RESULT")) + } + } + + private val scanQRCodeForUrlToCustomConfig = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { + if (it.resultCode == RESULT_OK) { + importConfigCustomUrl(it.data?.getStringExtra("SCAN_RESULT")) + } + } + + /** + * import config from clipboard + */ + fun importClipboard() + : Boolean { + try { + val clipboard = Utils.getClipboard(this) + importBatchConfig(clipboard) + } catch (e: Exception) { + e.printStackTrace() + return false + } + return true + } + + fun importBatchConfig(server: String?, subid: String = "") { + val subid2 = if(subid.isNullOrEmpty()){ + mainViewModel.subscriptionId + }else{ + subid + } + val append = subid.isNullOrEmpty() + + var count = AngConfigManager.importBatchConfig(server, subid2, append) + if (count <= 0) { + count = AngConfigManager.importBatchConfig(Utils.decode(server!!), subid2, append) + } + if (count > 0) { + toast(R.string.toast_success) + mainViewModel.reloadServerList() + } else { + toast(R.string.toast_failure) + } + } + + fun importConfigCustomClipboard() + : Boolean { + try { + val configText = Utils.getClipboard(this) + if (TextUtils.isEmpty(configText)) { + toast(R.string.toast_none_data_clipboard) + return false + } + importCustomizeConfig(configText) + return true + } catch (e: Exception) { + e.printStackTrace() + return false + } + } + + /** + * import config from local config file + */ + fun importConfigCustomLocal(): Boolean { + try { + showFileChooser() + } catch (e: Exception) { + e.printStackTrace() + return false + } + return true + } + + fun importConfigCustomUrlClipboard() + : Boolean { + try { + val url = Utils.getClipboard(this) + if (TextUtils.isEmpty(url)) { + toast(R.string.toast_none_data_clipboard) + return false + } + return importConfigCustomUrl(url) + } catch (e: Exception) { + e.printStackTrace() + return false + } + } + + /** + * import config from url + */ + fun importConfigCustomUrl(url: String?): Boolean { + try { + if (!Utils.isValidUrl(url)) { + toast(R.string.toast_invalid_url) + return false + } + lifecycleScope.launch(Dispatchers.IO) { + val configText = try { + Utils.getUrlContentWithCustomUserAgent(url) + } catch (e: Exception) { + e.printStackTrace() + "" + } + launch(Dispatchers.Main) { + importCustomizeConfig(configText) + } + } + } catch (e: Exception) { + e.printStackTrace() + return false + } + return true + } + + /** + * import config from sub + */ + fun importConfigViaSub() + : Boolean { + try { + toast(R.string.title_sub_update) + MmkvManager.decodeSubscriptions().forEach { + if (TextUtils.isEmpty(it.first) + || TextUtils.isEmpty(it.second.remarks) + || TextUtils.isEmpty(it.second.url) + ) { + return@forEach + } + if (!it.second.enabled) { + return@forEach + } + val url = Utils.idnToASCII(it.second.url) + if (!Utils.isValidUrl(url)) { + return@forEach + } + Log.d(ANG_PACKAGE, url) + lifecycleScope.launch(Dispatchers.IO) { + val configText = try { + Utils.getUrlContentWithCustomUserAgent(url) + } catch (e: Exception) { + e.printStackTrace() + launch(Dispatchers.Main) { + toast("\"" + it.second.remarks + "\" " + getString(R.string.toast_failure)) + } + return@launch + } + launch(Dispatchers.Main) { + importBatchConfig(configText, it.first) + } + } + } + } catch (e: Exception) { + e.printStackTrace() + return false + } + return true + } + + /** + * show file chooser + */ + private fun showFileChooser() { + val intent = Intent(Intent.ACTION_GET_CONTENT) + intent.type = "*/*" + intent.addCategory(Intent.CATEGORY_OPENABLE) + + try { + chooseFileForCustomConfig.launch(Intent.createChooser(intent, getString(R.string.title_file_chooser))) + } catch (ex: ActivityNotFoundException) { + toast(R.string.toast_require_file_manager) + } + } + + private val chooseFileForCustomConfig = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { + val uri = it.data?.data + if (it.resultCode == RESULT_OK && uri != null) { + readContentFromUri(uri) + } + } + + /** + * read content from uri + */ + private fun readContentFromUri(uri: Uri) { + RxPermissions(this) + .request(Manifest.permission.READ_EXTERNAL_STORAGE) + .subscribe { + if (it) { + try { + contentResolver.openInputStream(uri).use { input -> + importCustomizeConfig(input?.bufferedReader()?.readText()) + } + } catch (e: Exception) { + e.printStackTrace() + } + } else + toast(R.string.toast_permission_denied) + } + } + + /** + * import customize config + */ + fun importCustomizeConfig(server: String?) { + try { + if (server == null || TextUtils.isEmpty(server)) { + toast(R.string.toast_none_data) + return + } + mainViewModel.appendCustomConfigServer(server) + mainViewModel.reloadServerList() + toast(R.string.toast_success) + //adapter.notifyItemInserted(mainViewModel.serverList.lastIndex) + } catch (e: Exception) { + ToastCompat.makeText(this, "${getString(R.string.toast_malformed_josn)} ${e.cause?.message}", Toast.LENGTH_LONG).show() + e.printStackTrace() + return + } + } + + fun setTestState(content: String?) { + binding.tvTestState.text = content + } + +// val mConnection = object : ServiceConnection { +// override fun onServiceDisconnected(name: ComponentName?) { +// } +// +// override fun onServiceConnected(name: ComponentName?, service: IBinder?) { +// sendMsg(AppConfig.MSG_REGISTER_CLIENT, "") +// } +// } + + override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean { + if (keyCode == KeyEvent.KEYCODE_BACK) { + moveTaskToBack(false) + return true + } + return super.onKeyDown(keyCode, event) + } + + fun showCircle() { + binding.fabProgressCircle.show() + } + + fun hideCircle() { + try { + Observable.timer(300, TimeUnit.MILLISECONDS) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe { + try { + if (binding.fabProgressCircle.isShown) { + binding.fabProgressCircle.hide() + } + } catch (e: Exception) { + Log.w(ANG_PACKAGE, e) + } + } + } catch (e: Exception) { + Log.d(ANG_PACKAGE, e.toString()) + } + } + + override fun onBackPressed() { + if (binding.drawerLayout.isDrawerOpen(GravityCompat.START)) { + binding.drawerLayout.closeDrawer(GravityCompat.START) + } else { + super.onBackPressed() + } + } + + override fun onNavigationItemSelected(item: MenuItem): Boolean { + // Handle navigation view item clicks here. + when (item.itemId) { + //R.id.server_profile -> activityClass = MainActivity::class.java + R.id.sub_setting -> { + startActivity(Intent(this, SubSettingActivity::class.java)) + } + R.id.settings -> { + startActivity(Intent(this, SettingsActivity::class.java) + .putExtra("isRunning", mainViewModel.isRunning.value == true)) + } + R.id.user_asset_setting -> { + startActivity(Intent(this, UserAssetActivity::class.java)) + } + R.id.feedback -> { + Utils.openUri(this, AppConfig.v2rayNGIssues) + } + R.id.promotion -> { + Utils.openUri(this, "${Utils.decode(AppConfig.promotionUrl)}?t=${System.currentTimeMillis()}") + } + R.id.logcat -> { + startActivity(Intent(this, LogcatActivity::class.java)) + } + } + binding.drawerLayout.closeDrawer(GravityCompat.START) + return true + } +} diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainRecyclerAdapter.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainRecyclerAdapter.kt new file mode 100644 index 00000000..310d3af7 --- /dev/null +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainRecyclerAdapter.kt @@ -0,0 +1,260 @@ +package com.v2ray.ang.ui + +import android.content.Intent +import android.graphics.Color +import android.text.TextUtils +import androidx.core.content.ContextCompat +import androidx.recyclerview.widget.RecyclerView +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.appcompat.app.AlertDialog +import com.google.gson.Gson +import com.tencent.mmkv.MMKV +import com.v2ray.ang.AppConfig +import com.v2ray.ang.R +import com.v2ray.ang.databinding.ItemQrcodeBinding +import com.v2ray.ang.databinding.ItemRecyclerFooterBinding +import com.v2ray.ang.databinding.ItemRecyclerMainBinding +import com.v2ray.ang.dto.EConfigType +import com.v2ray.ang.dto.SubscriptionItem +import com.v2ray.ang.extension.toast +import com.v2ray.ang.helper.ItemTouchHelperAdapter +import com.v2ray.ang.helper.ItemTouchHelperViewHolder +import com.v2ray.ang.service.V2RayServiceManager +import com.v2ray.ang.util.AngConfigManager +import com.v2ray.ang.util.MmkvManager +import com.v2ray.ang.util.Utils +import rx.Observable +import rx.android.schedulers.AndroidSchedulers +import java.util.concurrent.TimeUnit + +class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter() + , ItemTouchHelperAdapter { + companion object { + private const val VIEW_TYPE_ITEM = 1 + private const val VIEW_TYPE_FOOTER = 2 + } + + private var mActivity: MainActivity = activity + private val mainStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_MAIN, MMKV.MULTI_PROCESS_MODE) } + private val subStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SUB, MMKV.MULTI_PROCESS_MODE) } + private val settingsStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SETTING, MMKV.MULTI_PROCESS_MODE) } + private val share_method: Array by lazy { + mActivity.resources.getStringArray(R.array.share_method) + } + var isRunning = false + + override fun getItemCount() = mActivity.mainViewModel.serversCache.size + 1 + + override fun onBindViewHolder(holder: BaseViewHolder, position: Int) { + if (holder is MainViewHolder) { + val guid = mActivity.mainViewModel.serversCache[position].guid + val config = mActivity.mainViewModel.serversCache[position].config +// //filter +// if (mActivity.mainViewModel.subscriptionId.isNotEmpty() +// && mActivity.mainViewModel.subscriptionId != config.subscriptionId +// ) { +// holder.itemMainBinding.cardView.visibility = View.GONE +// } else { +// holder.itemMainBinding.cardView.visibility = View.VISIBLE +// } + + val outbound = config.getProxyOutbound() + val aff = MmkvManager.decodeServerAffiliationInfo(guid) + + holder.itemMainBinding.tvName.text = config.remarks + holder.itemView.setBackgroundColor(Color.TRANSPARENT) + holder.itemMainBinding.tvTestResult.text = aff?.getTestDelayString() ?: "" + if ((aff?.testDelayMillis ?: 0L) < 0L) { + holder.itemMainBinding.tvTestResult.setTextColor(ContextCompat.getColor(mActivity, R.color.colorPingRed)) + } else { + holder.itemMainBinding.tvTestResult.setTextColor(ContextCompat.getColor(mActivity, R.color.colorPing)) + } + if (guid == mainStorage?.decodeString(MmkvManager.KEY_SELECTED_SERVER)) { + holder.itemMainBinding.layoutIndicator.setBackgroundResource(R.color.colorSelected) + } else { + holder.itemMainBinding.layoutIndicator.setBackgroundResource(R.color.colorUnselected) + } + holder.itemMainBinding.tvSubscription.text = "" + val json = subStorage?.decodeString(config.subscriptionId) + if (!json.isNullOrBlank()) { + val sub = Gson().fromJson(json, SubscriptionItem::class.java) + holder.itemMainBinding.tvSubscription.text = sub.remarks + } + + var shareOptions = share_method.asList() + when (config.configType) { + EConfigType.CUSTOM -> { + holder.itemMainBinding.tvType.text = mActivity.getString(R.string.server_customize_config) + shareOptions = shareOptions.takeLast(1) + } + EConfigType.VLESS -> { + holder.itemMainBinding.tvType.text = config.configType.name + } + else -> { + holder.itemMainBinding.tvType.text = config.configType.name.lowercase() + } + } + holder.itemMainBinding.tvStatistics.text = "${outbound?.getServerAddress()} : ${outbound?.getServerPort()}" + + holder.itemMainBinding.layoutShare.setOnClickListener { + AlertDialog.Builder(mActivity).setItems(shareOptions.toTypedArray()) { _, i -> + try { + when (i) { + 0 -> { + if (config.configType == EConfigType.CUSTOM) { + shareFullContent(guid) + } else { + val ivBinding = ItemQrcodeBinding.inflate(LayoutInflater.from(mActivity)) + ivBinding.ivQcode.setImageBitmap(AngConfigManager.share2QRCode(guid)) + AlertDialog.Builder(mActivity).setView(ivBinding.root).show() + } + } + 1 -> { + if (AngConfigManager.share2Clipboard(mActivity, guid) == 0) { + mActivity.toast(R.string.toast_success) + } else { + mActivity.toast(R.string.toast_failure) + } + } + 2 -> shareFullContent(guid) + else -> mActivity.toast("else") + } + } catch (e: Exception) { + e.printStackTrace() + } + }.show() + } + + holder.itemMainBinding.layoutEdit.setOnClickListener { + val intent = Intent().putExtra("guid", guid) + .putExtra("isRunning", isRunning) + if (config.configType == EConfigType.CUSTOM) { + mActivity.startActivity(intent.setClass(mActivity, ServerCustomConfigActivity::class.java)) + } else { + mActivity.startActivity(intent.setClass(mActivity, ServerActivity::class.java)) + } + } + holder.itemMainBinding.layoutRemove.setOnClickListener { + if (guid != mainStorage?.decodeString(MmkvManager.KEY_SELECTED_SERVER)) { + if (settingsStorage?.decodeBool(AppConfig.PREF_CONFIRM_REMOVE) == true) { + AlertDialog.Builder(mActivity).setMessage(R.string.del_config_comfirm) + .setPositiveButton(android.R.string.ok) { _, _ -> + removeServer(guid, position) + } + .show() + } else { + removeServer(guid, position) + } + } + } + + holder.itemMainBinding.infoContainer.setOnClickListener { + val selected = mainStorage?.decodeString(MmkvManager.KEY_SELECTED_SERVER) + if (guid != selected) { + mainStorage?.encode(MmkvManager.KEY_SELECTED_SERVER, guid) + if (!TextUtils.isEmpty(selected)) { + notifyItemChanged(mActivity.mainViewModel.getPosition(selected!!)) + } + notifyItemChanged(mActivity.mainViewModel.getPosition(guid)) + if (isRunning) { + mActivity.showCircle() + Utils.stopVService(mActivity) + Observable.timer(500, TimeUnit.MILLISECONDS) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe { + V2RayServiceManager.startV2Ray(mActivity) + mActivity.hideCircle() + } + } + } + } + } + if (holder is FooterViewHolder) { + //if (activity?.defaultDPreference?.getPrefBoolean(AppConfig.PREF_INAPP_BUY_IS_PREMIUM, false)) { + if (true) { + holder.itemFooterBinding.layoutEdit.visibility = View.INVISIBLE + } else { + holder.itemFooterBinding.layoutEdit.setOnClickListener { + Utils.openUri(mActivity, "${Utils.decode(AppConfig.promotionUrl)}?t=${System.currentTimeMillis()}") + } + } + } + } + + private fun shareFullContent(guid: String) { + if (AngConfigManager.shareFullContent2Clipboard(mActivity, guid) == 0) { + mActivity.toast(R.string.toast_success) + } else { + mActivity.toast(R.string.toast_failure) + } + } + + private fun removeServer(guid: String,position:Int) { + mActivity.mainViewModel.removeServer(guid) + notifyItemRemoved(position) + notifyItemRangeChanged(position, mActivity.mainViewModel.serversCache.size) + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder { + return when (viewType) { + VIEW_TYPE_ITEM -> + MainViewHolder(ItemRecyclerMainBinding.inflate(LayoutInflater.from(parent.context), parent, false)) + else -> + FooterViewHolder(ItemRecyclerFooterBinding.inflate(LayoutInflater.from(parent.context), parent, false)) + } + } + + override fun getItemViewType(position: Int): Int { + return if (position == mActivity.mainViewModel.serversCache.size) { + VIEW_TYPE_FOOTER + } else { + VIEW_TYPE_ITEM + } + } + + open class BaseViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + fun onItemSelected() { + itemView.setBackgroundColor(Color.LTGRAY) + } + + fun onItemClear() { + itemView.setBackgroundColor(0) + } + } + + class MainViewHolder(val itemMainBinding: ItemRecyclerMainBinding) : + BaseViewHolder(itemMainBinding.root), ItemTouchHelperViewHolder + + class FooterViewHolder(val itemFooterBinding: ItemRecyclerFooterBinding) : + BaseViewHolder(itemFooterBinding.root), ItemTouchHelperViewHolder + + override fun onItemDismiss(position: Int) { + val guid = mActivity.mainViewModel.serversCache.getOrNull(position)?.guid ?: return + if (guid != mainStorage?.decodeString(MmkvManager.KEY_SELECTED_SERVER)) { +// mActivity.alert(R.string.del_config_comfirm) { +// positiveButton(android.R.string.ok) { + mActivity.mainViewModel.removeServer(guid) + notifyItemRemoved(position) +// } +// show() +// } + } + } + + override fun onItemMove(fromPosition: Int, toPosition: Int): Boolean { + mActivity.mainViewModel.swapServer(fromPosition, toPosition) + notifyItemMoved(fromPosition, toPosition) + // position is changed, since position is used by click callbacks, need to update range + if (toPosition > fromPosition) + notifyItemRangeChanged(fromPosition, toPosition - fromPosition + 1) + else + notifyItemRangeChanged(toPosition, fromPosition - toPosition + 1) + return true + } + + override fun onItemMoveCompleted() { + // do nothing + } +} diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/PerAppProxyActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/PerAppProxyActivity.kt new file mode 100644 index 00000000..eabed93d --- /dev/null +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/PerAppProxyActivity.kt @@ -0,0 +1,355 @@ +package com.v2ray.ang.ui + +import android.os.Bundle +import android.text.TextUtils +import android.util.Log +import android.view.Menu +import android.view.MenuItem +import android.view.View +import androidx.appcompat.widget.SearchView +import androidx.lifecycle.lifecycleScope +import androidx.preference.PreferenceManager +import androidx.recyclerview.widget.DividerItemDecoration +import androidx.recyclerview.widget.LinearLayoutManager +import com.v2ray.ang.AppConfig +import com.v2ray.ang.AppConfig.ANG_PACKAGE +import com.v2ray.ang.R +import com.v2ray.ang.databinding.ActivityBypassListBinding +import com.v2ray.ang.dto.AppInfo +import com.v2ray.ang.extension.toast +import com.v2ray.ang.extension.v2RayApplication +import com.v2ray.ang.util.AppManagerUtil +import com.v2ray.ang.util.Utils +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch +import rx.android.schedulers.AndroidSchedulers +import rx.schedulers.Schedulers +import java.text.Collator +import java.util.* + +class PerAppProxyActivity : BaseActivity() { + private lateinit var binding: ActivityBypassListBinding + + private var adapter: PerAppProxyAdapter? = null + private var appsAll: List? = null + private val defaultSharedPreferences by lazy { PreferenceManager.getDefaultSharedPreferences(this) } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityBypassListBinding.inflate(layoutInflater) + val view = binding.root + setContentView(view) + + supportActionBar?.setDisplayHomeAsUpEnabled(true) + + val dividerItemDecoration = DividerItemDecoration(this, LinearLayoutManager.VERTICAL) + binding.recyclerView.addItemDecoration(dividerItemDecoration) + + val blacklist = defaultSharedPreferences.getStringSet(AppConfig.PREF_PER_APP_PROXY_SET, null) + + AppManagerUtil.rxLoadNetworkAppList(this) + .subscribeOn(Schedulers.io()) + .map { + if (blacklist != null) { + it.forEach { one -> + if ((blacklist.contains(one.packageName))) { + one.isSelected = 1 + } else { + one.isSelected = 0 + } + } + val comparator = Comparator { p1, p2 -> + when { + p1.isSelected > p2.isSelected -> -1 + p1.isSelected == p2.isSelected -> 0 + else -> 1 + } + } + it.sortedWith(comparator) + } else { + val comparator = object : Comparator { + val collator = Collator.getInstance() + override fun compare(o1: AppInfo, o2: AppInfo) = collator.compare(o1.appName, o2.appName) + } + it.sortedWith(comparator) + } + } +// .map { +// val comparator = object : Comparator { +// val collator = Collator.getInstance() +// override fun compare(o1: AppInfo, o2: AppInfo) = collator.compare(o1.appName, o2.appName) +// } +// it.sortedWith(comparator) +// } + .observeOn(AndroidSchedulers.mainThread()) + .subscribe { + appsAll = it + adapter = PerAppProxyAdapter(this, it, blacklist) + binding.recyclerView.adapter = adapter + binding.pbWaiting.visibility = View.GONE + } + /*** + recycler_view.addOnScrollListener(object : RecyclerView.OnScrollListener() { + var dst = 0 + val threshold = resources.getDimensionPixelSize(R.dimen.bypass_list_header_height) * 2 + override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { + dst += dy + if (dst > threshold) { + header_view.hide() + dst = 0 + } else if (dst < -20) { + header_view.show() + dst = 0 + } + } + + var hiding = false + fun View.hide() { + val target = -height.toFloat() + if (hiding || translationY == target) return + animate() + .translationY(target) + .setInterpolator(AccelerateInterpolator(2F)) + .setListener(object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator?) { + hiding = false + } + }) + hiding = true + } + + var showing = false + fun View.show() { + val target = 0f + if (showing || translationY == target) return + animate() + .translationY(target) + .setInterpolator(DecelerateInterpolator(2F)) + .setListener(object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator?) { + showing = false + } + }) + showing = true + } + }) + ***/ + + binding.switchPerAppProxy.setOnCheckedChangeListener { _, isChecked -> + defaultSharedPreferences.edit().putBoolean(AppConfig.PREF_PER_APP_PROXY, isChecked).apply() + } + binding.switchPerAppProxy.isChecked = defaultSharedPreferences.getBoolean(AppConfig.PREF_PER_APP_PROXY, false) + + binding.switchBypassApps.setOnCheckedChangeListener { _, isChecked -> + defaultSharedPreferences.edit().putBoolean(AppConfig.PREF_BYPASS_APPS, isChecked).apply() + } + binding.switchBypassApps.isChecked = defaultSharedPreferences.getBoolean(AppConfig.PREF_BYPASS_APPS, false) + + /*** + et_search.setOnEditorActionListener { v, actionId, event -> + if (actionId == EditorInfo.IME_ACTION_SEARCH) { + //hide + var imm: InputMethodManager = v.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager + imm.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS) + + val key = v.text.toString().toUpperCase() + val apps = ArrayList() + if (TextUtils.isEmpty(key)) { + appsAll?.forEach { + apps.add(it) + } + } else { + appsAll?.forEach { + if (it.appName.toUpperCase().indexOf(key) >= 0) { + apps.add(it) + } + } + } + adapter = PerAppProxyAdapter(this, apps, adapter?.blacklist) + recycler_view.adapter = adapter + adapter?.notifyDataSetChanged() + true + } else { + false + } + } + ***/ + } + + override fun onPause() { + super.onPause() + adapter?.let { + defaultSharedPreferences.edit().putStringSet(AppConfig.PREF_PER_APP_PROXY_SET, it.blacklist).apply() + } + } + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + menuInflater.inflate(R.menu.menu_bypass_list, menu) + + val searchItem = menu.findItem(R.id.search_view) + if (searchItem != null) { + val searchView = searchItem.actionView as SearchView + searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener { + override fun onQueryTextSubmit(query: String?): Boolean { + return false + } + + override fun onQueryTextChange(newText: String?): Boolean { + filterProxyApp(newText!!) + return false + } + }) + } + + + return super.onCreateOptionsMenu(menu) + } + + override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) { + R.id.select_all -> adapter?.let { + val pkgNames = it.apps.map { it.packageName } + if (it.blacklist.containsAll(pkgNames)) { + it.apps.forEach { + val packageName = it.packageName + adapter?.blacklist!!.remove(packageName) + } + } else { + it.apps.forEach { + val packageName = it.packageName + adapter?.blacklist!!.add(packageName) + } + } + it.notifyDataSetChanged() + true + } ?: false + R.id.select_proxy_app -> { + selectProxyApp() + true + } + R.id.import_proxy_app -> { + importProxyApp() + true + } + R.id.export_proxy_app -> { + exportProxyApp() + true + } + else -> super.onOptionsItemSelected(item) + } + + private fun selectProxyApp() { + toast(R.string.msg_downloading_content) + val url = AppConfig.androidpackagenamelistUrl + lifecycleScope.launch(Dispatchers.IO) { + val content = Utils.getUrlContext(url, 5000) + launch(Dispatchers.Main) { + Log.d(ANG_PACKAGE, content) + selectProxyApp(content, true) + toast(R.string.toast_success) + } + } + } + + private fun importProxyApp() { + val content = Utils.getClipboard(applicationContext) + if (TextUtils.isEmpty(content)) { + return + } + selectProxyApp(content, false) + toast(R.string.toast_success) + } + + private fun exportProxyApp() { + var lst = binding.switchBypassApps.isChecked.toString() + + adapter?.blacklist?.forEach block@{ + lst = lst + System.getProperty("line.separator") + it + } + Utils.setClipboard(applicationContext, lst) + toast(R.string.toast_success) + } + + private fun selectProxyApp(content: String, force: Boolean): Boolean { + try { + val proxyApps = if (TextUtils.isEmpty(content)) { + Utils.readTextFromAssets(v2RayApplication, "proxy_packagename.txt") + } else { + content + } + if (TextUtils.isEmpty(proxyApps)) { + return false + } + + adapter?.blacklist!!.clear() + + if (binding.switchBypassApps.isChecked) { + adapter?.let { + it.apps.forEach block@{ + val packageName = it.packageName + Log.d(ANG_PACKAGE, packageName) + if (!inProxyApps(proxyApps, packageName, force)) { + adapter?.blacklist!!.add(packageName) + println(packageName) + return@block + } + } + it.notifyDataSetChanged() + } + } else { + adapter?.let { + it.apps.forEach block@{ + val packageName = it.packageName + Log.d(ANG_PACKAGE, packageName) + if (inProxyApps(proxyApps, packageName, force)) { + adapter?.blacklist!!.add(packageName) + println(packageName) + return@block + } + } + it.notifyDataSetChanged() + } + } + } catch (e: Exception) { + e.printStackTrace() + return false + } + return true + } + + private fun inProxyApps(proxyApps: String, packageName: String, force: Boolean): Boolean { + if (force) { + if (packageName == "com.google.android.webview") { + return false + } + if (packageName.startsWith("com.google")) { + return true + } + } + + return proxyApps.indexOf(packageName) >= 0 + } + + private fun filterProxyApp(content: String): Boolean { + val apps = ArrayList() + + val key = content.uppercase() + if (key.isNotEmpty()) { + appsAll?.forEach { + if (it.appName.uppercase().indexOf(key) >= 0 + || it.packageName.uppercase().indexOf(key) >= 0) { + apps.add(it) + } + } + } else { + appsAll?.forEach { + apps.add(it) + } + } + + adapter = PerAppProxyAdapter(this, apps, adapter?.blacklist) + binding.recyclerView.adapter = adapter + adapter?.notifyDataSetChanged() + return true + } +} diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/PerAppProxyAdapter.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/PerAppProxyAdapter.kt new file mode 100644 index 00000000..040e9b9c --- /dev/null +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/PerAppProxyAdapter.kt @@ -0,0 +1,87 @@ +package com.v2ray.ang.ui + +import android.view.LayoutInflater +import androidx.recyclerview.widget.RecyclerView +import android.view.View +import android.view.ViewGroup +import com.v2ray.ang.R +import com.v2ray.ang.databinding.ItemRecyclerBypassListBinding +import com.v2ray.ang.dto.AppInfo +import java.util.* + +class PerAppProxyAdapter(val activity: BaseActivity, val apps: List, blacklist: MutableSet?) : + RecyclerView.Adapter() { + + companion object { + private const val VIEW_TYPE_HEADER = 0 + private const val VIEW_TYPE_ITEM = 1 + } + + val blacklist = if (blacklist == null) HashSet() else HashSet(blacklist) + + override fun onBindViewHolder(holder: BaseViewHolder, position: Int) { + if (holder is AppViewHolder) { + val appInfo = apps[position - 1] + holder.bind(appInfo) + } + } + + override fun getItemCount() = apps.size + 1 + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder { + val ctx = parent.context + + return when (viewType) { + VIEW_TYPE_HEADER -> { + val view = View(ctx) + view.layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, + ctx.resources.getDimensionPixelSize(R.dimen.bypass_list_header_height) * 0) + BaseViewHolder(view) + } +// VIEW_TYPE_ITEM -> AppViewHolder(ctx.layoutInflater +// .inflate(R.layout.item_recycler_bypass_list, parent, false)) + + else -> AppViewHolder(ItemRecyclerBypassListBinding.inflate(LayoutInflater.from(ctx), parent, false)) + + } + } + + override fun getItemViewType(position: Int) = if (position == 0) VIEW_TYPE_HEADER else VIEW_TYPE_ITEM + + open class BaseViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) + + inner class AppViewHolder(private val itemBypassBinding: ItemRecyclerBypassListBinding) : BaseViewHolder(itemBypassBinding.root), + View.OnClickListener { + private val inBlacklist: Boolean get() = blacklist.contains(appInfo.packageName) + private lateinit var appInfo: AppInfo + + fun bind(appInfo: AppInfo) { + this.appInfo = appInfo + + itemBypassBinding.icon.setImageDrawable(appInfo.appIcon) +// name.text = appInfo.appName + + itemBypassBinding.checkBox.isChecked = inBlacklist + itemBypassBinding.packageName.text = appInfo.packageName + if (appInfo.isSystemApp) { + itemBypassBinding.name.text = String.format("** %1s", appInfo.appName) + //name.textColor = Color.RED + } else { + itemBypassBinding.name.text = appInfo.appName + //name.textColor = Color.DKGRAY + } + + itemView.setOnClickListener(this) + } + + override fun onClick(v: View?) { + if (inBlacklist) { + blacklist.remove(appInfo.packageName) + itemBypassBinding.checkBox.isChecked = false + } else { + blacklist.add(appInfo.packageName) + itemBypassBinding.checkBox.isChecked = true + } + } + } +} diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/RoutingSettingsActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/RoutingSettingsActivity.kt new file mode 100644 index 00000000..030e80ff --- /dev/null +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/RoutingSettingsActivity.kt @@ -0,0 +1,38 @@ +package com.v2ray.ang.ui + +import android.os.Bundle +import com.v2ray.ang.R +import androidx.fragment.app.Fragment +import com.google.android.material.tabs.TabLayoutMediator +import com.v2ray.ang.AppConfig +import com.v2ray.ang.databinding.ActivityRoutingSettingsBinding + +class RoutingSettingsActivity : BaseActivity() { + private lateinit var binding: ActivityRoutingSettingsBinding + + private val titles: Array by lazy { + resources.getStringArray(R.array.routing_tag) + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityRoutingSettingsBinding.inflate(layoutInflater) + val view = binding.root + setContentView(view) + + title = getString(R.string.title_pref_routing_custom) + supportActionBar?.setDisplayHomeAsUpEnabled(true) + + val fragments = ArrayList() + fragments.add(RoutingSettingsFragment().newInstance(AppConfig.PREF_V2RAY_ROUTING_AGENT)) + fragments.add(RoutingSettingsFragment().newInstance(AppConfig.PREF_V2RAY_ROUTING_DIRECT)) + fragments.add(RoutingSettingsFragment().newInstance(AppConfig.PREF_V2RAY_ROUTING_BLOCKED)) + + val adapter = FragmentAdapter(this, fragments) + binding.viewpager.adapter = adapter + //tablayout.setTabTextColors(Color.BLACK, Color.RED) + TabLayoutMediator(binding.tablayout, binding.viewpager) { tab, position -> + tab.text = titles[position] + }.attach() + } +} diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/RoutingSettingsFragment.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/RoutingSettingsFragment.kt new file mode 100644 index 00000000..f5a8e395 --- /dev/null +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/RoutingSettingsFragment.kt @@ -0,0 +1,158 @@ +package com.v2ray.ang.ui + +import android.Manifest +import android.app.Activity.RESULT_OK +import android.content.Intent +import android.os.Bundle +import android.text.TextUtils +import android.view.* +import androidx.activity.result.contract.ActivityResultContracts +import androidx.fragment.app.Fragment +import androidx.lifecycle.lifecycleScope +import androidx.preference.PreferenceManager +import com.tbruyelle.rxpermissions.RxPermissions +import com.v2ray.ang.AppConfig +import com.v2ray.ang.R +import com.v2ray.ang.databinding.FragmentRoutingSettingsBinding +import com.v2ray.ang.extension.toast +import com.v2ray.ang.extension.v2RayApplication +import com.v2ray.ang.util.Utils +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch + +class RoutingSettingsFragment : Fragment() { + private lateinit var binding: FragmentRoutingSettingsBinding + companion object { + private const val routing_arg = "routing_arg" + } + + val defaultSharedPreferences by lazy { PreferenceManager.getDefaultSharedPreferences(requireContext()) } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + // Inflate the layout for this fragment + binding = FragmentRoutingSettingsBinding.inflate(layoutInflater) + return binding.root// inflater.inflate(R.layout.fragment_routing_settings, container, false) + } + + fun newInstance(arg: String): Fragment { + val fragment = RoutingSettingsFragment() + val bundle = Bundle() + bundle.putString(routing_arg, arg) + fragment.arguments = bundle + return fragment + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + val content = defaultSharedPreferences.getString(requireArguments().getString(routing_arg), "") + binding.etRoutingContent.text = Utils.getEditable(content!!) + + setHasOptionsMenu(true) + } + + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + inflater.inflate(R.menu.menu_routing, menu) + return super.onCreateOptionsMenu(menu, inflater) + } + + override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) { + R.id.save_routing -> { + saveRouting() + true + } + R.id.del_routing -> { + binding.etRoutingContent.text = null + true + } + R.id.scan_replace -> { + scanQRcode(true) + true + } + R.id.scan_append -> { + scanQRcode(false) + true + } + R.id.default_rules -> { + setDefaultRules() + true + } + else -> super.onOptionsItemSelected(item) + } + + private fun saveRouting() { + val content = binding.etRoutingContent.text.toString() + defaultSharedPreferences.edit().putString(requireArguments().getString(routing_arg), content).apply() + activity?.toast(R.string.toast_success) + } + + fun scanQRcode(forReplace: Boolean): Boolean { +// try { +// startActivityForResult(Intent("com.google.zxing.client.android.SCAN") +// .addCategory(Intent.CATEGORY_DEFAULT) +// .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP), requestCode) +// } catch (e: Exception) { + RxPermissions(requireActivity()) + .request(Manifest.permission.CAMERA) + .subscribe { + if (it) + if (forReplace) + scanQRCodeForReplace.launch(Intent(activity, ScannerActivity::class.java)) + else + scanQRCodeForAppend.launch(Intent(activity, ScannerActivity::class.java)) + else + activity?.toast(R.string.toast_permission_denied) + } +// } + return true + } + + private val scanQRCodeForReplace = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { + if (it.resultCode == RESULT_OK) { + val content = it.data?.getStringExtra("SCAN_RESULT") + binding.etRoutingContent.text = Utils.getEditable(content!!) + } + } + + private val scanQRCodeForAppend = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { + if (it.resultCode == RESULT_OK) { + val content = it.data?.getStringExtra("SCAN_RESULT") + binding.etRoutingContent.text = Utils.getEditable("${binding.etRoutingContent.text},$content") + } + } + + fun setDefaultRules(): Boolean { + var url = AppConfig.v2rayCustomRoutingListUrl + var tag = "" + when (requireArguments().getString(routing_arg)) { + AppConfig.PREF_V2RAY_ROUTING_AGENT -> { + tag = AppConfig.TAG_AGENT + } + AppConfig.PREF_V2RAY_ROUTING_DIRECT -> { + tag = AppConfig.TAG_DIRECT + } + AppConfig.PREF_V2RAY_ROUTING_BLOCKED -> { + tag = AppConfig.TAG_BLOCKED + } + } + url += tag + + activity?.toast(R.string.msg_downloading_content) + lifecycleScope.launch(Dispatchers.IO) { + val content = Utils.getUrlContext(url, 5000) + launch(Dispatchers.Main) { + val routingList = if (TextUtils.isEmpty(content)) { + Utils.readTextFromAssets(activity?.v2RayApplication!!, "custom_routing_$tag") + } else { + content + } + binding.etRoutingContent.text = Utils.getEditable(routingList) + saveRouting() + //toast(R.string.toast_success) + } + } + return true + } +} diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ScScannerActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ScScannerActivity.kt new file mode 100644 index 00000000..da1cb7cd --- /dev/null +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ScScannerActivity.kt @@ -0,0 +1,45 @@ +package com.v2ray.ang.ui + +import android.Manifest +import android.content.* +import com.tbruyelle.rxpermissions.RxPermissions +import com.v2ray.ang.R +import com.v2ray.ang.util.AngConfigManager +import android.os.Bundle +import androidx.activity.result.contract.ActivityResultContracts +import com.v2ray.ang.extension.toast + +class ScScannerActivity : BaseActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_none) + importQRcode() + } + + fun importQRcode(): Boolean { + RxPermissions(this) + .request(Manifest.permission.CAMERA) + .subscribe { + if (it) + scanQRCode.launch(Intent(this, ScannerActivity::class.java)) + else + toast(R.string.toast_permission_denied) + } + + return true + } + + private val scanQRCode = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { + if (it.resultCode == RESULT_OK) { + val count = AngConfigManager.importBatchConfig(it.data?.getStringExtra("SCAN_RESULT"), "", false) + if (count > 0) { + toast(R.string.toast_success) + } else { + toast(R.string.toast_failure) + } + startActivity(Intent(this, MainActivity::class.java)) + } + finish() + } +} diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ScSwitchActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ScSwitchActivity.kt new file mode 100644 index 00000000..3d3101ef --- /dev/null +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ScSwitchActivity.kt @@ -0,0 +1,22 @@ +package com.v2ray.ang.ui + +import com.v2ray.ang.R +import com.v2ray.ang.util.Utils +import android.os.Bundle +import com.v2ray.ang.service.V2RayServiceManager + +class ScSwitchActivity : BaseActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + moveTaskToBack(true) + + setContentView(R.layout.activity_none) + + if (V2RayServiceManager.v2rayPoint.isRunning) { + Utils.stopVService(this) + } else { + Utils.startVServiceFromToggle(this) + } + finish() + } +} diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ScannerActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ScannerActivity.kt new file mode 100644 index 00000000..bc3785ec --- /dev/null +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ScannerActivity.kt @@ -0,0 +1,116 @@ +package com.v2ray.ang.ui + +import android.Manifest +import android.app.Activity +import android.os.Bundle +import com.google.zxing.Result +import me.dm7.barcodescanner.zxing.ZXingScannerView +import android.content.Intent +import android.graphics.BitmapFactory +import android.view.Menu +import android.view.MenuItem +import androidx.activity.result.contract.ActivityResultContracts +import com.google.zxing.BarcodeFormat +import com.tbruyelle.rxpermissions.RxPermissions +import com.v2ray.ang.R +import com.v2ray.ang.extension.toast +import com.v2ray.ang.util.QRCodeDecoder + +class ScannerActivity : BaseActivity(), ZXingScannerView.ResultHandler { + + private var mScannerView: ZXingScannerView? = null + + public override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + mScannerView = ZXingScannerView(this) // Programmatically initialize the scanner view + + mScannerView?.setAutoFocus(true) + val formats = ArrayList() + formats.add(BarcodeFormat.QR_CODE) + mScannerView?.setFormats(formats) + + setContentView(mScannerView) // Set the scanner view as the content view + + supportActionBar?.setDisplayHomeAsUpEnabled(true) + } + + public override fun onResume() { + super.onResume() + mScannerView!!.setResultHandler(this) // Register ourselves as a handler for scan results. + mScannerView!!.startCamera() // Start camera on resume + } + + public override fun onPause() { + super.onPause() + mScannerView!!.stopCamera() // Stop camera on pause + } + + override fun handleResult(rawResult: Result) { + // Do something with the result here +// Log.v(FragmentActivity.TAG, rawResult.text) // Prints scan results +// Log.v(FragmentActivity.TAG, rawResult.barcodeFormat.toString()) // Prints the scan format (qrcode, pdf417 etc.) + + finished(rawResult.text) + + // If you would like to resume scanning, call this method below: +// mScannerView!!.resumeCameraPreview(this) + } + + private fun finished(text: String) { + val intent = Intent() + intent.putExtra("SCAN_RESULT", text) + setResult(Activity.RESULT_OK, intent) + finish() + } + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + menuInflater.inflate(R.menu.menu_scanner, menu) + return true + } + + override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) { + R.id.select_photo -> { + RxPermissions(this) + .request(Manifest.permission.READ_EXTERNAL_STORAGE) + .subscribe { + if (it) { + try { + showFileChooser() + } catch (e: Exception) { + e.printStackTrace() + } + } else + toast(R.string.toast_permission_denied) + } + true + } + else -> super.onOptionsItemSelected(item) + } + + private fun showFileChooser() { + val intent = Intent(Intent.ACTION_GET_CONTENT) + intent.type = "image/*" + intent.addCategory(Intent.CATEGORY_OPENABLE) + //intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true) + + try { + chooseFile.launch(Intent.createChooser(intent, getString(R.string.title_file_chooser))) + } catch (ex: android.content.ActivityNotFoundException) { + toast(R.string.toast_require_file_manager) + } + } + + private val chooseFile = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { + val uri = it.data?.data + if (it.resultCode == RESULT_OK && uri != null) { + try { + val bitmap = BitmapFactory.decodeStream(contentResolver.openInputStream(uri)) + val text = QRCodeDecoder.syncDecodeQRCode(bitmap) + finished(text!!) + } catch (e: Exception) { + e.printStackTrace() + toast(e.message.toString()) + } + } + } +} diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ServerActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ServerActivity.kt new file mode 100644 index 00000000..7fc1973b --- /dev/null +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ServerActivity.kt @@ -0,0 +1,412 @@ +package com.v2ray.ang.ui + +import android.os.Bundle +import android.text.TextUtils +import android.view.Menu +import android.view.MenuItem +import android.view.View +import android.widget.* +import androidx.appcompat.app.AlertDialog +import com.tencent.mmkv.MMKV +import com.v2ray.ang.AppConfig +import com.v2ray.ang.AppConfig.PREF_ALLOW_INSECURE +import com.v2ray.ang.R +import com.v2ray.ang.dto.EConfigType +import com.v2ray.ang.dto.ServerConfig +import com.v2ray.ang.dto.V2rayConfig +import com.v2ray.ang.dto.V2rayConfig.Companion.DEFAULT_PORT +import com.v2ray.ang.extension.toast +import com.v2ray.ang.util.MmkvManager +import com.v2ray.ang.util.MmkvManager.ID_MAIN +import com.v2ray.ang.util.MmkvManager.KEY_SELECTED_SERVER +import com.v2ray.ang.util.Utils + +class ServerActivity : BaseActivity() { + + private val mainStorage by lazy { MMKV.mmkvWithID(ID_MAIN, MMKV.MULTI_PROCESS_MODE) } + private val settingsStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SETTING, MMKV.MULTI_PROCESS_MODE) } + private val editGuid by lazy { intent.getStringExtra("guid").orEmpty() } + private val isRunning by lazy { + intent.getBooleanExtra("isRunning", false) + && editGuid.isNotEmpty() + && editGuid == mainStorage?.decodeString(KEY_SELECTED_SERVER) + } + private val createConfigType by lazy { + EConfigType.fromInt(intent.getIntExtra("createConfigType", EConfigType.VMESS.value)) ?: EConfigType.VMESS + } + private val subscriptionId by lazy { + intent.getStringExtra("subscriptionId") + } + + private val securitys: Array by lazy { + resources.getStringArray(R.array.securitys) + } + private val shadowsocksSecuritys: Array by lazy { + resources.getStringArray(R.array.ss_securitys) + } + private val flows: Array by lazy { + resources.getStringArray(R.array.flows) + } + private val networks: Array by lazy { + resources.getStringArray(R.array.networks) + } + private val tcpTypes: Array by lazy { + resources.getStringArray(R.array.header_type_tcp) + } + private val kcpAndQuicTypes: Array by lazy { + resources.getStringArray(R.array.header_type_kcp_and_quic) + } + private val grpcModes: Array by lazy { + resources.getStringArray(R.array.mode_type_grpc) + } + private val streamSecuritys: Array by lazy { + resources.getStringArray(R.array.streamsecurityxs) + } + private val allowinsecures: Array by lazy { + resources.getStringArray(R.array.allowinsecures) + } + private val uTlsItems: Array by lazy { + resources.getStringArray(R.array.streamsecurity_utls) + } + private val alpns: Array by lazy { + resources.getStringArray(R.array.streamsecurity_alpn) + } + // Kotlin synthetics was used, but since it is removed in 1.8. We switch to old manual approach. + // We don't use AndroidViewBinding because, it is better to share similar logics for different + // protocols. Use findViewById manually ensures the xml are de-coupled with the activity logic. + private val et_remarks: EditText by lazy { findViewById(R.id.et_remarks) } + private val et_address: EditText by lazy { findViewById(R.id.et_address) } + private val et_port: EditText by lazy { findViewById(R.id.et_port) } + private val et_id: EditText by lazy { findViewById(R.id.et_id) } + private val et_alterId: EditText? by lazy { findViewById(R.id.et_alterId) } + private val et_security: EditText? by lazy { findViewById(R.id.et_security) } + private val sp_flow: Spinner? by lazy { findViewById(R.id.sp_flow) } + private val sp_security: Spinner? by lazy { findViewById(R.id.sp_security) } + private val sp_stream_security: Spinner? by lazy { findViewById(R.id.sp_stream_security) } + private val sp_allow_insecure: Spinner? by lazy { findViewById(R.id.sp_allow_insecure) } + private val et_sni: EditText? by lazy { findViewById(R.id.et_sni) } + private val sp_stream_fingerprint: Spinner? by lazy { findViewById(R.id.sp_stream_fingerprint) } //uTLS + private val sp_network: Spinner? by lazy { findViewById(R.id.sp_network) } + private val sp_header_type: Spinner? by lazy { findViewById(R.id.sp_header_type) } + private val sp_header_type_title: TextView? by lazy { findViewById(R.id.sp_header_type_title) } + private val et_request_host: EditText? by lazy { findViewById(R.id.et_request_host) } + private val et_path: EditText? by lazy { findViewById(R.id.et_path) } + private val sp_stream_alpn: Spinner? by lazy { findViewById(R.id.sp_stream_alpn) } //uTLS + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + title = getString(R.string.title_server) + + val config = MmkvManager.decodeServerConfig(editGuid) + when(config?.configType ?: createConfigType) { + EConfigType.VMESS -> setContentView(R.layout.activity_server_vmess) + EConfigType.CUSTOM -> return + EConfigType.SHADOWSOCKS -> setContentView(R.layout.activity_server_shadowsocks) + EConfigType.SOCKS -> setContentView(R.layout.activity_server_socks) + EConfigType.VLESS -> setContentView(R.layout.activity_server_vless) + EConfigType.TROJAN -> setContentView(R.layout.activity_server_trojan) + } + sp_network?.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { + override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { + val types = transportTypes(networks[position]) + sp_header_type?.isEnabled = types.size > 1 + val adapter = ArrayAdapter(this@ServerActivity, android.R.layout.simple_spinner_item, types) + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) + sp_header_type?.adapter = adapter + sp_header_type_title?.text = if (networks[position] == "grpc") + getString(R.string.server_lab_mode_type) else + getString(R.string.server_lab_head_type) + config?.getProxyOutbound()?.getTransportSettingDetails()?.let { transportDetails -> + sp_header_type?.setSelection(Utils.arrayFind(types, transportDetails[0])) + et_request_host?.text = Utils.getEditable(transportDetails[1]) + et_path?.text = Utils.getEditable(transportDetails[2]) + } + } + override fun onNothingSelected(parent: AdapterView<*>?) { + // do nothing + } + } + if (config != null) { + bindingServer(config) + } else { + clearServer() + } + supportActionBar?.setDisplayHomeAsUpEnabled(true) + } + + /** + * bingding seleced server config + */ + private fun bindingServer(config: ServerConfig): Boolean { + val outbound = config.getProxyOutbound() ?: return false + val streamSetting = config.outboundBean?.streamSettings ?: return false + + et_remarks.text = Utils.getEditable(config.remarks) + et_address.text = Utils.getEditable(outbound.getServerAddress().orEmpty()) + et_port.text = Utils.getEditable(outbound.getServerPort()?.toString() ?: DEFAULT_PORT.toString()) + et_id.text = Utils.getEditable(outbound.getPassword().orEmpty()) + et_alterId?.text = Utils.getEditable(outbound.settings?.vnext?.get(0)?.users?.get(0)?.alterId.toString()) + if (config.configType == EConfigType.SOCKS) { + et_security?.text = Utils.getEditable(outbound.settings?.servers?.get(0)?.users?.get(0)?.user.orEmpty()) + } else if (config.configType == EConfigType.VLESS) { + et_security?.text = Utils.getEditable(outbound.getSecurityEncryption().orEmpty()) + val flow = Utils.arrayFind(flows, outbound.settings?.vnext?.get(0)?.users?.get(0)?.flow.orEmpty()) + if (flow >= 0) { + sp_flow?.setSelection(flow) + } + } else if (config.configType == EConfigType.TROJAN) { + val flow = Utils.arrayFind(flows, outbound.settings?.servers?.get(0)?.flow.orEmpty()) + if (flow >= 0) { + sp_flow?.setSelection(flow) + } + } + val securityEncryptions = if (config.configType == EConfigType.SHADOWSOCKS) shadowsocksSecuritys else securitys + val security = Utils.arrayFind(securityEncryptions, outbound.getSecurityEncryption().orEmpty()) + if (security >= 0) { + sp_security?.setSelection(security) + } + + val streamSecurity = Utils.arrayFind(streamSecuritys, streamSetting.security) + if (streamSecurity >= 0) { + sp_stream_security?.setSelection(streamSecurity) + (streamSetting.tlsSettings?: streamSetting.xtlsSettings)?.let { tlsSetting -> + val allowinsecure = Utils.arrayFind(allowinsecures, tlsSetting.allowInsecure.toString()) + if (allowinsecure >= 0) { + sp_allow_insecure?.setSelection(allowinsecure) + } + et_sni?.text = Utils.getEditable(tlsSetting.serverName) + + tlsSetting.fingerprint?.let { + val utlsIndex = Utils.arrayFind(uTlsItems, tlsSetting.fingerprint) + sp_stream_fingerprint?.setSelection(utlsIndex) + } + tlsSetting.alpn?.let { + val alpnIndex = Utils.arrayFind(alpns, Utils.removeWhiteSpace(tlsSetting.alpn.joinToString())!!) + sp_stream_alpn?.setSelection(alpnIndex) + } + + } + } + val network = Utils.arrayFind(networks, streamSetting.network) + if (network >= 0) { + sp_network?.setSelection(network) + } + return true + } + + /** + * clear or init server config + */ + private fun clearServer(): Boolean { + et_remarks.text = null + et_address.text = null + et_port.text = Utils.getEditable(DEFAULT_PORT.toString()) + et_id.text = null + et_alterId?.text = Utils.getEditable("0") + sp_security?.setSelection(0) + sp_network?.setSelection(0) + + sp_header_type?.setSelection(0) + et_request_host?.text = null + et_path?.text = null + sp_stream_security?.setSelection(0) + sp_allow_insecure?.setSelection(0) + et_sni?.text = null + + //et_security.text = null + sp_flow?.setSelection(0) + return true + } + + /** + * save server config + */ + private fun saveServer(): Boolean { + if (TextUtils.isEmpty(et_remarks.text.toString())) { + toast(R.string.server_lab_remarks) + return false + } + if (TextUtils.isEmpty(et_address.text.toString())) { + toast(R.string.server_lab_address) + return false + } + val port = Utils.parseInt(et_port.text.toString()) + if (port <= 0) { + toast(R.string.server_lab_port) + return false + } + val config = MmkvManager.decodeServerConfig(editGuid) ?: ServerConfig.create(createConfigType) + if (config.configType != EConfigType.SOCKS && TextUtils.isEmpty(et_id.text.toString())) { + toast(R.string.server_lab_id) + return false + } + sp_stream_security?.let { + if (config.configType == EConfigType.TROJAN && TextUtils.isEmpty(streamSecuritys[it.selectedItemPosition])) { + toast(R.string.server_lab_stream_security) + return false + } + } + et_alterId?.let { + val alterId = Utils.parseInt(it.text.toString()) + if (alterId < 0) { + toast(R.string.server_lab_alterid) + return false + } + } + + config.remarks = et_remarks.text.toString().trim() + config.outboundBean?.settings?.vnext?.get(0)?.let { vnext -> + saveVnext(vnext, port, config) + } + config.outboundBean?.settings?.servers?.get(0)?.let { server -> + saveServers(server, port, config) + } + config.outboundBean?.streamSettings?.let { + saveStreamSettings(it) + } + if(config.subscriptionId.isEmpty() && !subscriptionId.isNullOrEmpty()) { + config.subscriptionId = subscriptionId!! + } + + MmkvManager.encodeServerConfig(editGuid, config) + toast(R.string.toast_success) + finish() + return true + } + + private fun saveVnext(vnext: V2rayConfig.OutboundBean.OutSettingsBean.VnextBean, port: Int, config: ServerConfig) { + vnext.address = et_address.text.toString().trim() + vnext.port = port + vnext.users[0].id = et_id.text.toString().trim() + if (config.configType == EConfigType.VMESS) { + vnext.users[0].alterId = Utils.parseInt(et_alterId?.text.toString()) + vnext.users[0].security = securitys[sp_security?.selectedItemPosition ?: 0] + } else if (config.configType == EConfigType.VLESS) { + vnext.users[0].encryption = et_security?.text.toString().trim() + vnext.users[0].flow = flows[sp_flow?.selectedItemPosition ?: 0] + } + } + + private fun saveServers(server: V2rayConfig.OutboundBean.OutSettingsBean.ServersBean, port: Int, config: ServerConfig) { + server.address = et_address.text.toString().trim() + server.port = port + if (config.configType == EConfigType.SHADOWSOCKS) { + server.password = et_id.text.toString().trim() + server.method = shadowsocksSecuritys[sp_security?.selectedItemPosition ?: 0] + } else if (config.configType == EConfigType.SOCKS) { + if (TextUtils.isEmpty(et_security?.text) && TextUtils.isEmpty(et_id.text)) { + server.users = null + } else { + val socksUsersBean = V2rayConfig.OutboundBean.OutSettingsBean.ServersBean.SocksUsersBean() + socksUsersBean.user = et_security?.text.toString().trim() + socksUsersBean.pass = et_id.text.toString().trim() + server.users = listOf(socksUsersBean) + } + } else if (config.configType == EConfigType.TROJAN) { + server.password = et_id.text.toString().trim() + server.flow = + if (streamSecuritys[sp_stream_security?.selectedItemPosition ?: 0] == V2rayConfig.XTLS) { + flows[sp_flow?.selectedItemPosition ?: 0] + } else { + "" + } + } + } + + private fun saveStreamSettings(streamSetting: V2rayConfig.OutboundBean.StreamSettingsBean) { + val network = sp_network?.selectedItemPosition ?: return + val type = sp_header_type?.selectedItemPosition ?: return + val requestHost = et_request_host?.text?.toString()?.trim() ?: return + val path = et_path?.text?.toString()?.trim() ?: return + val sniField = et_sni?.text?.toString()?.trim() ?: return + val allowInsecureField = sp_allow_insecure?.selectedItemPosition ?: return + val streamSecurity = sp_stream_security?.selectedItemPosition ?: return + var utlsIndex = sp_stream_fingerprint?.selectedItemPosition ?: return + var alpnIndex = sp_stream_alpn?.selectedItemPosition ?: return + + var sni = streamSetting.populateTransportSettings( + transport = networks[network], + headerType = transportTypes(networks[network])[type], + host = requestHost, + path = path, + seed = path, + quicSecurity = requestHost, + key = path, + mode = transportTypes(networks[network])[type], + serviceName = path + ) + if (sniField.isNotBlank()) { + sni = sniField + } + val allowInsecure = if (allowinsecures[allowInsecureField].isBlank()) { + settingsStorage?.decodeBool(PREF_ALLOW_INSECURE) ?: false + } else { + allowinsecures[allowInsecureField].toBoolean() + } + + streamSetting.populateTlsSettings(streamSecuritys[streamSecurity], allowInsecure, sni, uTlsItems[utlsIndex], alpns[alpnIndex]) + } + + private fun transportTypes(network: String?): Array { + return if (network == "tcp") { + tcpTypes + } else if (network == "kcp" || network == "quic") { + kcpAndQuicTypes + } else if (network == "grpc") { + grpcModes + } else { + arrayOf("---") + } + } + + /** + * save server config + */ + private fun deleteServer(): Boolean { + if (editGuid.isNotEmpty()) { + if (editGuid != mainStorage?.decodeString(MmkvManager.KEY_SELECTED_SERVER)) { + if (settingsStorage?.decodeBool(AppConfig.PREF_CONFIRM_REMOVE) == true) { + AlertDialog.Builder(this).setMessage(R.string.del_config_comfirm) + .setPositiveButton(android.R.string.ok) { _, _ -> + MmkvManager.removeServer(editGuid) + finish() + } + .show() + } else { + MmkvManager.removeServer(editGuid) + finish() + } + } + } + return true + } + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + menuInflater.inflate(R.menu.action_server, menu) + val delButton = menu.findItem(R.id.del_config) + val saveButton = menu.findItem(R.id.save_config) + + if (editGuid.isNotEmpty()) { + if (isRunning) { + delButton?.isVisible = false + saveButton?.isVisible = false + } + } else { + delButton?.isVisible = false + } + + return super.onCreateOptionsMenu(menu) + } + + override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) { + R.id.del_config -> { + deleteServer() + true + } + R.id.save_config -> { + saveServer() + true + } + else -> super.onOptionsItemSelected(item) + } +} diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ServerCustomConfigActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ServerCustomConfigActivity.kt new file mode 100644 index 00000000..c8138915 --- /dev/null +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ServerCustomConfigActivity.kt @@ -0,0 +1,144 @@ +package com.v2ray.ang.ui + +import android.os.Bundle +import android.text.TextUtils +import android.view.Menu +import android.view.MenuItem +import android.widget.Toast +import androidx.appcompat.app.AlertDialog +import com.blacksquircle.ui.language.json.JsonLanguage +import com.google.gson.* +import com.tencent.mmkv.MMKV +import com.v2ray.ang.R +import com.v2ray.ang.databinding.ActivityServerCustomConfigBinding +import com.v2ray.ang.dto.EConfigType +import com.v2ray.ang.dto.ServerConfig +import com.v2ray.ang.dto.V2rayConfig +import com.v2ray.ang.extension.toast +import com.v2ray.ang.util.MmkvManager +import com.v2ray.ang.util.Utils +import me.drakeet.support.toast.ToastCompat + +class ServerCustomConfigActivity : BaseActivity() { + private lateinit var binding: ActivityServerCustomConfigBinding + + private val mainStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_MAIN, MMKV.MULTI_PROCESS_MODE) } + private val serverRawStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SERVER_RAW, MMKV.MULTI_PROCESS_MODE) } + private val editGuid by lazy { intent.getStringExtra("guid").orEmpty() } + private val isRunning by lazy { + intent.getBooleanExtra("isRunning", false) + && editGuid.isNotEmpty() + && editGuid == mainStorage?.decodeString(MmkvManager.KEY_SELECTED_SERVER) + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityServerCustomConfigBinding.inflate(layoutInflater) + val view = binding.root + setContentView(view) + title = getString(R.string.title_server) + + binding.editor.language = JsonLanguage() + val config = MmkvManager.decodeServerConfig(editGuid) + if (config != null) { + bindingServer(config) + } else { + clearServer() + } + supportActionBar?.setDisplayHomeAsUpEnabled(true) + } + + /** + * bingding seleced server config + */ + private fun bindingServer(config: ServerConfig): Boolean { + binding.etRemarks.text = Utils.getEditable(config.remarks) + val raw = serverRawStorage?.decodeString(editGuid) + if (raw.isNullOrBlank()) { + binding.editor.setTextContent(Utils.getEditable(config.fullConfig?.toPrettyPrinting().orEmpty())) + } else { + binding.editor.setTextContent(Utils.getEditable(raw)) + } + return true + } + + /** + * clear or init server config + */ + private fun clearServer(): Boolean { + binding.etRemarks.text = null + return true + } + + /** + * save server config + */ + private fun saveServer(): Boolean { + if (TextUtils.isEmpty(binding.etRemarks.text.toString())) { + toast(R.string.server_lab_remarks) + return false + } + + val v2rayConfig = try { + Gson().fromJson(binding.editor.text.toString(), V2rayConfig::class.java) + } catch (e: Exception) { + e.printStackTrace() + ToastCompat.makeText(this, "${getString(R.string.toast_malformed_josn)} ${e.cause?.message}", Toast.LENGTH_LONG).show() + return false + } + + val config = MmkvManager.decodeServerConfig(editGuid) ?: ServerConfig.create(EConfigType.CUSTOM) + config.remarks = binding.etRemarks.text.toString().trim() + config.fullConfig = v2rayConfig + + MmkvManager.encodeServerConfig(editGuid, config) + serverRawStorage?.encode(editGuid, binding.editor.text.toString()) + toast(R.string.toast_success) + finish() + return true + } + + /** + * save server config + */ + private fun deleteServer(): Boolean { + if (editGuid.isNotEmpty()) { + AlertDialog.Builder(this).setMessage(R.string.del_config_comfirm) + .setPositiveButton(android.R.string.ok) { _, _ -> + MmkvManager.removeServer(editGuid) + finish() + } + .show() + } + return true + } + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + menuInflater.inflate(R.menu.action_server, menu) + val delButton = menu.findItem(R.id.del_config) + val saveButton = menu.findItem(R.id.save_config) + + if (editGuid.isNotEmpty()) { + if (isRunning) { + delButton?.isVisible = false + saveButton?.isVisible = false + } + } else { + delButton?.isVisible = false + } + + return super.onCreateOptionsMenu(menu) + } + + override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) { + R.id.del_config -> { + deleteServer() + true + } + R.id.save_config -> { + saveServer() + true + } + else -> super.onOptionsItemSelected(item) + } +} diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/SettingsActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/SettingsActivity.kt new file mode 100644 index 00000000..5284cc64 --- /dev/null +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/SettingsActivity.kt @@ -0,0 +1,182 @@ +package com.v2ray.ang.ui + +import android.content.Intent +import android.os.Bundle +import android.text.TextUtils +import android.view.View +import androidx.activity.viewModels +import androidx.preference.* +import com.v2ray.ang.AppConfig +import com.v2ray.ang.R +import com.v2ray.ang.util.Utils +import com.v2ray.ang.viewmodel.SettingsViewModel + +class SettingsActivity : BaseActivity() { + private val settingsViewModel: SettingsViewModel by viewModels() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_settings) + + title = getString(R.string.title_settings) + + supportActionBar?.setDisplayHomeAsUpEnabled(true) + + settingsViewModel.startListenPreferenceChange() + } + + class SettingsFragment : PreferenceFragmentCompat() { + private val perAppProxy by lazy { findPreference(AppConfig.PREF_PER_APP_PROXY) } + private val localDns by lazy { findPreference(AppConfig.PREF_LOCAL_DNS_ENABLED) } + private val fakeDns by lazy { findPreference(AppConfig.PREF_FAKE_DNS_ENABLED) } + private val localDnsPort by lazy { findPreference(AppConfig.PREF_LOCAL_DNS_PORT) } + private val vpnDns by lazy { findPreference(AppConfig.PREF_VPN_DNS) } + // val autoRestart by lazy { findPreference(PREF_AUTO_RESTART) as CheckBoxPreference } + private val remoteDns by lazy { findPreference(AppConfig.PREF_REMOTE_DNS) } + private val domesticDns by lazy { findPreference(AppConfig.PREF_DOMESTIC_DNS) } + private val socksPort by lazy { findPreference(AppConfig.PREF_SOCKS_PORT) } + private val httpPort by lazy { findPreference(AppConfig.PREF_HTTP_PORT) } + private val routingCustom by lazy { findPreference(AppConfig.PREF_ROUTING_CUSTOM) } + // val licenses: Preference by lazy { findPreference(PREF_LICENSES) } +// val feedback: Preference by lazy { findPreference(PREF_FEEDBACK) } +// val tgGroup: Preference by lazy { findPreference(PREF_TG_GROUP) } + + private val mode by lazy { findPreference(AppConfig.PREF_MODE) } + + override fun onCreatePreferences(bundle: Bundle?, s: String?) { + addPreferencesFromResource(R.xml.pref_settings) + + routingCustom?.setOnPreferenceClickListener { + startActivity(Intent(activity, RoutingSettingsActivity::class.java)) + false + } + +// licenses.onClick { +// val fragment = LicensesDialogFragment.Builder(act) +// .setNotices(R.raw.licenses) +// .setIncludeOwnLicense(false) +// .build() +// fragment.show((act as AppCompatActivity).supportFragmentManager, null) +// } +// +// feedback.onClick { +// Utils.openUri(activity, "https://github.com/2dust/v2rayNG/issues") +// } +// tgGroup.onClick { +// // Utils.openUri(activity, "https://t.me/v2rayN") +// val intent = Intent(Intent.ACTION_VIEW, Uri.parse("tg:resolve?domain=v2rayN")) +// try { +// startActivity(intent) +// } catch (e: Exception) { +// e.printStackTrace() +// toast(R.string.toast_tg_app_not_found) +// } +// } + + perAppProxy?.setOnPreferenceClickListener { + startActivity(Intent(activity, PerAppProxyActivity::class.java)) + perAppProxy?.isChecked = true + false + } + + remoteDns?.setOnPreferenceChangeListener { _, any -> + // remoteDns.summary = any as String + val nval = any as String + remoteDns?.summary = if (nval == "") AppConfig.DNS_AGENT else nval + true + } + domesticDns?.setOnPreferenceChangeListener { _, any -> + // domesticDns.summary = any as String + val nval = any as String + domesticDns?.summary = if (nval == "") AppConfig.DNS_DIRECT else nval + true + } + + localDns?.setOnPreferenceChangeListener{ _, any -> + updateLocalDns(any as Boolean) + true + } + localDnsPort?.setOnPreferenceChangeListener { _, any -> + val nval = any as String + localDnsPort?.summary = if (TextUtils.isEmpty(nval)) AppConfig.PORT_LOCAL_DNS else nval + true + } + vpnDns?.setOnPreferenceChangeListener { _, any -> + vpnDns?.summary = any as String + true + } + socksPort?.setOnPreferenceChangeListener { _, any -> + val nval = any as String + socksPort?.summary = if (TextUtils.isEmpty(nval)) AppConfig.PORT_SOCKS else nval + true + } + httpPort?.setOnPreferenceChangeListener { _, any -> + val nval = any as String + httpPort?.summary = if (TextUtils.isEmpty(nval)) AppConfig.PORT_HTTP else nval + true + } + mode?.setOnPreferenceChangeListener { _, newValue -> + updateMode(newValue.toString()) + true + } + mode?.dialogLayoutResource = R.layout.preference_with_help_link + //loglevel.summary = "LogLevel" + } + + override fun onStart() { + super.onStart() + val defaultSharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireActivity()) + updateMode(defaultSharedPreferences.getString(AppConfig.PREF_MODE, "VPN")) + var remoteDnsString = defaultSharedPreferences.getString(AppConfig.PREF_REMOTE_DNS, "") + domesticDns?.summary = defaultSharedPreferences.getString(AppConfig.PREF_DOMESTIC_DNS, "") + + localDnsPort?.summary = defaultSharedPreferences.getString(AppConfig.PREF_LOCAL_DNS_PORT, AppConfig.PORT_LOCAL_DNS) + socksPort?.summary = defaultSharedPreferences.getString(AppConfig.PREF_SOCKS_PORT, AppConfig.PORT_SOCKS) + httpPort?.summary = defaultSharedPreferences.getString(AppConfig.PREF_HTTP_PORT, AppConfig.PORT_HTTP) + + if (TextUtils.isEmpty(remoteDnsString)) { + remoteDnsString = AppConfig.DNS_AGENT + } + if (TextUtils.isEmpty(domesticDns?.summary)) { + domesticDns?.summary = AppConfig.DNS_DIRECT + } + remoteDns?.summary = remoteDnsString + vpnDns?.summary = defaultSharedPreferences.getString(AppConfig.PREF_VPN_DNS, remoteDnsString) + + if (TextUtils.isEmpty(localDnsPort?.summary)) { + localDnsPort?.summary = AppConfig.PORT_LOCAL_DNS + } + if (TextUtils.isEmpty(socksPort?.summary)) { + socksPort?.summary = AppConfig.PORT_SOCKS + } + if (TextUtils.isEmpty(httpPort?.summary)) { + httpPort?.summary = AppConfig.PORT_HTTP + } + } + + private fun updateMode(mode: String?) { + val defaultSharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireActivity()) + val vpn = mode == "VPN" + perAppProxy?.isEnabled = vpn + perAppProxy?.isChecked = PreferenceManager.getDefaultSharedPreferences(requireActivity()) + .getBoolean(AppConfig.PREF_PER_APP_PROXY, false) + localDns?.isEnabled = vpn + fakeDns?.isEnabled = vpn + localDnsPort?.isEnabled = vpn + vpnDns?.isEnabled = vpn + if (vpn) { + updateLocalDns(defaultSharedPreferences.getBoolean(AppConfig.PREF_LOCAL_DNS_ENABLED, false)) + } + } + + private fun updateLocalDns(enabled: Boolean) { + fakeDns?.isEnabled = enabled + localDnsPort?.isEnabled = enabled + vpnDns?.isEnabled = !enabled + } + } + + fun onModeHelpClicked(view: View) { + Utils.openUri(this, AppConfig.v2rayNGWikiMode) + } +} diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/SubEditActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/SubEditActivity.kt new file mode 100644 index 00000000..5a5a8d44 --- /dev/null +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/SubEditActivity.kt @@ -0,0 +1,133 @@ +package com.v2ray.ang.ui + +import android.os.Bundle +import android.text.TextUtils +import android.view.Menu +import android.view.MenuItem +import androidx.appcompat.app.AlertDialog +import com.google.gson.Gson +import com.tencent.mmkv.MMKV +import com.v2ray.ang.R +import com.v2ray.ang.databinding.ActivitySubEditBinding +import com.v2ray.ang.dto.SubscriptionItem +import com.v2ray.ang.extension.toast +import com.v2ray.ang.util.MmkvManager +import com.v2ray.ang.util.Utils + +class SubEditActivity : BaseActivity() { + private lateinit var binding: ActivitySubEditBinding + + var del_config: MenuItem? = null + var save_config: MenuItem? = null + + private val subStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SUB, MMKV.MULTI_PROCESS_MODE) } + private val editSubId by lazy { intent.getStringExtra("subId").orEmpty() } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivitySubEditBinding.inflate(layoutInflater) + val view = binding.root + setContentView(view) + title = getString(R.string.title_sub_setting) + + val json = subStorage?.decodeString(editSubId) + if (!json.isNullOrBlank()) { + bindingServer(Gson().fromJson(json, SubscriptionItem::class.java)) + } else { + clearServer() + } + supportActionBar?.setDisplayHomeAsUpEnabled(true) + } + + /** + * bingding seleced server config + */ + private fun bindingServer(subItem: SubscriptionItem): Boolean { + binding.etRemarks.text = Utils.getEditable(subItem.remarks) + binding.etUrl.text = Utils.getEditable(subItem.url) + binding.chkEnable.isChecked = subItem.enabled + return true + } + + /** + * clear or init server config + */ + private fun clearServer(): Boolean { + binding.etRemarks.text = null + binding.etUrl.text = null + binding.chkEnable.isChecked = true + return true + } + + /** + * save server config + */ + private fun saveServer(): Boolean { + val subItem: SubscriptionItem + val json = subStorage?.decodeString(editSubId) + var subId = editSubId + if (!json.isNullOrBlank()) { + subItem = Gson().fromJson(json, SubscriptionItem::class.java) + } else { + subId = Utils.getUuid() + subItem = SubscriptionItem() + } + + subItem.remarks = binding.etRemarks.text.toString() + subItem.url = binding.etUrl.text.toString() + subItem.enabled = binding.chkEnable.isChecked + + if (TextUtils.isEmpty(subItem.remarks)) { + toast(R.string.sub_setting_remarks) + return false + } +// if (TextUtils.isEmpty(subItem.url)) { +// toast(R.string.sub_setting_url) +// return false +// } + + subStorage?.encode(subId, Gson().toJson(subItem)) + toast(R.string.toast_success) + finish() + return true + } + + /** + * save server config + */ + private fun deleteServer(): Boolean { + if (editSubId.isNotEmpty()) { + AlertDialog.Builder(this).setMessage(R.string.del_config_comfirm) + .setPositiveButton(android.R.string.ok) { _, _ -> + MmkvManager.removeSubscription(editSubId) + finish() + } + .show() + } + return true + } + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + menuInflater.inflate(R.menu.action_server, menu) + del_config = menu.findItem(R.id.del_config) + save_config = menu.findItem(R.id.save_config) + + if (editSubId.isEmpty()) { + del_config?.isVisible = false + } + + return super.onCreateOptionsMenu(menu) + } + + override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) { + R.id.del_config -> { + deleteServer() + true + } + R.id.save_config -> { + saveServer() + true + } + else -> super.onOptionsItemSelected(item) + } +} diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/SubSettingActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/SubSettingActivity.kt new file mode 100644 index 00000000..853a3878 --- /dev/null +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/SubSettingActivity.kt @@ -0,0 +1,55 @@ +package com.v2ray.ang.ui + +import android.content.Intent +import androidx.recyclerview.widget.LinearLayoutManager +import android.view.Menu +import android.view.MenuItem +import com.v2ray.ang.R +import android.os.Bundle +import com.v2ray.ang.databinding.ActivitySubSettingBinding +import com.v2ray.ang.dto.SubscriptionItem +import com.v2ray.ang.util.MmkvManager + +class SubSettingActivity : BaseActivity() { + private lateinit var binding: ActivitySubSettingBinding + + var subscriptions:List> = listOf() + private val adapter by lazy { SubSettingRecyclerAdapter(this) } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivitySubSettingBinding.inflate(layoutInflater) + val view = binding.root + setContentView(view) + + title = getString(R.string.title_sub_setting) + + binding.recyclerView.setHasFixedSize(true) + binding.recyclerView.layoutManager = LinearLayoutManager(this) + binding.recyclerView.adapter = adapter + + supportActionBar?.setDisplayHomeAsUpEnabled(true) + } + + override fun onResume() { + super.onResume() + subscriptions = MmkvManager.decodeSubscriptions() + adapter.notifyDataSetChanged() + } + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + menuInflater.inflate(R.menu.action_sub_setting, menu) + menu.findItem(R.id.del_config)?.isVisible = false + menu.findItem(R.id.save_config)?.isVisible = false + + return super.onCreateOptionsMenu(menu) + } + + override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) { + R.id.add_config -> { + startActivity(Intent(this, SubEditActivity::class.java)) + true + } + else -> super.onOptionsItemSelected(item) + } +} diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/SubSettingRecyclerAdapter.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/SubSettingRecyclerAdapter.kt new file mode 100644 index 00000000..f3732bf3 --- /dev/null +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/SubSettingRecyclerAdapter.kt @@ -0,0 +1,50 @@ +package com.v2ray.ang.ui + +import android.content.Intent +import android.graphics.Color +import android.view.LayoutInflater +import androidx.recyclerview.widget.RecyclerView +import android.view.ViewGroup +import com.google.gson.Gson +import com.tencent.mmkv.MMKV +import com.v2ray.ang.R +import com.v2ray.ang.databinding.ItemRecyclerSubSettingBinding +import com.v2ray.ang.util.MmkvManager + +class SubSettingRecyclerAdapter(val activity: SubSettingActivity) : RecyclerView.Adapter() { + + private var mActivity: SubSettingActivity = activity + private val subStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SUB, MMKV.MULTI_PROCESS_MODE) } + + override fun getItemCount() = mActivity.subscriptions.size + + override fun onBindViewHolder(holder: MainViewHolder, position: Int) { + val subId = mActivity.subscriptions[position].first + val subItem = mActivity.subscriptions[position].second + holder.itemSubSettingBinding.tvName.text = subItem.remarks + holder.itemSubSettingBinding.tvUrl.text = subItem.url + if (subItem.enabled) { + holder.itemSubSettingBinding.chkEnable.setBackgroundResource(R.color.colorSelected) + } else { + holder.itemSubSettingBinding.chkEnable.setBackgroundResource(R.color.colorUnselected) + } + holder.itemView.setBackgroundColor(Color.TRANSPARENT) + + holder.itemSubSettingBinding.layoutEdit.setOnClickListener { + mActivity.startActivity(Intent(mActivity, SubEditActivity::class.java) + .putExtra("subId", subId) + ) + } + holder.itemSubSettingBinding.infoContainer.setOnClickListener { + subItem.enabled = !subItem.enabled + subStorage?.encode(subId, Gson().toJson(subItem)) + notifyItemChanged(position) + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MainViewHolder { + return MainViewHolder(ItemRecyclerSubSettingBinding.inflate(LayoutInflater.from(parent.context), parent, false)) + } + + class MainViewHolder(val itemSubSettingBinding: ItemRecyclerSubSettingBinding) : RecyclerView.ViewHolder(itemSubSettingBinding.root) +} diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/TaskerActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/TaskerActivity.kt new file mode 100644 index 00000000..da7ca188 --- /dev/null +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/TaskerActivity.kt @@ -0,0 +1,117 @@ +package com.v2ray.ang.ui + +import android.app.Activity +import android.os.Bundle +import android.view.View +import android.widget.ArrayAdapter +import android.widget.ListView +import java.util.ArrayList +import com.v2ray.ang.R +import android.content.Intent +import android.text.TextUtils +import android.view.Menu +import android.view.MenuItem +import com.google.zxing.WriterException +import com.tencent.mmkv.MMKV +import com.v2ray.ang.AppConfig +import com.v2ray.ang.databinding.ActivityTaskerBinding +import com.v2ray.ang.util.MmkvManager + +class TaskerActivity : BaseActivity() { + private lateinit var binding: ActivityTaskerBinding + + private var listview: ListView? = null + private var lstData: ArrayList = ArrayList() + private var lstGuid: ArrayList = ArrayList() + + private val serverStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SERVER_CONFIG, MMKV.MULTI_PROCESS_MODE) } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityTaskerBinding.inflate(layoutInflater) + val view = binding.root + setContentView(view) + + //add def value + lstData.add("Default") + lstGuid.add(AppConfig.TASKER_DEFAULT_GUID) + + serverStorage?.allKeys()?.forEach { key -> + MmkvManager.decodeServerConfig(key)?.let { config -> + lstData.add(config.remarks) + lstGuid.add(key) + } + } + val adapter = ArrayAdapter(this, + android.R.layout.simple_list_item_single_choice, lstData) + listview = findViewById(R.id.listview) as ListView + listview!!.adapter = adapter + + init() + } + + private fun init() { + try { + val bundle = intent?.getBundleExtra(AppConfig.TASKER_EXTRA_BUNDLE) + val switch = bundle?.getBoolean(AppConfig.TASKER_EXTRA_BUNDLE_SWITCH, false) + val guid = bundle?.getString(AppConfig.TASKER_EXTRA_BUNDLE_GUID, "") + + if (switch == null || TextUtils.isEmpty(guid)) { + return + } else { + binding.switchStartService.isChecked = switch + val pos = lstGuid.indexOf(guid.toString()) + if (pos >= 0) { + listview?.setItemChecked(pos, true) + } + } + } catch (e: WriterException) { + e.printStackTrace() + + } + } + + private fun confirmFinish() { + val position = listview?.checkedItemPosition + if (position == null || position < 0) { + return + } + + val extraBundle = Bundle() + extraBundle.putBoolean(AppConfig.TASKER_EXTRA_BUNDLE_SWITCH, binding.switchStartService.isChecked) + extraBundle.putString(AppConfig.TASKER_EXTRA_BUNDLE_GUID, lstGuid[position]) + val intent = Intent() + + val remarks = lstData[position] + val blurb = if (binding.switchStartService.isChecked) { + "Start $remarks" + } else { + "Stop $remarks" + } + + intent.putExtra(AppConfig.TASKER_EXTRA_BUNDLE, extraBundle) + intent.putExtra(AppConfig.TASKER_EXTRA_STRING_BLURB, blurb) + setResult(Activity.RESULT_OK, intent) + finish() + } + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + menuInflater.inflate(R.menu.action_server, menu) + val del_config = menu.findItem(R.id.del_config) + del_config?.isVisible = false + return super.onCreateOptionsMenu(menu) + } + + override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) { + R.id.del_config -> { + true + } + R.id.save_config -> { + confirmFinish() + true + } + else -> super.onOptionsItemSelected(item) + } + +} + diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/UrlSchemeActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/UrlSchemeActivity.kt new file mode 100644 index 00000000..90681100 --- /dev/null +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/UrlSchemeActivity.kt @@ -0,0 +1,51 @@ +package com.v2ray.ang.ui + +import android.content.Intent +import android.net.Uri +import android.os.Bundle +import com.google.zxing.WriterException +import com.v2ray.ang.R +import com.v2ray.ang.databinding.ActivityLogcatBinding +import com.v2ray.ang.extension.toast +import com.v2ray.ang.util.AngConfigManager + +class UrlSchemeActivity : BaseActivity() { + private lateinit var binding: ActivityLogcatBinding + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityLogcatBinding.inflate(layoutInflater) + val view = binding.root + setContentView(view) + + var shareUrl: String = "" + try { + intent?.apply { + when (action) { + Intent.ACTION_SEND -> { + if ("text/plain" == type) { + intent.getStringExtra(Intent.EXTRA_TEXT)?.let { + shareUrl = it + } + } + } + Intent.ACTION_VIEW -> { + val uri: Uri? = intent.data + shareUrl = uri?.getQueryParameter("url")!! + } + } + } + toast(shareUrl) + val count = AngConfigManager.importBatchConfig(shareUrl, "", false) + if (count > 0) { + toast(R.string.toast_success) + } else { + toast(R.string.toast_failure) + } + startActivity(Intent(this, MainActivity::class.java)) + finish() + } catch (e: WriterException) { + e.printStackTrace() + } + } +} \ No newline at end of file diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/UserAssetActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/UserAssetActivity.kt new file mode 100644 index 00000000..17755bfb --- /dev/null +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/UserAssetActivity.kt @@ -0,0 +1,216 @@ +package com.v2ray.ang.ui + +import android.Manifest +import android.annotation.SuppressLint +import android.content.Intent +import android.net.Uri +import android.os.Bundle +import android.provider.OpenableColumns +import android.util.Log +import android.view.* +import android.view.View.GONE +import android.view.View.VISIBLE +import androidx.activity.result.contract.ActivityResultContracts +import androidx.lifecycle.lifecycleScope +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.tbruyelle.rxpermissions.RxPermissions +import com.tencent.mmkv.MMKV +import com.v2ray.ang.AppConfig +import com.v2ray.ang.R +import com.v2ray.ang.databinding.ActivitySubSettingBinding +import com.v2ray.ang.databinding.ItemRecyclerUserAssetBinding +import com.v2ray.ang.extension.toTrafficString +import com.v2ray.ang.extension.toast +import com.v2ray.ang.util.MmkvManager +import com.v2ray.ang.util.Utils +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import java.io.File +import java.io.FileOutputStream +import java.net.HttpURLConnection +import java.net.InetSocketAddress +import java.net.Proxy +import java.net.URL +import java.text.DateFormat +import java.util.* + +class UserAssetActivity : BaseActivity() { + private lateinit var binding: ActivitySubSettingBinding + private val settingsStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SETTING, MMKV.MULTI_PROCESS_MODE) } + + val extDir by lazy { File(Utils.userAssetPath(this)) } + val geofiles = arrayOf("geosite.dat", "geoip.dat") + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivitySubSettingBinding.inflate(layoutInflater) + val view = binding.root + setContentView(view) + title = getString(R.string.title_user_asset_setting) + supportActionBar?.setDisplayHomeAsUpEnabled(true) + + binding.recyclerView.setHasFixedSize(true) + binding.recyclerView.layoutManager = LinearLayoutManager(this) + binding.recyclerView.adapter = UserAssetAdapter() + } + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + menuInflater.inflate(R.menu.menu_asset, menu) + return super.onCreateOptionsMenu(menu) + } + + override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) { + R.id.add_file -> { + showFileChooser() + true + } + + R.id.download_file -> { + downloadGeoFiles() + true + } + + else -> super.onOptionsItemSelected(item) + } + + private fun showFileChooser() { + RxPermissions(this).request(Manifest.permission.READ_EXTERNAL_STORAGE).subscribe { + if (it) { + val intent = Intent(Intent.ACTION_GET_CONTENT) + intent.type = "*/*" + intent.addCategory(Intent.CATEGORY_OPENABLE) + + try { + chooseFile.launch( + Intent.createChooser( + intent, + getString(R.string.title_file_chooser) + ) + ) + } catch (ex: android.content.ActivityNotFoundException) { + toast(R.string.toast_require_file_manager) + } + } + } + } + + private val chooseFile = + registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { + val uri = it.data?.data + if (it.resultCode == RESULT_OK && uri != null) { + try { + copyFile(uri) + } catch (e: Exception) { + toast(R.string.toast_asset_copy_failed) + } + } + } + + private fun copyFile(uri: Uri): String { + val targetFile = File(extDir, getCursorName(uri) ?: uri.toString()) + contentResolver.openInputStream(uri).use { inputStream -> + targetFile.outputStream().use { fileOut -> + inputStream?.copyTo(fileOut) + toast(R.string.toast_success) + binding.recyclerView.adapter?.notifyDataSetChanged() + } + } + return targetFile.path + } + + private fun getCursorName(uri: Uri): String? = try { + contentResolver.query(uri, null, null, null, null)?.let { cursor -> + cursor.run { + if (moveToFirst()) getString(getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME)) + else null + }.also { cursor.close() } + } + } catch (e: Exception) { + e.printStackTrace() + null + } + + private fun downloadGeoFiles() { + val httpPort = Utils.parseInt(settingsStorage?.decodeString(AppConfig.PREF_HTTP_PORT), AppConfig.PORT_HTTP.toInt()) + + toast(R.string.msg_downloading_content) + geofiles.forEach { + //toast(getString(R.string.msg_downloading_content) + it) + lifecycleScope.launch(Dispatchers.IO) { + val result = downloadGeo(it, 60000, httpPort) + launch(Dispatchers.Main) { + if (result) { + toast(getString(R.string.toast_success) + " " + it) + binding.recyclerView.adapter?.notifyDataSetChanged() + } else { + toast(getString(R.string.toast_failure) + " " + it) + } + } + } + } + } + + private fun downloadGeo(name: String, timeout: Int, httpPort: Int): Boolean { + val url = AppConfig.geoUrl + name + val targetTemp = File(extDir, name + "_temp") + val target = File(extDir, name) + var conn: HttpURLConnection? = null + //Log.d(AppConfig.ANG_PACKAGE, url) + + try { + conn = URL(url).openConnection( + Proxy( + Proxy.Type.HTTP, + InetSocketAddress("127.0.0.1", httpPort) + ) + ) as HttpURLConnection + conn.connectTimeout = timeout + conn.readTimeout = timeout + val inputStream = conn.inputStream + val responseCode = conn.responseCode + if (responseCode == HttpURLConnection.HTTP_OK) { + FileOutputStream(targetTemp).use { output -> + inputStream.copyTo(output) + } + + targetTemp.renameTo(target) + } + return true + } catch (e: Exception) { + Log.e(AppConfig.ANG_PACKAGE, Log.getStackTraceString(e)) + return false + } finally { + conn?.disconnect() + } + } + + inner class UserAssetAdapter : RecyclerView.Adapter() { + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserAssetViewHolder { + return UserAssetViewHolder(ItemRecyclerUserAssetBinding.inflate(LayoutInflater.from(parent.context), parent, false)) + } + + @SuppressLint("SetTextI18n") + override fun onBindViewHolder(holder: UserAssetViewHolder, position: Int) { + val file = extDir.listFiles()?.getOrNull(position) ?: return + holder.itemUserAssetBinding.assetName.text = file.name + val dateFormat = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM) + holder.itemUserAssetBinding.assetProperties.text = "${file.length().toTrafficString()} • ${dateFormat.format(Date(file.lastModified()))}" + if (file.name in geofiles) { + holder.itemUserAssetBinding.layoutRemove.visibility = GONE + } else { + holder.itemUserAssetBinding.layoutRemove.visibility = VISIBLE + } + holder.itemUserAssetBinding.layoutRemove.setOnClickListener { + file.delete() + binding.recyclerView.adapter?.notifyItemRemoved(position) + } + } + + override fun getItemCount(): Int { + return extDir.listFiles()?.size ?: 0 + } + } + + class UserAssetViewHolder(val itemUserAssetBinding: ItemRecyclerUserAssetBinding) : RecyclerView.ViewHolder(itemUserAssetBinding.root) +} diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/AngConfigManager.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/AngConfigManager.kt new file mode 100644 index 00000000..5a58a8c4 --- /dev/null +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/AngConfigManager.kt @@ -0,0 +1,750 @@ +package com.v2ray.ang.util + +import android.content.Context +import android.content.SharedPreferences +import android.graphics.Bitmap +import android.text.TextUtils +import android.util.Log +import androidx.preference.PreferenceManager +import com.google.gson.Gson +import com.tencent.mmkv.MMKV +import com.v2ray.ang.AppConfig +import com.v2ray.ang.AppConfig.ANG_CONFIG +import com.v2ray.ang.AppConfig.HTTPS_PROTOCOL +import com.v2ray.ang.AppConfig.HTTP_PROTOCOL +import com.v2ray.ang.R +import com.v2ray.ang.dto.* +import com.v2ray.ang.dto.V2rayConfig.Companion.DEFAULT_SECURITY +import com.v2ray.ang.dto.V2rayConfig.Companion.TLS +import com.v2ray.ang.util.MmkvManager.KEY_SELECTED_SERVER +import java.net.URI +import java.util.* +import com.v2ray.ang.extension.idnHost + +object AngConfigManager { + private val mainStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_MAIN, MMKV.MULTI_PROCESS_MODE) } + private val serverRawStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SERVER_RAW, MMKV.MULTI_PROCESS_MODE) } + private val settingsStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SETTING, MMKV.MULTI_PROCESS_MODE) } + private val subStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SUB, MMKV.MULTI_PROCESS_MODE) } + + /** + * Legacy loading config + */ + fun migrateLegacyConfig(c: Context): Boolean? { + try { + val defaultSharedPreferences = PreferenceManager.getDefaultSharedPreferences(c) + val context = defaultSharedPreferences.getString(ANG_CONFIG, "") + if (context.isNullOrBlank()) { + return null + } + val angConfig = Gson().fromJson(context, AngConfig::class.java) + for (i in angConfig.vmess.indices) { + upgradeServerVersion(angConfig.vmess[i]) + } + + copyLegacySettings(defaultSharedPreferences) + migrateVmessBean(angConfig, defaultSharedPreferences) + migrateSubItemBean(angConfig) + + defaultSharedPreferences.edit().remove(ANG_CONFIG).apply() + return true + } catch (e: Exception) { + e.printStackTrace() + } + return false + } + + private fun copyLegacySettings(sharedPreferences: SharedPreferences) { + listOf( + AppConfig.PREF_MODE, + AppConfig.PREF_REMOTE_DNS, + AppConfig.PREF_DOMESTIC_DNS, + AppConfig.PREF_LOCAL_DNS_PORT, + AppConfig.PREF_SOCKS_PORT, + AppConfig.PREF_HTTP_PORT, + AppConfig.PREF_LOGLEVEL, + AppConfig.PREF_ROUTING_DOMAIN_STRATEGY, + AppConfig.PREF_ROUTING_MODE, + AppConfig.PREF_V2RAY_ROUTING_AGENT, + AppConfig.PREF_V2RAY_ROUTING_BLOCKED, + AppConfig.PREF_V2RAY_ROUTING_DIRECT, + ).forEach { key -> + settingsStorage?.encode(key, sharedPreferences.getString(key, null)) + } + listOf( + AppConfig.PREF_SPEED_ENABLED, + AppConfig.PREF_PROXY_SHARING, + AppConfig.PREF_LOCAL_DNS_ENABLED, + AppConfig.PREF_ALLOW_INSECURE, + AppConfig.PREF_PREFER_IPV6, + AppConfig.PREF_PER_APP_PROXY, + AppConfig.PREF_BYPASS_APPS, + ).forEach { key -> + settingsStorage?.encode(key, sharedPreferences.getBoolean(key, false)) + } + settingsStorage?.encode(AppConfig.PREF_SNIFFING_ENABLED, sharedPreferences.getBoolean(AppConfig.PREF_SNIFFING_ENABLED, true)) + settingsStorage?.encode(AppConfig.PREF_PER_APP_PROXY_SET, sharedPreferences.getStringSet(AppConfig.PREF_PER_APP_PROXY_SET, setOf())) + } + + private fun migrateVmessBean(angConfig: AngConfig, sharedPreferences: SharedPreferences) { + angConfig.vmess.forEachIndexed { index, vmessBean -> + val type = EConfigType.fromInt(vmessBean.configType) ?: return@forEachIndexed + val config = ServerConfig.create(type) + config.remarks = vmessBean.remarks + config.subscriptionId = vmessBean.subid + if (type == EConfigType.CUSTOM) { + val jsonConfig = sharedPreferences.getString(ANG_CONFIG + vmessBean.guid, "") + val v2rayConfig = try { + Gson().fromJson(jsonConfig, V2rayConfig::class.java) + } catch (e: Exception) { + e.printStackTrace() + return@forEachIndexed + } + config.fullConfig = v2rayConfig + serverRawStorage?.encode(vmessBean.guid, jsonConfig) + } else { + config.outboundBean?.settings?.vnext?.get(0)?.let { vnext -> + vnext.address = vmessBean.address + vnext.port = vmessBean.port + vnext.users[0].id = vmessBean.id + if (config.configType == EConfigType.VMESS) { + vnext.users[0].alterId = vmessBean.alterId + vnext.users[0].security = vmessBean.security + } else if (config.configType == EConfigType.VLESS) { + vnext.users[0].encryption = vmessBean.security + vnext.users[0].flow = vmessBean.flow + } + } + config.outboundBean?.settings?.servers?.get(0)?.let { server -> + server.address = vmessBean.address + server.port = vmessBean.port + if (config.configType == EConfigType.SHADOWSOCKS) { + server.password = vmessBean.id + server.method = vmessBean.security + } else if (config.configType == EConfigType.SOCKS) { + if (TextUtils.isEmpty(vmessBean.security) && TextUtils.isEmpty(vmessBean.id)) { + server.users = null + } else { + val socksUsersBean = V2rayConfig.OutboundBean.OutSettingsBean.ServersBean.SocksUsersBean() + socksUsersBean.user = vmessBean.security + socksUsersBean.pass = vmessBean.id + server.users = listOf(socksUsersBean) + } + } else if (config.configType == EConfigType.TROJAN) { + server.password = vmessBean.id + } + } + config.outboundBean?.streamSettings?.let { streamSetting -> + val sni = streamSetting.populateTransportSettings(vmessBean.network, vmessBean.headerType, + vmessBean.requestHost, vmessBean.path, vmessBean.path, vmessBean.requestHost, vmessBean.path, + vmessBean.headerType, vmessBean.path) + val allowInsecure = if (vmessBean.allowInsecure.isBlank()) { + settingsStorage?.decodeBool(AppConfig.PREF_ALLOW_INSECURE) ?: false + } else { + vmessBean.allowInsecure.toBoolean() + } + var fingerprint = streamSetting.tlsSettings?.fingerprint + streamSetting.populateTlsSettings(vmessBean.streamSecurity, allowInsecure, + vmessBean.sni.ifBlank { sni }, fingerprint, null) + } + } + val key = MmkvManager.encodeServerConfig(vmessBean.guid, config) + if (index == angConfig.index) { + mainStorage?.encode(KEY_SELECTED_SERVER, key) + } + } + } + + private fun migrateSubItemBean(angConfig: AngConfig) { + angConfig.subItem.forEach { + val subItem = SubscriptionItem() + subItem.remarks = it.remarks + subItem.url = it.url + subItem.enabled = it.enabled + subStorage?.encode(it.id, Gson().toJson(subItem)) + } + } + + /** + * import config form qrcode or... + */ + private fun importConfig(str: String?, subid: String, removedSelectedServer: ServerConfig?): Int { + try { + if (str == null || TextUtils.isEmpty(str)) { + return R.string.toast_none_data + } + + //maybe sub + if (TextUtils.isEmpty(subid) && (str.startsWith(HTTP_PROTOCOL) || str.startsWith(HTTPS_PROTOCOL))) { + MmkvManager.importUrlAsSubscription(str) + return 0 + } + + var config: ServerConfig? = null + val allowInsecure = settingsStorage?.decodeBool(AppConfig.PREF_ALLOW_INSECURE) ?: false + if (str.startsWith(EConfigType.VMESS.protocolScheme)) { + config = ServerConfig.create(EConfigType.VMESS) + val streamSetting = config.outboundBean?.streamSettings ?: return -1 + + var fingerprint = streamSetting.tlsSettings?.fingerprint + + + if (!tryParseNewVmess(str, config, allowInsecure)) { + if (str.indexOf("?") > 0) { + if (!tryResolveVmess4Kitsunebi(str, config)) { + return R.string.toast_incorrect_protocol + } + } else { + var result = str.replace(EConfigType.VMESS.protocolScheme, "") + result = Utils.decode(result) + if (TextUtils.isEmpty(result)) { + return R.string.toast_decoding_failed + } + val vmessQRCode = Gson().fromJson(result, VmessQRCode::class.java) + // Although VmessQRCode fields are non null, looks like Gson may still create null fields + if (TextUtils.isEmpty(vmessQRCode.add) + || TextUtils.isEmpty(vmessQRCode.port) + || TextUtils.isEmpty(vmessQRCode.id) + || TextUtils.isEmpty(vmessQRCode.net) + ) { + return R.string.toast_incorrect_protocol + } + + config.remarks = vmessQRCode.ps + config.outboundBean?.settings?.vnext?.get(0)?.let { vnext -> + vnext.address = vmessQRCode.add + vnext.port = Utils.parseInt(vmessQRCode.port) + vnext.users[0].id = vmessQRCode.id + vnext.users[0].security = if (TextUtils.isEmpty(vmessQRCode.scy)) DEFAULT_SECURITY else vmessQRCode.scy + vnext.users[0].alterId = Utils.parseInt(vmessQRCode.aid) + } + val sni = streamSetting.populateTransportSettings(vmessQRCode.net, vmessQRCode.type, vmessQRCode.host, + vmessQRCode.path, vmessQRCode.path, vmessQRCode.host, vmessQRCode.path, vmessQRCode.type, vmessQRCode.path) + + + streamSetting.populateTlsSettings(vmessQRCode.tls, allowInsecure, + if (TextUtils.isEmpty(vmessQRCode.sni)) sni else vmessQRCode.sni, fingerprint, vmessQRCode.alpn) + } + } + } else if (str.startsWith(EConfigType.SHADOWSOCKS.protocolScheme)) { + config = ServerConfig.create(EConfigType.SHADOWSOCKS) + if (!tryResolveResolveSip002(str, config)) { + var result = str.replace(EConfigType.SHADOWSOCKS.protocolScheme, "") + val indexSplit = result.indexOf("#") + if (indexSplit > 0) { + try { + config.remarks = Utils.urlDecode(result.substring(indexSplit + 1, result.length)) + } catch (e: Exception) { + e.printStackTrace() + } + + result = result.substring(0, indexSplit) + } + + //part decode + val indexS = result.indexOf("@") + result = if (indexS > 0) { + Utils.decode(result.substring(0, indexS)) + result.substring(indexS, result.length) + } else { + Utils.decode(result) + } + + val legacyPattern = "^(.+?):(.*)@(.+?):(\\d+?)/?$".toRegex() + val match = legacyPattern.matchEntire(result) ?: return R.string.toast_incorrect_protocol + + config.outboundBean?.settings?.servers?.get(0)?.let { server -> + server.address = match.groupValues[3].removeSurrounding("[", "]") + server.port = match.groupValues[4].toInt() + server.password = match.groupValues[2] + server.method = match.groupValues[1].lowercase() + } + } + } else if (str.startsWith(EConfigType.SOCKS.protocolScheme)) { + var result = str.replace(EConfigType.SOCKS.protocolScheme, "") + val indexSplit = result.indexOf("#") + config = ServerConfig.create(EConfigType.SOCKS) + if (indexSplit > 0) { + try { + config.remarks = Utils.urlDecode(result.substring(indexSplit + 1, result.length)) + } catch (e: Exception) { + e.printStackTrace() + } + + result = result.substring(0, indexSplit) + } + + //part decode + val indexS = result.indexOf("@") + if (indexS > 0) { + result = Utils.decode(result.substring(0, indexS)) + result.substring(indexS, result.length) + } else { + result = Utils.decode(result) + } + + val legacyPattern = "^(.*):(.*)@(.+?):(\\d+?)$".toRegex() + val match = legacyPattern.matchEntire(result) ?: return R.string.toast_incorrect_protocol + + config.outboundBean?.settings?.servers?.get(0)?.let { server -> + server.address = match.groupValues[3].removeSurrounding("[", "]") + server.port = match.groupValues[4].toInt() + val socksUsersBean = V2rayConfig.OutboundBean.OutSettingsBean.ServersBean.SocksUsersBean() + socksUsersBean.user = match.groupValues[1].lowercase() + socksUsersBean.pass = match.groupValues[2] + server.users = listOf(socksUsersBean) + } + } else if (str.startsWith(EConfigType.TROJAN.protocolScheme)) { + val uri = URI(Utils.fixIllegalUrl(str)) + config = ServerConfig.create(EConfigType.TROJAN) + config.remarks = Utils.urlDecode(uri.fragment ?: "") + + var flow = "" + var fingerprint = config.outboundBean?.streamSettings?.tlsSettings?.fingerprint + if (uri.rawQuery != null) { + val queryParam = uri.rawQuery.split("&") + .associate { it.split("=").let { (k, v) -> k to Utils.urlDecode(v) } } + + val sni = config.outboundBean?.streamSettings?.populateTransportSettings(queryParam["type"] ?: "tcp", queryParam["headerType"], + queryParam["host"], queryParam["path"], queryParam["seed"], queryParam["quicSecurity"], queryParam["key"], + queryParam["mode"], queryParam["serviceName"]) + config.outboundBean?.streamSettings?.populateTlsSettings(queryParam["security"] ?: TLS, allowInsecure, queryParam["sni"] ?: sni!!, fingerprint, queryParam["alpn"]) + flow = queryParam["flow"] ?: "" + } else { + + config.outboundBean?.streamSettings?.populateTlsSettings(TLS, allowInsecure, "", fingerprint, null) + } + + config.outboundBean?.settings?.servers?.get(0)?.let { server -> + server.address = uri.idnHost + server.port = uri.port + server.password = uri.userInfo + server.flow = flow + } + } else if (str.startsWith(EConfigType.VLESS.protocolScheme)) { + val uri = URI(Utils.fixIllegalUrl(str)) + val queryParam = uri.rawQuery.split("&") + .associate { it.split("=").let { (k, v) -> k to Utils.urlDecode(v) } } + config = ServerConfig.create(EConfigType.VLESS) + val streamSetting = config.outboundBean?.streamSettings ?: return -1 + var fingerprint = streamSetting.tlsSettings?.fingerprint + + config.remarks = Utils.urlDecode(uri.fragment ?: "") + config.outboundBean?.settings?.vnext?.get(0)?.let { vnext -> + vnext.address = uri.idnHost + vnext.port = uri.port + vnext.users[0].id = uri.userInfo + vnext.users[0].encryption = queryParam["encryption"] ?: "none" + vnext.users[0].flow =queryParam["flow"] ?: "" + } + + val sni = streamSetting.populateTransportSettings(queryParam["type"] ?: "tcp", queryParam["headerType"], + queryParam["host"], queryParam["path"], queryParam["seed"], queryParam["quicSecurity"], queryParam["key"], + queryParam["mode"], queryParam["serviceName"]) + streamSetting.populateTlsSettings(queryParam["security"] ?: "", allowInsecure, queryParam["sni"] ?: sni, fingerprint, queryParam["alpn"]) + } + if (config == null){ + return R.string.toast_incorrect_protocol + } + config.subscriptionId = subid + val guid = MmkvManager.encodeServerConfig("", config) + if (removedSelectedServer != null && + config.getProxyOutbound()?.getServerAddress() == removedSelectedServer.getProxyOutbound()?.getServerAddress() && + config.getProxyOutbound()?.getServerPort() == removedSelectedServer.getProxyOutbound()?.getServerPort()) { + mainStorage?.encode(KEY_SELECTED_SERVER, guid) + } + } catch (e: Exception) { + e.printStackTrace() + return -1 + } + return 0 + } + + private fun tryParseNewVmess(uriString: String, config: ServerConfig, allowInsecure: Boolean): Boolean { + return runCatching { + val uri = URI(uriString) + check(uri.scheme == "vmess") + val (_, protocol, tlsStr, uuid, alterId) = + Regex("(tcp|http|ws|kcp|quic|grpc)(\\+tls)?:([0-9a-z]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12})") + .matchEntire(uri.userInfo)?.groupValues + ?: error("parse user info fail.") + val tls = tlsStr.isNotBlank() + val queryParam = uri.rawQuery.split("&") + .associate { it.split("=").let { (k, v) -> k to Utils.urlDecode(v) } } + + val streamSetting = config.outboundBean?.streamSettings ?: return false + config.remarks = Utils.urlDecode(uri.fragment ?: "") + config.outboundBean.settings?.vnext?.get(0)?.let { vnext -> + vnext.address = uri.idnHost + vnext.port = uri.port + vnext.users[0].id = uuid + vnext.users[0].security = DEFAULT_SECURITY + vnext.users[0].alterId = alterId.toInt() + } + var fingerprint = streamSetting.tlsSettings?.fingerprint + val sni = streamSetting.populateTransportSettings(protocol, queryParam["type"], + queryParam["host"]?.split("|")?.get(0) ?: "", + queryParam["path"]?.takeIf { it.trim() != "/" } ?: "", queryParam["seed"], queryParam["security"], + queryParam["key"], queryParam["mode"], queryParam["serviceName"]) + streamSetting.populateTlsSettings(if (tls) TLS else "", allowInsecure, sni, fingerprint, null) + true + }.getOrElse { false } + } + + private fun tryResolveVmess4Kitsunebi(server: String, config: ServerConfig): Boolean { + + var result = server.replace(EConfigType.VMESS.protocolScheme, "") + val indexSplit = result.indexOf("?") + if (indexSplit > 0) { + result = result.substring(0, indexSplit) + } + result = Utils.decode(result) + + val arr1 = result.split('@') + if (arr1.count() != 2) { + return false + } + val arr21 = arr1[0].split(':') + val arr22 = arr1[1].split(':') + if (arr21.count() != 2) { + return false + } + + config.remarks = "Alien" + config.outboundBean?.settings?.vnext?.get(0)?.let { vnext -> + vnext.address = arr22[0] + vnext.port = Utils.parseInt(arr22[1]) + vnext.users[0].id = arr21[1] + vnext.users[0].security = arr21[0] + vnext.users[0].alterId = 0 + } + return true + } + + private fun tryResolveResolveSip002(str: String, config: ServerConfig): Boolean { + try { + val uri = URI(Utils.fixIllegalUrl(str)) + config.remarks = Utils.urlDecode(uri.fragment ?: "") + + val method: String + val password: String + if (uri.userInfo.contains(":")) { + val arrUserInfo = uri.userInfo.split(":").map { it.trim() } + if (arrUserInfo.count() != 2) { + return false + } + method = arrUserInfo[0] + password = Utils.urlDecode(arrUserInfo[1]) + } else { + val base64Decode = Utils.decode(uri.userInfo) + val arrUserInfo = base64Decode.split(":").map { it.trim() } + if (arrUserInfo.count() < 2) { + return false + } + method = arrUserInfo[0] + password = base64Decode.substringAfter(":") + } + + config.outboundBean?.settings?.servers?.get(0)?.let { server -> + server.address = uri.idnHost + server.port = uri.port + server.password = password + server.method = method + } + return true + } catch (e: Exception) { + Log.d(AppConfig.ANG_PACKAGE, e.toString()) + return false + } + } + + /** + * share config + */ + private fun shareConfig(guid: String): String { + try { + val config = MmkvManager.decodeServerConfig(guid) ?: return "" + val outbound = config.getProxyOutbound() ?: return "" + val streamSetting = outbound.streamSettings ?: return "" + return config.configType.protocolScheme + when (config.configType) { + EConfigType.VMESS -> { + val vmessQRCode = VmessQRCode() + vmessQRCode.v = "2" + vmessQRCode.ps = config.remarks + vmessQRCode.add = outbound.getServerAddress().orEmpty() + vmessQRCode.port = outbound.getServerPort().toString() + vmessQRCode.id = outbound.getPassword().orEmpty() + vmessQRCode.aid = outbound.settings?.vnext?.get(0)?.users?.get(0)?.alterId.toString() + vmessQRCode.scy = outbound.settings?.vnext?.get(0)?.users?.get(0)?.security.toString() + vmessQRCode.net = streamSetting.network + vmessQRCode.tls = streamSetting.security + vmessQRCode.sni = streamSetting.tlsSettings?.serverName.orEmpty() + vmessQRCode.alpn = Utils.removeWhiteSpace(streamSetting.tlsSettings?.alpn?.joinToString()).orEmpty() + outbound.getTransportSettingDetails()?.let { transportDetails -> + vmessQRCode.type = transportDetails[0] + vmessQRCode.host = transportDetails[1] + vmessQRCode.path = transportDetails[2] + } + val json = Gson().toJson(vmessQRCode) + Utils.encode(json) + } + EConfigType.CUSTOM, EConfigType.WIREGUARD -> "" + EConfigType.SHADOWSOCKS -> { + val remark = "#" + Utils.urlEncode(config.remarks) + val pw = Utils.encode("${outbound.getSecurityEncryption()}:${outbound.getPassword()}") + val url = String.format("%s@%s:%s", + pw, + Utils.getIpv6Address(outbound.getServerAddress()!!), + outbound.getServerPort()) + url + remark + } + EConfigType.SOCKS -> { + val remark = "#" + Utils.urlEncode(config.remarks) + val pw = Utils.encode("${outbound.settings?.servers?.get(0)?.users?.get(0)?.user}:${outbound.getPassword()}") + val url = String.format("%s@%s:%s", + pw, + Utils.getIpv6Address(outbound.getServerAddress()!!), + outbound.getServerPort()) + url + remark + } + EConfigType.VLESS, + EConfigType.TROJAN -> { + val remark = "#" + Utils.urlEncode(config.remarks) + + val dicQuery = HashMap() + if (config.configType == EConfigType.VLESS) { + outbound.settings?.vnext?.get(0)?.users?.get(0)?.flow?.let { + if (!TextUtils.isEmpty(it)) { + dicQuery["flow"] = it + } + } + dicQuery["encryption"] = + if (outbound.getSecurityEncryption().isNullOrEmpty()) "none" + else outbound.getSecurityEncryption().orEmpty() + } else if (config.configType == EConfigType.TROJAN) { + config.outboundBean?.settings?.servers?.get(0)?.flow?.let { + if (!TextUtils.isEmpty(it)) { + dicQuery["flow"] = it + } + } + } + + dicQuery["security"] = streamSetting.security.ifEmpty { "none" } + (streamSetting.tlsSettings?: streamSetting.xtlsSettings)?.let { tlsSetting -> + if (!TextUtils.isEmpty(tlsSetting.serverName)) { + dicQuery["sni"] = tlsSetting.serverName + } + if (!tlsSetting.alpn.isNullOrEmpty() && tlsSetting.alpn.isNotEmpty()) { + dicQuery["alpn"] = Utils.removeWhiteSpace(tlsSetting.alpn.joinToString()).orEmpty() + } + } + dicQuery["type"] = streamSetting.network.ifEmpty { V2rayConfig.DEFAULT_NETWORK } + + outbound.getTransportSettingDetails()?.let { transportDetails -> + when (streamSetting.network) { + "tcp" -> { + dicQuery["headerType"] = transportDetails[0].ifEmpty { "none" } + if (!TextUtils.isEmpty(transportDetails[1])) { + dicQuery["host"] = Utils.urlEncode(transportDetails[1]) + } + } + "kcp" -> { + dicQuery["headerType"] = transportDetails[0].ifEmpty { "none" } + if (!TextUtils.isEmpty(transportDetails[2])) { + dicQuery["seed"] = Utils.urlEncode(transportDetails[2]) + } + } + "ws" -> { + if (!TextUtils.isEmpty(transportDetails[1])) { + dicQuery["host"] = Utils.urlEncode(transportDetails[1]) + } + if (!TextUtils.isEmpty(transportDetails[2])) { + dicQuery["path"] = Utils.urlEncode(transportDetails[2]) + } + } + "http", "h2" -> { + dicQuery["type"] = "http" + if (!TextUtils.isEmpty(transportDetails[1])) { + dicQuery["host"] = Utils.urlEncode(transportDetails[1]) + } + if (!TextUtils.isEmpty(transportDetails[2])) { + dicQuery["path"] = Utils.urlEncode(transportDetails[2]) + } + } + "quic" -> { + dicQuery["headerType"] = transportDetails[0].ifEmpty { "none" } + dicQuery["quicSecurity"] = Utils.urlEncode(transportDetails[1]) + dicQuery["key"] = Utils.urlEncode(transportDetails[2]) + } + "grpc" -> { + dicQuery["mode"] = transportDetails[0] + dicQuery["serviceName"] = transportDetails[2] + } + } + } + val query = "?" + dicQuery.toList().joinToString( + separator = "&", + transform = { it.first + "=" + it.second }) + + val url = String.format("%s@%s:%s", + outbound.getPassword(), + Utils.getIpv6Address(outbound.getServerAddress()!!), + outbound.getServerPort()) + url + query + remark + } + } + } catch (e: Exception) { + e.printStackTrace() + return "" + } + } + + /** + * share2Clipboard + */ + fun share2Clipboard(context: Context, guid: String): Int { + try { + val conf = shareConfig(guid) + if (TextUtils.isEmpty(conf)) { + return -1 + } + + Utils.setClipboard(context, conf) + + } catch (e: Exception) { + e.printStackTrace() + return -1 + } + return 0 + } + + /** + * share2Clipboard + */ + fun shareNonCustomConfigsToClipboard(context: Context, serverList: List): Int { + try { + val sb = StringBuilder() + for (guid in serverList) { + val url = shareConfig(guid) + if (TextUtils.isEmpty(url)) { + continue + } + sb.append(url) + sb.appendLine() + } + if (sb.count() > 0) { + Utils.setClipboard(context, sb.toString()) + } + } catch (e: Exception) { + e.printStackTrace() + return -1 + } + return 0 + } + + /** + * share2QRCode + */ + fun share2QRCode(guid: String): Bitmap? { + try { + val conf = shareConfig(guid) + if (TextUtils.isEmpty(conf)) { + return null + } + return Utils.createQRCode(conf) + + } catch (e: Exception) { + e.printStackTrace() + return null + } + } + + /** + * shareFullContent2Clipboard + */ + fun shareFullContent2Clipboard(context: Context, guid: String?): Int { + try { + if (guid == null) return -1 + val result = V2rayConfigUtil.getV2rayConfig(context, guid) + if (result.status) { + Utils.setClipboard(context, result.content) + } else { + return -1 + } + } catch (e: Exception) { + e.printStackTrace() + return -1 + } + return 0 + } + + /** + * upgrade + */ + private fun upgradeServerVersion(vmess: AngConfig.VmessBean): Int { + try { + if (vmess.configVersion == 2) { + return 0 + } + + when (vmess.network) { + "ws", "h2" -> { + var path = "" + var host = "" + val lstParameter = vmess.requestHost.split(";") + if (lstParameter.isNotEmpty()) { + path = lstParameter[0].trim() + } + if (lstParameter.size > 1) { + path = lstParameter[0].trim() + host = lstParameter[1].trim() + } + vmess.path = path + vmess.requestHost = host + } + } + vmess.configVersion = 2 + return 0 + } catch (e: Exception) { + e.printStackTrace() + return -1 + } + } + + fun importBatchConfig(servers: String?, subid: String, append: Boolean): Int { + try { + if (servers == null) { + return 0 + } + val removedSelectedServer = + if (!TextUtils.isEmpty(subid) && !append) { + MmkvManager.decodeServerConfig(mainStorage?.decodeString(KEY_SELECTED_SERVER) ?: "")?.let { + if (it.subscriptionId == subid) { + return@let it + } + return@let null + } + } else { + null + } + if(!append) { + MmkvManager.removeServerViaSubid(subid) + } +// var servers = server +// if (server.indexOf("vmess") >= 0 && server.indexOf("vmess") == server.lastIndexOf("vmess")) { +// servers = server.replace("\n", "") +// } + + var count = 0 + servers.lines() + .forEach { + val resId = importConfig(it, subid, removedSelectedServer) + if (resId == 0) { + count++ + } + } + return count + } catch (e: Exception) { + e.printStackTrace() + } + return 0 + } +} diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/AppManagerUtil.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/AppManagerUtil.kt new file mode 100644 index 00000000..b95b408b --- /dev/null +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/AppManagerUtil.kt @@ -0,0 +1,43 @@ +package com.v2ray.ang.util + +import android.Manifest +import android.content.Context +import android.content.pm.ApplicationInfo +import android.content.pm.PackageInfo +import android.content.pm.PackageManager +import com.v2ray.ang.dto.AppInfo +import rx.Observable +import java.util.* + +object AppManagerUtil { + fun loadNetworkAppList(ctx: Context): ArrayList { + val packageManager = ctx.packageManager + val packages = packageManager.getInstalledPackages(PackageManager.GET_PERMISSIONS) + val apps = ArrayList() + + for (pkg in packages) { + if (!pkg.hasInternetPermission && pkg.packageName != "android") continue + + val applicationInfo = pkg.applicationInfo + + val appName = applicationInfo.loadLabel(packageManager).toString() + val appIcon = applicationInfo.loadIcon(packageManager) + val isSystemApp = (applicationInfo.flags and ApplicationInfo.FLAG_SYSTEM) > 0 + + val appInfo = AppInfo(appName, pkg.packageName, appIcon, isSystemApp, 0) + apps.add(appInfo) + } + + return apps + } + + fun rxLoadNetworkAppList(ctx: Context): Observable> = Observable.unsafeCreate { + it.onNext(loadNetworkAppList(ctx)) + } + + val PackageInfo.hasInternetPermission: Boolean + get() { + val permissions = requestedPermissions + return permissions?.any { it == Manifest.permission.INTERNET } ?: false + } +} diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/MessageUtil.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/MessageUtil.kt new file mode 100644 index 00000000..0f521f57 --- /dev/null +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/MessageUtil.kt @@ -0,0 +1,45 @@ +package com.v2ray.ang.util + +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import com.v2ray.ang.AppConfig +import com.v2ray.ang.service.V2RayTestService +import java.io.Serializable + + +object MessageUtil { + + fun sendMsg2Service(ctx: Context, what: Int, content: Serializable) { + sendMsg(ctx, AppConfig.BROADCAST_ACTION_SERVICE, what, content) + } + + fun sendMsg2UI(ctx: Context, what: Int, content: Serializable) { + sendMsg(ctx, AppConfig.BROADCAST_ACTION_ACTIVITY, what, content) + } + + fun sendMsg2TestService(ctx: Context, what: Int, content: Serializable) { + try { + val intent = Intent() + intent.component = ComponentName(ctx, V2RayTestService::class.java) + intent.putExtra("key", what) + intent.putExtra("content", content) + ctx.startService(intent) + } catch (e: Exception) { + e.printStackTrace() + } + } + + private fun sendMsg(ctx: Context, action: String, what: Int, content: Serializable) { + try { + val intent = Intent() + intent.action = action + intent.`package` = AppConfig.ANG_PACKAGE + intent.putExtra("key", what) + intent.putExtra("content", content) + ctx.sendBroadcast(intent) + } catch (e: Exception) { + e.printStackTrace() + } + } +} diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/MmkvManager.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/MmkvManager.kt new file mode 100644 index 00000000..533527e7 --- /dev/null +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/MmkvManager.kt @@ -0,0 +1,179 @@ +package com.v2ray.ang.util + +import com.google.gson.Gson +import com.tencent.mmkv.MMKV +import com.v2ray.ang.dto.ServerAffiliationInfo +import com.v2ray.ang.dto.ServerConfig +import com.v2ray.ang.dto.SubscriptionItem + +object MmkvManager { + const val ID_MAIN = "MAIN" + const val ID_SERVER_CONFIG = "SERVER_CONFIG" + const val ID_SERVER_RAW = "SERVER_RAW" + const val ID_SERVER_AFF = "SERVER_AFF" + const val ID_SUB = "SUB" + const val ID_SETTING = "SETTING" + const val KEY_SELECTED_SERVER = "SELECTED_SERVER" + const val KEY_ANG_CONFIGS = "ANG_CONFIGS" + + private val mainStorage by lazy { MMKV.mmkvWithID(ID_MAIN, MMKV.MULTI_PROCESS_MODE) } + private val serverStorage by lazy { MMKV.mmkvWithID(ID_SERVER_CONFIG, MMKV.MULTI_PROCESS_MODE) } + private val serverAffStorage by lazy { MMKV.mmkvWithID(ID_SERVER_AFF, MMKV.MULTI_PROCESS_MODE) } + private val subStorage by lazy { MMKV.mmkvWithID(ID_SUB, MMKV.MULTI_PROCESS_MODE) } + + fun decodeServerList(): MutableList { + val json = mainStorage?.decodeString(KEY_ANG_CONFIGS) + return if (json.isNullOrBlank()) { + mutableListOf() + } else { + Gson().fromJson(json, Array::class.java).toMutableList() + } + } + + fun decodeServerConfig(guid: String): ServerConfig? { + if (guid.isBlank()) { + return null + } + val json = serverStorage?.decodeString(guid) + if (json.isNullOrBlank()) { + return null + } + return Gson().fromJson(json, ServerConfig::class.java) + } + + fun encodeServerConfig(guid: String, config: ServerConfig): String { + val key = guid.ifBlank { Utils.getUuid() } + serverStorage?.encode(key, Gson().toJson(config)) + val serverList = decodeServerList() + if (!serverList.contains(key)) { + serverList.add(key) + mainStorage?.encode(KEY_ANG_CONFIGS, Gson().toJson(serverList)) + if (mainStorage?.decodeString(KEY_SELECTED_SERVER).isNullOrBlank()) { + mainStorage?.encode(KEY_SELECTED_SERVER, key) + } + } + return key + } + + fun removeServer(guid: String) { + if (guid.isBlank()) { + return + } + if (mainStorage?.decodeString(KEY_SELECTED_SERVER) == guid) { + mainStorage?.remove(KEY_SELECTED_SERVER) + } + val serverList = decodeServerList() + serverList.remove(guid) + mainStorage?.encode(KEY_ANG_CONFIGS, Gson().toJson(serverList)) + serverStorage?.remove(guid) + serverAffStorage?.remove(guid) + } + + fun removeServerViaSubid(subid: String) { + if (subid.isBlank()) { + return + } + serverStorage?.allKeys()?.forEach { key -> + decodeServerConfig(key)?.let { config -> + if (config.subscriptionId == subid) { + removeServer(key) + } + } + } + } + + fun decodeServerAffiliationInfo(guid: String): ServerAffiliationInfo? { + if (guid.isBlank()) { + return null + } + val json = serverAffStorage?.decodeString(guid) + if (json.isNullOrBlank()) { + return null + } + return Gson().fromJson(json, ServerAffiliationInfo::class.java) + } + + fun encodeServerTestDelayMillis(guid: String, testResult: Long) { + if (guid.isBlank()) { + return + } + val aff = decodeServerAffiliationInfo(guid) ?: ServerAffiliationInfo() + aff.testDelayMillis = testResult + serverAffStorage?.encode(guid, Gson().toJson(aff)) + } + + fun clearAllTestDelayResults() { + serverAffStorage?.allKeys()?.forEach { key -> + decodeServerAffiliationInfo(key)?.let { aff -> + aff.testDelayMillis = 0 + serverAffStorage?.encode(key, Gson().toJson(aff)) + } + } + } + + fun importUrlAsSubscription(url: String): Int { + val subscriptions = decodeSubscriptions() + subscriptions.forEach { + if (it.second.url == url) { + return 0 + } + } + val subItem = SubscriptionItem() + subItem.remarks = "import sub" + subItem.url = url + subStorage?.encode(Utils.getUuid(), Gson().toJson(subItem)) + return 1 + } + + fun decodeSubscriptions(): List> { + val subscriptions = mutableListOf>() + subStorage?.allKeys()?.forEach { key -> + val json = subStorage?.decodeString(key) + if (!json.isNullOrBlank()) { + subscriptions.add(Pair(key, Gson().fromJson(json, SubscriptionItem::class.java))) + } + } + subscriptions.sortedBy { (_, value) -> value.addedTime } + return subscriptions + } + + fun removeSubscription(subid: String) { + subStorage?.remove(subid) + removeServerViaSubid(subid) + } + + fun removeAllServer() { + mainStorage?.clearAll() + serverStorage?.clearAll() + serverAffStorage?.clearAll() + } + + fun removeInvalidServer() { + serverAffStorage?.allKeys()?.forEach { key -> + decodeServerAffiliationInfo(key)?.let { aff -> + if (aff.testDelayMillis <= 0L) { + removeServer(key) + } + } + } + } + + fun sortByTestResults( ) { + data class ServerDelay(var guid: String, var testDelayMillis: Long) + + val serverDelays = mutableListOf() + val serverList = decodeServerList() + serverList.forEach { key -> + val delay = decodeServerAffiliationInfo(key)?.testDelayMillis ?: 0L + serverDelays.add(ServerDelay(key, if (delay <= 0L) 999999 else delay)) + } + serverDelays.sortBy { it.testDelayMillis } + + serverDelays.forEach { + serverList.remove(it.guid) + serverList.add(it.guid) + } + + mainStorage?.encode(KEY_ANG_CONFIGS, Gson().toJson(serverList)) + } +} diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/MyContextWrapper.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/MyContextWrapper.kt new file mode 100644 index 00000000..cf9bd35e --- /dev/null +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/MyContextWrapper.kt @@ -0,0 +1,33 @@ +package com.v2ray.ang.util + +import android.content.Context +import android.content.ContextWrapper +import android.content.res.Configuration +import android.content.res.Resources +import android.os.Build +import android.os.LocaleList +import androidx.annotation.RequiresApi +import java.util.* + +open class MyContextWrapper(base: Context?) : ContextWrapper(base) { + companion object { + @RequiresApi(Build.VERSION_CODES.N) + fun wrap(context: Context, newLocale: Locale?): ContextWrapper { + var mContext = context + val res: Resources = mContext.resources + val configuration: Configuration = res.configuration + //注意 Android 7.0 前后的不同处理方法 + mContext = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + configuration.setLocale(newLocale) + val localeList = LocaleList(newLocale) + LocaleList.setDefault(localeList) + configuration.setLocales(localeList) + mContext.createConfigurationContext(configuration) + } else { + configuration.setLocale(newLocale) + mContext.createConfigurationContext(configuration) + } + return ContextWrapper(mContext) + } + } +} \ No newline at end of file diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/QRCodeDecoder.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/QRCodeDecoder.kt new file mode 100644 index 00000000..250f84df --- /dev/null +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/QRCodeDecoder.kt @@ -0,0 +1,100 @@ +package com.v2ray.ang.util + +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import com.google.zxing.* +import com.google.zxing.common.GlobalHistogramBinarizer +import com.google.zxing.common.HybridBinarizer +import java.util.* + +/** + * 描述:解析二维码图片 + */ +object QRCodeDecoder { + val HINTS: MutableMap = EnumMap(DecodeHintType::class.java) + + /** + * 同步解析本地图片二维码。该方法是耗时操作,请在子线程中调用。 + * + * @param picturePath 要解析的二维码图片本地路径 + * @return 返回二维码图片里的内容 或 null + */ + fun syncDecodeQRCode(picturePath: String): String? { + return syncDecodeQRCode(getDecodeAbleBitmap(picturePath)) + } + + /** + * 同步解析bitmap二维码。该方法是耗时操作,请在子线程中调用。 + * + * @param bitmap 要解析的二维码图片 + * @return 返回二维码图片里的内容 或 null + */ + fun syncDecodeQRCode(bitmap: Bitmap?): String? { + var source: RGBLuminanceSource? = null + try { + val width = bitmap!!.width + val height = bitmap.height + val pixels = IntArray(width * height) + bitmap.getPixels(pixels, 0, width, 0, 0, width, height) + source = RGBLuminanceSource(width, height, pixels) + return MultiFormatReader().decode(BinaryBitmap(HybridBinarizer(source)), HINTS).text + } catch (e: Exception) { + e.printStackTrace() + } + if (source != null) { + try { + return MultiFormatReader().decode(BinaryBitmap(GlobalHistogramBinarizer(source)), HINTS).text + } catch (e2: Throwable) { + e2.printStackTrace() + } + } + return null + } + + /** + * 将本地图片文件转换成可解码二维码的 Bitmap。为了避免图片太大,这里对图片进行了压缩。感谢 https://github.com/devilsen 提的 PR + * + * @param picturePath 本地图片文件路径 + * @return + */ + private fun getDecodeAbleBitmap(picturePath: String): Bitmap? { + return try { + val options = BitmapFactory.Options() + options.inJustDecodeBounds = true + BitmapFactory.decodeFile(picturePath, options) + var sampleSize = options.outHeight / 400 + if (sampleSize <= 0) { + sampleSize = 1 + } + options.inSampleSize = sampleSize + options.inJustDecodeBounds = false + BitmapFactory.decodeFile(picturePath, options) + } catch (e: Exception) { + null + } + } + + init { + val allFormats: List = arrayListOf( + BarcodeFormat.AZTEC + ,BarcodeFormat.CODABAR + ,BarcodeFormat.CODE_39 + ,BarcodeFormat.CODE_93 + ,BarcodeFormat.CODE_128 + ,BarcodeFormat.DATA_MATRIX + ,BarcodeFormat.EAN_8 + ,BarcodeFormat.EAN_13 + ,BarcodeFormat.ITF + ,BarcodeFormat.MAXICODE + ,BarcodeFormat.PDF_417 + ,BarcodeFormat.QR_CODE + ,BarcodeFormat.RSS_14 + ,BarcodeFormat.RSS_EXPANDED + ,BarcodeFormat.UPC_A + ,BarcodeFormat.UPC_E + ,BarcodeFormat.UPC_EAN_EXTENSION) + HINTS[DecodeHintType.TRY_HARDER] = BarcodeFormat.QR_CODE + HINTS[DecodeHintType.POSSIBLE_FORMATS] = allFormats + HINTS[DecodeHintType.CHARACTER_SET] = "utf-8" + } +} diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/SpeedtestUtil.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/SpeedtestUtil.kt new file mode 100644 index 00000000..5a0e5989 --- /dev/null +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/SpeedtestUtil.kt @@ -0,0 +1,142 @@ +package com.v2ray.ang.util + +import android.content.Context +import android.os.SystemClock +import android.text.TextUtils +import android.util.Log +import com.v2ray.ang.AppConfig +import com.v2ray.ang.R +import com.v2ray.ang.extension.responseLength +import kotlinx.coroutines.isActive +import libv2ray.Libv2ray +import java.io.IOException +import java.net.* +import java.util.* +import kotlin.coroutines.coroutineContext + +object SpeedtestUtil { + + private val tcpTestingSockets = ArrayList() + + suspend fun tcping(url: String, port: Int): Long { + var time = -1L + for (k in 0 until 2) { + val one = socketConnectTime(url, port) + if (!coroutineContext.isActive) { + break + } + if (one != -1L && (time == -1L || one < time)) { + time = one + } + } + return time + } + + fun realPing(config: String): Long { + return try { + Libv2ray.measureOutboundDelay(config) + } catch (e: Exception) { + Log.d(AppConfig.ANG_PACKAGE, "realPing: $e") + -1L + } + } + + fun ping(url: String): String { + try { + val command = "/system/bin/ping -c 3 $url" + val process = Runtime.getRuntime().exec(command) + val allText = process.inputStream.bufferedReader().use { it.readText() } + if (!TextUtils.isEmpty(allText)) { + val tempInfo = allText.substring(allText.indexOf("min/avg/max/mdev") + 19) + val temps = tempInfo.split("/".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() + if (temps.count() > 0 && temps[0].length < 10) { + return temps[0].toFloat().toInt().toString() + "ms" + } + } + } catch (e: Exception) { + e.printStackTrace() + } + return "-1ms" + } + + fun socketConnectTime(url: String, port: Int): Long { + try { + val socket = Socket() + synchronized(this) { + tcpTestingSockets.add(socket) + } + val start = System.currentTimeMillis() + socket.connect(InetSocketAddress(url, port),3000) + val time = System.currentTimeMillis() - start + synchronized(this) { + tcpTestingSockets.remove(socket) + } + socket.close() + return time + } catch (e: UnknownHostException) { + e.printStackTrace() + } catch (e: IOException) { + Log.d(AppConfig.ANG_PACKAGE, "socketConnectTime IOException: $e") + } catch (e: Exception) { + e.printStackTrace() + } + return -1 + } + + fun closeAllTcpSockets() { + synchronized(this) { + tcpTestingSockets.forEach { + it?.close() + } + tcpTestingSockets.clear() + } + } + + fun testConnection(context: Context, port: Int): String { + // return V2RayVpnService.measureV2rayDelay() + var result: String + var conn: HttpURLConnection? = null + + try { + val url = URL("https", + "www.google.com", + "/generate_204") + + conn = url.openConnection( + Proxy(Proxy.Type.HTTP, + InetSocketAddress("127.0.0.1", port))) as HttpURLConnection + conn.connectTimeout = 30000 + conn.readTimeout = 30000 + conn.setRequestProperty("Connection", "close") + conn.instanceFollowRedirects = false + conn.useCaches = false + + val start = SystemClock.elapsedRealtime() + val code = conn.responseCode + val elapsed = SystemClock.elapsedRealtime() - start + + if (code == 204 || code == 200 && conn.responseLength == 0L) { + result = context.getString(R.string.connection_test_available, elapsed) + } else { + throw IOException(context.getString(R.string.connection_test_error_status_code, code)) + } + } catch (e: IOException) { + // network exception + Log.d(AppConfig.ANG_PACKAGE, "testConnection IOException: " + Log.getStackTraceString(e)) + result = context.getString(R.string.connection_test_error, e.message) + } catch (e: Exception) { + // library exception, eg sumsung + Log.d(AppConfig.ANG_PACKAGE, "testConnection Exception: " + Log.getStackTraceString(e)) + result = context.getString(R.string.connection_test_error, e.message) + } finally { + conn?.disconnect() + } + + return result + } + + fun getLibVersion(): String { + return Libv2ray.checkVersionX() + } + +} diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/Utils.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/Utils.kt new file mode 100644 index 00000000..48e9c488 --- /dev/null +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/Utils.kt @@ -0,0 +1,439 @@ +package com.v2ray.ang.util + +import android.content.ClipboardManager +import android.content.Context +import android.text.Editable +import android.util.Base64 +import com.google.zxing.WriterException +import android.graphics.Bitmap +import com.google.zxing.BarcodeFormat +import com.google.zxing.qrcode.QRCodeWriter +import com.google.zxing.EncodeHintType +import java.util.* +import kotlin.collections.HashMap +import android.content.ClipData +import android.content.Intent +import android.content.res.Configuration.UI_MODE_NIGHT_MASK +import android.content.res.Configuration.UI_MODE_NIGHT_YES +import android.net.Uri +import android.os.Build +import android.os.LocaleList +import android.util.Log +import android.util.Patterns +import android.webkit.URLUtil +import com.tencent.mmkv.MMKV +import com.v2ray.ang.AppConfig +import com.v2ray.ang.AppConfig.ANG_PACKAGE +import com.v2ray.ang.BuildConfig +import com.v2ray.ang.R +import com.v2ray.ang.extension.toast +import java.net.* +import com.v2ray.ang.service.V2RayServiceManager +import java.io.IOException + +object Utils { + + private val mainStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_MAIN, MMKV.MULTI_PROCESS_MODE) } + private val settingsStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SETTING, MMKV.MULTI_PROCESS_MODE) } + + /** + * convert string to editalbe for kotlin + * + * @param text + * @return + */ + fun getEditable(text: String): Editable { + return Editable.Factory.getInstance().newEditable(text) + } + + /** + * find value in array position + */ + fun arrayFind(array: Array, value: String): Int { + for (i in array.indices) { + if (array[i] == value) { + return i + } + } + return -1 + } + + /** + * parseInt + */ + fun parseInt(str: String): Int { + return parseInt(str, 0) + } + + fun parseInt(str: String?, default: Int): Int { + str ?: return default + return try { + Integer.parseInt(str) + } catch (e: Exception) { + e.printStackTrace() + default + } + } + + /** + * get text from clipboard + */ + fun getClipboard(context: Context): String { + return try { + val cmb = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager + cmb.primaryClip?.getItemAt(0)?.text.toString() + } catch (e: Exception) { + e.printStackTrace() + "" + } + } + + /** + * set text to clipboard + */ + fun setClipboard(context: Context, content: String) { + try { + val cmb = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager + val clipData = ClipData.newPlainText(null, content) + cmb.setPrimaryClip(clipData) + } catch (e: Exception) { + e.printStackTrace() + } + } + + /** + * base64 decode + */ + fun decode(text: String): String { + tryDecodeBase64(text)?.let { return it } + if (text.endsWith('=')) { + // try again for some loosely formatted base64 + tryDecodeBase64(text.trimEnd('='))?.let { return it } + } + return "" + } + + fun tryDecodeBase64(text: String): String? { + try { + return Base64.decode(text, Base64.NO_WRAP).toString(charset("UTF-8")) + } catch (e: Exception) { + Log.i(ANG_PACKAGE, "Parse base64 standard failed $e") + } + try { + return Base64.decode(text, Base64.NO_WRAP.or(Base64.URL_SAFE)).toString(charset("UTF-8")) + } catch (e: Exception) { + Log.i(ANG_PACKAGE, "Parse base64 url safe failed $e") + } + return null + } + + /** + * base64 encode + */ + fun encode(text: String): String { + return try { + Base64.encodeToString(text.toByteArray(charset("UTF-8")), Base64.NO_WRAP) + } catch (e: Exception) { + e.printStackTrace() + "" + } + } + + /** + * get remote dns servers from preference + */ + fun getRemoteDnsServers(): List { + val remoteDns = settingsStorage?.decodeString(AppConfig.PREF_REMOTE_DNS) ?: AppConfig.DNS_AGENT + val ret = remoteDns.split(",").filter { isPureIpAddress(it) || isCoreDNSAddress(it) } + if (ret.isEmpty()) { + return listOf(AppConfig.DNS_AGENT) + } + return ret + } + + fun getVpnDnsServers(): List { + val vpnDns = settingsStorage?.decodeString(AppConfig.PREF_VPN_DNS) + ?: settingsStorage?.decodeString(AppConfig.PREF_REMOTE_DNS) + ?: AppConfig.DNS_AGENT + return vpnDns.split(",").filter { isPureIpAddress(it) } + // allow empty, in that case dns will use system default + } + + /** + * get remote dns servers from preference + */ + fun getDomesticDnsServers(): List { + val domesticDns = settingsStorage?.decodeString(AppConfig.PREF_DOMESTIC_DNS) ?: AppConfig.DNS_DIRECT + val ret = domesticDns.split(",").filter { isPureIpAddress(it) || isCoreDNSAddress(it) } + if (ret.isEmpty()) { + return listOf(AppConfig.DNS_DIRECT) + } + return ret + } + + /** + * create qrcode using zxing + */ + fun createQRCode(text: String, size: Int = 800): Bitmap? { + try { + val hints = HashMap() + hints[EncodeHintType.CHARACTER_SET] = "utf-8" + val bitMatrix = QRCodeWriter().encode(text, + BarcodeFormat.QR_CODE, size, size, hints) + val pixels = IntArray(size * size) + for (y in 0 until size) { + for (x in 0 until size) { + if (bitMatrix.get(x, y)) { + pixels[y * size + x] = 0xff000000.toInt() + } else { + pixels[y * size + x] = 0xffffffff.toInt() + } + + } + } + val bitmap = Bitmap.createBitmap(size, size, + Bitmap.Config.ARGB_8888) + bitmap.setPixels(pixels, 0, size, 0, 0, size, size) + return bitmap + } catch (e: WriterException) { + e.printStackTrace() + return null + } + } + + /** + * is ip address + */ + fun isIpAddress(value: String): Boolean { + try { + var addr = value + if (addr.isEmpty() || addr.isBlank()) { + return false + } + //CIDR + if (addr.indexOf("/") > 0) { + val arr = addr.split("/") + if (arr.count() == 2 && Integer.parseInt(arr[1]) > 0) { + addr = arr[0] + } + } + + // "::ffff:192.168.173.22" + // "[::ffff:192.168.173.22]:80" + if (addr.startsWith("::ffff:") && '.' in addr) { + addr = addr.drop(7) + } else if (addr.startsWith("[::ffff:") && '.' in addr) { + addr = addr.drop(8).replace("]", "") + } + + // addr = addr.toLowerCase() + val octets = addr.split('.').toTypedArray() + if (octets.size == 4) { + if (octets[3].indexOf(":") > 0) { + addr = addr.substring(0, addr.indexOf(":")) + } + return isIpv4Address(addr) + } + + // Ipv6addr [2001:abc::123]:8080 + return isIpv6Address(addr) + } catch (e: Exception) { + e.printStackTrace() + return false + } + } + + fun isPureIpAddress(value: String): Boolean { + return (isIpv4Address(value) || isIpv6Address(value)) + } + + fun isIpv4Address(value: String): Boolean { + val regV4 = Regex("^([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])$") + return regV4.matches(value) + } + + fun isIpv6Address(value: String): Boolean { + var addr = value + if (addr.indexOf("[") == 0 && addr.lastIndexOf("]") > 0) { + addr = addr.drop(1) + addr = addr.dropLast(addr.count() - addr.lastIndexOf("]")) + } + val regV6 = Regex("^((?:[0-9A-Fa-f]{1,4}))?((?::[0-9A-Fa-f]{1,4}))*::((?:[0-9A-Fa-f]{1,4}))?((?::[0-9A-Fa-f]{1,4}))*|((?:[0-9A-Fa-f]{1,4}))((?::[0-9A-Fa-f]{1,4})){7}$") + return regV6.matches(addr) + } + + private fun isCoreDNSAddress(s: String): Boolean { + return s.startsWith("https") || s.startsWith("tcp") || s.startsWith("quic") + } + + /** + * is valid url + */ + fun isValidUrl(value: String?): Boolean { + try { + if (value != null && Patterns.WEB_URL.matcher(value).matches() || URLUtil.isValidUrl(value)) { + return true + } + } catch (e: WriterException) { + e.printStackTrace() + return false + } + return false + } + + fun startVServiceFromToggle(context: Context): Boolean { + if (mainStorage?.decodeString(MmkvManager.KEY_SELECTED_SERVER).isNullOrEmpty()) { + context.toast(R.string.app_tile_first_use) + return false + } + V2RayServiceManager.startV2Ray(context) + return true + } + + /** + * stopVService + */ + fun stopVService(context: Context) { + context.toast(R.string.toast_services_stop) + MessageUtil.sendMsg2Service(context, AppConfig.MSG_STATE_STOP, "") + } + + fun openUri(context: Context, uriString: String) { + val uri = Uri.parse(uriString) + context.startActivity(Intent(Intent.ACTION_VIEW, uri)) + } + + /** + * uuid + */ + fun getUuid(): String { + return try { + UUID.randomUUID().toString().replace("-", "") + } catch (e: Exception) { + e.printStackTrace() + "" + } + } + + fun urlDecode(url: String): String { + return try { + URLDecoder.decode(URLDecoder.decode(url), "utf-8") + } catch (e: Exception) { + e.printStackTrace() + url + } + } + + fun urlEncode(url: String): String { + return try { + URLEncoder.encode(url, "UTF-8") + } catch (e: Exception) { + e.printStackTrace() + url + } + } + + + /** + * readTextFromAssets + */ + fun readTextFromAssets(context: Context, fileName: String): String { + val content = context.assets.open(fileName).bufferedReader().use { + it.readText() + } + return content + } + + fun userAssetPath(context: Context?): String { + if (context == null) + return "" + val extDir = context.getExternalFilesDir(AppConfig.DIR_ASSETS) + ?: return context.getDir(AppConfig.DIR_ASSETS, 0).absolutePath + return extDir.absolutePath + } + + fun getUrlContext(url: String, timeout: Int): String { + var result: String + var conn: HttpURLConnection? = null + + try { + conn = URL(url).openConnection() as HttpURLConnection + conn.connectTimeout = timeout + conn.readTimeout = timeout + conn.setRequestProperty("Connection", "close") + conn.instanceFollowRedirects = false + conn.useCaches = false + //val code = conn.responseCode + result = conn.inputStream.bufferedReader().readText() + } catch (e: Exception) { + result = "" + } finally { + conn?.disconnect() + } + return result + } + + @Throws(IOException::class) + fun getUrlContentWithCustomUserAgent(urlStr: String?): String { + val url = URL(urlStr) + val conn = url.openConnection() + conn.setRequestProperty("Connection", "close") + conn.setRequestProperty("User-agent", "v2rayNG/${BuildConfig.VERSION_NAME}") + url.userInfo?.let { + conn.setRequestProperty("Authorization", + "Basic ${encode(urlDecode(it))}") + } + conn.useCaches = false + return conn.inputStream.use { + it.bufferedReader().readText() + } + } + + fun getDarkModeStatus(context: Context): Boolean { + val mode = context.resources.configuration.uiMode and UI_MODE_NIGHT_MASK + return mode == UI_MODE_NIGHT_YES + } + + fun getIpv6Address(address: String): String { + return if (isIpv6Address(address)) { + String.format("[%s]", address) + } else { + address + } + } + + fun getLocale(context: Context): Locale = + when (settingsStorage?.decodeString(AppConfig.PREF_LANGUAGE) ?: "auto") { + "auto" -> getSysLocale() + "en" -> Locale("en") + "zh-rCN" -> Locale("zh", "CN") + "zh-rTW" -> Locale("zh", "TW") + "vi" -> Locale("vi") + "ru" -> Locale("ru") + "fa" -> Locale("fa") + else -> getSysLocale() + } + + private fun getSysLocale(): Locale = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + LocaleList.getDefault()[0] + } else { + Locale.getDefault() + } + + fun fixIllegalUrl(str: String): String { + return str + .replace(" ","%20") + .replace("|","%7C") + } + + fun removeWhiteSpace(str: String?): String? { + return str?.replace(" ", "") + } + + fun idnToASCII(str: String): String { + val url = URL(str) + return URL(url.protocol, IDN.toASCII(url.host, IDN.ALLOW_UNASSIGNED), url.port, url.file) + .toExternalForm() + } +} + diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/V2rayConfigUtil.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/V2rayConfigUtil.kt new file mode 100644 index 00000000..f5177aca --- /dev/null +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/V2rayConfigUtil.kt @@ -0,0 +1,432 @@ +package com.v2ray.ang.util + +import android.content.Context +import android.text.TextUtils +import android.util.Log +import com.google.gson.* +import com.tencent.mmkv.MMKV +import com.v2ray.ang.AppConfig +import com.v2ray.ang.AppConfig.ANG_PACKAGE +import com.v2ray.ang.dto.V2rayConfig +import com.v2ray.ang.dto.EConfigType +import com.v2ray.ang.dto.ERoutingMode +import com.v2ray.ang.dto.V2rayConfig.Companion.DEFAULT_NETWORK +import com.v2ray.ang.dto.V2rayConfig.Companion.HTTP + +object V2rayConfigUtil { + private val serverRawStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SERVER_RAW, MMKV.MULTI_PROCESS_MODE) } + private val settingsStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SETTING, MMKV.MULTI_PROCESS_MODE) } + + data class Result(var status: Boolean, var content: String) + + /** + * 生成v2ray的客户端配置文件 + */ + fun getV2rayConfig(context: Context, guid: String): Result { + try { + val config = MmkvManager.decodeServerConfig(guid) ?: return Result(false, "") + if (config.configType == EConfigType.CUSTOM) { + val raw = serverRawStorage?.decodeString(guid) + val customConfig = if (raw.isNullOrBlank()) { + config.fullConfig?.toPrettyPrinting() ?: return Result(false, "") + } else { + raw + } + Log.d(ANG_PACKAGE, customConfig) + return Result(true, customConfig) + } + val outbound = config.getProxyOutbound() ?: return Result(false, "") + val result = getV2rayNonCustomConfig(context, outbound) + Log.d(ANG_PACKAGE, result.content) + return result + } catch (e: Exception) { + e.printStackTrace() + return Result(false, "") + } + } + + /** + * 生成v2ray的客户端配置文件 + */ + private fun getV2rayNonCustomConfig(context: Context, outbound: V2rayConfig.OutboundBean): Result { + val result = Result(false, "") + //取得默认配置 + val assets = Utils.readTextFromAssets(context, "v2ray_config.json") + if (TextUtils.isEmpty(assets)) { + return result + } + + //转成Json + val v2rayConfig = Gson().fromJson(assets, V2rayConfig::class.java) ?: return result + + v2rayConfig.log.loglevel = settingsStorage?.decodeString(AppConfig.PREF_LOGLEVEL) + ?: "warning" + + inbounds(v2rayConfig) + + httpRequestObject(outbound) + + v2rayConfig.outbounds[0] = outbound + + routing(v2rayConfig) + + fakedns(v2rayConfig) + + dns(v2rayConfig) + + if (settingsStorage?.decodeBool(AppConfig.PREF_LOCAL_DNS_ENABLED) == true) { + customLocalDns(v2rayConfig) + } + if (settingsStorage?.decodeBool(AppConfig.PREF_SPEED_ENABLED) != true) { + v2rayConfig.stats = null + v2rayConfig.policy = null + } + result.status = true + result.content = v2rayConfig.toPrettyPrinting() + return result + } + + /** + * + */ + private fun inbounds(v2rayConfig: V2rayConfig): Boolean { + try { + val socksPort = Utils.parseInt(settingsStorage?.decodeString(AppConfig.PREF_SOCKS_PORT), AppConfig.PORT_SOCKS.toInt()) + val httpPort = Utils.parseInt(settingsStorage?.decodeString(AppConfig.PREF_HTTP_PORT), AppConfig.PORT_HTTP.toInt()) + + v2rayConfig.inbounds.forEach { curInbound -> + if (settingsStorage?.decodeBool(AppConfig.PREF_PROXY_SHARING) != true) { + //bind all inbounds to localhost if the user requests + curInbound.listen = "127.0.0.1" + } + } + v2rayConfig.inbounds[0].port = socksPort + val fakedns = settingsStorage?.decodeBool(AppConfig.PREF_FAKE_DNS_ENABLED) + ?: false + val sniffAllTlsAndHttp = settingsStorage?.decodeBool(AppConfig.PREF_SNIFFING_ENABLED, true) + ?: true + v2rayConfig.inbounds[0].sniffing?.enabled = fakedns || sniffAllTlsAndHttp + if (!sniffAllTlsAndHttp) { + v2rayConfig.inbounds[0].sniffing?.destOverride?.clear() + } + if (fakedns) { + v2rayConfig.inbounds[0].sniffing?.destOverride?.add("fakedns") + } + + v2rayConfig.inbounds[1].port = httpPort + +// if (httpPort > 0) { +// val httpCopy = v2rayConfig.inbounds[0].copy() +// httpCopy.port = httpPort +// httpCopy.protocol = "http" +// v2rayConfig.inbounds.add(httpCopy) +// } + } catch (e: Exception) { + e.printStackTrace() + return false + } + return true + } + + private fun fakedns(v2rayConfig: V2rayConfig) { + if (settingsStorage?.decodeBool(AppConfig.PREF_FAKE_DNS_ENABLED) == true) { + v2rayConfig.fakedns = listOf(V2rayConfig.FakednsBean()) + v2rayConfig.outbounds.filter { it.protocol == "freedom" }.forEach { + it.settings?.domainStrategy = "UseIP" + } + } + } + + /** + * routing + */ + private fun routing(v2rayConfig: V2rayConfig): Boolean { + try { + routingUserRule(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_AGENT) + ?: "", AppConfig.TAG_AGENT, v2rayConfig) + routingUserRule(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_DIRECT) + ?: "", AppConfig.TAG_DIRECT, v2rayConfig) + routingUserRule(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_BLOCKED) + ?: "", AppConfig.TAG_BLOCKED, v2rayConfig) + + v2rayConfig.routing.domainStrategy = settingsStorage?.decodeString(AppConfig.PREF_ROUTING_DOMAIN_STRATEGY) + ?: "IPIfNonMatch" + v2rayConfig.routing.domainMatcher = "mph" + val routingMode = settingsStorage?.decodeString(AppConfig.PREF_ROUTING_MODE) ?: ERoutingMode.GLOBAL_PROXY.value + + // Hardcode googleapis.cn + val googleapisRoute = V2rayConfig.RoutingBean.RulesBean( + type = "field", + outboundTag = AppConfig.TAG_AGENT, + domain = arrayListOf("domain:googleapis.cn") + ) + + when (routingMode) { + ERoutingMode.BYPASS_LAN.value -> { + routingGeo("ip", "private", AppConfig.TAG_DIRECT, v2rayConfig) + } + ERoutingMode.BYPASS_MAINLAND.value -> { + routingGeo("", "cn", AppConfig.TAG_DIRECT, v2rayConfig) + v2rayConfig.routing.rules.add(0, googleapisRoute) + } + ERoutingMode.BYPASS_LAN_MAINLAND.value -> { + routingGeo("ip", "private", AppConfig.TAG_DIRECT, v2rayConfig) + routingGeo("", "cn", AppConfig.TAG_DIRECT, v2rayConfig) + v2rayConfig.routing.rules.add(0, googleapisRoute) + } + ERoutingMode.GLOBAL_DIRECT.value -> { + val globalDirect = V2rayConfig.RoutingBean.RulesBean( + type = "field", + outboundTag = AppConfig.TAG_DIRECT, + port = "0-65535" + ) + v2rayConfig.routing.rules.add(globalDirect) + } + } + } catch (e: Exception) { + e.printStackTrace() + return false + } + return true + } + + private fun routingGeo(ipOrDomain: String, code: String, tag: String, v2rayConfig: V2rayConfig) { + try { + if (!TextUtils.isEmpty(code)) { + //IP + if (ipOrDomain == "ip" || ipOrDomain == "") { + val rulesIP = V2rayConfig.RoutingBean.RulesBean() + rulesIP.type = "field" + rulesIP.outboundTag = tag + rulesIP.ip = ArrayList() + rulesIP.ip?.add("geoip:$code") + v2rayConfig.routing.rules.add(rulesIP) + } + + if (ipOrDomain == "domain" || ipOrDomain == "") { + //Domain + val rulesDomain = V2rayConfig.RoutingBean.RulesBean() + rulesDomain.type = "field" + rulesDomain.outboundTag = tag + rulesDomain.domain = ArrayList() + rulesDomain.domain?.add("geosite:$code") + v2rayConfig.routing.rules.add(rulesDomain) + } + } + } catch (e: Exception) { + e.printStackTrace() + } + } + + private fun routingUserRule(userRule: String, tag: String, v2rayConfig: V2rayConfig) { + try { + if (!TextUtils.isEmpty(userRule)) { + //Domain + val rulesDomain = V2rayConfig.RoutingBean.RulesBean() + rulesDomain.type = "field" + rulesDomain.outboundTag = tag + rulesDomain.domain = ArrayList() + + //IP + val rulesIP = V2rayConfig.RoutingBean.RulesBean() + rulesIP.type = "field" + rulesIP.outboundTag = tag + rulesIP.ip = ArrayList() + + userRule.split(",").map { it.trim() }.forEach { + if (Utils.isIpAddress(it) || it.startsWith("geoip:")) { + rulesIP.ip?.add(it) + } else if (it.isNotEmpty()) +// if (Utils.isValidUrl(it) +// || it.startsWith("geosite:") +// || it.startsWith("regexp:") +// || it.startsWith("domain:") +// || it.startsWith("full:")) + { + rulesDomain.domain?.add(it) + } + } + if (rulesDomain.domain?.size!! > 0) { + v2rayConfig.routing.rules.add(rulesDomain) + } + if (rulesIP.ip?.size!! > 0) { + v2rayConfig.routing.rules.add(rulesIP) + } + } + } catch (e: Exception) { + e.printStackTrace() + } + } + + private fun userRule2Domian(userRule: String): ArrayList { + val domain = ArrayList() + userRule.split(",").map { it.trim() }.forEach { + if (it.startsWith("geosite:") || it.startsWith("domain:")) { + domain.add(it) + } + } + return domain + } + + /** + * Custom Dns + */ + private fun customLocalDns(v2rayConfig: V2rayConfig): Boolean { + try { + if (settingsStorage?.decodeBool(AppConfig.PREF_FAKE_DNS_ENABLED) == true) { + val geositeCn = arrayListOf("geosite:cn") + val proxyDomain = userRule2Domian(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_AGENT) + ?: "") + val directDomain = userRule2Domian(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_DIRECT) + ?: "") + // fakedns with all domains to make it always top priority + v2rayConfig.dns.servers?.add(0, + V2rayConfig.DnsBean.ServersBean(address = "fakedns", domains = geositeCn.plus(proxyDomain).plus(directDomain))) + } + + // DNS inbound对象 + val remoteDns = Utils.getRemoteDnsServers() + if (v2rayConfig.inbounds.none { e -> e.protocol == "dokodemo-door" && e.tag == "dns-in" }) { + val dnsInboundSettings = V2rayConfig.InboundBean.InSettingsBean( + address = if (Utils.isPureIpAddress(remoteDns.first())) remoteDns.first() else "1.1.1.1", + port = 53, + network = "tcp,udp") + + val localDnsPort = Utils.parseInt(settingsStorage?.decodeString(AppConfig.PREF_LOCAL_DNS_PORT), AppConfig.PORT_LOCAL_DNS.toInt()) + v2rayConfig.inbounds.add( + V2rayConfig.InboundBean( + tag = "dns-in", + port = localDnsPort, + listen = "127.0.0.1", + protocol = "dokodemo-door", + settings = dnsInboundSettings, + sniffing = null)) + } + + // DNS outbound对象 + if (v2rayConfig.outbounds.none { e -> e.protocol == "dns" && e.tag == "dns-out" }) { + v2rayConfig.outbounds.add( + V2rayConfig.OutboundBean( + protocol = "dns", + tag = "dns-out", + settings = null, + streamSettings = null, + mux = null)) + } + + // DNS routing tag + v2rayConfig.routing.rules.add(0, V2rayConfig.RoutingBean.RulesBean( + type = "field", + inboundTag = arrayListOf("dns-in"), + outboundTag = "dns-out", + domain = null) + ) + } catch (e: Exception) { + e.printStackTrace() + return false + } + return true + } + + private fun dns(v2rayConfig: V2rayConfig): Boolean { + try { + val hosts = mutableMapOf() + val servers = ArrayList() + val remoteDns = Utils.getRemoteDnsServers() + val proxyDomain = userRule2Domian(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_AGENT) + ?: "") + + remoteDns.forEach { + servers.add(it) + } + if (proxyDomain.size > 0) { + servers.add(V2rayConfig.DnsBean.ServersBean(remoteDns.first(), 53, proxyDomain, null)) + } + + // domestic DNS + val directDomain = userRule2Domian(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_DIRECT) + ?: "") + val routingMode = settingsStorage?.decodeString(AppConfig.PREF_ROUTING_MODE) ?: ERoutingMode.GLOBAL_PROXY.value + if (directDomain.size > 0 || routingMode == ERoutingMode.BYPASS_MAINLAND.value || routingMode == ERoutingMode.BYPASS_LAN_MAINLAND.value) { + val domesticDns = Utils.getDomesticDnsServers() + val geositeCn = arrayListOf("geosite:cn") + val geoipCn = arrayListOf("geoip:cn") + if (directDomain.size > 0) { + servers.add(V2rayConfig.DnsBean.ServersBean(domesticDns.first(), 53, directDomain, geoipCn)) + } + if (routingMode == ERoutingMode.BYPASS_MAINLAND.value || routingMode == ERoutingMode.BYPASS_LAN_MAINLAND.value) { + servers.add(V2rayConfig.DnsBean.ServersBean(domesticDns.first(), 53, geositeCn, geoipCn)) + } + if (Utils.isPureIpAddress(domesticDns.first())) { + v2rayConfig.routing.rules.add(0, V2rayConfig.RoutingBean.RulesBean( + type = "field", + outboundTag = AppConfig.TAG_DIRECT, + port = "53", + ip = arrayListOf(domesticDns.first()), + domain = null) + ) + } + } + + val blkDomain = userRule2Domian(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_BLOCKED) + ?: "") + if (blkDomain.size > 0) { + hosts.putAll(blkDomain.map { it to "127.0.0.1" }) + } + + // hardcode googleapi rule to fix play store problems + hosts["domain:googleapis.cn"] = "googleapis.com" + + // DNS dns对象 + v2rayConfig.dns = V2rayConfig.DnsBean( + servers = servers, + hosts = hosts) + + // DNS routing + if (Utils.isPureIpAddress(remoteDns.first())) { + v2rayConfig.routing.rules.add(0, V2rayConfig.RoutingBean.RulesBean( + type = "field", + outboundTag = AppConfig.TAG_AGENT, + port = "53", + ip = arrayListOf(remoteDns.first()), + domain = null) + ) + } + } catch (e: Exception) { + e.printStackTrace() + return false + } + return true + } + + private fun httpRequestObject(outbound: V2rayConfig.OutboundBean): Boolean { + try { + if (outbound.streamSettings?.network == DEFAULT_NETWORK + && outbound.streamSettings?.tcpSettings?.header?.type == HTTP) { + val path = outbound.streamSettings?.tcpSettings?.header?.request?.path + val host = outbound.streamSettings?.tcpSettings?.header?.request?.headers?.Host + + val requestString: String by lazy { + """{"version":"1.1","method":"GET","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36","Mozilla/5.0 (iPhone; CPU iPhone OS 10_0_2 like Mac OS X) AppleWebKit/601.1 (KHTML, like Gecko) CriOS/53.0.2785.109 Mobile/14A456 Safari/601.1.46"],"Accept-Encoding":["gzip, deflate"],"Connection":["keep-alive"],"Pragma":"no-cache"}}""" + } + outbound.streamSettings?.tcpSettings?.header?.request = Gson().fromJson( + requestString, + V2rayConfig.OutboundBean.StreamSettingsBean.TcpSettingsBean.HeaderBean.RequestBean::class.java + ) + outbound.streamSettings?.tcpSettings?.header?.request?.path = + if (path.isNullOrEmpty()) { + listOf("/") + } else { + path + } + outbound.streamSettings?.tcpSettings?.header?.request?.headers?.Host = host!! + } + + } catch (e: Exception) { + e.printStackTrace() + return false + } + return true + } + +} diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/viewmodel/MainViewModel.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/viewmodel/MainViewModel.kt new file mode 100644 index 00000000..30673162 --- /dev/null +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/viewmodel/MainViewModel.kt @@ -0,0 +1,235 @@ +package com.v2ray.ang.viewmodel + +import android.app.Application +import android.content.* +import android.util.Log +import android.view.LayoutInflater +import android.widget.ArrayAdapter +import androidx.appcompat.app.AlertDialog +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.viewModelScope +import com.google.gson.Gson +import com.tencent.mmkv.MMKV +import com.v2ray.ang.AngApplication +import com.v2ray.ang.AppConfig +import com.v2ray.ang.AppConfig.ANG_PACKAGE +import com.v2ray.ang.R +import com.v2ray.ang.databinding.DialogConfigFilterBinding +import com.v2ray.ang.dto.* +import com.v2ray.ang.extension.toast +import com.v2ray.ang.util.* +import com.v2ray.ang.util.MmkvManager.KEY_ANG_CONFIGS +import kotlinx.coroutines.* +import java.util.* + +class MainViewModel(application: Application) : AndroidViewModel(application) { + private val mainStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_MAIN, MMKV.MULTI_PROCESS_MODE) } + private val serverRawStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SERVER_RAW, MMKV.MULTI_PROCESS_MODE) } + + var serverList = MmkvManager.decodeServerList() + var subscriptionId: String = "" + var keywordFilter: String = "" + private set + val serversCache = mutableListOf() + val isRunning by lazy { MutableLiveData() } + val updateListAction by lazy { MutableLiveData() } + val updateTestResultAction by lazy { MutableLiveData() } + + private val tcpingTestScope by lazy { CoroutineScope(Dispatchers.IO) } + + fun startListenBroadcast() { + isRunning.value = false + getApplication().registerReceiver(mMsgReceiver, IntentFilter(AppConfig.BROADCAST_ACTION_ACTIVITY)) + MessageUtil.sendMsg2Service(getApplication(), AppConfig.MSG_REGISTER_CLIENT, "") + } + + override fun onCleared() { + getApplication().unregisterReceiver(mMsgReceiver) + tcpingTestScope.coroutineContext[Job]?.cancelChildren() + SpeedtestUtil.closeAllTcpSockets() + Log.i(ANG_PACKAGE, "Main ViewModel is cleared") + super.onCleared() + } + + fun reloadServerList() { + serverList = MmkvManager.decodeServerList() + updateCache() + updateListAction.value = -1 + } + + fun removeServer(guid: String) { + serverList.remove(guid) + MmkvManager.removeServer(guid) + val index = getPosition(guid) + if(index >= 0){ + serversCache.removeAt(index) + } + } + + fun appendCustomConfigServer(server: String) { + val config = ServerConfig.create(EConfigType.CUSTOM) + config.remarks = System.currentTimeMillis().toString() + config.subscriptionId = subscriptionId + config.fullConfig = Gson().fromJson(server, V2rayConfig::class.java) + val key = MmkvManager.encodeServerConfig("", config) + serverRawStorage?.encode(key, server) + serverList.add(key) + serversCache.add(ServersCache(key,config)) + } + + fun swapServer(fromPosition: Int, toPosition: Int) { + Collections.swap(serverList, fromPosition, toPosition) + Collections.swap(serversCache, fromPosition, toPosition) + mainStorage?.encode(KEY_ANG_CONFIGS, Gson().toJson(serverList)) + } + + @Synchronized + fun updateCache() { + serversCache.clear() + for (guid in serverList) { + val config = MmkvManager.decodeServerConfig(guid) ?: continue + if (subscriptionId.isNotEmpty() && subscriptionId != config.subscriptionId) { + continue + } + + if (keywordFilter.isEmpty() || config.remarks.contains(keywordFilter)) { + serversCache.add(ServersCache(guid, config)) + } + } + } + + fun testAllTcping() { + tcpingTestScope.coroutineContext[Job]?.cancelChildren() + SpeedtestUtil.closeAllTcpSockets() + MmkvManager.clearAllTestDelayResults() + updateListAction.value = -1 // update all + + getApplication().toast(R.string.connection_test_testing) + for (item in serversCache) { + item.config.getProxyOutbound()?.let { outbound -> + val serverAddress = outbound.getServerAddress() + val serverPort = outbound.getServerPort() + if (serverAddress != null && serverPort != null) { + tcpingTestScope.launch { + val testResult = SpeedtestUtil.tcping(serverAddress, serverPort) + launch(Dispatchers.Main) { + MmkvManager.encodeServerTestDelayMillis(item.guid, testResult) + updateListAction.value = getPosition(item.guid) + } + } + } + } + } + } + + fun testAllRealPing() { + MessageUtil.sendMsg2TestService(getApplication(), AppConfig.MSG_MEASURE_CONFIG_CANCEL, "") + MmkvManager.clearAllTestDelayResults() + updateListAction.value = -1 // update all + + getApplication().toast(R.string.connection_test_testing) + viewModelScope.launch(Dispatchers.Default) { // without Dispatchers.Default viewModelScope will launch in main thread + for (item in serversCache) { + val config = V2rayConfigUtil.getV2rayConfig(getApplication(), item.guid) + if (config.status) { + MessageUtil.sendMsg2TestService(getApplication(), AppConfig.MSG_MEASURE_CONFIG, Pair(item.guid, config.content)) + } + } + } + } + + fun testCurrentServerRealPing() { + MessageUtil.sendMsg2Service(getApplication(), AppConfig.MSG_MEASURE_DELAY, "") + } + + fun filterConfig(context :Context) { + val subscriptions = MmkvManager.decodeSubscriptions() + val listId = subscriptions.map { it.first }.toList().toMutableList() + val listRemarks = subscriptions.map { it.second.remarks }.toList().toMutableList() + listRemarks += context.getString(R.string.filter_config_all) + val checkedItem = if (subscriptionId.isNotEmpty()) { + listId.indexOf(subscriptionId) + } else { + listRemarks.count() - 1 + } + + val ivBinding = DialogConfigFilterBinding.inflate(LayoutInflater.from(context)) + ivBinding.spSubscriptionId.adapter = ArrayAdapter( context, android.R.layout.simple_spinner_dropdown_item, listRemarks) + ivBinding.spSubscriptionId.setSelection(checkedItem) + ivBinding.etKeyword.text = Utils.getEditable(keywordFilter) + val builder = AlertDialog.Builder(context).setView(ivBinding.root) + builder.setTitle(R.string.title_filter_config) + builder.setPositiveButton(R.string.tasker_setting_confirm) { dialogInterface: DialogInterface?, _: Int -> + try { + val position = ivBinding.spSubscriptionId.selectedItemPosition + subscriptionId = if (listRemarks.count() - 1 == position) { + "" + } else { + subscriptions[position].first + } + keywordFilter = ivBinding.etKeyword.text.toString() + reloadServerList() + + dialogInterface?.dismiss() + } catch (e: Exception) { + e.printStackTrace() + } + } + builder.show() +// AlertDialog.Builder(context) +// .setSingleChoiceItems(listRemarks.toTypedArray(), checkedItem) { dialog, i -> +// try { +// subscriptionId = if (listRemarks.count() - 1 == i) { +// "" +// } else { +// subscriptions[i].first +// } +// reloadServerList() +// dialog.dismiss() +// } catch (e: Exception) { +// e.printStackTrace() +// } +// }.show() + } + + fun getPosition(guid: String) : Int { + serversCache.forEachIndexed { index, it -> + if (it.guid == guid) + return index + } + return -1 + } + + private val mMsgReceiver = object : BroadcastReceiver() { + override fun onReceive(ctx: Context?, intent: Intent?) { + when (intent?.getIntExtra("key", 0)) { + AppConfig.MSG_STATE_RUNNING -> { + isRunning.value = true + } + AppConfig.MSG_STATE_NOT_RUNNING -> { + isRunning.value = false + } + AppConfig.MSG_STATE_START_SUCCESS -> { + getApplication().toast(R.string.toast_services_success) + isRunning.value = true + } + AppConfig.MSG_STATE_START_FAILURE -> { + getApplication().toast(R.string.toast_services_failure) + isRunning.value = false + } + AppConfig.MSG_STATE_STOP_SUCCESS -> { + isRunning.value = false + } + AppConfig.MSG_MEASURE_DELAY_SUCCESS -> { + updateTestResultAction.value = intent.getStringExtra("content") + } + AppConfig.MSG_MEASURE_CONFIG_SUCCESS -> { + val resultPair = intent.getSerializableExtra("content") as Pair + MmkvManager.encodeServerTestDelayMillis(resultPair.first, resultPair.second) + updateListAction.value = getPosition(resultPair.first) + } + } + } + } +} diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/viewmodel/SettingsViewModel.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/viewmodel/SettingsViewModel.kt new file mode 100644 index 00000000..2b40cc39 --- /dev/null +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/viewmodel/SettingsViewModel.kt @@ -0,0 +1,64 @@ +package com.v2ray.ang.viewmodel + +import android.app.Application +import android.content.SharedPreferences +import android.util.Log +import androidx.lifecycle.AndroidViewModel +import androidx.preference.PreferenceManager +import com.tencent.mmkv.MMKV +import com.v2ray.ang.AppConfig +import com.v2ray.ang.util.MmkvManager + +class SettingsViewModel(application: Application) : AndroidViewModel(application), SharedPreferences.OnSharedPreferenceChangeListener { + + private val settingsStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SETTING, MMKV.MULTI_PROCESS_MODE) } + + fun startListenPreferenceChange() { + PreferenceManager.getDefaultSharedPreferences(getApplication()).registerOnSharedPreferenceChangeListener(this) + } + + override fun onCleared() { + PreferenceManager.getDefaultSharedPreferences(getApplication()).unregisterOnSharedPreferenceChangeListener(this) + Log.i(AppConfig.ANG_PACKAGE, "Settings ViewModel is cleared") + super.onCleared() + } + + override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String?) { + Log.d(AppConfig.ANG_PACKAGE, "Observe settings changed: $key") + when(key) { + AppConfig.PREF_MODE, + AppConfig.PREF_VPN_DNS, + AppConfig.PREF_REMOTE_DNS, + AppConfig.PREF_DOMESTIC_DNS, + AppConfig.PREF_LOCAL_DNS_PORT, + AppConfig.PREF_SOCKS_PORT, + AppConfig.PREF_HTTP_PORT, + AppConfig.PREF_LOGLEVEL, + AppConfig.PREF_LANGUAGE, + AppConfig.PREF_ROUTING_DOMAIN_STRATEGY, + AppConfig.PREF_ROUTING_MODE, + AppConfig.PREF_V2RAY_ROUTING_AGENT, + AppConfig.PREF_V2RAY_ROUTING_BLOCKED, + AppConfig.PREF_V2RAY_ROUTING_DIRECT, -> { + settingsStorage?.encode(key, sharedPreferences.getString(key, "")) + } + AppConfig.PREF_SPEED_ENABLED, + AppConfig.PREF_PROXY_SHARING, + AppConfig.PREF_LOCAL_DNS_ENABLED, + AppConfig.PREF_FAKE_DNS_ENABLED, + AppConfig.PREF_ALLOW_INSECURE, + AppConfig.PREF_PREFER_IPV6, + AppConfig.PREF_PER_APP_PROXY, + AppConfig.PREF_BYPASS_APPS, + AppConfig.PREF_CONFIRM_REMOVE, -> { + settingsStorage?.encode(key, sharedPreferences.getBoolean(key, false)) + } + AppConfig.PREF_SNIFFING_ENABLED -> { + settingsStorage?.encode(key, sharedPreferences.getBoolean(key, true)) + } + AppConfig.PREF_PER_APP_PROXY_SET -> { + settingsStorage?.encode(key, sharedPreferences.getStringSet(key, setOf())) + } + } + } +} diff --git a/V2rayNG/app/src/main/res/anim/fade_in.xml b/V2rayNG/app/src/main/res/anim/fade_in.xml new file mode 100644 index 00000000..29e04320 --- /dev/null +++ b/V2rayNG/app/src/main/res/anim/fade_in.xml @@ -0,0 +1,6 @@ + + diff --git a/V2rayNG/app/src/main/res/anim/fade_out.xml b/V2rayNG/app/src/main/res/anim/fade_out.xml new file mode 100644 index 00000000..2b8bb1cb --- /dev/null +++ b/V2rayNG/app/src/main/res/anim/fade_out.xml @@ -0,0 +1,6 @@ + + diff --git a/V2rayNG/app/src/main/res/color/color_highlight_material.xml b/V2rayNG/app/src/main/res/color/color_highlight_material.xml new file mode 100644 index 00000000..5029d3e9 --- /dev/null +++ b/V2rayNG/app/src/main/res/color/color_highlight_material.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/V2rayNG/app/src/main/res/drawable-hdpi/ic_stat_direct.png b/V2rayNG/app/src/main/res/drawable-hdpi/ic_stat_direct.png new file mode 100644 index 0000000000000000000000000000000000000000..a9fe7cdf17edbe8d625fb9f1310d5b4cd50f74f8 GIT binary patch literal 964 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 literal 0 HcmV?d00001 diff --git a/V2rayNG/app/src/main/res/drawable-mdpi/ic_stat_proxy.png b/V2rayNG/app/src/main/res/drawable-mdpi/ic_stat_proxy.png new file mode 100644 index 0000000000000000000000000000000000000000..f0a39da4d852ae7519383479b51aa1ab8173a0d2 GIT binary patch literal 882 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x}o+VD!!mi71Ki^|4CM&(%vz$xlkv ztH>-n*TA>HIW;5GqpB!1xXLdi zxhgx^GDXSWj?1RP3TQxXYDuC(MQ%=Bu~mhw64+cTAR8pCucQE0Qj%?}lpi<;HsXMd|v6mX?Q~Mh#JgZ}0DSqAd%f~jg+OzIuN$9&*hYBSk z*lXDJn57#v6S&|0wV%EA&4T8~uVPF6?#kx>v&cFobni%#l?7W3^EKw|2Hyik39JQj z7VI%hzZwD)&fhO7Q2j7%mswivv2Xr2Ju29n${qg(rGGHe-1Yv?#|t06``(-=;iLAW z!74i4K-Se?=uKah{yTGv`Z*#O=NC+0#s8Wo#GZZQfxZ>*ul{}YdD4%qLFucntekVu z>#O4#E8SOo8>dwT-CYyM`)1|a;;hmF_p52=7A#kp^DKqqp4Wz4Pxa)JEPB^^HKyBU z8MAP`Nl5x|;#>Lb;>N{`^w_VMzse~I>U-I=JEB?X_|@&Nj_-&IFJCri{(QfMoSPek Q_&{03)78&qol`;+0EVDs82|tP literal 0 HcmV?d00001 diff --git a/V2rayNG/app/src/main/res/drawable-xhdpi/ic_stat_direct.png b/V2rayNG/app/src/main/res/drawable-xhdpi/ic_stat_direct.png new file mode 100644 index 0000000000000000000000000000000000000000..2dcd5d92fe5d9b87f88f4aac58b31af90cb5a0c6 GIT binary patch literal 1144 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezr3(FnVW(M3hAM`dB6B=jtV<?;Zqle1Gx6p~WYGxKbf-tXS8q>!0ns}yePYv5bpoSKp8QB{;0T;&&% zT$P<{nWAKG$7NGt1vDTxwIorYA~z?m*s8)-32d$vkPQ;nS5g2gDap1~as*kZ5aAo3 z;GAESs$i;TrkiYNY@uLosb^?vX<=ldqhMrUXr^yqsc&eaYhY+)U}!BPbD zzk=sFD+UIp7oIMTAsLNtZ}^`O4V5_l@qG3&7DdIDMi84WC)wn61}J=N*ksKeDrVnCDz})v-@>L`m;IJ z>GyoUb06Mo{Q1xCvvZ1{%{e^7F!@-|PkZCQa$}`?2cvfcKbF7Iw!iJff4yms?n}+C z+8qx(X_OwwNGct(27Oa;CH6ch;P{3m&>Hy7uzRai+e6+C^`< zf9!jeHo4)qYX7mEn%1eE-&S7{x$ohk)6F;0I%eS%4IJCL*TeQKYOV>eKHVT;@2UT< z_RVR}3ricGe^i$9-g3YH?oQ8LLLa2|9X{OoZGPf!gU(w*7v$4r`yMvB+*iJF$L&$z zj+?*l_AHjHi%&YN(Ro|Iptt)*hrjV3zl5(VP8PRcbi8v!vC{w3ltuGnm*4A1+SVx83V`w8-D(WnTA|HSmAD$MLV(Y_2D<}99q;>?f)Qr&%y|mrIT-T_M4TQd)z5&-`e|B%01WVxXJqs zTa8}7`B?bVX(cO&F&pB5B1x5_=Vk{xi2SsRo?!@zvjUEQa{VugsAeP#p@e_yLunodNlK=X`yWJ m-us6m*8N&+o_1!2-8&|;o3)b|zSfq53JgzIKbLh*2~7Zap7+cE literal 0 HcmV?d00001 diff --git a/V2rayNG/app/src/main/res/drawable-xhdpi/ic_stat_name.png b/V2rayNG/app/src/main/res/drawable-xhdpi/ic_stat_name.png new file mode 100644 index 0000000000000000000000000000000000000000..7af529f1d85e42104a63c67dedb6f7f98302a0c5 GIT binary patch literal 494 zcmVb??CPXIJW_8Dqh~eq|IL_R{@;Y1a_E%{{Yjr&p;y>sA9%c z0Oz!T1Lokrz`ToxIyDiX2N0Yv2Tyl=$czErnDg>j=DqsnCjiuD~al3$aU)G7FN$P}?jVf#s0kx93K0VMgRZ+07*qoM6N<$f{>-%z5oCK literal 0 HcmV?d00001 diff --git a/V2rayNG/app/src/main/res/drawable-xhdpi/ic_stat_proxy.png b/V2rayNG/app/src/main/res/drawable-xhdpi/ic_stat_proxy.png new file mode 100644 index 0000000000000000000000000000000000000000..36bba56050c5d3b177966109168c3d05ed3b29b8 GIT binary patch literal 1535 zcmX|>dr;C@6vq(>8MLy@OtbbE;iEMkKQj^pNk#J~YHFCImIC4<6%~ zwB5*U!_rN0Erx~09-5V9TlvWBVe5z*mF9#!cCl@`Gw0rO?mhGQ&iDLrkA#J8nvZrs z!(g!aP#`11bQ0$Rg*5F^aN{8u%#4}BVvE@!!4#e_9nVP+a`|{kdWPv82BXp?85~|J zUkq^hNhtyv#-P?>fRqFpCTe{MF(ku}pPUkyC*p6(3uW>0QhDSA4BZ<|l~7E8biSAa zNYc{;SriEk^NmX}<#X2r4DhW*oJzy^%n1VQkTAecDB=U_@gy7%T;~aFAmhRHWKVaG zwSYSjBoT;Y0_cGwf)pZ|LIi>D3*(IjsG@{KN(3X|yO^n>VUoq-3<`melaqtbS%(*j zk_aG~OePTB3GVJVQwJ_9S0Ls{aDuGWbBO;L41N|*l#(G%5ek4gCWkBBC8l98ggFty zcNLR8!ViC@;>U#e0@GMTCjS6*i2{Qmgb>4rl`NY)S&&>A>3EA?gZslODgqfEUV%NA zczN;iN1e^V@&|4MmRzR$(u9ho4C`sqTKSh%2V}mgHXRyl>|A`Baf(3*Q!0Rz_lYsk7s>oL??MzX^FZ6iO|hnFiVS`losiBgcf zXc;nE_6##yxVYN#v;EnBm$zGgbY$0gT~(0y9scspTul|&hQ%E%&hlL(#O;4YPD_>D z)$O%v+az<#Ty4#8Fyr^Q**L>vGNE%mx?Nao{YoFo_FgeGI`~Bj`sMo^T$9zHXc7h2 z8115HKM43(A5VID*OW{o;&4uV>x3-pug3gj{ zgGj2pLpc9Zt*gViWwNuL@SZY)aTj@VpX&{jm6^~yOY|{KXM2Pfum%;3iF?077h$-9 zY)7x4nl-Zuvan-_`1+5NyTI{zEEQCul-1?DcwCA6}RhyDZqMeKI z#hEHaz4>gqyp5?Z?XTZX`HyJevJ$~|k{JGwSMKwDw+shZ^X0ANZ`shue+-d96BaPYCI>M>s`mZ$~7?pxHgaiMQ9YOol`%&6kFu) zuB_SWx5&7nE^eQT_V-xCg5-olGmTx|Gu&9gWsST6idGahQN6R74hCas(YBrN0@0M+ zxVXMcIK{2SjNzdz)c)Jm5;gX;W7iAnX1cNh@l`HW&l?OV|EWwI4OA6gTzK8fb;CTr znV35bD%R`i%%@;iQe9euN{TX^Yl3YxABJ_qXKgpe1s4uICRJXjV58DXJJFk5hAk!x zNRspQakFPZ_YE!Cd20fIHwcbgb%57#QpanbRg#?&lZtlh1~Pe087}W!P_)N8gp>JC z>p1Je>aAN&Oi#XElKSbUZ#?;J-rn7ZRP(ck&7K}zEe_c`tUT&njDGRb_ov)K^8+>q zD-{N*_GaY`n>_*2H}I9D0y@#W-ENt|c-OuD+Wx)`Z1p;DpYFYi+1tH_`DWgo@B(?^b%OvXI@7jntv~?pC#u;@ytnoywHIV3o(; zmuv)|lmz<5a3}OmlMAicqy3Gf{<5cbS1^dg?#kQVr{LMe@GfR=X@hpCd?@Oz5@CiN zvVrtthrdQZ8W6Fd`^!@Fyg2n}6bSFM&6wwSy!Fn=!x8=Ywe01`Vjy_nsk6t?ks`J7 zK9XLgi=$~?&U9RtIST~@aaM#o-zg7N@Gq-l#c-5Q&|fZHeU Z(}Se?-wykKs-F9|A^%XuCExhH{{bdelY9UG literal 0 HcmV?d00001 diff --git a/V2rayNG/app/src/main/res/drawable-xxhdpi/donate.png b/V2rayNG/app/src/main/res/drawable-xxhdpi/donate.png new file mode 100644 index 0000000000000000000000000000000000000000..8825c5320f2146691887b523c07b81874609d6db GIT binary patch literal 3237 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 literal 0 HcmV?d00001 diff --git a/V2rayNG/app/src/main/res/drawable-xxhdpi/ic_stat_direct.png b/V2rayNG/app/src/main/res/drawable-xxhdpi/ic_stat_direct.png new file mode 100644 index 0000000000000000000000000000000000000000..f8bd77ef59303000781a681090983bed2802c318 GIT binary patch literal 1507 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY1|&n@Zgyv2VD!!mi71Ki^|4CM&(%vz$xlkv ztH>-n*TA>HIW;5GqpB!1xXLdi zxhgx^GDXSWj?1RP3TQxXYDuC(MQ%=Bu~mhw64+cTAR8pCucQE0Qj%?}lpi<;HsXMd|v6mX??P_N}7wT123|k?ULFR z@J+!b{LG6w^G&y#10xk<<(yOxp6Xh#Nuh^hSAe8qkVtE<>Qu|euM&MNPR*IQ_uH-X z`MGcObJ~on*WUkiKL6XUcmEeIMFx4lg1FiR_+0I`sc+lJ>!0B5Rn5ACqdie$X0V`mPnJ$Yw4R9ghz@hZOgJ3mfcO||Nazi0mcBYl%f-5)#N znG*QF>`v&CgjJ4vm+|{Q?&9fX7cprsX;xF4wtmve3eNQO44s3Arf{uf%N5^!^Ms$Y zL1g*a+wmsPr||SHFNn=r8q$34aG`|Ezdg=#9PJR-PF%@tjBBG3!rfDtU{Sti4^fP^)>>vx+rG zzVP&ZKbkjx>z<59CiPlzN9`n~UKh^e3VY{U-Sq3@E}LSzqmn@>ul$fxe|Y;(LD{7i!;FnP`c6GmpVR&R)8U&_t66(){jN?cR(Ee}fl-cdo5IHaqXa<{kT{Gn(97 z@N~zf>y9Q@7lcYB@SQbz;d>_dpKjh#ld}uT3;SlgxxfMRZq-MV5AWmHjV3LV_`>No zy*|W>@q4nc+@ZLcRhc~#yEa)U^FBQ-aHz*{OYpjziY*K82E}k)ee1G*{sP}e3vVv# z`jU3@YyK|RJ2$4)d0+BwHhYv(6T9$P#^dWYA9>&Q7&YxR%IL0;`r+&QR#tVMjojp@ zjpA&)4}5Auigz0yO?3$p^SaCSw{_=_1>V2*_ZQc5#T~G9Wqn<@`eOfft|_ydgbQ~( zdcBCz@Z~~|?juJ!escbLJlXK%%z7c9qHt4k&*qS8F+V6Gdlh!HD*skRA zzN1z$H&;5yvc-0%3%$;H>!u)UA#lEQ*Uj8!u}2$)zFOITzMB}fT>7rb3qM2kojhOP zEqpZd`-+lmv1*C(lq>(&F0we5%yC`facEE|Tkjho@51C|_s%{NshRM@v`r~T`1oXH zu4(@R&(y`vmWfqAHe;H+>^!sSsyBTza+z0{*Q3Db6-;`j=~m5AO$+&7Q7)F6*2UngFoDol5`! literal 0 HcmV?d00001 diff --git a/V2rayNG/app/src/main/res/drawable-xxhdpi/ic_stat_name.png b/V2rayNG/app/src/main/res/drawable-xxhdpi/ic_stat_name.png new file mode 100644 index 0000000000000000000000000000000000000000..0ac2448f1da43e33c6e2395638a202a7be944746 GIT binary patch literal 698 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 literal 0 HcmV?d00001 diff --git a/V2rayNG/app/src/main/res/drawable-xxhdpi/side_nav_bar.xml b/V2rayNG/app/src/main/res/drawable-xxhdpi/side_nav_bar.xml new file mode 100644 index 00000000..6d81870b --- /dev/null +++ b/V2rayNG/app/src/main/res/drawable-xxhdpi/side_nav_bar.xml @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/V2rayNG/app/src/main/res/drawable-xxxhdpi/ic_stat_direct.png b/V2rayNG/app/src/main/res/drawable-xxxhdpi/ic_stat_direct.png new file mode 100644 index 0000000000000000000000000000000000000000..406a0c76906cc821cc56ddfa2f6176b32eb5736a GIT binary patch literal 1915 zcmYjSd05if8vgwtpiJ|orVgeOA;nzTL{LZzw*pr*aw|(xP*TAKgUs?r<(QLYI+~`r zC6+o_jd-oAQ<81gxMhnM)5PjJ*`khSi>6(i+cVFc=Q-y)-*>*}eb4*;aZWbRe~Xa; z+5i9mBQD1?NFO5?=TfA8eO@eg1_01U%ny}?`uVb=qzNQJv~;(Sq)15Ap96r)CPktk zDo!ZF>=wp|C9c?s);26g9PNq?q5Dz%5# zBsq;FO^zW`nM@{`;y`wAAnH4aDf=Wcfr2PWS-S}Fe+*AyN>s8qQ6`p3FpHRi-O^N< zD;7&$Bq4ue(aR(MrB9#y<&aRKw?<$DjLm55M9u%n z3dO@aljA(E~{)k7G#AHJ5L()5}+?;Itk6EyswijHaaO=K52M40Qh_! zdg>wHSkMb_@nilvYWvHtCX~jNWyW?^D9#>Z7a-?$z$!_=D9~T5Jpl|f1fk(2F^=b9 z{V0Q9fDoP*&1u`_EnrXQj81EG>^0`k&si=Qs)q5IPVAtV!JoPjO84}`rP>{~oQz9m z0{}Nd*$<__GDDF&3Ti6z3)|6{Iuymi*_C&+@M?DtpLfrDjs9+{C9Kb%*lU8p`Odd@ z*|YSmSM_#0Or(`mu`;YLR!*g2=3Ca6fCu!IA^4aSNusHHU}OD$c@j74BzP4ZHYPlbh0QUa&)P=pe?`ivKClBuVF`B);Br#jHPV-% zu0#}G;AeB_bS*UreXSL=t&bSqsN?vq-;cA{wpYr^w`j-lID=@VRa){1hk)23@fHp3F= zg!^}4gQDT%sBwK^N!rMVb7d95m9rtzHBNqgWUgim^!5Ml>8AWUkN3Uox985C%b~k3T6qwwikel zetQbCzzm&a!*JRYVqbDW=GBO@T{wDt6KIu$DC~j9ZsN>>#Q`F49-4>~4|e2Dn(uJ? zZ#8??_=QbOE`E-qFz998o-qdHy=kshf4r*5n`Ceg!|qPa3~cFp&Dzqo0e3vXkDR?Z z9aaKab-jIzyft;h?UDQ+9cUeEu;o$9vIGdCicUFW2!vrUxaD-x{#5dZ6@Y(HYkVUe)n*!q&LH=A+mBf>7TZwJM(hY-=5lnvNlk9$4;3J S#ak@?nOrY_&x`JnhyDjG=r0EV literal 0 HcmV?d00001 diff --git a/V2rayNG/app/src/main/res/drawable-xxxhdpi/ic_stat_name.png b/V2rayNG/app/src/main/res/drawable-xxxhdpi/ic_stat_name.png new file mode 100644 index 0000000000000000000000000000000000000000..dd313da08ca2d71c7f758a6020acea3eaea16434 GIT binary patch literal 867 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 literal 0 HcmV?d00001 diff --git a/V2rayNG/app/src/main/res/drawable/ic_action_done.xml b/V2rayNG/app/src/main/res/drawable/ic_action_done.xml new file mode 100644 index 00000000..33a117f6 --- /dev/null +++ b/V2rayNG/app/src/main/res/drawable/ic_action_done.xml @@ -0,0 +1,9 @@ + + + diff --git a/V2rayNG/app/src/main/res/drawable/ic_add_white_24dp.xml b/V2rayNG/app/src/main/res/drawable/ic_add_white_24dp.xml new file mode 100644 index 00000000..b9b8eca8 --- /dev/null +++ b/V2rayNG/app/src/main/res/drawable/ic_add_white_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/V2rayNG/app/src/main/res/drawable/ic_attach_money_black_24dp.xml b/V2rayNG/app/src/main/res/drawable/ic_attach_money_black_24dp.xml new file mode 100644 index 00000000..b520fc98 --- /dev/null +++ b/V2rayNG/app/src/main/res/drawable/ic_attach_money_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/V2rayNG/app/src/main/res/drawable/ic_attach_money_white_24dp.xml b/V2rayNG/app/src/main/res/drawable/ic_attach_money_white_24dp.xml new file mode 100644 index 00000000..2b65f0c6 --- /dev/null +++ b/V2rayNG/app/src/main/res/drawable/ic_attach_money_white_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/V2rayNG/app/src/main/res/drawable/ic_close_grey_800_24dp.xml b/V2rayNG/app/src/main/res/drawable/ic_close_grey_800_24dp.xml new file mode 100644 index 00000000..8a9a5226 --- /dev/null +++ b/V2rayNG/app/src/main/res/drawable/ic_close_grey_800_24dp.xml @@ -0,0 +1,10 @@ + + + + diff --git a/V2rayNG/app/src/main/res/drawable/ic_cloud_download_white_24dp.xml b/V2rayNG/app/src/main/res/drawable/ic_cloud_download_white_24dp.xml new file mode 100644 index 00000000..171a9b30 --- /dev/null +++ b/V2rayNG/app/src/main/res/drawable/ic_cloud_download_white_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/V2rayNG/app/src/main/res/drawable/ic_copy_white.xml b/V2rayNG/app/src/main/res/drawable/ic_copy_white.xml new file mode 100644 index 00000000..e50927b2 --- /dev/null +++ b/V2rayNG/app/src/main/res/drawable/ic_copy_white.xml @@ -0,0 +1,9 @@ + + + diff --git a/V2rayNG/app/src/main/res/drawable/ic_delete_black_24dp.xml b/V2rayNG/app/src/main/res/drawable/ic_delete_black_24dp.xml new file mode 100644 index 00000000..2f5557af --- /dev/null +++ b/V2rayNG/app/src/main/res/drawable/ic_delete_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/V2rayNG/app/src/main/res/drawable/ic_delete_white_24dp.xml b/V2rayNG/app/src/main/res/drawable/ic_delete_white_24dp.xml new file mode 100644 index 00000000..ab38bb6d --- /dev/null +++ b/V2rayNG/app/src/main/res/drawable/ic_delete_white_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/V2rayNG/app/src/main/res/drawable/ic_description_black_24dp.xml b/V2rayNG/app/src/main/res/drawable/ic_description_black_24dp.xml new file mode 100644 index 00000000..38c33351 --- /dev/null +++ b/V2rayNG/app/src/main/res/drawable/ic_description_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/V2rayNG/app/src/main/res/drawable/ic_description_white_24dp.xml b/V2rayNG/app/src/main/res/drawable/ic_description_white_24dp.xml new file mode 100644 index 00000000..7e0d28e3 --- /dev/null +++ b/V2rayNG/app/src/main/res/drawable/ic_description_white_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/V2rayNG/app/src/main/res/drawable/ic_edit_black_24dp.xml b/V2rayNG/app/src/main/res/drawable/ic_edit_black_24dp.xml new file mode 100644 index 00000000..2ab2fb75 --- /dev/null +++ b/V2rayNG/app/src/main/res/drawable/ic_edit_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/V2rayNG/app/src/main/res/drawable/ic_fab_check.xml b/V2rayNG/app/src/main/res/drawable/ic_fab_check.xml new file mode 100644 index 00000000..54f825f8 --- /dev/null +++ b/V2rayNG/app/src/main/res/drawable/ic_fab_check.xml @@ -0,0 +1,9 @@ + + + diff --git a/V2rayNG/app/src/main/res/drawable/ic_feedback_white_24dp.xml b/V2rayNG/app/src/main/res/drawable/ic_feedback_white_24dp.xml new file mode 100644 index 00000000..3e08fae2 --- /dev/null +++ b/V2rayNG/app/src/main/res/drawable/ic_feedback_white_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/V2rayNG/app/src/main/res/drawable/ic_file_white_24dp.xml b/V2rayNG/app/src/main/res/drawable/ic_file_white_24dp.xml new file mode 100644 index 00000000..f7f9df2e --- /dev/null +++ b/V2rayNG/app/src/main/res/drawable/ic_file_white_24dp.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/V2rayNG/app/src/main/res/drawable/ic_image_photo.xml b/V2rayNG/app/src/main/res/drawable/ic_image_photo.xml new file mode 100644 index 00000000..c701a63f --- /dev/null +++ b/V2rayNG/app/src/main/res/drawable/ic_image_photo.xml @@ -0,0 +1,9 @@ + + + diff --git a/V2rayNG/app/src/main/res/drawable/ic_info_black_24dp.xml b/V2rayNG/app/src/main/res/drawable/ic_info_black_24dp.xml new file mode 100644 index 00000000..34b8202e --- /dev/null +++ b/V2rayNG/app/src/main/res/drawable/ic_info_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/V2rayNG/app/src/main/res/drawable/ic_logcat_white_24dp.xml b/V2rayNG/app/src/main/res/drawable/ic_logcat_white_24dp.xml new file mode 100644 index 00000000..e7d3eb31 --- /dev/null +++ b/V2rayNG/app/src/main/res/drawable/ic_logcat_white_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/V2rayNG/app/src/main/res/drawable/ic_outline_filter_alt_white_24.xml b/V2rayNG/app/src/main/res/drawable/ic_outline_filter_alt_white_24.xml new file mode 100644 index 00000000..6cb82eff --- /dev/null +++ b/V2rayNG/app/src/main/res/drawable/ic_outline_filter_alt_white_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/V2rayNG/app/src/main/res/drawable/ic_qu_scan_black_24dp.xml b/V2rayNG/app/src/main/res/drawable/ic_qu_scan_black_24dp.xml new file mode 100644 index 00000000..e0b32bf4 --- /dev/null +++ b/V2rayNG/app/src/main/res/drawable/ic_qu_scan_black_24dp.xml @@ -0,0 +1,19 @@ + + + + + + \ No newline at end of file diff --git a/V2rayNG/app/src/main/res/drawable/ic_qu_settings_black_24dp.xml b/V2rayNG/app/src/main/res/drawable/ic_qu_settings_black_24dp.xml new file mode 100644 index 00000000..063171a3 --- /dev/null +++ b/V2rayNG/app/src/main/res/drawable/ic_qu_settings_black_24dp.xml @@ -0,0 +1,13 @@ + + + + + diff --git a/V2rayNG/app/src/main/res/drawable/ic_qu_switch_black_24dp.xml b/V2rayNG/app/src/main/res/drawable/ic_qu_switch_black_24dp.xml new file mode 100644 index 00000000..97f8859d --- /dev/null +++ b/V2rayNG/app/src/main/res/drawable/ic_qu_switch_black_24dp.xml @@ -0,0 +1,18 @@ + + + + + + + + \ No newline at end of file diff --git a/V2rayNG/app/src/main/res/drawable/ic_rounded_corner_grey.xml b/V2rayNG/app/src/main/res/drawable/ic_rounded_corner_grey.xml new file mode 100644 index 00000000..50af166d --- /dev/null +++ b/V2rayNG/app/src/main/res/drawable/ic_rounded_corner_grey.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/V2rayNG/app/src/main/res/drawable/ic_rounded_corner_theme.xml b/V2rayNG/app/src/main/res/drawable/ic_rounded_corner_theme.xml new file mode 100644 index 00000000..0463d8bf --- /dev/null +++ b/V2rayNG/app/src/main/res/drawable/ic_rounded_corner_theme.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/V2rayNG/app/src/main/res/drawable/ic_save_white_24dp.xml b/V2rayNG/app/src/main/res/drawable/ic_save_white_24dp.xml new file mode 100644 index 00000000..a7a81a25 --- /dev/null +++ b/V2rayNG/app/src/main/res/drawable/ic_save_white_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/V2rayNG/app/src/main/res/drawable/ic_scan_black_24dp.xml b/V2rayNG/app/src/main/res/drawable/ic_scan_black_24dp.xml new file mode 100644 index 00000000..a31063ba --- /dev/null +++ b/V2rayNG/app/src/main/res/drawable/ic_scan_black_24dp.xml @@ -0,0 +1,19 @@ + + + + + + \ No newline at end of file diff --git a/V2rayNG/app/src/main/res/drawable/ic_select_all_white_24dp.xml b/V2rayNG/app/src/main/res/drawable/ic_select_all_white_24dp.xml new file mode 100644 index 00000000..a24c01bf --- /dev/null +++ b/V2rayNG/app/src/main/res/drawable/ic_select_all_white_24dp.xml @@ -0,0 +1,10 @@ + + + + diff --git a/V2rayNG/app/src/main/res/drawable/ic_settings_white_24dp.xml b/V2rayNG/app/src/main/res/drawable/ic_settings_white_24dp.xml new file mode 100644 index 00000000..ce997a72 --- /dev/null +++ b/V2rayNG/app/src/main/res/drawable/ic_settings_white_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/V2rayNG/app/src/main/res/drawable/ic_share_black_24dp.xml b/V2rayNG/app/src/main/res/drawable/ic_share_black_24dp.xml new file mode 100644 index 00000000..e3fe874d --- /dev/null +++ b/V2rayNG/app/src/main/res/drawable/ic_share_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/V2rayNG/app/src/main/res/drawable/ic_share_white_24dp.xml b/V2rayNG/app/src/main/res/drawable/ic_share_white_24dp.xml new file mode 100644 index 00000000..90406663 --- /dev/null +++ b/V2rayNG/app/src/main/res/drawable/ic_share_white_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/V2rayNG/app/src/main/res/drawable/ic_shortcut_background.xml b/V2rayNG/app/src/main/res/drawable/ic_shortcut_background.xml new file mode 100644 index 00000000..a62b720b --- /dev/null +++ b/V2rayNG/app/src/main/res/drawable/ic_shortcut_background.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/V2rayNG/app/src/main/res/drawable/ic_subscriptions_black_24dp.xml b/V2rayNG/app/src/main/res/drawable/ic_subscriptions_black_24dp.xml new file mode 100644 index 00000000..6f0ed455 --- /dev/null +++ b/V2rayNG/app/src/main/res/drawable/ic_subscriptions_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/V2rayNG/app/src/main/res/drawable/ic_subscriptions_white_24dp.xml b/V2rayNG/app/src/main/res/drawable/ic_subscriptions_white_24dp.xml new file mode 100644 index 00000000..bc20a83a --- /dev/null +++ b/V2rayNG/app/src/main/res/drawable/ic_subscriptions_white_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/V2rayNG/app/src/main/res/drawable/ic_whatshot_black_24dp.xml b/V2rayNG/app/src/main/res/drawable/ic_whatshot_black_24dp.xml new file mode 100644 index 00000000..1cbc037f --- /dev/null +++ b/V2rayNG/app/src/main/res/drawable/ic_whatshot_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/V2rayNG/app/src/main/res/drawable/ic_whatshot_white_24dp.xml b/V2rayNG/app/src/main/res/drawable/ic_whatshot_white_24dp.xml new file mode 100644 index 00000000..ad460f3c --- /dev/null +++ b/V2rayNG/app/src/main/res/drawable/ic_whatshot_white_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/V2rayNG/app/src/main/res/drawable/nav_header_bg.png b/V2rayNG/app/src/main/res/drawable/nav_header_bg.png new file mode 100644 index 0000000000000000000000000000000000000000..82cb00af9a4362963b6bbfbe9127519ef88a1a74 GIT binary patch literal 11934 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/V2rayNG/app/src/main/res/layout/activity_logcat.xml b/V2rayNG/app/src/main/res/layout/activity_logcat.xml new file mode 100644 index 00000000..06916be1 --- /dev/null +++ b/V2rayNG/app/src/main/res/layout/activity_logcat.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + diff --git a/V2rayNG/app/src/main/res/layout/activity_main.xml b/V2rayNG/app/src/main/res/layout/activity_main.xml new file mode 100644 index 00000000..5f3c9e95 --- /dev/null +++ b/V2rayNG/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/V2rayNG/app/src/main/res/layout/activity_none.xml b/V2rayNG/app/src/main/res/layout/activity_none.xml new file mode 100644 index 00000000..d7681698 --- /dev/null +++ b/V2rayNG/app/src/main/res/layout/activity_none.xml @@ -0,0 +1,5 @@ + + + diff --git a/V2rayNG/app/src/main/res/layout/activity_routing_settings.xml b/V2rayNG/app/src/main/res/layout/activity_routing_settings.xml new file mode 100644 index 00000000..7052cc5f --- /dev/null +++ b/V2rayNG/app/src/main/res/layout/activity_routing_settings.xml @@ -0,0 +1,17 @@ + + + + + + + diff --git a/V2rayNG/app/src/main/res/layout/activity_server_custom_config.xml b/V2rayNG/app/src/main/res/layout/activity_server_custom_config.xml new file mode 100644 index 00000000..2f1aefc9 --- /dev/null +++ b/V2rayNG/app/src/main/res/layout/activity_server_custom_config.xml @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/V2rayNG/app/src/main/res/layout/activity_server_shadowsocks.xml b/V2rayNG/app/src/main/res/layout/activity_server_shadowsocks.xml new file mode 100644 index 00000000..dba1c142 --- /dev/null +++ b/V2rayNG/app/src/main/res/layout/activity_server_shadowsocks.xml @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/V2rayNG/app/src/main/res/layout/activity_server_socks.xml b/V2rayNG/app/src/main/res/layout/activity_server_socks.xml new file mode 100644 index 00000000..5f34c37c --- /dev/null +++ b/V2rayNG/app/src/main/res/layout/activity_server_socks.xml @@ -0,0 +1,130 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/V2rayNG/app/src/main/res/layout/activity_server_trojan.xml b/V2rayNG/app/src/main/res/layout/activity_server_trojan.xml new file mode 100644 index 00000000..757a6c78 --- /dev/null +++ b/V2rayNG/app/src/main/res/layout/activity_server_trojan.xml @@ -0,0 +1,215 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/V2rayNG/app/src/main/res/layout/activity_server_vless.xml b/V2rayNG/app/src/main/res/layout/activity_server_vless.xml new file mode 100644 index 00000000..8e70bd02 --- /dev/null +++ b/V2rayNG/app/src/main/res/layout/activity_server_vless.xml @@ -0,0 +1,236 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/V2rayNG/app/src/main/res/layout/activity_server_vmess.xml b/V2rayNG/app/src/main/res/layout/activity_server_vmess.xml new file mode 100644 index 00000000..ebfeb7e1 --- /dev/null +++ b/V2rayNG/app/src/main/res/layout/activity_server_vmess.xml @@ -0,0 +1,235 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/V2rayNG/app/src/main/res/layout/activity_settings.xml b/V2rayNG/app/src/main/res/layout/activity_settings.xml new file mode 100644 index 00000000..f674f2ae --- /dev/null +++ b/V2rayNG/app/src/main/res/layout/activity_settings.xml @@ -0,0 +1,8 @@ + + \ No newline at end of file diff --git a/V2rayNG/app/src/main/res/layout/activity_sub_edit.xml b/V2rayNG/app/src/main/res/layout/activity_sub_edit.xml new file mode 100644 index 00000000..770f93e4 --- /dev/null +++ b/V2rayNG/app/src/main/res/layout/activity_sub_edit.xml @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/V2rayNG/app/src/main/res/layout/activity_sub_setting.xml b/V2rayNG/app/src/main/res/layout/activity_sub_setting.xml new file mode 100644 index 00000000..bb87f387 --- /dev/null +++ b/V2rayNG/app/src/main/res/layout/activity_sub_setting.xml @@ -0,0 +1,22 @@ + + + + + + + + + + \ No newline at end of file diff --git a/V2rayNG/app/src/main/res/layout/activity_tasker.xml b/V2rayNG/app/src/main/res/layout/activity_tasker.xml new file mode 100644 index 00000000..5c798c37 --- /dev/null +++ b/V2rayNG/app/src/main/res/layout/activity_tasker.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/V2rayNG/app/src/main/res/layout/dialog_config_filter.xml b/V2rayNG/app/src/main/res/layout/dialog_config_filter.xml new file mode 100644 index 00000000..115371df --- /dev/null +++ b/V2rayNG/app/src/main/res/layout/dialog_config_filter.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/V2rayNG/app/src/main/res/layout/fragment_routing_settings.xml b/V2rayNG/app/src/main/res/layout/fragment_routing_settings.xml new file mode 100644 index 00000000..45f2a305 --- /dev/null +++ b/V2rayNG/app/src/main/res/layout/fragment_routing_settings.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + diff --git a/V2rayNG/app/src/main/res/layout/item_qrcode.xml b/V2rayNG/app/src/main/res/layout/item_qrcode.xml new file mode 100644 index 00000000..3c53162a --- /dev/null +++ b/V2rayNG/app/src/main/res/layout/item_qrcode.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/V2rayNG/app/src/main/res/layout/item_recycler_bypass_list.xml b/V2rayNG/app/src/main/res/layout/item_recycler_bypass_list.xml new file mode 100644 index 00000000..03ad10a7 --- /dev/null +++ b/V2rayNG/app/src/main/res/layout/item_recycler_bypass_list.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/V2rayNG/app/src/main/res/layout/item_recycler_footer.xml b/V2rayNG/app/src/main/res/layout/item_recycler_footer.xml new file mode 100644 index 00000000..268f7690 --- /dev/null +++ b/V2rayNG/app/src/main/res/layout/item_recycler_footer.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/V2rayNG/app/src/main/res/layout/item_recycler_main.xml b/V2rayNG/app/src/main/res/layout/item_recycler_main.xml new file mode 100644 index 00000000..acae822a --- /dev/null +++ b/V2rayNG/app/src/main/res/layout/item_recycler_main.xml @@ -0,0 +1,189 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/V2rayNG/app/src/main/res/layout/item_recycler_sub_setting.xml b/V2rayNG/app/src/main/res/layout/item_recycler_sub_setting.xml new file mode 100644 index 00000000..a4467e27 --- /dev/null +++ b/V2rayNG/app/src/main/res/layout/item_recycler_sub_setting.xml @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/V2rayNG/app/src/main/res/layout/item_recycler_user_asset.xml b/V2rayNG/app/src/main/res/layout/item_recycler_user_asset.xml new file mode 100644 index 00000000..54651771 --- /dev/null +++ b/V2rayNG/app/src/main/res/layout/item_recycler_user_asset.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/V2rayNG/app/src/main/res/layout/nav_header.xml b/V2rayNG/app/src/main/res/layout/nav_header.xml new file mode 100644 index 00000000..2f5d3ed6 --- /dev/null +++ b/V2rayNG/app/src/main/res/layout/nav_header.xml @@ -0,0 +1,25 @@ + + + + + + + diff --git a/V2rayNG/app/src/main/res/layout/nav_toolbar.xml b/V2rayNG/app/src/main/res/layout/nav_toolbar.xml new file mode 100644 index 00000000..262787df --- /dev/null +++ b/V2rayNG/app/src/main/res/layout/nav_toolbar.xml @@ -0,0 +1,13 @@ + + + + + + \ No newline at end of file diff --git a/V2rayNG/app/src/main/res/layout/nav_view.xml b/V2rayNG/app/src/main/res/layout/nav_view.xml new file mode 100644 index 00000000..34fd03a3 --- /dev/null +++ b/V2rayNG/app/src/main/res/layout/nav_view.xml @@ -0,0 +1,9 @@ + + \ No newline at end of file diff --git a/V2rayNG/app/src/main/res/layout/preference_with_help_link.xml b/V2rayNG/app/src/main/res/layout/preference_with_help_link.xml new file mode 100644 index 00000000..ec6e6423 --- /dev/null +++ b/V2rayNG/app/src/main/res/layout/preference_with_help_link.xml @@ -0,0 +1,11 @@ + +