Compare commits

..

286 Commits

Author SHA1 Message Date
2dust 3923b379a6 Merge pull request #1563 from yuhan6665/ss-fix
Fix ss2022 import for old base64 format
2022-06-26 08:43:46 +08:00
yuhan6665 e23e9c48a4 Fix ss2022 import for old base64 format
Old format has userinfo = null, will throw NPE
2022-06-24 23:43:37 -04:00
2dust 1822613985 add xchacha20 2022-06-24 23:42:08 -04:00
2dust e8ec9ad17c Merge pull request #1555 from yuhan6665/ss-fix
Fix ss2022 import for multi clients format
2022-06-19 08:39:08 +08:00
yuhan6665 b4d970552e Fix ss2022 import for multi clients format 2022-06-17 20:30:39 -04:00
2dust c6579556c4 Support 2022-blake3 share 2022-06-17 20:25:43 -04:00
2dust 5a36844036 add Shadowsocks-2022 for xray-core 2022-06-17 20:25:43 -04:00
2dust 2b21203c53 Update AngConfigManager.kt 2022-06-17 20:24:54 -04:00
2dust a4558cf954 Add support for sharing ipv6 addresses 2022-06-17 20:24:29 -04:00
2dust 5c2f11fb19 Merge pull request #1547 from emp3826/master
update dependences
2022-06-10 20:32:32 +08:00
cjhhz d7f3d0df80 update dependences 2022-06-10 13:08:20 +08:00
2dust c5e2ca0d8d Merge pull request #1495 from cuynu/master
Improve Vietnamese translation
2022-05-08 18:31:12 +08:00
Cuynu 252bea2432 Add files via upload 2022-05-07 18:25:53 +07:00
2dust f58ed74a4a add external res 2022-05-07 15:29:36 +08:00
2dust 8ed17f9da0 Merge pull request #1446 from pozhiloy-enotik/patch-1
Allow external use
2022-04-15 07:21:23 +08:00
pozhiloy-enotik 1bbfda64fe Allow external use 2022-04-12 16:18:34 +03:00
2dust 5cadef8b2a Merge pull request #1416 from yuhan6665/master
Refactor and remove Kotlin synthetics
2022-03-20 10:16:16 +08:00
yuhan6665 5b92158353 Update readme 2022-03-19 21:27:26 -04:00
yuhan6665 73706c1d0f Refactor and remove Kotlin synthetics 2022-03-19 21:21:44 -04:00
2dust c633a267ff Merge pull request #1393 from yuhan6665/master
Update readme and sync project
2022-02-27 08:55:34 +08:00
yuhan6665 c8e5bf4f9f Update project 2022-02-26 12:15:46 -05:00
2dust bb91b3baa9 onModeHelpClicked 2022-02-26 12:05:02 -05:00
2dust 35dc8d661c Update AndroidManifest.xml 2022-02-26 12:05:02 -05:00
2dust f61f30fdc4 change sdk version 2022-02-26 12:05:02 -05:00
2dust a54c327a07 Merge pull request #1374 from Kaitul/patch-1
Update strings.xml
2022-02-26 19:21:22 +08:00
人工知能 87d2854fb2 Update strings.xml 2022-02-15 12:48:44 +08:00
2dust e9b1052ef7 Merge pull request #1336 from yuhan6665/remove-alterid
Remove alterid
2022-01-09 18:04:08 +08:00
yuhan6665 c6560e9bc0 Remove alterId field
VmessQRCode "aid" is kept for backwards compatibility and always set to 0
Since 2022, new *ray server will reject alterId > 0.
If it is enabled and set specifically > 0, for v2fly server v4.28.1+ and Xray server v1.0.0+, client with either 0 or above 0 can connect
For older v2ray server, the default config will not work, user has to use custom config to set alterId. This is NOT recommended for security reason
2022-01-07 18:30:11 -05:00
yuhan6665 a44ca16aa7 Fix some compile warnings 2022-01-07 18:30:11 -05:00
2dust b28c7f54ff Merge pull request #1333 from xiandanin/master
更新xray-core版本
2022-01-06 19:37:50 +08:00
xiandanin 61a155b799 更新xray-core版本 2022-01-05 20:25:45 +08:00
2dust 10cc117e81 Merge pull request #1268 from yuhan6665/new-fakedns
New config format
2021-10-30 19:02:26 +08:00
yuhan6665 ce9bed2e1f New config format 2021-10-29 20:37:12 -04:00
2dust bb8f7de6eb Merge pull request #1263 from yuhan6665/new-dns
Add support for DOT and DOQ
2021-10-24 19:51:47 +08:00
yuhan6665 a1ed4836c7 Add support for DOT and DOQ 2021-10-23 20:24:08 -04:00
2dust 2d5351ec9e Merge pull request #1218 from yuhan6665/fix-sniffing2
Fix sniffing default value again
2021-09-07 20:02:52 +08:00
yuhan6665 d9fb121d67 Fix sniffing default value again
I made a mistake, thought it would return null when key is not in storage,
But it is found default "true" must be specified
2021-09-03 18:15:06 -04:00
2dust 62d0951a24 Merge pull request #1208 from yuhan6665/fix-viewpager
Fix an issue with ViewPage2 and menu bar
2021-08-28 17:06:21 +08:00
2dust d6605cc866 Merge pull request #1204 from douo/master
Add support in subscription url for http basic authentication.
2021-08-28 17:06:10 +08:00
yuhan6665 1fc493d879 Fix an issue with ViewPage2 and menu bar 2021-08-27 17:48:45 -04:00
tiou bdc27dd180 Use built-in urlDecode. 2021-08-23 16:58:48 +08:00
tiou e257c4cb56 Subscription support http basic authentication in url. 2021-08-23 16:49:35 +08:00
2dust e9d2ed98af Merge pull request #1198 from yuhan6665/user-agent
Set user agent to v2rayNG/version when update subscription
2021-08-21 19:19:20 +08:00
yuhan6665 901a82eb54 Set user agent to v2rayNG/version when update subscription 2021-08-20 18:29:34 -04:00
2dust 26cc29944b Merge pull request #1183 from yuhan6665/fix-drag
Fix a bug when user delete item right after drag it
2021-08-17 20:20:56 +08:00
yuhan6665 7052546f2b Fix a bug when user delete item right after drag it 2021-08-13 18:04:43 -04:00
2dust ae2b10ede7 Merge pull request #1177 from yuhan6665/fix-base64
Fix parsing for some loosely formatted base64
2021-08-10 13:09:35 +08:00
2dust 0df051e640 Merge pull request #1175 from jiuqianyuan/master
Add prompts for update subscription error
2021-08-10 13:06:14 +08:00
jiuqianyuan 79aa86a402 Update MainActivity.kt 2021-08-09 17:22:49 +08:00
yuhan6665 7bf32c2b30 Fix parsing for some loosely formatted base64 2021-08-07 17:35:09 -04:00
jiuqianyuan ceb1c55e49 Fix missing prompts for update subscription error 2021-08-07 08:08:45 +08:00
2dust 4ab3134eac Merge pull request #1164 from yuhan6665/update
Fix compile warnings
2021-07-31 15:38:19 +08:00
yuhan6665 0391685d42 Fix some more compile warnings 2021-07-30 18:31:29 -04:00
yuhan6665 6482253697 Fix some compile warnings
remove flatDir
2021-07-30 18:31:29 -04:00
yuhan6665 c033358126 Fix some compile warnings
Migrate to Androidx Activity Result APIs
2021-07-30 18:31:29 -04:00
yuhan6665 0b52c78b72 Fix some compile warnings
Some deprecated methods
2021-07-30 18:31:29 -04:00
yuhan6665 a2a7d790e7 Fix some compile warnings
Migrate to viewpager2
2021-07-30 18:31:29 -04:00
yuhan6665 c7c6564624 Fix some compile warnings
upper case and lower case
2021-07-30 18:31:29 -04:00
2dust f7d534fc00 Merge pull request #1153 from yuhan6665/update
Migrate to View Binding
2021-07-26 08:07:28 +08:00
yuhan6665 ecb1d58e7a Migrate to View Binding
Use view binding to simplify some code
2021-07-23 18:17:17 -04:00
2dust 54a191d181 Merge pull request #1147 from yuhan6665/update
Update project dependencies
2021-07-20 08:12:30 +08:00
2dust 90b6979f94 Merge pull request #1146 from jiuqianyuan/master
fix update subscription error
2021-07-20 08:12:18 +08:00
jiuqianyuan 5daf4886b7 fix wrong commit
wrong commit for dbee208
2021-07-17 16:18:33 +08:00
jiuqianyuan dbee2085fb clean useless “” 2021-07-17 15:58:29 +08:00
yuhan6665 3c6c4ca3a5 Update project dependencies 2021-07-16 18:11:28 -04:00
jiuqianyuan 3932ee6e9b fix update subscription error
Prevent server list from being emptied when update subscription failed or the subscription url is invalid
2021-07-16 17:49:56 +08:00
2dust 242c96a4de Merge pull request #1136 from yuhan6665/EditorKit
Editor kit
2021-07-14 08:26:14 +08:00
yuhan6665 a23245435f Use EditorKit for custom config
https://github.com/massivemadness/Squircle-IDE
2021-07-09 20:45:48 -04:00
yuhan6665 57476290d3 Auto Migrate to AndroidX 2021-07-09 20:44:38 -04:00
2dust c5617ac65a Merge pull request #1129 from yuhan6665/fix-host
Fix host should be empty list when it is blank
2021-07-03 16:48:12 +08:00
yuhan6665 3795348b04 Fix host should be empty list when it is blank 2021-07-02 17:40:15 -04:00
2dust 50bf9c8da0 Merge pull request #1116 from yuhan6665/fix-base64
Fix base64-url-safe format decode
2021-06-27 11:52:20 +08:00
yuhan6665 78fe4e4bc4 Fix base64-url-safe format decode 2021-06-26 19:44:21 -04:00
2dust 962519854d Merge pull request #1092 from yuhan6665/Logtag
Log with consistent tag
2021-06-13 19:53:46 +08:00
yuhan6665 841629f9a3 Cleanup some files 2021-06-12 23:38:31 -04:00
yuhan6665 d32ccc817a Log with consistent tag 2021-06-12 23:38:31 -04:00
2dust 19cc665f2d Merge pull request #1088 from yuhan6665/import-fix
Fix a null pointer when subscribe link doesn't have sni field
2021-06-07 08:17:13 +08:00
yuhan6665 031e9105e2 Fix a null pointer when subscribe link doesn't have sni field 2021-06-06 11:18:58 -04:00
2dust 35c5d64863 Merge pull request #1070 from yuhan6665/bypass-fix
bypass local also bypass multicast address
2021-05-29 10:29:25 +08:00
yuhan6665 0b05756d12 bypass local also bypass multicast address
https://www.iana.org/assignments/ipv4-address-space/ipv4-address-space.xhtml
2021-05-28 19:51:08 -04:00
2dust 3548dcbb67 Merge pull request #1065 from yuhan6665/domestic
Always add domestic dns to config
2021-05-24 08:14:43 +08:00
yuhan6665 b897b2a0e9 Always add domestic dns to config
Previously, domestic dns is only added to config under
"local dns mode". However, it should be used for V2ray
core routing DNS as well.
2021-05-23 11:56:23 -04:00
2dust eb60e4a0e4 Merge pull request #1058 from yuhan6665/grpc
Grpc
2021-05-16 19:35:16 +08:00
yuhan6665 c3dfa8cedc Support gRPC import and export
New Format https://github.com/XTLS/Xray-core/issues/91
For Vmess QRcode https://github.com/2dust/v2rayN/wiki/%E5%88%86%E4%BA%AB%E9%93%BE%E6%8E%A5%E6%A0%BC%E5%BC%8F%E8%AF%B4%E6%98%8E(ver-2)
gRPC mode is mapped to "type"
2021-05-15 23:18:09 -04:00
yuhan6665 f58bf85b6d Update UI for gRPC
Separate transport header types for different networks
Change UI dynamically based on user selection of network
2021-05-15 23:04:58 -04:00
2dust 906d0714b5 Merge pull request #1050 from yuhan6665/fakedns
Fakedns
2021-05-08 11:28:42 +08:00
yuhan6665 701fed2525 Update settings UI, add new VPN section 2021-05-07 21:55:44 -04:00
yuhan6665 e83208465f Add vpn dns preference setting
Vpn Dns can now be changed separately from remote Dns
2021-05-07 21:17:11 -04:00
yuhan6665 2b031033d3 Add fake dns preference setting 2021-05-07 20:55:02 -04:00
2dust e0e16b5934 fix tcp http request 2021-05-07 20:50:52 -04:00
2dust 0748f994ef Merge pull request #1038 from yuhan6665/new-storage-fix
New storage fix
2021-05-01 18:20:11 +08:00
yuhan6665 233b34bda6 Fix scrolling by cache server config in ViewModel 2021-04-30 20:17:41 -04:00
yuhan6665 6ece3385fe Fix trojan sni saved as null when it should be empty 2021-04-30 20:17:41 -04:00
2dust 9b8a810445 Merge pull request #1028 from yuhan6665/master
Update tun2socks
2021-04-26 08:22:47 +08:00
yuhan6665 f63242d147 Update tun2socks
https://github.com/XTLS/badvpn/commit/60a66a363f78def8831309cff0932c0bd5535e8e
compiled with NDK 22.1.7171670
2021-04-25 19:56:14 -04:00
2dust 7024fabb82 Merge pull request #1016 from yuhan6665/new-storage-fix
Fix data migration sniff setting default to true
2021-04-17 10:24:19 +08:00
2dust 459f52fec6 Merge pull request #1015 from yuhan6665/xray
Update AndroidLibrayLite project to Xray
2021-04-17 10:24:10 +08:00
yuhan6665 90f2d33d97 Fix data migration sniff setting default to true 2021-04-16 20:33:59 -04:00
yuhan6665 115008a8a4 Update AndroidLibrayLite project to Xray 2021-04-16 09:17:58 -04:00
2dust 8cdc7fb3c9 Merge pull request #1001 from yuhan6665/new-storage-fix
Rollback parsed custom config
2021-04-11 19:28:47 +08:00
yuhan6665 d0a2fa0086 Rollback parsed custom config
To minimize change, the data structure for ServerConfig still stay the same
Add another table for server raw config
2021-04-10 21:30:34 -04:00
2dust f6c54841d2 Merge pull request #990 from yuhan6665/new-storage-fix
New storage fix
2021-04-05 20:21:51 +08:00
yuhan6665 54fa356999 Add some new config for v2fly 4.37.0 2021-04-04 19:25:52 -04:00
2dust 9642b7f64f up grpc 2021-04-04 19:25:52 -04:00
yuhan6665 dd2d2c1638 Add some missing config from Xray 2021-04-04 19:25:51 -04:00
yuhan6665 e21950dbcd Improve custom config error toast 2021-04-04 19:25:51 -04:00
2dust d016ab06d4 Merge pull request #973 from yuhan6665/new-storage-fix
New storage fix
2021-03-29 08:06:43 +08:00
yuhan6665 4d9aced5a4 Gson display to not escape HTML character like "=" 2021-03-28 11:34:41 -04:00
yuhan6665 62b928e6a0 Support Trojan flow and email 2021-03-28 11:34:34 -04:00
2dust 0ce60eae73 Update MainRecyclerAdapter.kt 2021-03-28 11:34:12 -04:00
2dust 5930a6a9eb Update V2rayConfig.kt 2021-03-28 11:34:01 -04:00
2dust a360310be2 fix header type none 2021-03-28 11:33:51 -04:00
2dust 820e6cdf36 fix migration inbound port parsed as 0 2021-03-28 11:31:18 -04:00
2dust 658b890325 Merge pull request #965 from yuhan6665/new-storage
New storage
2021-03-27 09:42:12 +08:00
yuhan6665 fb017c6659 Add migration
- Fix some compile warnings
- Add migration method
- Add vmessBean migration
2021-03-26 20:55:00 -04:00
yuhan6665 00e6314afe Merge branch 'master' into new-storage 2021-03-26 20:55:00 -04:00
yuhan6665 463f45804f Add missing data fields for config
Make custom config able to parse any config
Fix subscription TODO
Fix some bugs
- vless tls should clear out flow
- trojan tls settings
- import with null rawQuery link
- socks share link username
- remove an item in the middle of server list
2021-03-26 20:55:00 -04:00
yuhan6665 572955dd1e Move everything else to MMKV
- Exclude old armv5 so lib from MMKV
- Move subscription to MMKV
- Clean up settings key
- Map settings to MMKV
- Say goodbye to DPreference

Fix some bugs:
- null pointer check
- server list refresh
- show test result
- custom config display
- fix port number in DNS object shown as Double
- shadowsocks, socks streamsettings
- quic settings with wrong security
- main list footer
2021-03-20 01:00:12 -04:00
yuhan6665 375a209beb Move main storage references to MMKV
- Move current index to MMKV
- Move ServiceManager to MMKV
- Fix isRunning in ServerActivity
- Drop support for VMESS qrcode v1
- Change all protocols importing
2021-03-19 23:23:10 -04:00
yuhan6665 872f9ce199 Add MMKV and data class
- Move ServerActivity to MMKV
- Move ServerCustomConfigActivity to MMKV
- Cleanup ServerActivities
- Cleanup V2rayConfigUtil
- Change data reads in RecyclerAdapter and ConfigManager
- Cleanup some constants
2021-03-19 23:23:09 -04:00
2dust b4f02c9bd6 Merge pull request #894 from Humilton/master
Support keyboard navigation for fab
2021-02-21 12:56:05 +08:00
hubaozhong e567719f5b Support keyboard navigation for fab
Signed-off-by: hubaozhong <d63hbz@gmail.com>
2021-02-18 23:01:31 +08:00
2dust 8407fc5825 Merge pull request #866 from yuhan6665/trim-r
Fix the string list trim
2021-02-01 08:05:37 +08:00
yuhan6665 a3e49dcc3d Fix the string list trim 2021-01-30 21:59:54 -05:00
2dust 7b47bbe99a Merge pull request #846 from yuhan6665/list-ui
Add subscription remarks in server list
2021-01-16 13:18:27 +08:00
yuhan6665 0fb2165015 Add subscription remarks in server list 2021-01-15 20:44:28 -05:00
2dust 03eeeb9b62 Merge pull request #827 from yuhan6665/master
Update readme
2021-01-02 09:18:45 +08:00
yuhan6665 038daf5fda Update readme 2021-01-01 09:21:10 -05:00
2dust bfd1387d9b Merge pull request #817 from yuhan6665/fix-manual-update
Fix manual update
2020-12-26 12:03:38 +08:00
yuhan6665 5afec5cf25 Update full config when edit manually 2020-12-25 18:34:58 -05:00
yuhan6665 ec29bdf5bf Refactor for re-use genStoreV2rayConfig() 2020-12-25 18:34:58 -05:00
2dust 57efab093f Merge pull request #803 from yuhan6665/fix-712
Fix toast BadTokenException in OS 7.1.2
2020-12-14 08:21:51 +08:00
2dust 9c92a64811 Merge pull request #801 from yuhan6665/fix-oculus
Fix widget manager null pointer in Oculus
2020-12-14 08:21:41 +08:00
yuhan6665 7ddc82d5cd Fix toast BadTokenException in OS 7.1.2
Apparently recent changes with ViewModel affect the internal of
Activity which lead to toast throwing BadTokenException in OS
7.1.2.
The error is not easily catchable. This library use reflection
to override a key function in WindowManager to catch the error.
I have audit the code of the library.

See https://github.com/PureWriter/ToastCompat for more details
2020-12-13 18:45:04 -05:00
yuhan6665 c286ba18a8 Fix widget manager null pointer in Oculus 2020-12-12 23:11:00 -05:00
2dust 867b5fc880 Merge pull request #795 from rurirei/v2init
init update
2020-12-10 08:25:10 +08:00
rurirei e8a7fa5320 create init 2020-12-09 17:35:09 +08:00
rurirei f2f9e55286 do not init all 2020-12-09 17:32:50 +08:00
2dust 4a1c62a67c Merge pull request #787 from yuhan6665/fix-selected
Fix update full config when settings change
2020-12-06 13:48:03 +08:00
yuhan6665 c9a6a459d4 Fix update full config when settings change
Now daemon process does not reference the node list at all and
only depend on a couple of settings like PREF_CURR_CONFIG..
2020-12-05 23:42:44 -05:00
2dust 21fdcf4ccf Merge pull request #783 from yuhan6665/fix-selected
Fix select node of two processes in sync
2020-12-05 18:20:11 +08:00
yuhan6665 7c7a623ae5 Fix select node of two processes in sync
As proxy only mode is added in 1.4.0, I moved the toggle components to the daemon process
When user start service from toggle, the full config is generated from a cached list.
However, this cache is not in sync with the main process list.
In fact, we don't need to generate full config right before the service start. We only
need to when active node is changed.
This way, code logic and daemon process is kept simple
2020-12-04 22:37:31 -05:00
2dust b3074e9697 Merge pull request #763 from yuhan6665/memory-optimization
Slightly improve memory by reduce unnecessary DPreference usage
2020-11-29 09:31:54 +08:00
yuhan6665 513ebcfa23 Slightly improve memory by reduce unnecessary DPreference usage
See more details in _Ext.kt.
In the future, change will be made to our config storage, so that
the service started through TileService/Widget/ScSwitchActivity will
also not launch main process. That will greatly reduce memory usage
2020-11-28 18:08:50 -05:00
2dust 50d9057f1a Merge pull request #750 from yuhan6665/viewmodel-test
Add ViewModel
2020-11-16 13:22:08 +08:00
2dust 2a563e7884 Merge pull request #747 from liyufan/new_template
Use new issue template
2020-11-16 13:21:40 +08:00
2dust c69cd18842 Merge pull request #732 from rurirei/isTun2socksRunningi
isTun2socksRunning bool
2020-11-16 13:20:48 +08:00
2dust 7f2ced85a8 Merge pull request #730 from rurirei/stoploop
stoploop update
2020-11-16 13:18:46 +08:00
yuhan6665 6c5eef99b5 Move tests and broadcast listener to ViewModel 2020-11-15 18:37:46 -05:00
yuhan6665 d7c3bae8cc Add MainViewModel
ViewModel is the recommend approach for asynchronous loading
for Activity.
The variable stays even if the Activity is killed temporarily.
2020-11-15 18:37:46 -05:00
LYF 57c98f7c50 Use new issue template 2020-11-13 22:21:35 +08:00
rurirei 49be23c56a bool in return 2020-11-08 11:40:02 +08:00
rurirei 9b658e9a22 IsTun2socksRunning bool 2020-11-08 11:34:49 +08:00
rurirei aaa84d081f stoploop update 2020-11-08 10:53:23 +08:00
2dust 5628cbee3a Merge pull request #725 from mzz2017/patch-1
fix: the problem that importing SS URL not support standard format
2020-11-05 14:30:56 +08:00
2dust 3dd663d927 Merge pull request #698 from yuhan6665/master
Release AndroidLibV2rayLite 22
2020-11-05 14:30:41 +08:00
mzz 9e49a2dbd9 fix: the problem that importing SS URL not support standard format 2020-11-02 12:37:26 +08:00
2dust 6c199c1687 Merge pull request #662 from yuhan6665/ext-geo
Add external geofile replacement help
2020-10-24 13:35:21 +08:00
yuhan6665 39af5fdb86 Release AndroidLibV2rayLite 22 2020-10-21 21:31:23 -04:00
2dust 7b2794f6be Merge pull request #695 from rurirei/proxyOnly
proxyOnly update
2020-10-22 07:59:19 +08:00
2dust 411d9e5c9a Merge pull request #691 from rurirei/api30
target 30
2020-10-22 07:58:41 +08:00
Rurirei a57aee9424 proxyOnly update
* do not run Tun2socks under proxyOnly
2020-10-21 15:19:55 +08:00
Rurirei 4602afc67e call stopV2ray on onDestroy 2020-10-21 15:14:44 +08:00
Rurirei ceb29840f2 BuildTools 30 fix 2020-10-20 09:49:16 +08:00
Rurirei 1c1f130ca7 update badge 2020-10-20 09:42:48 +08:00
Rurirei afa0eb9375 package visibility 2020-10-20 07:20:37 +08:00
Rurirei 49b682f0f3 target 30
* update dependencies
2020-10-20 07:10:19 +08:00
Rurirei d5def3bf2f useless anko 2020-10-20 07:06:36 +08:00
2dust 51b97b64f2 Merge pull request #670 from yuhan6665/fix-gson
Fix crash when custom config cannot be parsed by Gson
2020-10-15 08:18:59 +08:00
2dust 3af9ce1a1a Merge pull request #657 from rurirei/native
native fixes
2020-10-15 08:18:48 +08:00
yuhan6665 a5d3dda941 Fix crash when custom config cannot be parsed by Gson 2020-10-14 11:28:32 -04:00
yuhan6665 71a3e300d8 Add external geofile replacement help 2020-10-09 21:34:15 -04:00
Rurirei 030b9a3900 native fixes
* call sendFd() before tun2socks start
2020-10-08 15:50:51 +08:00
Rurirei 24d105a53c catch logs to checkout this error 2020-10-08 15:49:30 +08:00
2dust 45805d6df7 Merge pull request #644 from yuhan6665/clean-dependency
Clean dependency
2020-10-04 19:16:27 +08:00
2dust 4437e6699b Merge pull request #635 from rurirei/diff
issues of VPNService.kt
2020-10-04 19:16:14 +08:00
2dust fa409f91e4 Merge pull request #626 from yuhan6665/go
Clean up tun2socks
2020-10-04 19:16:00 +08:00
yuhan6665 89be5f077b Say goodbye to Anko
replace with support lib PreferenceManager
replace LayoutInflater
2020-10-03 11:19:46 -04:00
yuhan6665 896889778f Replace Anko with support lib AlertDialog
replace some property setter
2020-10-03 11:19:46 -04:00
yuhan6665 10f705a8b2 Use standard startActivity 2020-10-03 11:19:46 -04:00
yuhan6665 2956fa2030 Replace doasync with Kotlin coroutine
Using a custom coroutine scope, we don't need to keep track of the
running job. We can simply call cancel children for the scope.
2020-10-03 11:19:46 -04:00
yuhan6665 c2a704a6ea Deprecate some anko function
- Replace toast with inline function
- Use standard startActivityForResult
2020-10-03 11:19:46 -04:00
yuhan6665 78cac0cd90 Replace divider with standard library
Also, cleanup some unused classes
2020-10-03 11:19:46 -04:00
Rurirei 3ffb2e8e05 catch exception for lateinit
PropertyNotInitialized exception needs catch for lateinit var
2020-10-03 20:23:43 +08:00
Rurirei e2d667e0bb sendFd() should not called twice 2020-10-01 17:36:07 +08:00
Rurirei 3ae0777d7f useless bool 2020-10-01 17:07:51 +08:00
Rurirei 5e6348676c ignore err of registerNetworkConnectivity 2020-10-01 15:32:08 +08:00
2dust df3f1ca3ef Merge pull request #620 from yuhan6665/up-version
Update dependencies
2020-09-29 08:05:46 +08:00
yuhan6665 94f2bec329 Clean up tun2socks 2020-09-27 23:17:47 -04:00
yuhan6665 c54d8fa43a Update dependencies
Fix a delegate annotation for Kotlin 1.4
2020-09-25 18:37:29 -04:00
2dust 5bbbdcf6f2 Merge pull request #610 from yuhan6665/fix-protect
Fix protect() when there is no service
2020-09-21 07:59:55 +08:00
yuhan6665 4abf20fa32 Fix protect() when there is no service 2020-09-20 19:00:14 -04:00
2dust 723727feb9 Merge pull request #607 from yuhan6665/help-proxy-mode
Help proxy mode
2020-09-19 18:56:15 +08:00
yuhan6665 2efd4b741c Add badge, wiki link and dev guide 2020-09-18 17:48:54 -04:00
yuhan6665 83aab0f880 Add help link in mode setting 2020-09-18 17:48:54 -04:00
2dust 66ea17877e Merge pull request #598 from yuhan6665/fix-process
Add missing process for ScSwitchActivity
2020-09-12 19:48:04 +08:00
2dust 26bc985368 Merge pull request #597 from yuhan6665/settings-ui
Settings ui
2020-09-12 19:47:48 +08:00
yuhan6665 5bbf40c784 Grey out per-app proxy in proxy only mode 2020-09-11 21:30:08 -04:00
yuhan6665 6d5c23245c Move version to drawer
Make it easier to check version.
Technically, version should not be selectable setting
2020-09-11 21:30:08 -04:00
yuhan6665 b148290211 Add missing process for ScSwitchActivity 2020-09-11 21:29:58 -04:00
2dust c473f9bb13 Merge pull request #587 from yuhan6665/proxy-mode
Proxy only mode
2020-09-06 08:09:10 +08:00
2dust c7c3d27f36 Merge pull request #584 from akiirui/fix-metered
V2RayVpnService.kt: fix OS marks VPN as metered
2020-09-06 08:08:58 +08:00
yuhan6665 bebc6fea13 Add mode in settings 2020-09-04 18:19:51 -04:00
yuhan6665 9271857b1e Remove VPN related logic in toggle components 2020-09-04 18:19:51 -04:00
yuhan6665 7ea78c1840 Make all toggle component run in daemon process
This commit make the two process with clear role:
 - Daemon process will run proxy core, keep minimum configuration
 and long running in the background, support toggle on/off
 - Main process will have all UI, all servers configuration
2020-09-04 18:19:51 -04:00
yuhan6665 ac668788b3 Add proxy only service 2020-09-04 18:19:51 -04:00
yuhan6665 adfcf0a5d9 Fix some trivial IDE warnings 2020-09-04 18:19:51 -04:00
yuhan6665 15b5595797 Refactor service
Break VpnService so that it only control vpn and tun2socks,
Other function is moved to a singleton service manager.
This code makes it possible to add proxy only service next.
2020-09-04 18:19:51 -04:00
Akatsuki 5a18296cb2 V2RayVpnService.kt: use setMetered
setMetered to false let VPN network to inherit its meteredness from its underlying networks.

Revert `addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)`
2020-09-03 02:07:11 +08:00
Akatsuki 1256edbaf5 V2RayVpnService.kt: fix OS marks VPN as metered
VPN apps targeting Build.VERSION_CODES.Q or above will be considered metered by default.

Ref:
https://developer.android.com/reference/android/net/VpnService.Builder#setMetered(boolean)
https://developer.android.com/about/versions/pie/android-9.0-changes-all#network-capabilities-vpn
2020-09-02 13:46:18 +08:00
2dust 870950e807 Merge pull request #577 from yuhan6665/fix-target-29
Fix release build with tun2socks
2020-08-30 07:21:24 +08:00
yuhan6665 e798cb3f42 Fix release build with tun2socks 2020-08-29 12:34:54 -04:00
2dust b970f0bcff Merge pull request #574 from yuhan6665/all-outbounds-speed
All outbounds speed
2020-08-29 20:33:18 +08:00
2dust 93b9a56428 Merge pull request #387 from yuhan6665/target-29
Target sdk 29
2020-08-29 20:32:59 +08:00
yuhan6665 eb8bd13266 Add speed notification for all outbounds 2020-08-28 21:52:44 -04:00
yuhan6665 d850c88c63 Support ordered set in DPreference
Note the ordered set need to be stored internally as String,
not string set
2020-08-28 21:52:43 -04:00
yuhan6665 6bee795c0d Update target sdk to 29 2020-08-27 20:19:00 -04:00
yuhan6665 d7d7e029e0 Fix compile warnings that will be error in sdk 29 2020-08-27 20:18:59 -04:00
2dust 8efdab43d7 Merge pull request #566 from yuhan6665/array-crash
Fix test crash if node is removed
2020-08-22 08:06:36 +08:00
yuhan6665 5e0235cf70 Fix test crash if node is removed
This should fix almost all issues when the testing is in progress
and the array is changed. However, there might be uncovered edge
cases since the vmess array is in both process and accessed by
multiple threads.
2020-08-21 19:47:33 -04:00
2dust 9f668b3da7 Merge pull request #559 from yuhan6665/custom-config-improve
Custom config improve
2020-08-16 20:26:17 +08:00
yuhan6665 b2437279dc Update UI for custom config
- add server address and port
- add share button (full configuration)
- update "Export all config to clipboard" text to indicate
it will not include custom config
2020-08-15 19:37:59 -04:00
yuhan6665 180b5efd93 Enable tcping test for custom config 2020-08-15 19:37:59 -04:00
yuhan6665 dc31380cc2 Refactor EConfigType to be an enum
Using enum will simplify logic and conveniently providing Int value
and a String name
2020-08-15 19:37:59 -04:00
2dust 1361e0dacf Merge pull request #555 from yuhan6665/per-app-list
Update latest per-app list
2020-08-15 20:40:10 +08:00
2dust a45eb66bd1 Merge pull request #554 from ShrdBB/master
Fix typo in strings.xml
2020-08-15 20:39:50 +08:00
yuhan6665 508102cebe Update latest per-app list
https://github.com/2dust/androidpackagenamelist/blob/master/proxy.txt
2020-08-14 20:45:30 -04:00
ShrdBB 1d86dbb9f3 fix typo in strings.xml 2020-08-14 12:48:46 +08:00
2dust 20ca554be2 Merge pull request #544 from yuhan6665/android-system
Add "Android System" in per-app vpn
2020-08-09 12:57:49 +08:00
yuhan6665 25ba455656 Add "Android System" in per-app vpn 2020-08-08 23:49:06 -04:00
2dust 17e7c62d53 Merge pull request #542 from yuhan6665/master
Sanitize project configuration
2020-08-08 15:35:16 +08:00
yuhan6665 6f0f2fdeda Sanitize project configuration
Make project compilable out of box. This commit address
the following:
- remove release key
- fix gitignore for Android Studio temp files
- add gradle wrapper properties
2020-08-07 21:37:00 -04:00
2dust 3c9c9b5a4c Merge pull request #531 from yuhan6665/emui-notification
Fix notification for Emui 4.1
2020-08-06 20:40:45 +08:00
2dust e13024d6bb Merge pull request #526 from Vixb1122/master
exclude ScSwitchActivity from recent list
2020-08-06 20:39:08 +08:00
2dust 9d58edd31f Merge pull request #537 from yuhan6665/revert
Revert "Refresh prepared domain every 30 minutes"
2020-08-06 20:38:50 +08:00
yuhan6665 af7dfc3a43 Revert "Refresh prepared domain every 30 minutes"
This reverts commit 903352ec9c.
2020-08-05 20:22:01 -04:00
yuhan6665 ca554e6ac4 Fix notification for Emui 4.1 2020-08-02 08:42:04 -04:00
xuezhixin ec391d8689 exclude ScSwitchActivity from recent list 2020-07-29 16:47:40 +08:00
2dust 6afd4d0549 Update AngConfigManager.kt 2020-07-27 21:00:34 +08:00
2dust 957cf85362 Merge pull request #517 from yuhan6665/bypass-private
Bypass private IP at VPN service
2020-07-25 19:42:12 +08:00
2dust 881152e10a Merge pull request #516 from cpdyj/master
add support to new version vmess link
2020-07-25 19:41:46 +08:00
2dust 6e47ebb27a Merge pull request #484 from DevRyz3n/drag-item-store-config
Optimize invoke timing of store config file after dragging item.
2020-07-25 19:41:13 +08:00
2dust a731e6c360 Merge pull request #453 from yuhan6665/widget
Widget
2020-07-25 19:40:39 +08:00
yuhan6665 d1466ba4b2 Bypass private IP at VPN service 2020-07-24 18:12:20 -04:00
iseki 617f28f399 Update AngConfigManager.kt 2020-07-24 11:23:46 +08:00
iseki c0ec0d9404 Update AngConfigManager.kt 2020-07-24 11:21:20 +08:00
iseki 3419dc8837 add support to new version vmess link
some information can be found at: https://github.com/v2ray/discussion/issues/720
2020-07-23 00:51:08 +08:00
2dust 786aaf823a Merge pull request #511 from yuhan6665/outbound-speed
Outbound speed
2020-07-19 08:19:03 +08:00
yuhan6665 1144183da4 Update notification icon based on traffic
Notification icon would be a good indication for current traffic
- going through proxy
- connect directly
- not going through v2rayNG
2020-07-18 18:48:47 -04:00
yuhan6665 5bf2fda990 Measure traffic from outbounds
With v2ray-core 4.26.0, traffic can be measured from outbounds.
Stats is shown separately for proxy and direct traffic.

In the future, it is possible to add stats for each server node and
even historic usage graph.
2020-07-18 18:47:59 -04:00
2dust 28639cc388 Merge pull request #468 from yuhan6665/cleanup-config
Remove legacy "connectionReuse" in config
2020-07-18 14:01:38 +08:00
2dust ca254b2aa1 Merge pull request #443 from yuhan6665/resume-speed
Fix speed display after screen turns on
2020-07-18 14:00:48 +08:00
2dust 9721879713 Merge pull request #432 from yuhan6665/drawer-ui
fix highlight issue in drawer ui
2020-07-18 13:59:47 +08:00
2dust 623c1807c5 Merge pull request #409 from yuhan6665/test-cleanup
Tcp test cleanup
2020-07-18 13:58:37 +08:00
2dust 739fa88ba7 Merge pull request #335 from yuhan6665/sub-index
Fix selected index when update subscription
2020-07-18 13:58:11 +08:00
2dust 85d6f00f8c Merge pull request #474 from yuhan6665/dns-cache-timeout
Refresh prepared domain every 30 minutes
2020-07-18 09:48:16 +08:00
yuhan6665 8986710453 Cancel async block when user test tcping again
Change async code to use Kotlin coroutine instead of Anko,
since Anko is deprecated and coroutine is the recommanded
approach now.
2020-07-16 19:42:43 -04:00
yuhan6665 f54faacbf6 Close connecting sockets when user test tcping again 2020-07-16 19:42:43 -04:00
r23 993ee0b8d2 Optimize invoke timing of store config file after dragging item. 2020-06-29 01:24:19 +08:00
yuhan6665 903352ec9c Refresh prepared domain every 30 minutes 2020-06-19 23:42:02 -04:00
yuhan6665 ad56106c08 Remove legacy "connectionReuse" in config
Also auto-refactor the data class naming to be proper camelcase
2020-06-13 23:38:33 -04:00
yuhan6665 91b8284afd Improve widget UI
Change widget color when clicked
2020-06-06 00:18:19 -04:00
yuhan6665 ef9e0cc0d2 Add back widget, fix implicit Intent 2020-06-06 00:18:19 -04:00
2dust 6577c46a31 Merge pull request #425 from yuhan6665/translation
Fix a traditional Chinese translation
2020-05-31 16:13:35 +08:00
yuhan6665 c6dab001b2 Fix traditional Chinese translation 2020-05-30 12:34:29 -04:00
yuhan6665 aea8369b8a Fix speed display after screen turns on
Also, use the tab to make the text less jumpy
2020-05-29 22:54:55 -04:00
yuhan6665 92d2cb35c4 Make drawer item unchecked
Current checked item is not consistent with the active activity.
In fact, we don't need checked state. This is a standard behavior.
You can find in apps like Google Playstore.
2020-05-24 11:13:12 -04:00
yuhan6665 6ce3d540e8 Remove config item in drawer
The drawer is attached to MainActivity. When user see the drawer, the
MainActivity must be active. Showing an menu item to launch itself is
confusing.
2020-05-24 11:09:11 -04:00
2dust c105d84b35 Merge pull request #300 from yuhan6665/master
Fix UI touch issues for Android TV
2020-04-12 10:04:52 +08:00
yuhan6665 8b149fb52f Fix selected index when update subscription 2020-04-03 18:58:41 -04:00
2dust 3ea04c076c Merge pull request #314 from yuhan6665/chrome-os
Add tags in AndroidManifest to support PlayStore filtering
2020-03-16 16:24:58 +08:00
2dust 98475460bf Merge pull request #313 from yuhan6665/lint-fix
Use annotation to fix lint error
2020-03-16 16:24:08 +08:00
yuhan6665 68ee61a753 Add tags in AndroidManifest to support PlayStore filtering
Google Playstore is using uses-feature and supports-screens
to filter devices.
By setting all screens to true and specifing features as
"not required" should make app compatible with more devices
Particularly it includes Chrome OS devices.
2020-03-14 14:07:31 -04:00
yuhan6665 90ba9ef2b7 Use annotation to fix lint error 2020-03-14 13:03:51 -04:00
yuhan6665 0ec114322e Fix UI touch issues for Android TV 2020-03-08 12:06:44 -04:00
2dust 76f52e7aa7 Merge pull request #287 from yuhan6665/multidex
Fix multidex for Android devices below L
2020-03-03 14:52:19 +08:00
2dust caa25ce424 Merge pull request #278 from yuhan6665/master
Add delete log button in logcat
2020-03-03 14:52:08 +08:00
yuhan6665 e33d7e9bcf Fix multidex for Android devices below L 2020-02-29 10:51:58 -05:00
yuhan6665 e120fce0b7 Add delete log button in logcat
Also, automatically scroll to bottom of the log if it is too long
2020-02-22 12:01:16 -05:00
2dust 6a90965c7b Update README.md 2019-11-14 13:09:31 +08:00
2dust 0542d913f2 Merge pull request #131 from galvasis193/feature-customediting
Give user the ability to edit/copy/paste custom config for convenience. Fix Import
2019-10-14 08:22:39 +08:00
Annoymous contributor 746254bb7e Give user the ability to edit/copy/paste custom config for convenience.
Fix import for go.Seq
2019-10-13 04:55:00 +00:00
2dust 2227ea3ed6 Merge pull request #129 from galvasis193/feature-ProxySharing
Add proxy sharing function. Fix potential security vulnerbility.
2019-10-12 09:40:19 +08:00
Annoymous contributor 4d0e9e2b44 Bugfix: When a setting is changed proxy will now auto restart to apply the change. Fix proxy service cannot be stopped bug. 2019-10-11 10:28:45 +00:00
Annoymous contributor 5cba8a228c Add proxy sharing. Fix potential unahthorized access from LAN/Internet. 2019-10-11 10:26:46 +00:00
2dust 2d8ed22dba mge 2019-10-11 14:17:10 +08:00
178 changed files with 4224 additions and 4078 deletions
+5 -1
View File
@@ -1,5 +1,9 @@
V2rayNG/app/src/main/res/layout/activity_inapp_buy.xml
V2rayNG/app/src/main/assets/geoip.dat
V2rayNG/app/src/main/assets/geosite.dat
V2rayNG/app/src/main/java/com/v2ray/ang/InappBuyActivity.java
*.dat
*.jks
V2rayNG/app/release/output.json
.idea/
.gradle/
.gradle/
+2
View File
@@ -0,0 +1,2 @@
*.jar
*.aar
+65
View File
@@ -0,0 +1,65 @@
package CoreI
import (
v2core "github.com/xtls/xray-core/core"
)
type Status struct {
IsRunning bool
IsTRunning bool
PackageName string
PackageCodePath string
Vpoint v2core.Server
}
func CheckVersion() int {
return 23
}
func (v *Status) GetDataDir() string {
return v.PackageName
}
func (v *Status) GetApp(name string) string {
return v.PackageCodePath + name
}
func (v *Status) GetTun2socksArgs(localDNS bool, enableIPv6 bool) (ret []string) {
ret = []string{"--netif-ipaddr",
"26.26.26.2",
"--netif-netmask",
"255.255.255.252",
"--socks-server-addr",
"127.0.0.1:10808",
"--tunmtu",
"1500",
"--loglevel",
"notice",
"--enable-udprelay",
"--sock-path",
v.GetDataDir() + "sock_path",
}
if enableIPv6 {
ret = append(ret, "--netif-ip6addr", "da26:2626::2")
}
if localDNS {
ret = append(ret, "--dnsgw", "127.0.0.1:10807")
}
return
}
func (v *Status) GetVPNSetupArg(localDNS bool, enableIPv6 bool) (ret string) {
ret = "m,1500 a,26.26.26.1,30 r,0.0.0.0,0"
if enableIPv6 {
ret += " a,da26:2626::1,126 r,::,0"
}
if localDNS {
ret += " d,26.26.26.2"
}
return
}
+26
View File
@@ -0,0 +1,26 @@
pb:
go get -u github.com/golang/protobuf/protoc-gen-go
@echo "pb Start"
asset:
bash gen_assets.sh download
mkdir assets
cp -v data/*.dat assets/
# cd assets;curl https://raw.githubusercontent.com/2dust/AndroidLibV2rayLite/master/data/geosite.dat > geosite.dat
# cd assets;curl https://raw.githubusercontent.com/2dust/AndroidLibV2rayLite/master/data/geoip.dat > geoip.dat
ANDROID_HOME=$(HOME)/android-sdk-linux
export ANDROID_HOME
PATH:=$(PATH):$(GOPATH)/bin
export PATH
downloadGoMobile:
sudo apt-get install -qq libstdc++6:i386 lib32z1 expect
cd ~ ;curl -L https://raw.githubusercontent.com/2dust/AndroidLibV2rayLite/master/ubuntu-cli-install-android-sdk.sh | sudo bash - > /dev/null
ls ~
ls ~/android-sdk-linux/
BuildMobile:
gomobile init
gomobile bind -v -ldflags='-s -w' github.com/2dust/AndroidLibV2rayLite
all: asset pb
@echo DONE
@@ -0,0 +1,79 @@
package Escort
import (
"os"
"os/exec"
"log"
"github.com/2dust/AndroidLibV2rayLite/CoreI"
)
func (v *Escorting) EscortRun(proc string, pt []string, additionalEnv string) {
log.Println(proc, pt)
count := 0
for count <= 42 {
cmd := exec.Command(proc, pt...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if len(additionalEnv) > 0 {
//additionalEnv := "FOO=bar"
newEnv := append(os.Environ(), additionalEnv)
cmd.Env = newEnv
}
if err := cmd.Start(); err != nil {
log.Println("EscortRun cmd.Start err", err)
goto CMDERROR
}
if v.escortProcess == nil {
log.Println("EscortRun v.escortProcess nil")
break
}
*v.escortProcess = append(*v.escortProcess, cmd.Process)
log.Println("EscortRun Waiting....")
if err := cmd.Wait(); err != nil {
log.Println("EscortRun cmd.Wait err:", err)
}
CMDERROR:
if v.Status.IsRunning {
log.Println("EscortRun Unexpected Exit, Restart now.")
count++
} else {
log.Println("EscortRun Exit")
break
}
}
}
func (v *Escorting) EscortingUp() {
if v.escortProcess != nil {
return
}
v.escortProcess = new([](*os.Process))
}
func (v *Escorting) EscortingDown() {
if v.escortProcess == nil {
return
}
log.Println("EscortingDown() Killing all escorted process ")
for _, pr := range *v.escortProcess {
pr.Kill()
if _, err := pr.Wait(); err != nil {
log.Println("EscortingDown pr.Wait err:", err)
}
}
v.escortProcess = nil
}
type Escorting struct {
escortProcess *[](*os.Process)
Status *CoreI.Status
}
+14 -18
View File
@@ -1,20 +1,16 @@
# 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:<wherever you ndk is located>
- 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)
进入`AndroidLibV2rayLite`文件夹
```
go mod download
```
编译aar
```
gomobile bind -v -o android.aar -target=android ./
```
@@ -0,0 +1,286 @@
package VPN
import (
"context"
"errors"
"fmt"
"log"
"net"
"os"
"strings"
"sync"
"time"
"golang.org/x/sys/unix"
v2net "github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/features/dns"
"github.com/xtls/xray-core/features/outbound"
v2internet "github.com/xtls/xray-core/transport/internet"
)
type protectSet interface {
Protect(int) int
}
type resolved struct {
domain string
IPs []net.IP
Port int
ipIdx uint8
ipLock sync.Mutex
lastSwitched time.Time
}
// NextIP switch to another resolved result.
// there still be race-condition here if multiple err concurently occured
// may cause idx keep switching,
// but that's an outside error can hardly handled here
func (r *resolved) NextIP() {
r.ipLock.Lock()
defer r.ipLock.Unlock()
if len(r.IPs) > 1 {
// throttle, don't switch too quickly
now := time.Now()
if now.Sub(r.lastSwitched) < time.Second*5 {
log.Println("switch too quickly")
return
}
r.lastSwitched = now
r.ipIdx++
} else {
return
}
if r.ipIdx >= uint8(len(r.IPs)) {
r.ipIdx = 0
}
cur := r.currentIP()
log.Printf("switched to next IP: %s", cur)
}
func (r *resolved) currentIP() net.IP {
if len(r.IPs) > 0 {
return r.IPs[r.ipIdx]
}
return nil
}
// NewPreotectedDialer ...
func NewPreotectedDialer(p protectSet) *ProtectedDialer {
d := &ProtectedDialer{
// prefer native lookup on Android
resolver: &net.Resolver{PreferGo: false},
protectSet: p,
}
return d
}
// ProtectedDialer ...
type ProtectedDialer struct {
currentServer string
resolveChan chan struct{}
vServer *resolved
resolver *net.Resolver
protectSet
}
func (d *ProtectedDialer) IsVServerReady() bool {
return (d.vServer != nil)
}
func (d *ProtectedDialer) PrepareResolveChan() {
d.resolveChan = make(chan struct{})
}
func (d *ProtectedDialer) ResolveChan() <-chan struct{} {
return d.resolveChan
}
// simplicated version of golang: internetAddrList in src/net/ipsock.go
func (d *ProtectedDialer) lookupAddr(addr string) (*resolved, error) {
var (
err error
host, port string
portnum int
)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if host, port, err = net.SplitHostPort(addr); err != nil {
log.Printf("PrepareDomain SplitHostPort Err: %v", err)
return nil, err
}
if portnum, err = d.resolver.LookupPort(ctx, "tcp", port); err != nil {
log.Printf("PrepareDomain LookupPort Err: %v", err)
return nil, err
}
addrs, err := d.resolver.LookupIPAddr(ctx, host)
if err != nil {
return nil, err
}
if len(addrs) == 0 {
return nil, fmt.Errorf("domain %s Failed to resolve", addr)
}
IPs := make([]net.IP, len(addrs))
for i, ia := range addrs {
IPs[i] = ia.IP
}
rs := &resolved{
domain: host,
IPs: IPs,
Port: portnum,
}
return rs, nil
}
// PrepareDomain caches direct v2ray server host
func (d *ProtectedDialer) PrepareDomain(domainName string, closeCh <-chan struct{}) {
log.Printf("Preparing Domain: %s", domainName)
d.currentServer = domainName
defer close(d.resolveChan)
maxRetry := 10
for {
if maxRetry == 0 {
log.Println("PrepareDomain maxRetry reached. exiting.")
return
}
resolved, err := d.lookupAddr(domainName)
if err != nil {
maxRetry--
log.Printf("PrepareDomain err: %v\n", err)
select {
case <-closeCh:
log.Printf("PrepareDomain exit due to v2ray closed")
return
case <-time.After(time.Second * 2):
}
continue
}
d.vServer = resolved
log.Printf("Prepare Result:\n Domain: %s\n Port: %d\n IPs: %v\n",
resolved.domain, resolved.Port, resolved.IPs)
return
}
}
func (d *ProtectedDialer) getFd(network v2net.Network) (fd int, err error) {
switch network {
case v2net.Network_TCP:
fd, err = unix.Socket(unix.AF_INET6, unix.SOCK_STREAM, unix.IPPROTO_TCP)
case v2net.Network_UDP:
fd, err = unix.Socket(unix.AF_INET6, unix.SOCK_DGRAM, unix.IPPROTO_UDP)
default:
err = fmt.Errorf("unknow network")
}
return
}
// Init implement internet.SystemDialer
func (d *ProtectedDialer) Init(_ dns.Client, _ outbound.Manager) {
// do nothing
}
// Dial exported as the protected dial method
func (d *ProtectedDialer) Dial(ctx context.Context,
src v2net.Address, dest v2net.Destination, sockopt *v2internet.SocketConfig) (net.Conn, error) {
network := dest.Network.SystemString()
Address := dest.NetAddr()
// v2ray server address,
// try to connect fixed IP if multiple IP parsed from domain,
// and switch to next IP if error occurred
if strings.Compare(Address, d.currentServer) == 0 {
if d.vServer == nil {
log.Println("Dial pending prepare ...", Address)
<-d.resolveChan
// user may close connection during PrepareDomain,
// fast return release resources.
if d.vServer == nil {
return nil, fmt.Errorf("fail to prepare domain %s", d.currentServer)
}
}
fd, err := d.getFd(dest.Network)
if err != nil {
return nil, err
}
curIP := d.vServer.currentIP()
conn, err := d.fdConn(ctx, curIP, d.vServer.Port, fd)
if err != nil {
d.vServer.NextIP()
return nil, err
}
log.Printf("Using Prepared: %s", curIP)
return conn, nil
}
// v2ray connecting to "domestic" servers, no caching results
log.Printf("Not Using Prepared: %s,%s", network, Address)
resolved, err := d.lookupAddr(Address)
if err != nil {
return nil, err
}
fd, err := d.getFd(dest.Network)
if err != nil {
return nil, err
}
// use the first resolved address.
// the result IP may vary, eg: IPv6 addrs comes first if client has ipv6 address
return d.fdConn(ctx, resolved.IPs[0], resolved.Port, fd)
}
func (d *ProtectedDialer) fdConn(ctx context.Context, ip net.IP, port int, fd int) (net.Conn, error) {
defer unix.Close(fd)
// call android VPN service to "protect" the fd connecting straight out
d.Protect(fd)
sa := &unix.SockaddrInet6{
Port: port,
}
copy(sa.Addr[:], ip)
if err := unix.Connect(fd, sa); err != nil {
log.Printf("fdConn unix.Connect err, Close Fd: %d Err: %v", fd, err)
return nil, err
}
file := os.NewFile(uintptr(fd), "Socket")
if file == nil {
// returned value will be nil if fd is not a valid file descriptor
return nil, errors.New("fdConn fd invalid")
}
defer file.Close()
//Closing conn does not affect file, and closing file does not affect conn.
conn, err := net.FileConn(file)
if err != nil {
log.Printf("fdConn FileConn Close Fd: %d Err: %v", fd, err)
return nil, err
}
return conn, nil
}
@@ -0,0 +1,151 @@
package VPN
import (
"bufio"
"context"
"fmt"
"net"
"sync"
"testing"
"time"
v2net "github.com/xtls/xray-core/common/net"
)
type fakeSupportSet struct{}
func (f fakeSupportSet) Protect(int) int {
return 0
}
func TestProtectedDialer_PrepareDomain(t *testing.T) {
type args struct {
domainName string
}
tests := []struct {
name string
args args
}{
// TODO: Add test cases.
{"", args{"baidu.com:80"}},
// {"", args{"cloudflare.com:443"}},
// {"", args{"apple.com:443"}},
// {"", args{"110.110.110.110:443"}},
// {"", args{"[2002:1234::1]:443"}},
}
d := NewPreotectedDialer(fakeSupportSet{})
for _, tt := range tests {
ch := make(chan struct{})
t.Run(tt.name, func(t *testing.T) {
go d.PrepareDomain(tt.args.domainName, ch)
time.Sleep(time.Second)
go d.vServer.NextIP()
t.Log(d.vServer.currentIP())
})
}
time.Sleep(time.Second)
}
func TestProtectedDialer_Dial(t *testing.T) {
tests := []struct {
name string
wantErr bool
}{
// TODO: Add test cases.
{"baidu.com:80", false},
{"cloudflare.com:80", false},
{"172.16.192.11:80", true},
// {"172.16.192.10:80", true},
// {"[2fff:4322::1]:443", true},
// {"[fc00::1]:443", true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ch := make(chan struct{})
d := NewPreotectedDialer(fakeSupportSet{})
d.currentServer = tt.name
go d.PrepareDomain(tt.name, ch)
var wg sync.WaitGroup
dial := func() {
defer wg.Done()
dest, _ := v2net.ParseDestination("tcp:" + tt.name)
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
conn, err := d.Dial(ctx, nil, dest, nil)
if err != nil {
t.Log(err)
return
}
_host, _, _ := net.SplitHostPort(tt.name)
fmt.Fprintf(conn, fmt.Sprintf("GET / HTTP/1.1\r\nHost: %s\r\n\r\n", _host))
status, err := bufio.NewReader(conn).ReadString('\n')
t.Logf("%#v, %#v\n", status, err)
conn.Close()
}
for n := 0; n < 3; n++ {
wg.Add(1)
go dial()
// time.Sleep(time.Millisecond * 10)
// d.pendingMap[tt.name] = make(chan struct{})
}
wg.Wait()
})
}
}
func Test_resolved_NextIP(t *testing.T) {
type fields struct {
domain string
IPs []net.IP
Port int
}
tests := []struct {
name string
fields fields
}{
// TODO: Add test cases.
{"test1",
fields{
domain: "www.baidu.com",
IPs: []net.IP{
net.ParseIP("1.2.3.4"),
net.ParseIP("4.3.2.1"),
net.ParseIP("1234::1"),
net.ParseIP("4321::1"),
},
}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &resolved{
domain: tt.fields.domain,
IPs: tt.fields.IPs,
Port: tt.fields.Port,
}
t.Logf("%v", r.IPs)
t.Logf("%v", r.currentIP())
r.NextIP()
t.Logf("%v", r.currentIP())
r.NextIP()
t.Logf("%v", r.currentIP())
r.NextIP()
t.Logf("%v", r.currentIP())
time.Sleep(3 * time.Second)
r.NextIP()
t.Logf("%v", r.currentIP())
time.Sleep(5 * time.Second)
r.NextIP()
t.Logf("%v", r.currentIP())
})
}
}
+43
View File
@@ -0,0 +1,43 @@
#!/bin/bash
set -o errexit
set -o pipefail
set -o nounset
# Set magic variables for current file & dir
__dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
__file="${__dir}/$(basename "${BASH_SOURCE[0]}")"
__base="$(basename ${__file} .sh)"
if [[ ! -d $NDK_HOME ]]; then
echo "Android NDK: NDK_HOME not found. please set env \$NDK_HOME"
exit 1
fi
TMPDIR=$(mktemp -d)
clear_tmp () {
rm -rf $TMPDIR
}
trap 'echo -e "Aborted, error $? in command: $BASH_COMMAND"; trap ERR; clear_tmp; exit 1' ERR INT
install -m644 $__dir/tun2socks.mk $TMPDIR/
pushd $TMPDIR
git clone --depth=1 https://github.com/XTLS/badvpn.git
git clone --depth=1 https://github.com/XTLS/libancillary.git
$NDK_HOME/ndk-build \
NDK_PROJECT_PATH=. \
APP_BUILD_SCRIPT=./tun2socks.mk \
APP_ABI=all \
APP_PLATFORM=android-19 \
NDK_LIBS_OUT=$TMPDIR/libs \
NDK_OUT=$TMPDIR/tmp \
APP_SHORT_COMMANDS=false LOCAL_SHORT_COMMANDS=false -B -j4
install -v -m755 libs/armeabi-v7a/tun2socks $__dir/../V2rayNG/app/src/main/jniLibs/armeabi-v7a/libtun2socks.so
install -v -m755 libs/arm64-v8a/tun2socks $__dir/../V2rayNG/app/src/main/jniLibs/arm64-v8a/libtun2socks.so
install -v -m755 libs/x86/tun2socks $__dir/../V2rayNG/app/src/main/jniLibs/x86/libtun2socks.so
install -v -m755 libs/x86_64/tun2socks $__dir/../V2rayNG/app/src/main/jniLibs/x86_64/libtun2socks.so
popd
rm -rf $TMPDIR
+81
View File
@@ -0,0 +1,81 @@
#!/bin/bash
set -o errexit
set -o pipefail
set -o nounset
# Set magic variables for current file & dir
__dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
__file="${__dir}/$(basename "${BASH_SOURCE[0]}")"
__base="$(basename ${__file} .sh)"
DATADIR=${__dir}/data
compile_dat () {
local TMPDIR=$(mktemp -d)
trap 'echo -e "Aborted, error $? in command: $BASH_COMMAND"; rm -rf $TMPDIR; trap ERR; exit 1' ERR
local GEOSITE=${GOPATH}/src/github.com/v2ray/domain-list-community
if [[ -d ${GEOSITE} ]]; then
cd ${GEOSITE} && git pull
else
mkdir -p ${GEOSITE}
cd ${GEOSITE} && git clone https://github.com/v2ray/domain-list-community.git .
fi
go run main.go
if [[ -e dlc.dat ]]; then
rm -f $DATADIR/geosite.dat
mv dlc.dat $DATADIR/geosite.dat
echo "----------> geosite.dat updated."
else
echo "----------> geosite.dat failed to update."
fi
if [[ ! -x $GOPATH/bin/geoip ]]; then
go get -v -u github.com/v2ray/geoip
fi
cd $TMPDIR
curl -L -O http://geolite.maxmind.com/download/geoip/database/GeoLite2-Country-CSV.zip
unzip -q GeoLite2-Country-CSV.zip
mkdir geoip && find . -name '*.csv' -exec mv -t ./geoip {} +
$GOPATH/bin/geoip \
--country=./geoip/GeoLite2-Country-Locations-en.csv \
--ipv4=./geoip/GeoLite2-Country-Blocks-IPv4.csv \
--ipv6=./geoip/GeoLite2-Country-Blocks-IPv6.csv
if [[ -e geoip.dat ]]; then
rm -f $DATADIR/geoip.dat
mv ./geoip.dat $DATADIR/geoip.dat
echo "----------> geoip.dat updated."
else
echo "----------> geoip.dat failed to update."
fi
trap ERR
return 0
}
download_dat () {
wget -qO - https://api.github.com/repos/v2ray/geoip/releases/latest \
| grep browser_download_url | cut -d '"' -f 4 \
| wget -i - -O $DATADIR/geoip.dat
wget -qO - https://api.github.com/repos/v2ray/domain-list-community/releases/latest \
| grep browser_download_url | cut -d '"' -f 4 \
| wget -i - -O $DATADIR/geosite.dat
}
ACTION="${1:-}"
if [[ -z $ACTION ]]; then
ACTION=download
fi
case $ACTION in
"download") download_dat;;
"compile") compile_dat;;
esac
+49
View File
@@ -0,0 +1,49 @@
module github.com/2dust/AndroidLibV2rayLite
go 1.18
require (
github.com/xtls/xray-core v1.5.7
golang.org/x/mobile v0.0.0-20220518205345-8578da9835fd
golang.org/x/sys v0.0.0-20220608164250-635b8c9b7f68
)
require (
github.com/cheekybits/genny v1.0.0 // indirect
github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 // indirect
github.com/francoispqt/gojay v1.2.13 // indirect
github.com/fsnotify/fsnotify v1.5.4 // indirect
github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 // indirect
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/klauspost/cpuid/v2 v2.0.12 // indirect
github.com/lucas-clemente/quic-go v0.27.1 // indirect
github.com/marten-seemann/qtls-go1-16 v0.1.5 // indirect
github.com/marten-seemann/qtls-go1-17 v0.1.1 // indirect
github.com/marten-seemann/qtls-go1-18 v0.1.1 // indirect
github.com/nxadm/tail v1.4.8 // indirect
github.com/onsi/ginkgo v1.16.5 // indirect
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/pires/go-proxyproto v0.6.2 // indirect
github.com/refraction-networking/utls v1.1.0 // indirect
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
github.com/sagernet/sing v0.0.0-20220605012533-e0f722558141 // indirect
github.com/sagernet/sing-shadowsocks v0.0.0-20220605012719-1e22882ea853 // indirect
github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb // indirect
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e // indirect
github.com/xtls/go v0.0.0-20210920065950-d4af136d3672 // indirect
go.starlark.net v0.0.0-20220328144851-d1966c6b9fcd // indirect
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e // indirect
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect
golang.org/x/net v0.0.0-20220531201128-c960675eff93 // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/tools v0.1.11-0.20220325154526-54af36eca237 // indirect
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df // indirect
google.golang.org/genproto v0.0.0-20220527130721-00d5c0f3be58 // indirect
google.golang.org/grpc v1.47.0 // indirect
google.golang.org/protobuf v1.28.0 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
lukechampine.com/blake3 v1.1.7 // indirect
)
+408
View File
@@ -0,0 +1,408 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo=
dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU=
dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU=
dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=
dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE=
github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-metro v0.0.0-20200812162917-85c65e2d0165/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw=
github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 h1:y7y0Oa6UawqTFPCDw9JG6pdKt4F9pAhHv0B7FMGaGD0=
github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk=
github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 h1:Mn26/9ZMNWSw9C9ERFA1PUxfmGpolnw2v0bKOREu5ew=
github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I=
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.0.12 h1:p9dKCg8i4gmOxtv35DvrYoWqYzQrvEVdjQ762Y0OqZE=
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lucas-clemente/quic-go v0.27.1 h1:sOw+4kFSVrdWOYmUjufQ9GBVPqZ+tu+jMtXxXNmRJyk=
github.com/lucas-clemente/quic-go v0.27.1/go.mod h1:AzgQoPda7N+3IqMMMkywBKggIFo2KT6pfnlrQ2QieeI=
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc=
github.com/marten-seemann/qtls-go1-16 v0.1.5 h1:o9JrYPPco/Nukd/HpOHMHZoBDXQqoNtUCmny98/1uqQ=
github.com/marten-seemann/qtls-go1-16 v0.1.5/go.mod h1:gNpI2Ol+lRS3WwSOtIUUtRwZEQMXjYK+dQSBFbethAk=
github.com/marten-seemann/qtls-go1-17 v0.1.1 h1:DQjHPq+aOzUeh9/lixAGunn6rIOQyWChPSI4+hgW7jc=
github.com/marten-seemann/qtls-go1-17 v0.1.1/go.mod h1:C2ekUKcDdz9SDWxec1N/MvcXBpaX9l3Nx67XaR84L5s=
github.com/marten-seemann/qtls-go1-18 v0.1.1 h1:qp7p7XXUFL7fpBvSS1sWD+uSqPvzNQK43DH+/qEkj0Y=
github.com/marten-seemann/qtls-go1-18 v0.1.1/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
github.com/miekg/dns v1.1.49 h1:qe0mQU3Z/XpFeE+AEBo2rqaS1IPBJ3anmqZ4XiZJVG8=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E=
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.13.0 h1:7lLHu94wT9Ij0o6EWWclhu0aOh32VxhkwEJvzuWPeak=
github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY=
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pires/go-proxyproto v0.6.2 h1:KAZ7UteSOt6urjme6ZldyFm4wDe/z0ZUP0Yv0Dos0d8=
github.com/pires/go-proxyproto v0.6.2/go.mod h1:Odh9VFOZJCf9G8cLW5o435Xf1J95Jw9Gw5rnCjcwzAY=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/refraction-networking/utls v1.1.0 h1:dKXJwSqni/t5csYJ+aQcEgqB7AMWYi6EUc9u3bEmjX0=
github.com/refraction-networking/utls v1.1.0/go.mod h1:tz9gX959MEFfFN5whTIocCLUG57WiILqtdVxI8c6Wj0=
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg=
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/sagernet/sing v0.0.0-20220605012533-e0f722558141 h1:YN9EeHRIFYKei1woz2OucLTSpiNrULAHH+jl6upDemQ=
github.com/sagernet/sing v0.0.0-20220605012533-e0f722558141/go.mod h1:w2HnJzXKHpD6F5Z/9XlSD4qbcpHY2RSZuQnFzqgELMg=
github.com/sagernet/sing-shadowsocks v0.0.0-20220605012719-1e22882ea853 h1:t1pn8v3kPvlaST/fY7GsPV8yIUFJDXzNcmSYQ+nrbEM=
github.com/sagernet/sing-shadowsocks v0.0.0-20220605012719-1e22882ea853/go.mod h1:Afu8UIFlEwxTnlidTMSWqWHIduAWOrwY0tFr2LxBACQ=
github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb h1:XfLJSPIOUX+osiMraVgIrMR27uMXnRJWGm1+GL8/63U=
github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb/go.mod h1:bR6DqgcAl1zTcOX8/pE2Qkj9XO00eCNqmKb7lXP8EAg=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY=
github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM=
github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0=
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw=
github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI=
github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU=
github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag=
github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg=
github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw=
github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y=
github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q=
github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ=
github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I=
github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0=
github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ=
github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk=
github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4=
github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw=
github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e h1:5QefA066A1tF8gHIiADmOVOV5LS43gt3ONnlEl3xkwI=
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e/go.mod h1:5t19P9LBIrNamL6AcMQOncg/r10y3Pc01AbHeMhwlpU=
github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=
github.com/xtls/go v0.0.0-20210920065950-d4af136d3672 h1:4mkzGhKqt3JO1BWYjtD3iRFyAx4ow67hmSqOcGjuxqQ=
github.com/xtls/go v0.0.0-20210920065950-d4af136d3672/go.mod h1:YGGVbz9cOxyKFUmhW7LGaLZaMA0cPlHJinvAmVxEMSU=
github.com/xtls/xray-core v1.5.7 h1:xQqUpRtzVHL/to2sDmvgn9GelD0jIwC0I8B36hbDgDo=
github.com/xtls/xray-core v1.5.7/go.mod h1:H4q5qoCwtsYeF06Zsb0tk8oV8UuaQ9NdnOaPv49Cj7I=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.starlark.net v0.0.0-20220328144851-d1966c6b9fcd h1:Uo/x0Ir5vQJ+683GXB9Ug+4fcjsbp7z7Ul8UaZbhsRM=
go.starlark.net v0.0.0-20220328144851-d1966c6b9fcd/go.mod h1:t3mmBBPzAVvK0L0n1drDmrQsJ8FoIx4INCqVMTr/Zo0=
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM=
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20220518205345-8578da9835fd h1:x1GptNaTtxPAlTVIAJk61fuXg0y17h09DTxyb+VNC/k=
golang.org/x/mobile v0.0.0-20220518205345-8578da9835fd/go.mod h1:pe2sM7Uk+2Su1y7u/6Z8KJ24D7lepUjFZbhFOrmDfuQ=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o=
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220531201128-c960675eff93 h1:MYimHLfoXEpOhqd/zgoA/uoXzHB86AEky4LAx5ij9xA=
golang.org/x/net v0.0.0-20220531201128-c960675eff93/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f h1:Ax0t5p6N38Ga0dThY21weqDEyz2oklo4IvDkpigvkD8=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220608164250-635b8c9b7f68 h1:z8Hj/bl9cOV2grsOpEaQFUaly0JWN3i97mo3jXKJNp0=
golang.org/x/sys v0.0.0-20220608164250-635b8c9b7f68/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.8-0.20211022200916-316ba0b74098/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
golang.org/x/tools v0.1.11-0.20220325154526-54af36eca237 h1:mAhaIX1KEgotq+ju3XYdXUHvll7bzJDTgiDzIAKDdPc=
golang.org/x/tools v0.1.11-0.20220325154526-54af36eca237/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df h1:5Pf6pFKu98ODmgnpvkJ3kFUOQGGLIzLIkbzUHp47618=
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20220527130721-00d5c0f3be58 h1:a221mAAEAzq4Lz6ZWRkcS8ptb2mxoxYSt4N68aRyQHM=
google.golang.org/genproto v0.0.0-20220527130721-00d5c0f3be58/go.mod h1:yKyY4AMRwFiC8yMMNaMi+RkCnjZJt9LoWuvhXjMs+To=
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
google.golang.org/grpc v1.47.0 h1:9n77onPX5F3qfFCqjy9dhn8PbNQsIKeVU04J9G7umt8=
google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
lukechampine.com/blake3 v1.1.7 h1:GgRMhmdsuK8+ii6UZFDL8Nb+VyMwadAgcJyfYHxG6n0=
lukechampine.com/blake3 v1.1.7/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA=
sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck=
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=
+260
View File
@@ -0,0 +1,260 @@
package libv2ray
import (
"fmt"
"io"
"log"
"os"
"strings"
"sync"
"github.com/2dust/AndroidLibV2rayLite/CoreI"
"github.com/2dust/AndroidLibV2rayLite/Process/Escort"
"github.com/2dust/AndroidLibV2rayLite/VPN"
mobasset "golang.org/x/mobile/asset"
v2core "github.com/xtls/xray-core/core"
v2filesystem "github.com/xtls/xray-core/common/platform/filesystem"
v2stats "github.com/xtls/xray-core/features/stats"
v2serial "github.com/xtls/xray-core/infra/conf/serial"
_ "github.com/xtls/xray-core/main/distro/all"
v2internet "github.com/xtls/xray-core/transport/internet"
v2applog "github.com/xtls/xray-core/app/log"
v2commlog "github.com/xtls/xray-core/common/log"
)
const (
v2Assert = "xray.location.asset"
assetperfix = "/dev/libv2rayfs0/asset"
)
/*V2RayPoint V2Ray Point Server
This is territory of Go, so no getter and setters!
*/
type V2RayPoint struct {
SupportSet V2RayVPNServiceSupportsSet
statsManager v2stats.Manager
dialer *VPN.ProtectedDialer
status *CoreI.Status
escorter *Escort.Escorting
v2rayOP *sync.Mutex
closeChan chan struct{}
PackageName string
PackageCodePath string
DomainName string
ConfigureFileContent string
EnableLocalDNS bool
ForwardIpv6 bool
ProxyOnly bool
}
/*V2RayVPNServiceSupportsSet To support Android VPN mode*/
type V2RayVPNServiceSupportsSet interface {
Setup(Conf string) int
Prepare() int
Shutdown() int
Protect(int) int
OnEmitStatus(int, string) int
}
/*RunLoop Run V2Ray main loop
*/
func (v *V2RayPoint) RunLoop() (err error) {
v.v2rayOP.Lock()
defer v.v2rayOP.Unlock()
//Construct Context
v.status.PackageName = v.PackageName
v.status.PackageCodePath = v.PackageCodePath
if !v.status.IsRunning {
v.closeChan = make(chan struct{})
v.dialer.PrepareResolveChan()
go v.dialer.PrepareDomain(v.DomainName, v.closeChan)
go func() {
select {
// wait until resolved
case <-v.dialer.ResolveChan():
// shutdown VPNService if server name can not reolved
if !v.dialer.IsVServerReady() {
log.Println("vServer cannot resolved, shutdown")
v.StopLoop()
}
// stop waiting if manually closed
case <-v.closeChan:
}
}()
err = v.pointloop()
}
return
}
/*StopLoop Stop V2Ray main loop
*/
func (v *V2RayPoint) StopLoop() (err error) {
v.v2rayOP.Lock()
defer v.v2rayOP.Unlock()
if v.status.IsRunning {
v.shutdownInit()
}
return
}
//Delegate Funcation
func (v *V2RayPoint) GetIsRunning() bool {
return v.status.IsRunning
}
func (v *V2RayPoint) GetIsTRunning() bool {
return v.status.IsTRunning
}
//Delegate Funcation
func (v V2RayPoint) QueryStats(tag string, direct string) int64 {
if v.statsManager == nil {
return 0
}
counter := v.statsManager.GetCounter(fmt.Sprintf("outbound>>>%s>>>traffic>>>%s", tag, direct))
if counter == nil {
return 0
}
return counter.Set(0)
}
func (v *V2RayPoint) shutdownInit() {
close(v.closeChan)
v.statsManager = nil
v.status.Vpoint.Close()
v.status.Vpoint = nil
v.status.IsRunning = false
v.escorter.EscortingDown()
v.SupportSet.Shutdown()
v.SupportSet.OnEmitStatus(0, "Closed")
}
func (v *V2RayPoint) pointloop() error {
log.Println("loading v2ray config")
config, err := v2serial.LoadJSONConfig(strings.NewReader(v.ConfigureFileContent))
if err != nil {
log.Println(err)
return err
}
log.Println("new v2ray core")
inst, err := v2core.New(config)
if err != nil {
log.Println(err)
return err
}
v.status.Vpoint = inst
v.statsManager = inst.GetFeature(v2stats.ManagerType()).(v2stats.Manager)
log.Println("start v2ray core")
v.status.IsRunning = true
if err := v.status.Vpoint.Start(); err != nil {
v.status.IsRunning = false
log.Println(err)
return err
}
v.SupportSet.Prepare()
v.SupportSet.Setup(v.status.GetVPNSetupArg(v.EnableLocalDNS, v.ForwardIpv6))
v.SupportSet.OnEmitStatus(0, "Running")
v.status.IsTRunning = false
if !v.ProxyOnly {
if err := v.runTun2socks(); err != nil {
log.Println(err)
return err
}
v.status.IsTRunning = true
log.Printf("EnableLocalDNS: %v\nForwardIpv6: %v\nDomainName: %s",
v.EnableLocalDNS,
v.ForwardIpv6,
v.DomainName)
}
return nil
}
func initV2Env() {
if os.Getenv(v2Assert) != "" {
return
}
//Initialize asset API, Since Raymond Will not let notify the asset location inside Process,
//We need to set location outside V2Ray
os.Setenv(v2Assert, assetperfix)
//Now we handle read
v2filesystem.NewFileReader = func(path string) (io.ReadCloser, error) {
if strings.HasPrefix(path, assetperfix) {
p := path[len(assetperfix)+1:]
//is it overridden?
//by, ok := overridedAssets[p]
//if ok {
// return os.Open(by)
//}
return mobasset.Open(p)
}
return os.Open(path)
}
}
//Delegate Funcation
func TestConfig(ConfigureFileContent string) error {
initV2Env()
_, err := v2serial.LoadJSONConfig(strings.NewReader(ConfigureFileContent))
return err
}
/*NewV2RayPoint new V2RayPoint*/
func NewV2RayPoint(s V2RayVPNServiceSupportsSet) *V2RayPoint {
initV2Env()
// inject our own log writer
v2applog.RegisterHandlerCreator(v2applog.LogType_Console,
func(lt v2applog.LogType,
options v2applog.HandlerCreatorOptions) (v2commlog.Handler, error) {
return v2commlog.NewLogger(createStdoutLogWriter()), nil
})
dialer := VPN.NewPreotectedDialer(s)
v2internet.UseAlternativeSystemDialer(dialer)
status := &CoreI.Status{}
return &V2RayPoint{
SupportSet: s,
v2rayOP: new(sync.Mutex),
status: status,
dialer: dialer,
escorter: &Escort.Escorting{Status: status},
}
}
func (v V2RayPoint) runTun2socks() error {
v.escorter.EscortingUp()
go v.escorter.EscortRun(
v.status.GetApp("libtun2socks.so"),
v.status.GetTun2socksArgs(v.EnableLocalDNS, v.ForwardIpv6), "")
return nil
}
/*CheckVersion int
This func will return libv2ray binding version.
*/
func CheckVersion() int {
return CoreI.CheckVersion()
}
/*CheckVersionX string
This func will return libv2ray binding version and V2Ray version used.
*/
func CheckVersionX() string {
return fmt.Sprintf("Libv2rayLite V%d, Core V%s", CheckVersion(), v2core.Version())
}
+3
View File
@@ -0,0 +1,3 @@
package libv2ray
//go:generate make all
+1
View File
@@ -0,0 +1 @@
readme.txt
+128
View File
@@ -0,0 +1,128 @@
# Copyright (C) 2009 The Android Open Source Project
#
# 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.
#
#
LOCAL_PATH := $(call my-dir)
ROOT_PATH := $(LOCAL_PATH)
########################################################
## libancillary
########################################################
include $(CLEAR_VARS)
ANCILLARY_SOURCE := fd_recv.c fd_send.c
LOCAL_MODULE := libancillary
#LOCAL_CFLAGS += -I$(LOCAL_PATH)/libancillary
LOCAL_C_INCLUDES := $(LOCAL_PATH)/libancillary
LOCAL_SRC_FILES := $(addprefix libancillary/, $(ANCILLARY_SOURCE))
include $(BUILD_STATIC_LIBRARY)
########################################################
## tun2socks
########################################################
include $(CLEAR_VARS)
LOCAL_CFLAGS := -std=gnu99
LOCAL_CFLAGS += -DBADVPN_THREADWORK_USE_PTHREAD -DBADVPN_LINUX -DBADVPN_BREACTOR_BADVPN -D_GNU_SOURCE
LOCAL_CFLAGS += -DBADVPN_USE_SIGNALFD -DBADVPN_USE_EPOLL
LOCAL_CFLAGS += -DBADVPN_LITTLE_ENDIAN -DBADVPN_THREAD_SAFE
LOCAL_CFLAGS += -DNDEBUG -DANDROID
LOCAL_CFLAGS += -I
LOCAL_STATIC_LIBRARIES := libancillary
LOCAL_C_INCLUDES := \
$(LOCAL_PATH)/badvpn/libancillary \
$(LOCAL_PATH)/badvpn/lwip/src/include/ipv4 \
$(LOCAL_PATH)/badvpn/lwip/src/include/ipv6 \
$(LOCAL_PATH)/badvpn/lwip/src/include \
$(LOCAL_PATH)/badvpn/lwip/custom \
$(LOCAL_PATH)/badvpn \
$(LOCAL_PATH)/libancillary
TUN2SOCKS_SOURCES := \
base/BLog_syslog.c \
system/BReactor_badvpn.c \
system/BSignal.c \
system/BConnection_common.c \
system/BConnection_unix.c \
system/BTime.c \
system/BUnixSignal.c \
system/BNetwork.c \
flow/StreamRecvInterface.c \
flow/PacketRecvInterface.c \
flow/PacketPassInterface.c \
flow/StreamPassInterface.c \
flow/SinglePacketBuffer.c \
flow/BufferWriter.c \
flow/PacketBuffer.c \
flow/PacketStreamSender.c \
flow/PacketPassConnector.c \
flow/PacketProtoFlow.c \
flow/PacketPassFairQueue.c \
flow/PacketProtoEncoder.c \
flow/PacketProtoDecoder.c \
socksclient/BSocksClient.c \
tuntap/BTap.c \
lwip/src/core/udp.c \
lwip/src/core/memp.c \
lwip/src/core/init.c \
lwip/src/core/pbuf.c \
lwip/src/core/tcp.c \
lwip/src/core/tcp_out.c \
lwip/src/core/netif.c \
lwip/src/core/def.c \
lwip/src/core/ip.c \
lwip/src/core/mem.c \
lwip/src/core/tcp_in.c \
lwip/src/core/stats.c \
lwip/src/core/inet_chksum.c \
lwip/src/core/timeouts.c \
lwip/src/core/ipv4/icmp.c \
lwip/src/core/ipv4/igmp.c \
lwip/src/core/ipv4/ip4_addr.c \
lwip/src/core/ipv4/ip4_frag.c \
lwip/src/core/ipv4/ip4.c \
lwip/src/core/ipv4/autoip.c \
lwip/src/core/ipv6/ethip6.c \
lwip/src/core/ipv6/inet6.c \
lwip/src/core/ipv6/ip6_addr.c \
lwip/src/core/ipv6/mld6.c \
lwip/src/core/ipv6/dhcp6.c \
lwip/src/core/ipv6/icmp6.c \
lwip/src/core/ipv6/ip6.c \
lwip/src/core/ipv6/ip6_frag.c \
lwip/src/core/ipv6/nd6.c \
lwip/custom/sys.c \
tun2socks/tun2socks.c \
base/DebugObject.c \
base/BLog.c \
base/BPending.c \
system/BDatagram_unix.c \
flowextra/PacketPassInactivityMonitor.c \
tun2socks/SocksUdpGwClient.c \
udpgw_client/UdpGwClient.c
LOCAL_MODULE := tun2socks
LOCAL_LDLIBS := -ldl -llog
LOCAL_SRC_FILES := $(addprefix badvpn/, $(TUN2SOCKS_SOURCES))
include $(BUILD_SYSTEM)/build-executable.mk
@@ -0,0 +1,79 @@
#!/bin/bash
# Thanks to https://gist.github.com/wenzhixin/43cf3ce909c24948c6e7
# Execute this script in your home directory. Lines 17 and 21 will prompt you for a y/n
# Install Oracle JDK 8
apt-get update
apt-get install -y openjdk-8-jdk
apt-get install -y unzip make expect # NDK stuff
# Get SDK tools (link from https://developer.android.com/studio/index.html#downloads)
wget -q https://dl.google.com/android/repository/sdk-tools-linux-3859397.zip
mkdir android-sdk-linux
unzip sdk*.zip -d android-sdk-linux
# Get NDK (https://developer.android.com/ndk/downloads/index.html)
# wget -q https://dl.google.com/android/repository/android-ndk-r15c-linux-x86_64.zip
# unzip android-ndk*.zip >> /dev/null
ACCEPT_LICENSES_URL=https://gist.githubusercontent.com/xiaokangwang/1489fd223d26581bfec92adb3cb0088e/raw/328eb6925099df5aae3e76790f8232f0fc378f8b/accept-licenses
ACCEPT_LICENSES_ITEM="android-sdk-license-bcbbd656|intel-android-sysimage-license-1ea702d1|android-sdk-license-2742d1c5"
# Let it update itself and install some stuff
cd android-sdk-linux/tools
curl -L -o accept-licenses $ACCEPT_LICENSES_URL
chmod +x accept-licenses
./accept-licenses "./android update sdk --use-sdk-wrapper --all --no-ui" $ACCEPT_LICENSES_ITEM >/dev/null
# Download every build-tools version that has ever existed
# This will save you time! Thank me later for this
#./accept-licenses "./android update sdk --use-sdk-wrapper --all --no-ui --filter 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27" $ACCEPT_LICENSES_ITEM
PACKAGE_PARSE_URL=https://gist.githubusercontent.com/xiaokangwang/06268fb23034ed94bc301880e862da09/raw/afd95cbbe2f8c1d9e7b0277b7c5ef39af756a6ee/parse.awk
reduceout=https://gist.githubusercontent.com/xiaokangwang/4684bdb5c3415b943f52aa4803386480/raw/b46dab1cc60f02c0d87f88f01e27157034218faa/out.awk
cd bin
curl -L -o parse.awk $PACKAGE_PARSE_URL
curl -L -o reduce.awk $reduceout
sudo apt-get install gawk
./sdkmanager --verbose --list |awk -f parse.awk > ~/package_to_install
readarray -t filenames < $HOME/package_to_install
cat $HOME/package_to_install
yes|./sdkmanager --verbose "${filenames[@]}" |awk -f reduce.awk
# If you need additional packages for your app, check available packages with:
# ./android list sdk --all
# install certain packages with:
# ./android update sdk --no-ui --all --filter 1,2,3,<...>,N
# where N is the number of the package in the list (see previous command)
./sdkmanager "ndk-bundle"
# Add the directory containing executables in PATH so that they can be found
echo 'export ANDROID_HOME=$HOME/android-sdk-linux' >> ~/.bashrc
echo 'export PATH=$PATH:$ANDROID_HOME/tools:$ANDROID_HOME/platform-tools' >> ~/.bashrc
# echo 'export NDK_HOME=$HOME/android-ndk-r15c' >> ~/.bashrc
# echo 'export ANDROID_NDK_HOME=$NDK_HOME' >> ~/.bashrc
source ~/.bashrc
# Make sure you can execute 32 bit executables if this is 64 bit machine, otherwise skip this
dpkg --add-architecture i386
apt-get update
apt-get install -y libc6:i386 libstdc++6:i386 zlib1g:i386
+32
View File
@@ -0,0 +1,32 @@
package libv2ray
// This struct creates our own log writer without datatime stamp
// As Android adds time stamps on each line
import (
"log"
"os"
v2commlog "github.com/xtls/xray-core/common/log"
)
type consoleLogWriter struct {
logger *log.Logger
}
func (w *consoleLogWriter) Write(s string) error {
w.logger.Print(s)
return nil
}
func (w *consoleLogWriter) Close() error {
return nil
}
// This logger won't print data/time stamps
func createStdoutLogWriter() v2commlog.WriterCreator {
return func() v2commlog.Writer {
return &consoleLogWriter{
logger: log.New(os.Stdout, "", 0)}
}
}
@@ -2,78 +2,78 @@
<resources>
<string name="app_widget_name">Kết nối ngay</string>
<string name="app_tile_name">Kết nối ngay</string>
<string name="app_tile_first_use">Vui lòng thêm một cấu hình vào v2rayNG để sử dụng!</string>
<string name="navigation_drawer_open">Mở Menu ứng dụng</string>
<string name="navigation_drawer_close">Đóng Menu ứng dụng</string>
<string name="app_tile_first_use">Vui lòng thêm một cấu hình vào v2rayNG để sử dụng.</string>
<string name="navigation_drawer_open">Mở menu ứng dụng</string>
<string name="navigation_drawer_close">Đóng menu ứng dụng</string>
<string name="migration_success">Đã chuyển dữ liệu!</string>
<string name="migration_fail">Không thể chuyển dữ liệu!</string>
<!-- Notifications -->
<string name="notification_action_stop_v2ray">Ngắt kết nối v2rayNG</string>
<string name="toast_permission_denied">Vui lòng cấp quyền cần thiết cho v2rayNG! Bạn đã từ chối các quyền cần thiết như Camera hay Bộ nhớ?</string>
<string name="toast_permission_denied">Vui lòng cấp quyền cần thiết cho v2rayNG. Bạn đã từ chối các quyền cần thiết như Camera hay Bộ nhớ.</string>
<string name="notification_action_more">Nhấn để biết thêm</string>
<string name="toast_services_start">Đang bắt đầu v2rayNG...</string>
<string name="toast_services_stop">Đã dừng v2rayNG!</string>
<string name="toast_services_success">Đã bắt đầu v2rayNG!</string>
<string name="toast_services_failure">Không thể bắt đầu v2rayNG, kiểm tra lại cấu hình hoặc khởi động lại thiết bị.</string>
<string name="toast_services_start">Đang bắt đầu dịch vụ v2rayNG.</string>
<string name="toast_services_stop">Đã dừng dịch vụ v2rayNG.</string>
<string name="toast_services_success">Đã bắt đầu dịch vụ v2rayNG.</string>
<string name="toast_services_failure">Không thể bắt đầu dịch vụ, hãy thử kiểm tra lại cấu hình hoặc khởi động lại thiết bị.</string>
<!--ServerActivity-->
<string name="title_server">v2rayNG</string>
<string name="title_server">V2RayNG App :3</string>
<string name="menu_item_add_config">Thêm cấu hình</string>
<string name="menu_item_save_config">Lưu cấu hình</string>
<string name="menu_item_del_config">Xoá cấu hình</string>
<string name="menu_item_import_config_qrcode">Nhập cấu hình từ mã QR</string>
<string name="menu_item_import_config_clipboard">Nhập cấu hình từ Clipboard</string>
<string name="menu_item_import_config_clipboard">Nhập cấu hình từ bộ nhớ tạm thời</string>
<string name="menu_item_import_config_manually_vmess">Nhập thủ công [Vmess]</string>
<string name="menu_item_import_config_manually_vless">Nhập thủ công [VLESS]</string>
<string name="menu_item_import_config_manually_ss">Nhập thủ công [Shadowsocks]</string>
<string name="menu_item_import_config_manually_socks">Nhập thủ công [Socks]</string>
<string name="menu_item_import_config_manually_trojan">Nhập thủ công [Trojan]</string>
<string name="menu_item_import_config_custom">Nâng cao / Cấu hình tùy chỉnh</string>
<string name="menu_item_import_config_custom_clipboard">Nhập cấu hình tùy chỉnh từ Clipboard</string>
<string name="menu_item_import_config_custom_clipboard">Nhập cấu hình tùy chỉnh từ bộ nhớ tạm thời</string>
<string name="menu_item_import_config_custom_local">Nhập cấu hình tùy chỉnh từ Tệp</string>
<string name="menu_item_import_config_custom_url">Nhập cấu hình tùy chỉnh từ URL</string>
<string name="menu_item_import_config_custom_url_scan">Nhập cấu hình tùy chỉnh quét URL</string>
<string name="del_config_comfirm">Bạn có muốn xóa cấu hình không?</string>
<string name="del_config_comfirm">Bạn có muốn xóa cấu hình ?</string>
<string name="server_lab_remarks">Tên cấu hình</string>
<string name="server_lab_address">Địa chỉ</string>
<string name="server_lab_port">Cổng</string>
<string name="server_lab_id">ID</string>
<string name="server_lab_alterid">ID thay thế</string>
<string name="server_lab_id">Địa chỉ ID</string>
<string name="server_lab_alterid">alterId</string>
<string name="server_lab_security">Bảo mật</string>
<string name="server_lab_network">Kiểu kết nối</string>
<string name="server_lab_more_function">Nâng cao</string>
<string name="server_lab_head_type">Loại Head</string>
<string name="server_lab_head_type">Kiểu Head</string>
<string name="server_lab_mode_type">Chế độ gRPC</string>
<string name="server_lab_request_host">Yêu cầu host (Host/WS/H2) / Bảo mật QUIC</string>
<string name="server_lab_path">Đường dẫn (WS/H2) / Khóa QUIC / KCP seed / Dịch vụ gRPC</string>
<string name="server_lab_stream_security">TLS</string>
<string name="server_lab_allow_insecure">Bỏ qua xác minh chứng chỉ</string>
<string name="server_lab_request_host">Yêu cầu host(host/ws host/h2 host)/Bảo mật QUIC</string>
<string name="server_lab_path">Đường dẫn (ws path/h2 path)/QUIC key/kcp seed/gRPC serviceName</string>
<string name="server_lab_stream_security">tls</string>
<string name="server_lab_allow_insecure">allowInsecure</string>
<string name="server_lab_sni">Địa chỉ SNI</string>
<string name="server_lab_address3">Địa chỉ</string>
<string name="server_lab_port3">Cổng</string>
<string name="server_lab_id3">Mật khẩu</string>
<string name="server_lab_security3">Bảo mật</string>
<string name="server_lab_id4">Mật khẩu (Bổ sung)</string>
<string name="server_lab_security4">Tên người dùng (Bổ sung)</string>
<string name="server_lab_id4">Mật khẩu(Bổ sung)</string>
<string name="server_lab_security4">Tên người dùng(Bổ sung)</string>
<string name="server_lab_encryption">Mã hoá</string>
<string name="server_lab_flow">Kiểm soát lưu lượng</string>
<string name="toast_success">Thành công!</string>
<string name="toast_failure">Đã xảy ra lỗi, vui lòng thử lại!</string>
<string name="server_lab_flow">flow</string>
<string name="toast_success">Đã thực hiện thành công thao tác của bạn, Nếu có gì đó không ổn, hãy thao tác lại.</string>
<string name="toast_failure">Đã xảy ra lỗi, hãy thử kiểm tra lại hoặc thử lại.</string>
<string name="toast_none_data">Không có gì ở đây</string>
<string name="toast_incorrect_protocol">Không đúng Protocol</string>
<string name="toast_decoding_failed">Không thể giải mã</string>
<string name="title_file_chooser">Vui lòng chọn tệp cấu hình!</string>
<string name="toast_require_file_manager">Vui lòng cài đặt trình quản lý tệp để tiếp tục!</string>
<string name="toast_incorrect_protocol">Không đúng protocol</string>
<string name="toast_decoding_failed">Không thể decode</string>
<string name="title_file_chooser">Vui lòng chọn tệp cấu hình</string>
<string name="toast_require_file_manager">Vui lòng cài đặt trình quản lý tệp để tiếp tục.</string>
<string name="server_customize_config">Cấu hình tùy chỉnh</string>
<string name="toast_config_file_invalid">Cấu hình không hợp lệ!</string>
<string name="toast_config_file_invalid">Cấu hình không hợp lệ</string>
<string name="server_lab_content">Nội dung</string>
<string name="toast_none_data_clipboard">Không có dữ liệu nào trong Clipboard!</string>
<string name="toast_invalid_url">URL không hợp lệ hoặc trống!</string>
<string name="server_lab_need_inbound">Vui lòng đảm bảo cấu hình tùy chỉnh này không bị lỗi trước khi sử dụng!</string>
<string name="toast_malformed_josn">Cấu hình không hợp lệ!</string>
<string name="server_lab_request_host6">Host (SNI) (Bổ sung)</string>
<string name="toast_asset_copy_failed">Không thể sao chép tệp tin, hãy dùng trình quản lý tệp!</string>
<string name="toast_none_data_clipboard">Không có dữ liệu nào trong bộ nhớ tạm thời</string>
<string name="toast_invalid_url">URL không hợp lệ hoặc không có gì</string>
<string name="server_lab_need_inbound">Vui lòng đảm bảo cấu hình tùy chỉnh này không bị lỗi trước khi sử dụng. v2rayNG được Dịch Tiếng Việt bởi CuynuTT😘</string>
<string name="toast_malformed_josn">Cấu hình không hợp lệ </string>
<string name="server_lab_request_host6">Host(SNI)(Bổ sung)</string>
<string name="toast_asset_copy_failed">Không thể sao chép tệp tin, hãy dùng trình quản lý tệp</string>
<string name="menu_item_add_file">Thêm tệp</string>
<string name="menu_item_download_file">Tải xuống tệp tin</string>
@@ -86,15 +86,15 @@
<string name="menu_item_select_proxy_app">Tự động chọn ứng dụng Proxy</string>
<string name="msg_downloading_content">Đang tải xuống nội dung...</string>
<string name="menu_item_export_proxy_app">Xuất và sao chép</string>
<string name="menu_item_import_proxy_app">Nhập từ Clipboard</string>
<string name="menu_item_import_proxy_app">Nhập từ bộ nhớ tạm thời</string>
<!-- Preferences -->
<string name="title_settings">Cài đặt</string>
<string name="title_advanced">Cài đặt nâng cao</string>
<string name="title_vpn_settings">Cài đặt VPN</string>
<string name="title_pref_per_app_proxy">Proxy ứng dụng</string>
<string name="summary_pref_per_app_proxy">- Chung: Ứng dụng đã chọn sẽ kết nối Proxy, chưa lựa chọn sẽ kết nối trực tiếp. \n- Bỏ qua kết nối: Ứng dụng được chọn sẽ trực tiếp kết nối, không lựa chọn sẽ kết nối qua Proxy. \n- Lựa chọn để tự động chọn ứng dụng Proxy trong Menu.</string>
<string name="title_vpn_settings">Cài đặt cho VPN</string>
<string name="title_pref_per_app_proxy">Proxy cho ứng dụng</string>
<string name="summary_pref_per_app_proxy">Chung: Ứng dụng đã chọn sẽ kết nối Proxy, Chưa lựa chọn sẽ kết nối trực tiếp; \nBỏ qua kết nối: Ứng dụng được chọn sẽ trực tiếp kết nối, không lựa chọn Proxy. \nLựa chọn để tự động chọn ứng dụng Proxy trong Menu.</string>
<string name="title_pref_mux_enabled">Cho phép Mux</string>
<string name="summary_pref_mux_enabled">Bật lên có thể làm tăng tốc độ mạng và chuyển mạng nhanh hơn.</string>
@@ -102,14 +102,16 @@
<string name="title_pref_speed_enabled">Cho phép hiển thị tốc độ mạng</string>
<string name="summary_pref_speed_enabled">Hiển thị tốc độ mạng hiện tại trên thanh thông báo.\nBiểu tượng trên thanh trạng thái có thể thay đổi tùy vào mức sử dụng.</string>
<string name="title_pref_sniffing_enabled">Bật tính năng phát hiện luồng</string>
<string name="summary_pref_sniffing_enabled">Thử chuyển kết nối hiện tại của bạn qua trung gian để trung gian xử lý kết nối về lại cho bạn. (Mặc định là bật, hãy tắt nó nếu kết nối không ổn định)</string>
<string name="title_pref_sniffing_enabled">Cho phép Sniffing</string>
<string name="summary_pref_sniffing_enabled">Thử chuyển kết nối hiện tại của bạn qua trung gian để trung gian xử lý kết nối về lại cho bạn (Mặc định là bật, hãy tắt nó nếu kết nối không ổn định.)</string>
<string name="title_pref_local_dns_enabled">Cho phép DNS cục bộ</string>
<string name="summary_pref_local_dns_enabled">DNS được xử lý bởi mô đun của lõi DNS. (Khuyến nghị, nếu cần định tuyến Bỏ qua mạng LAN và địa chỉ đất liền)</string>
<string name="summary_pref_local_dns_enabled">DNS được xử lý bởi mô đun của lõi DNS.
(Khuyến cáo, nếu cần lộ trình Bẻ khoá LAN và
địa chỉ mainland)</string>
<string name="title_pref_fake_dns_enabled">Cho phép DNS giả</string>
<string name="summary_pref_fake_dns_enabled">DNS cục bộ trả về địa chỉ IP giả. (Nhanh hơn, nhưng có thể không hoạt động với một số ứng dụng)</string>
<string name="summary_pref_fake_dns_enabled">DNS cục bộ trả về địa chỉ IP giả (Nhanh hơn, nhưng có thể không hoạt động với một số ứng dụng)</string>
<string name="title_pref_prefer_ipv6">Ưu tiên IPv6</string>
<string name="summary_pref_prefer_ipv6">Ưu tiên sử dụng địa chỉ IPv6 cho kết nối và lộ trình.</string>
@@ -122,17 +124,17 @@
<string name="title_pref_remote_dns">Điều khiển DNS (Bổ sung)</string>
<string name="summary_pref_remote_dns">DNS</string>
<string name="title_pref_vpn_dns">VPN DNS (Chỉ IPv4/IPv6)</string>
<string name="title_pref_vpn_dns">VPN DNS (Chỉ IPv4/v6)</string>
<string name="title_pref_domestic_dns">DNS trong nước (Bổ sung)</string>
<string name="title_pref_domestic_dns">Domestic DNS (Bổ sung)</string>
<string name="summary_pref_domestic_dns">DNS</string>
<string name="title_pref_proxy_sharing_enabled">Cho phép kết nối từ mạng LAN</string>
<string name="summary_pref_proxy_sharing_enabled">Các thiết bị khác có thể kết nối đến Proxy bởi địa chỉ IP thông qua Socks/HTTP, chỉ bật khi bạn tin tưởng kết nối đó.</string>
<string name="toast_warning_pref_proxysharing_short">Cho phép kết nối từ mạng LAN, đảm bảo rằng bạn tin tưởng kết nối hiện tại!</string>
<string name="summary_pref_proxy_sharing_enabled">Các thiết bị khác có thể kết nối đến proxy bởi địa chỉ IP thông qua socks/http, Chỉ bật khi bạn tin tưởng kết nối để tránh kết nối lạ.</string>
<string name="toast_warning_pref_proxysharing_short">Cho phép kết nối từ mạng LAN, Đảm bảo rằng bạn tin tưởng kết nối hiện tại.</string>
<string name="title_pref_allow_insecure">Cho phép đặt lại Bỏ qua xác minh chứng chỉ</string>
<string name="summary_pref_allow_insecure">Khi kết nối TLS, đặt cài đặt Bỏ qua xác minh chứng chỉ thành mặc định.</string>
<string name="title_pref_allow_insecure">Cho phép đặt lại allowInsecure</string>
<string name="summary_pref_allow_insecure">Khi kết nối TLS, đặt cài đặt allowInsecure thành mặc định</string>
<string name="title_pref_socks_port">Cổng Proxy SOCKS5</string>
<string name="summary_pref_socks_port">Cổng Proxy SOCKS5</string>
@@ -147,17 +149,17 @@
<string name="summary_pref_confirm_remove">Hiển thị thông báo xác nhận xoá cấu hình khi bạn xoá một cấu hình.</string>
<string name="title_pref_feedback">Phản hồi lỗi</string>
<string name="summary_pref_feedback">Phản hồi cải tiến hoặc lỗi lên GitHub</string>
<string name="summary_pref_feedback">Phản hồi cải tiến hoặc bug lên GitHub</string>
<string name="summary_pref_tg_group">Tham gia nhóm Telegram</string>
<string name="toast_tg_app_not_found">Không tìm thấy ứng dụng Telegram</string>
<string name="title_pref_promotion">Quảng cáo Server</string>
<string name="summary_pref_promotion">Quảng cáo, nhấn để biết thêm (Ủng hộ có thể được gỡ bỏ)</string>
<string name="summary_pref_promotion">Quảng cáo,nhấn để biết thêm(Ủng hộ có thể được gỡ bỏ)</string>
<string name="title_core_loglevel">Mức độ nhật ký</string>
<string name="title_mode">Chế độ kết nối</string>
<string name="title_mode_help">Nhấn vào đây nếu bạn cần trợ giúp!</string>
<string name="title_language">Ngôn ngữ</string>
<string name="title_mode_help">Nhấn vào đây nếu bạn cần trợ giúp</string>
<string name="title_language">Ngôn ngữ ứng dụng</string>
<string name="title_logcat">Nhật ký hoạt động</string>
<string name="logcat_copy">Sao chép nhật ký</string>
@@ -173,30 +175,30 @@
<string name="title_sub_update">Cập nhật các gói đăng ký</string>
<string name="title_ping_all_server">Ping tất cả máy chủ</string>
<string name="title_real_ping_all_server">Kiểm tra máy chủ</string>
<string name="title_user_asset_setting">Tệp Geo asset</string>
<string name="title_user_asset_setting">Tệp Geo assets</string>
<string name="title_sort_by_test_results">Sắp xếp lại theo lần kiểm tra cuối cùng</string>
<string name="title_filter_config">Lọc cấu hình theo các gói đăng ký</string>
<string name="filter_config_all">Hiển thị tất cả các gói đăng ký</string>
<string name="tasker_start_service">Bắt đầu v2rayNG</string>
<string name="tasker_start_service">Bắt đầu dịch vụ</string>
<string name="tasker_setting_confirm">Xác nhận</string>
<string name="routing_settings_title">Cài đặt lộ trình</string>
<string name="routing_settings_tips">Được phân cách bằng dấu phẩy (,). Hãy nhớ nó để lưu lại!</string>
<string name="routing_settings_tips">Được phân cách bằng dấu chấm phẩy(,),Hãy nhớ nó để lưu lại.</string>
<string name="routing_settings_save">Lưu lại</string>
<string name="routing_settings_delete">Xoá</string>
<string name="routing_settings_scan_replace">Quét và thay thế</string>
<string name="routing_settings_scan_append">Quét và nối</string>
<string name="routing_settings_default_rules">Đặt luật cho lộ trình mặc định</string>
<string name="routing_settings_scan_replace"> và thay thế</string>
<string name="routing_settings_scan_append"> và nối</string>
<string name="routing_settings_default_rules">Đặt luật lệ lộ trình mặc định</string>
<string name="connection_test_pending">Kiểm tra kết nối</string>
<string name="connection_test_testing">Đang kiểm tra kết nối mạng...</string>
<string name="connection_test_available">Kiểm tra kết nối mạng thành công! Ping hiện tại là %d</string>
<string name="connection_test_error">Lỗi kết nối mạng, hãy thử đổi cấu hình hoặc kiểm tra lại! Mã lỗi: %s</string>
<string name="connection_test_fail">Không có kết nối mạng!</string>
<string name="connection_test_testing">Đang kiểm tra kết nối mạng</string>
<string name="connection_test_available">Đã kiểm tra kết nối mạng thành công, Ping hiện tại là %d</string>
<string name="connection_test_error">Lỗi kết nối mạng hãy thử đổi cấu hình hoặc kiểm tra lại. Mã lỗi: %s</string>
<string name="connection_test_fail">Không có kết nối mạng</string>
<string name="connection_test_error_status_code">Mã lỗi: #%d</string>
<string name="connection_connected">Đã kết nối, hãy nhấn vào đây để kiểm tra kết nối mạng!</string>
<string name="connection_not_connected">Chưa kết nối, hãy chọn một cấu hình để kết nối!</string>
<string name="connection_connected">Đã kết nối, hãy nhấn vào đây để kiểm tra kết nối mạng.</string>
<string name="connection_not_connected">Chưa kết nối, hãy thêm một cấu hình để kết nối. Đừng để bị lừa đảo bởi cấu hình mất tiền,Dịch TV bởi CuynuTT😘</string>
<string-array name="share_method">
<item>Xuất ra mã QR (Chụp màn hình để lưu)</item>
@@ -205,17 +207,17 @@
</string-array>
<string-array name="routing_tag">
<item>Proxy URL hoặc IP</item>
<item>Direct URL hoặc IP</item>
<item>proxy URL hoặc IP</item>
<item>direct URL hoặc IP</item>
<item>URL đã chặn hoặc IP</item>
</string-array>
<string-array name="routing_mode">
<item>Proxy toàn cầu</item>
<item>Bỏ qua địa chỉ LAN rồi Proxy</item>
<item>Bỏ qua địa chỉ đất liền rồi Proxy</item>
<item>Bỏ qua LAN và địa chỉ đất liền rồi Proxy</item>
<item>Kết nối trực tiếp toàn cầu</item>
<item>Proxy Global</item>
<item>Bẻ khoá địa chỉ LAN rồi proxy</item>
<item>Bẻ khoá địa chỉ mainland rồi proxy</item>
<item>Bẻ khoá LAN và địa chỉ mainland rồi proxy</item>
<item>Trực tiếp Global</item>
</string-array>
<string-array name="mode_entries">
+226
View File
@@ -0,0 +1,226 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_widget_name">开关</string>
<string name="app_tile_name">开关</string>
<string name="app_tile_first_use">初次使用此功能请先用APP添加配置</string>
<string name="navigation_drawer_open">Open navigation drawer</string>
<string name="navigation_drawer_close">Close navigation drawer</string>
<string name="migration_success">数据迁移成功!</string>
<string name="migration_fail">数据迁移失败啦!</string>
<!-- Notifications -->
<string name="notification_action_stop_v2ray">停止</string>
<string name="toast_permission_denied">无法取得权D:\vssHotel\SourceCode\Hotel.root\Hotel\Clubank.Hotel\FrontCounter\CheckoutListForm.cs限</string>
<string name="notification_action_more">点击了解更多</string>
<string name="toast_services_start">启动服务中</string>
<string name="toast_services_stop">关闭中</string>
<string name="toast_services_success">启动服务成功</string>
<string name="toast_services_failure">启动服务失败</string>
<!--ServerActivity-->
<string name="title_server">配置文件</string>
<string name="menu_item_add_config">添加配置</string>
<string name="menu_item_save_config">保存配置</string>
<string name="menu_item_del_config">删除配置</string>
<string name="menu_item_import_config_qrcode">扫描二维码</string>
<string name="menu_item_import_config_clipboard">从剪贴板导入</string>
<string name="menu_item_import_config_manually_vmess">手动输入[Vmess]</string>
<string name="menu_item_import_config_manually_vless">手动输入[VLESS]</string>
<string name="menu_item_import_config_manually_ss">手动输入[Shadowsocks]</string>
<string name="menu_item_import_config_manually_socks">手动输入[Socks]</string>
<string name="menu_item_import_config_manually_trojan">手动输入[Trojan]</string>
<string name="menu_item_import_config_custom">自定义配置</string>
<string name="menu_item_import_config_custom_clipboard">从剪贴板导入自定义配置</string>
<string name="menu_item_import_config_custom_local">从本地导入自定义配置</string>
<string name="menu_item_import_config_custom_url">剪贴板URL导入自定义配置</string>
<string name="menu_item_import_config_custom_url_scan">扫描URL导入自定义配置</string>
<string name="del_config_comfirm">确认删除?</string>
<string name="server_lab_remarks">别名(remarks)</string>
<string name="server_lab_address">地址(address)</string>
<string name="server_lab_port">端口(port)</string>
<string name="server_lab_id">用户ID(id)</string>
<string name="server_lab_alterid">额外ID(alterId)</string>
<string name="server_lab_security">加密方式(security)</string>
<string name="server_lab_network">传输协议(network)</string>
<string name="server_lab_more_function">底层传输方式(transport)</string>
<string name="server_lab_head_type">伪装类型(type)</string>
<string name="server_lab_mode_type">gRPC 传输模式 (mode)</string>
<string name="server_lab_request_host">伪装域名(host)(host/ws host/h2 host)/QUIC 加密方式</string>
<string name="server_lab_path">path(ws path/h2 path)/QUIC 加密密钥/kcp seed/gRPC serviceName</string>
<string name="server_lab_stream_security">传输层安全(tls)</string>
<string name="server_lab_allow_insecure">跳过证书验证(allowInsecure)</string>
<string name="server_lab_sni">SNI</string>
<string name="server_lab_address3">服务器地址</string>
<string name="server_lab_port3">服务器端口</string>
<string name="server_lab_id3">密码</string>
<string name="server_lab_security3">加密方式</string>
<string name="server_lab_id4">密码(可选)</string>
<string name="server_lab_security4">用户名(可选)</string>
<string name="server_lab_encryption">加密(encryption)</string>
<string name="server_lab_flow">流控(flow)</string>
<string name="toast_success">成功</string>
<string name="toast_failure">失败</string>
<string name="toast_none_data">没有数据</string>
<string name="toast_incorrect_protocol">不正确的协议</string>
<string name="toast_decoding_failed">解码失败</string>
<string name="title_file_chooser">选择一个配置文件</string>
<string name="toast_require_file_manager">请安装一个文件管理器</string>
<string name="server_customize_config">自定义配置</string>
<string name="toast_config_file_invalid">无效的配置文件</string>
<string name="server_lab_content">内容</string>
<string name="toast_none_data_clipboard">剪贴板中没有数据</string>
<string name="toast_invalid_url">无效的网址</string>
<string name="server_lab_need_inbound">确保inbounds port和设置中的一致</string>
<string name="toast_malformed_josn">配置格式错误</string>
<string name="server_lab_request_host6">Host(SNI)(可选)</string>
<string name="toast_asset_copy_failed">失败, 请使用文件管理器</string>
<string name="menu_item_add_file">添加文件</string>
<string name="menu_item_download_file">下载文件</string>
<!-- PerAppProxyActivity -->
<string name="msg_dialog_progress">正在加载</string>
<string name="menu_item_search">搜索</string>
<string name="menu_item_select_all">全选</string>
<string name="msg_enter_keywords">输入关键字</string>
<string name="switch_bypass_apps_mode">绕行模式</string>
<string name="menu_item_select_proxy_app">自动选中需代理应用</string>
<string name="msg_downloading_content">正在下载内容</string>
<string name="menu_item_export_proxy_app">导出至剪贴板</string>
<string name="menu_item_import_proxy_app">从剪贴板导入</string>
<!-- Preferences -->
<string name="title_settings">设置</string>
<string name="title_advanced">进阶设置</string>
<string name="title_vpn_settings">VPN 设置</string>
<string name="title_pref_per_app_proxy">分应用代理</string>
<string name="summary_pref_per_app_proxy">常规:勾选的App被代理,未勾选的直连;\n绕行模式:勾选的App直连,未勾选的被代理.\n不明白者在菜单中选择自动选中需代理应用</string>
<string name="title_pref_mux_enabled">启用Mux多路复用</string>
<string name="summary_pref_mux_enabled">开启可能会加速,关闭可能会减少断流</string>
<string name="title_pref_speed_enabled">启用速度显示</string>
<string name="summary_pref_speed_enabled">在通知中显示当前速度\n小图标显示流量的路由情况</string>
<string name="title_pref_sniffing_enabled">启用流量探测</string>
<string name="summary_pref_sniffing_enabled">从流量中探测域名 (默认启用)</string>
<string name="title_pref_local_dns_enabled">启用本地DNS</string>
<string name="summary_pref_local_dns_enabled">DNS 请求导入 core 由 DNS 模块处理(推荐启用 如果需要路由绕过局域网及大陆地址)</string>
<string name="title_pref_fake_dns_enabled">启用虚拟DNS</string>
<string name="summary_pref_fake_dns_enabled">本地返回虚构解析结果 (减低延时 但个别应用可能无法使用)</string>
<string name="title_pref_prefer_ipv6">IPv6优先</string>
<string name="summary_pref_prefer_ipv6">App优先使用IPv6地址连接服务器,同时开启VPN的IPv6路由</string>
<string name="title_pref_routing">路由设置</string>
<string name="title_pref_routing_domain_strategy">域名策略</string>
<string name="title_pref_routing_mode">预定义规则</string>
<string name="title_pref_routing_custom">自定义规则</string>
<string name="title_pref_remote_dns">远程DNS (可选)</string>
<string name="summary_pref_remote_dns">DNS</string>
<string name="title_pref_vpn_dns">VPN DNS (仅支持 IPv4/v6)</string>
<string name="title_pref_domestic_dns">境内DNS (可选)</string>
<string name="summary_pref_domestic_dns">DNS</string>
<string name="title_pref_proxy_sharing_enabled">允许来自局域网的连接</string>
<string name="summary_pref_proxy_sharing_enabled">其他设备可以使用socks/http协议通过您的IP地址连接到代理,仅在受信任的网络中启用以避免未经授权的连接</string>
<string name="toast_warning_pref_proxysharing_short">允许来自局域网的连接,请确保处于受信网络</string>
<string name="title_pref_allow_insecure">跳过证书验证(allowInsecure)</string>
<string name="summary_pref_allow_insecure">传输层安全选tls时,默认跳过证书验证(allowInsecure)</string>
<string name="title_pref_socks_port">SOCKS5代理端口</string>
<string name="summary_pref_socks_port">SOCKS5代理端口</string>
<string name="title_pref_http_port">HTTP代理端口</string>
<string name="summary_pref_http_port">HTTP代理端口</string>
<string name="title_pref_local_dns_port">本地DNS端口</string>
<string name="summary_pref_local_dns_port">本地DNS端口</string>
<string name="title_pref_confirm_remove">删除配置文件确认</string>
<string name="summary_pref_confirm_remove">删除配置文件是否需要用户二次确认</string>
<string name="title_pref_feedback">反馈</string>
<string name="summary_pref_feedback">反馈改进或漏洞至 GitHub</string>
<string name="summary_pref_tg_group">加入Telegram Group</string>
<string name="toast_tg_app_not_found">未找到Telegram app</string>
<string name="title_pref_promotion">推广</string>
<string name="summary_pref_promotion">一些推广,点击查看详情(捐赠可去除)</string>
<string name="title_core_loglevel">日志级别</string>
<string name="title_mode">模式</string>
<string name="title_mode_help">点此查看更多帮助</string>
<string name="title_language">语言</string>
<string name="title_logcat">Logcat</string>
<string name="logcat_copy">复制</string>
<string name="logcat_clear">清除</string>
<string name="title_service_restart">服务重启</string>
<string name="title_del_all_config">删除全部配置</string>
<string name="title_del_invalid_config">删除无效配置(先测试)</string>
<string name="title_export_all">导出全部(非自定义)配置至剪贴板</string>
<string name="title_sub_setting">订阅分组设置</string>
<string name="sub_setting_remarks">备注</string>
<string name="sub_setting_url">可选地址(url)</string>
<string name="sub_setting_enable">启用更新</string>
<string name="title_sub_update">更新订阅</string>
<string name="title_ping_all_server">测试全部配置Tcping</string>
<string name="title_real_ping_all_server">测试全部配置真连接</string>
<string name="title_user_asset_setting">Geo 资源文件</string>
<string name="title_sort_by_test_results">按测试结果排序</string>
<string name="title_filter_config">过滤配置文件</string>
<string name="filter_config_all">所有订阅分组</string>
<string name="tasker_start_service">启动服务</string>
<string name="tasker_setting_confirm">确定</string>
<string name="routing_settings_title">路由设置</string>
<string name="routing_settings_tips">用逗号(,)隔开,可以一行多个,记得保存</string>
<string name="routing_settings_save">保存</string>
<string name="routing_settings_delete">清空</string>
<string name="routing_settings_scan_replace">扫描并替换</string>
<string name="routing_settings_scan_append">扫描并追加</string>
<string name="routing_settings_default_rules">设置默认路由规则</string>
<string name="connection_test_pending">"检查网络连接"</string>
<string name="connection_test_testing">"测试中…"</string>
<string name="connection_test_available">"连接成功:延时 %d 毫秒"</string>
<string name="connection_test_error">"失败:%s"</string>
<string name="connection_test_fail">"无互联网连接"</string>
<string name="connection_test_error_status_code">"状态码无效(#%d"</string>
<string name="connection_connected">"已连接,点击测试连接"</string>
<string name="connection_not_connected">"未连接"</string>
<string-array name="share_method">
<item>二维码</item>
<item>导出至剪贴板</item>
<item>导出完整配置至剪贴板</item>
</string-array>
share_method
<string-array name="routing_tag">
<item>代理的网址或IP</item>
<item>直连的网址或IP</item>
<item>阻止的网址或IP</item>
</string-array>
<string-array name="routing_mode">
<item>全局代理</item>
<item>绕过局域网地址而后代理</item>
<item>绕过大陆地址而后代理</item>
<item>绕过局域网及大陆地址而后代理</item>
<item>全局直连</item>
</string-array>
<string-array name="mode_entries">
<item>VPN</item>
<item>仅代理</item>
</string-array>
</resources>
+226
View File
@@ -0,0 +1,226 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_widget_name">開關</string>
<string name="app_tile_name">開關</string>
<string name="app_tile_first_use">首次使用此功能,請使用此應用程式新增伺服器</string>
<string name="navigation_drawer_open">開啟導覽匣</string>
<string name="navigation_drawer_close">關閉導覽匣</string>
<string name="migration_success">資料遷移成功!</string>
<string name="migration_fail">資料遷移失敗!</string>
<!-- Notifications -->
<string name="notification_action_stop_v2ray">停止</string>
<string name="toast_permission_denied">無法取得此權限</string>
<string name="notification_action_more">瞭解更多</string>
<string name="toast_services_start">啟動服務</string>
<string name="toast_services_stop">停止服務</string>
<string name="toast_services_success">啟動服務成功</string>
<string name="toast_services_failure">啟動服務失敗</string>
<!--ServerActivity-->
<string name="title_server">組態檔案</string>
<string name="menu_item_add_config">新增組態</string>
<string name="menu_item_save_config">儲存組態</string>
<string name="menu_item_del_config">刪除組態</string>
<string name="menu_item_import_config_qrcode">從 QR Code 匯入組態</string>
<string name="menu_item_import_config_clipboard">從剪貼簿匯入組態</string>
<string name="menu_item_import_config_manually_vmess">手動鍵入 [Vmess]</string>
<string name="menu_item_import_config_manually_vless">手動鍵入 [VLESS]</string>
<string name="menu_item_import_config_manually_ss">手動鍵入 [Shadowsocks]</string>
<string name="menu_item_import_config_manually_socks">手動鍵入 [Socks]</string>
<string name="menu_item_import_config_manually_trojan">手動鍵入 [Trojan]</string>
<string name="menu_item_import_config_custom">自訂組態</string>
<string name="menu_item_import_config_custom_clipboard">從剪貼簿匯入自訂組態</string>
<string name="menu_item_import_config_custom_local">從 URL 匯入自訂組態</string>
<string name="menu_item_import_config_custom_url">從 URL 匯入自訂組態</string>
<string name="menu_item_import_config_custom_url_scan">掃描 URL 匯入自訂組態</string>
<string name="del_config_comfirm">確定刪除?</string>
<string name="server_lab_remarks">備註</string>
<string name="server_lab_address">位址</string>
<string name="server_lab_port"></string>
<string name="server_lab_id">使用者 ID</string>
<string name="server_lab_alterid">alterId</string>
<string name="server_lab_security">安全性</string>
<string name="server_lab_network">網路</string>
<string name="server_lab_more_function">底層傳輸方式 (transport)</string>
<string name="server_lab_head_type">標頭類型</string>
<string name="server_lab_mode_type">gRPC 傳輸模式 (mode)</string>
<string name="server_lab_request_host">要求主機 (host)(host/ws host/h2 host)/QUIC 加密方式</string>
<string name="server_lab_path">path(ws path/h2 path)/QUIC 加密金鑰/kcp seed/gRPC serviceName</string>
<string name="server_lab_stream_security">傳輸層安全 (tls)</string>
<string name="server_lab_allow_insecure">跳過憑證驗證 (allowInsecure)</string>
<string name="server_lab_sni">SNI</string>
<string name="server_lab_address3">伺服器位址</string>
<string name="server_lab_port3">伺服器埠</string>
<string name="server_lab_id3">密碼</string>
<string name="server_lab_security3">加密方式</string>
<string name="server_lab_id4">密碼 (可選)</string>
<string name="server_lab_security4">使用者名稱 (可選)</string>
<string name="server_lab_encryption">加密 (encryption)</string>
<string name="server_lab_flow">流程 (flow)</string>
<string name="toast_success">成功</string>
<string name="toast_failure">失敗</string>
<string name="toast_none_data">無資料</string>
<string name="toast_incorrect_protocol">通訊協定不正確</string>
<string name="toast_decoding_failed">解碼失敗</string>
<string name="title_file_chooser">選取一個組態檔</string>
<string name="toast_require_file_manager">請安裝檔案總管。</string>
<string name="server_customize_config">自訂組態</string>
<string name="toast_config_file_invalid">無效組態</string>
<string name="server_lab_content">內容</string>
<string name="toast_none_data_clipboard">剪貼簿內無資料</string>
<string name="toast_invalid_url">URL 無效</string>
<string name="server_lab_need_inbound">​確保 inbounds port 和設定中的一致</string>
<string name="toast_malformed_josn">組態格式不正確</string>
<string name="server_lab_request_host6">Host(SNI)(可選)</string>
<string name="toast_asset_copy_failed">失敗,請使用檔案總管</string>
<string name="menu_item_add_file">新增檔案</string>
<string name="menu_item_download_file">下載檔案</string>
<!-- PerAppProxyActivity -->
<string name="msg_dialog_progress">載入</string>
<string name="menu_item_search">搜尋</string>
<string name="menu_item_select_all">全選</string>
<string name="msg_enter_keywords">輸入關鍵字</string>
<string name="switch_bypass_apps_mode">略過模式</string>
<string name="menu_item_select_proxy_app">自動選中需 Proxy 應用</string>
<string name="msg_downloading_content">正在下載內容</string>
<string name="menu_item_export_proxy_app">匯出至剪貼簿</string>
<string name="menu_item_import_proxy_app">從剪貼簿匯入</string>
<!-- Preferences -->
<string name="title_settings">設定</string>
<string name="title_advanced">進階</string>
<string name="title_vpn_settings">VPN 設定</string>
<string name="title_pref_per_app_proxy">Proxy 個別應用程式</string>
<string name="summary_pref_per_app_proxy">常規:勾選的 App 啟用 Proxy,未勾選的直接連線;\n繞行模式:勾選的 App 直接連線,未勾選的啟用 Proxy。\n可在選單中選擇自動選中需 Proxy 應用</string>
<string name="title_pref_mux_enabled">啟用 Mux</string>
<string name="summary_pref_mux_enabled">啟用或許會加快網路速度,切換或許會閃爍</string>
<string name="title_pref_speed_enabled">啟用速度顯示</string>
<string name="summary_pref_speed_enabled">在通知中顯示當前速度\n小圖示顯示流量的轉送狀況</string>
<string name="title_pref_sniffing_enabled">啟用流量監聽</string>
<string name="summary_pref_sniffing_enabled">從流量中監聽網域 (預設啟用)</string>
<string name="title_pref_local_dns_enabled">啟用本機 DNS</string>
<string name="summary_pref_local_dns_enabled">DNS 請求匯入 core 由 DNS 模塊處理 (建議啟用,如果需要轉送略過區域網路及中國大陸)</string>
<string name="title_pref_fake_dns_enabled">啟用假 DNS</string>
<string name="summary_pref_fake_dns_enabled">本機退回假解析結果 (減低延時,但個別應用可能無法使用)</string>
<string name="title_pref_prefer_ipv6">IPv6 偏好</string>
<string name="summary_pref_prefer_ipv6">App 優先使用 IPv6 位址連線伺服器,同时開啟 VPN 的 IPv6 路由</string>
<string name="title_pref_routing">轉送設定</string>
<string name="title_pref_routing_domain_strategy">網域策略</string>
<string name="title_pref_routing_mode">轉送模式</string>
<string name="title_pref_routing_custom">自訂轉送</string>
<string name="title_pref_remote_dns">遠端 DNS (可選)</string>
<string name="summary_pref_remote_dns">DNS</string>
<string name="title_pref_vpn_dns">VPN DNS (僅支援 IPv4/v6)</string>
<string name="title_pref_domestic_dns">國內 DNS (可選)</string>
<string name="summary_pref_domestic_dns">DNS</string>
<string name="title_pref_proxy_sharing_enabled">允許來自區域網路的連線</string>
<string name="summary_pref_proxy_sharing_enabled">其他裝置可以使用 socks/http 協定透過您的 IP 位址連線到 Proxy,僅在受信任的網路中啟用以避免未經授權的連線</string>
<string name="toast_warning_pref_proxysharing_short">允許來自區域網路的連線,請確保處於受信網路</string>
<string name="title_pref_allow_insecure">跳過憑證驗證 (allowInsecure)</string>
<string name="summary_pref_allow_insecure">傳輸層安全選 tls 時,預設跳過憑證驗證 (allowInsecure)</string>
<string name="title_pref_socks_port">SOCKS5 Proxy 埠</string>
<string name="summary_pref_socks_port">SOCKS5 Proxy 埠</string>
<string name="title_pref_http_port">HTTP Proxy 埠</string>
<string name="summary_pref_http_port">HTTP Proxy 埠</string>
<string name="title_pref_local_dns_port">本機 DNS 埠</string>
<string name="summary_pref_local_dns_port">本機 DNS 埠</string>
<string name="title_pref_confirm_remove">刪除配置文件確認</string>
<string name="summary_pref_confirm_remove">刪除配置文件是否需要用戶二次確認</string>
<string name="title_pref_feedback">意見回饋</string>
<string name="summary_pref_feedback">前往 GitHub 回報錯誤</string>
<string name="summary_pref_tg_group">加入 Telegram 群組</string>
<string name="toast_tg_app_not_found">未找到 Telegram 應用程式</string>
<string name="title_pref_promotion">推廣</string>
<string name="summary_pref_promotion">一些推廣,輕觸以檢視 (捐贈可去除)</string>
<string name="title_core_loglevel">記錄層級</string>
<string name="title_mode">模式</string>
<string name="title_mode_help">輕觸以檢視說明</string>
<string name="title_language">語言</string>
<string name="title_logcat">Logcat</string>
<string name="logcat_copy">複製</string>
<string name="logcat_clear">清除</string>
<string name="title_service_restart">服務重啟</string>
<string name="title_del_all_config">刪除全部組態</string>
<string name="title_del_invalid_config">刪除無效組態 (先偵測)</string>
<string name="title_export_all">匯出全部 (非自訂) 組態至剪貼簿</string>
<string name="title_sub_setting">訂閱分組設定</string>
<string name="sub_setting_remarks">備註</string>
<string name="sub_setting_url">Optional URL</string>
<string name="sub_setting_enable">啟用更新</string>
<string name="title_sub_update">更新訂閱</string>
<string name="title_ping_all_server">偵測所有組態 Tcping</string>
<string name="title_real_ping_all_server">偵測所有組態真延遲</string>
<string name="title_user_asset_setting">Geo 資源檔案</string>
<string name="title_sort_by_test_results">依偵測結果排序</string>
<string name="title_filter_config">過濾組態</string>
<string name="filter_config_all">所有訂閱分組</string>
<string name="tasker_start_service">啟動服務</string>
<string name="tasker_setting_confirm">確定</string>
<string name="routing_settings_title">轉送設定</string>
<string name="routing_settings_tips">以半形逗號「,」分隔,並手動儲存</string>
<string name="routing_settings_save">儲存</string>
<string name="routing_settings_delete">清除</string>
<string name="routing_settings_scan_replace">掃描並取代</string>
<string name="routing_settings_scan_append">掃描並附加</string>
<string name="routing_settings_default_rules">設定預設轉送規則</string>
<string name="connection_test_pending">"測試連線能力"</string>
<string name="connection_test_testing">"測試中……"</string>
<string name="connection_test_available">"成功:%d ms延遲"</string>
<string name="connection_test_error">"測試網際網路連線失敗:%s"</string>
<string name="connection_test_fail">"無法使用網際網路"</string>
<string name="connection_test_error_status_code">"錯誤碼:(#%d)"</string>
<string name="connection_connected">"已連線,輕觸以檢查連線能力"</string>
<string name="connection_not_connected">"未連線"</string>
<string-array name="share_method">
<item>QR Code</item>
<item>匯出至剪貼簿</item>
<item>匯出完整組態至剪貼簿</item>
</string-array>
<string-array name="routing_tag">
<item>Proxy URL 或 IP</item>
<item>直接連線 URL 或 IP</item>
<item>已封鎖的 URL 或 IP</item>
</string-array>
<string-array name="routing_mode">
<item>全域 Proxy</item>
<item>略過區域網路的 Proxy</item>
<item>略過中國大陸的 Proxy</item>
<item>略過區域網路及中國大陸的 Proxy</item>
<item>直接連線</item>
</string-array>
<string-array name="mode_entries">
<item>VPN</item>
<item>僅 Proxy</item>
</string-array>
</resources>
+229
View File
@@ -0,0 +1,229 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name" translatable="false">v2rayNG</string>
<string name="app_widget_name">Switch</string>
<string name="app_tile_name">Switch</string>
<string name="app_tile_first_use">First use of this feature, please use the app to add server</string>
<string name="navigation_drawer_open">Open navigation drawer</string>
<string name="navigation_drawer_close">Close navigation drawer</string>
<string name="migration_success">Data migration success!</string>
<string name="migration_fail">Data migration failed!</string>
<!-- Notifications -->
<string name="notification_action_stop_v2ray">Stop</string>
<string name="toast_permission_denied">Unable to obtain the permission</string>
<string name="notification_action_more">click for more</string>
<string name="toast_services_start">Start Services</string>
<string name="toast_services_stop">Stop Services</string>
<string name="toast_services_success">Start Services Success</string>
<string name="toast_services_failure">Start Services Failure</string>
<!--ServerActivity-->
<string name="title_server">Configuration file</string>
<string name="menu_item_add_config">Add config</string>
<string name="menu_item_save_config">Save config</string>
<string name="menu_item_del_config">Delete config</string>
<string name="menu_item_import_config_qrcode">Import config from QRcode</string>
<string name="menu_item_import_config_clipboard">Import config from Clipboard</string>
<string name="menu_item_import_config_manually_vmess">Type manually[Vmess]</string>
<string name="menu_item_import_config_manually_vless">Type manually[VLESS]</string>
<string name="menu_item_import_config_manually_ss">Type manually[Shadowsocks]</string>
<string name="menu_item_import_config_manually_socks">Type manually[Socks]</string>
<string name="menu_item_import_config_manually_trojan">Type manually[Trojan]</string>
<string name="menu_item_import_config_custom">custom config</string>
<string name="menu_item_import_config_custom_clipboard">Import custom config from Clipboard</string>
<string name="menu_item_import_config_custom_local">Import custom config from locally</string>
<string name="menu_item_import_config_custom_url">Import custom config from URL</string>
<string name="menu_item_import_config_custom_url_scan">Import custom config scan URL</string>
<string name="del_config_comfirm">Confirm delete</string>
<string name="server_lab_remarks">remarks</string>
<string name="server_lab_address">address</string>
<string name="server_lab_port">port</string>
<string name="server_lab_id">id</string>
<string name="server_lab_alterid">alterId</string>
<string name="server_lab_security">security</string>
<string name="server_lab_network">Network</string>
<string name="server_lab_more_function">Transport</string>
<string name="server_lab_head_type">head type</string>
<string name="server_lab_mode_type">gRPC mode</string>
<string name="server_lab_request_host">request host(host/ws host/h2 host)/QUIC security</string>
<string name="server_lab_path">path(ws path/h2 path)/QUIC key/kcp seed/gRPC serviceName</string>
<string name="server_lab_stream_security">tls</string>
<string name="server_lab_allow_insecure">allowInsecure</string>
<string name="server_lab_sni">SNI</string>
<string name="server_lab_address3">address</string>
<string name="server_lab_port3">port</string>
<string name="server_lab_id3">password</string>
<string name="server_lab_security3">security</string>
<string name="server_lab_id4">Password(Optional)</string>
<string name="server_lab_security4">User(Optional)</string>
<string name="server_lab_encryption">encryption</string>
<string name="server_lab_flow">flow</string>
<string name="toast_success">Success</string>
<string name="toast_failure">Failure</string>
<string name="toast_none_data">There is nothing</string>
<string name="toast_incorrect_protocol">Incorrect protocol</string>
<string name="toast_decoding_failed">Decoding failed</string>
<string name="title_file_chooser">Select a Config File</string>
<string name="toast_require_file_manager">Please install a File Manager.</string>
<string name="server_customize_config">Customize Config</string>
<string name="toast_config_file_invalid">Invalid Config</string>
<string name="server_lab_content">Content</string>
<string name="toast_none_data_clipboard">There is no data in the clipboard</string>
<string name="toast_invalid_url">Invalid URL</string>
<string name="server_lab_need_inbound">Ensure inbounds port is consistent with the settings</string>
<string name="toast_malformed_josn">Config malformed</string>
<string name="server_lab_request_host6">Host(SNI)(Optional)</string>
<string name="toast_asset_copy_failed">File copy failed, please use File Manager</string>
<string name="menu_item_add_file">Add files</string>
<string name="menu_item_download_file">Download files</string>
<!-- PerAppProxyActivity -->
<string name="msg_dialog_progress">Loading</string>
<string name="menu_item_search">Search</string>
<string name="menu_item_select_all">Select all</string>
<string name="msg_enter_keywords">Enter keywords</string>
<string name="switch_bypass_apps_mode">Bypass Mode</string>
<string name="menu_item_select_proxy_app">Auto select proxy app</string>
<string name="msg_downloading_content">Downloading content</string>
<string name="menu_item_export_proxy_app">Export to Clipboard</string>
<string name="menu_item_import_proxy_app">Import from Clipboard</string>
<!-- Preferences -->
<string name="title_settings">Settings</string>
<string name="title_advanced">Advanced Settings</string>
<string name="title_vpn_settings">VPN Settings</string>
<string name="title_pref_per_app_proxy">Per-app proxy</string>
<string name="summary_pref_per_app_proxy">General: Checked App is proxy, unchecked direct connection; \nbypass mode: checked app directly connected, unchecked proxy. \nThe option to automatically select the proxy application in the menu</string>
<string name="title_pref_mux_enabled">Enable Mux</string>
<string name="summary_pref_mux_enabled">Enable maybe speed up network and switch network maybe flash</string>
<string name="title_pref_speed_enabled">Enable speed display</string>
<string name="summary_pref_speed_enabled">Display current speed in the notification.\nNotification icon would change based on
usage.</string>
<string name="title_pref_sniffing_enabled">Enable Sniffing</string>
<string name="summary_pref_sniffing_enabled">Try sniff domain from the packet (default on)</string>
<string name="title_pref_local_dns_enabled">Enable local DNS</string>
<string name="summary_pref_local_dns_enabled">DNS processed by cores DNS module (Recommended, if need routing Bypassing LAN and
mainland address)</string>
<string name="title_pref_fake_dns_enabled">Enable fake DNS</string>
<string name="summary_pref_fake_dns_enabled">local DNS returns fake IP address (faster, but it may not work for some apps)</string>
<string name="title_pref_prefer_ipv6">Prefer IPv6</string>
<string name="summary_pref_prefer_ipv6">Prefer IPv6 address and routes</string>
<string name="title_pref_routing">Routing</string>
<string name="title_pref_routing_domain_strategy">Domain strategy</string>
<string name="title_pref_routing_mode">Predefined rules</string>
<string name="title_pref_routing_custom">Custom rules</string>
<string name="title_pref_remote_dns">Remote DNS (Optional)</string>
<string name="summary_pref_remote_dns">DNS</string>
<string name="title_pref_vpn_dns">VPN DNS (only IPv4/v6)</string>
<string name="title_pref_domestic_dns">Domestic DNS (Optional)</string>
<string name="summary_pref_domestic_dns">DNS</string>
<string name="title_pref_proxy_sharing_enabled">Allow connections from the LAN</string>
<string name="summary_pref_proxy_sharing_enabled">Other devices can connect to proxy by your ip address through socks/http, Only enable in trusted network to avoid unauthorized connection</string>
<string name="toast_warning_pref_proxysharing_short">Allow connections from the LAN, Make sure you are in a trusted network</string>
<string name="title_pref_allow_insecure">allowInsecure</string>
<string name="summary_pref_allow_insecure">When TLS, the default allowInsecure</string>
<string name="title_pref_socks_port">SOCKS5 proxy port</string>
<string name="summary_pref_socks_port">SOCKS5 proxy port</string>
<string name="title_pref_http_port">HTTP proxy port</string>
<string name="summary_pref_http_port">HTTP proxy port</string>
<string name="title_pref_local_dns_port">Local DNS port</string>
<string name="summary_pref_local_dns_port">Local DNS port</string>
<string name="title_pref_confirm_remove">Delete configuration file confirmation</string>
<string name="summary_pref_confirm_remove">Whether to delete the configuration file requires a second confirmation by the user</string>
<string name="title_pref_feedback">Feedback</string>
<string name="summary_pref_feedback">Feedback enhancements or bugs to GitHub</string>
<string name="summary_pref_tg_group">Join Telegram Group</string>
<string name="toast_tg_app_not_found">Telegram app not found</string>
<string name="title_pref_promotion">Promotion</string>
<string name="summary_pref_promotion">Promotion,click for details(Donation can be removed)</string>
<string name="title_core_loglevel">Log Level</string>
<string name="title_mode">Mode</string>
<string name="title_mode_help">Click me for more help</string>
<string name="title_language">Language</string>
<string name="title_logcat">Logcat</string>
<string name="logcat_copy">Copy</string>
<string name="logcat_clear">Clear</string>
<string name="title_service_restart">Service restart</string>
<string name="title_del_all_config">Delete all config</string>
<string name="title_del_invalid_config">Delete invalid config(Test first)</string>
<string name="title_export_all">Export non-custom configs to clipboard</string>
<string name="title_sub_setting">Subscription group setting</string>
<string name="sub_setting_remarks">remarks</string>
<string name="sub_setting_url">Optional URL</string>
<string name="sub_setting_enable">enable update</string>
<string name="title_sub_update">Update subscription</string>
<string name="title_ping_all_server">Tcping all configuration</string>
<string name="title_real_ping_all_server">Real delay all configuration</string>
<string name="title_user_asset_setting">Geo asset files</string>
<string name="title_sort_by_test_results">Sorting by test results</string>
<string name="title_filter_config">Filter configuration file</string>
<string name="filter_config_all">All subscription groups</string>
<string name="tasker_start_service">Start Service</string>
<string name="tasker_setting_confirm">Confirm</string>
<string name="routing_settings_title">Routing Settings</string>
<string name="routing_settings_tips">Separated by commas(,),remember to save</string>
<string name="routing_settings_save">Save</string>
<string name="routing_settings_delete">Clear</string>
<string name="routing_settings_scan_replace">Scan and replace</string>
<string name="routing_settings_scan_append">Scan and append</string>
<string name="routing_settings_default_rules"> set default routing rules</string>
<string name="connection_test_pending">Check Connectivity</string>
<string name="connection_test_testing">Testing…</string>
<string name="connection_test_available">Success: HTTP connection took %dms</string>
<string name="connection_test_error">Fail to detect internet connection: %s</string>
<string name="connection_test_fail">Internet Unavailable</string>
<string name="connection_test_error_status_code">Error code: #%d</string>
<string name="connection_connected">Connected, tap to check connection</string>
<string name="connection_not_connected">Not connected</string>
<string-array name="share_method">
<item>QRcode</item>
<item>Export to clipboard</item>
<item>Export full configuration to clipboard</item>
</string-array>
<string-array name="routing_tag">
<item>proxy URL or IP</item>
<item>direct URL or IP</item>
<item>blocked URL or IP</item>
</string-array>
<string-array name="routing_mode">
<item>Global proxy</item>
<item>Bypassing the LAN address then proxy</item>
<item>Bypass mainland address then proxy</item>
<item>Bypassing LAN and mainland address then proxy</item>
<item>Global direct</item>
</string-array>
<string-array name="mode_entries">
<item>VPN</item>
<item>Proxy only</item>
</string-array>
</resources>
+3 -6
View File
@@ -3,7 +3,7 @@
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)
[![Kotlin Version](https://img.shields.io/badge/Kotlin-1.6.10-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)
@@ -13,9 +13,6 @@ A V2Ray client for Android, support [Xray core](https://github.com/XTLS/Xray-cor
<img alt="Get it on Google Play" src="https://play.google.com/intl/en_us/badges/images/generic/en_badge_web_generic.png" width="165" height="64" />
</a>
### Telegram Channel
[github_2dust](https://t.me/github_2dust)
### Usage
#### Geoip and Geosite
@@ -29,8 +26,8 @@ A V2Ray client for Android, support [Xray core](https://github.com/XTLS/Xray-cor
### 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/)
The aar can be compiled from the Golang project under AndroidLibV2rayLite folder. 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`
-1
View File
@@ -7,4 +7,3 @@
/captures
*.apk
signing.properties
*.aar
+24 -54
View File
@@ -1,12 +1,9 @@
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"
buildToolsVersion buildToolsVer
compileOptions {
targetCompatibility = "8"
@@ -18,25 +15,8 @@ android {
minSdkVersion 21
targetSdkVersion Integer.parseInt("$targetSdkVer")
multiDexEnabled true
versionCode 501
versionName "1.7.37"
}
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'
}
}
versionCode 212
versionName "1.0.2"
}
buildTypes {
@@ -44,9 +24,6 @@ android {
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'
}
@@ -54,18 +31,12 @@ android {
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'
}
main.java.srcDirs += 'src/main/kotlin'
}
kotlinOptions {
@@ -105,22 +76,20 @@ dependencies {
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.constraintlayout:constraintlayout:2.1.3'
implementation "androidx.legacy:legacy-support-v4:1.0.0"
implementation "androidx.appcompat:appcompat:1.4.1"
implementation "com.google.android.material:material:1.5.0"
implementation "androidx.cardview:cardview:1.0.0"
implementation "androidx.preference:preference:1.0.0"
implementation "androidx.recyclerview:recyclerview:1.2.1"
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'
implementation 'androidx.activity:activity-ktx:1.4.0'
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.4.1'
//kotlin
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion"
@@ -128,7 +97,7 @@ dependencies {
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 'com.google.code.gson:gson:2.8.6'
implementation 'io.reactivex:rxjava:1.3.4'
implementation 'io.reactivex:rxandroid:1.2.1'
implementation 'com.tbruyelle.rxpermissions:rxpermissions:0.9.4@aar'
@@ -141,11 +110,12 @@ dependencies {
implementation 'com.blacksquircle.ui:language-json:2.1.1'
}
//buildscript {
// repositories {
// google()
// mavenCentral()
// maven { url 'https://maven.google.com' }
// maven { url 'https://jitpack.io' }
// }
//}
buildscript {
repositories {
google()
mavenCentral()
maven { url 'https://maven.google.com' }
maven { url 'https://jitpack.io' }
jcenter()
}
}
+1
View File
@@ -0,0 +1 @@
https://github.com/2dust/v2rayNG/tree/master/AndroidLibV2rayLite
Binary file not shown.
+58
View File
@@ -0,0 +1,58 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in G:\android-sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Gson uses generic type information stored in a class file when working with fields. Proguard
# removes such information by default, so configure it to keep all of it.
-keepattributes Signature
# For using GSON @Expose annotation
-keepattributes *Annotation*
# Gson specific classes
-keep class sun.misc.Unsafe { *; }
-dontwarn org.apache.commons.**
-keep class org.apache.commons.** { *;}
# Disable debug info output
-assumenosideeffects class android.util.Log {
public static boolean isLoggable(java.lang.String,int);
public static int v(...);
public static int i(...);
public static int w(...);
public static int d(...);
public static int e(...);
}
-assumenosideeffects class kotlin.jvm.internal.Intrinsics {
static void checkParameterIsNotNull(java.lang.Object, java.lang.String);
static void checkExpressionValueIsNotNull(java.lang.Object, java.lang.String);
static void throwUninitializedPropertyAccessException(java.lang.String);
}
-dontwarn rx.internal.util.unsafe.**
-keep class rx.internal.util.unsafe.** { *;}
-dontwarn app.dinus.**
-keep class app.dinus.** { *;}
-keepclassmembers class ** {
@com.hwangjr.rxbus.annotation.Subscribe public *;
@com.hwangjr.rxbus.annotation.Produce public *;
}
-keep class libv2ray.** { *;}
+22 -41
View File
@@ -4,24 +4,27 @@
package="com.v2ray.ang">
<supports-screens
android:anyDensity="true"
android:smallScreens="true"
android:normalScreens="true"
android:largeScreens="true"
android:xlargeScreens="true"/>
android:anyDensity="true"
android:smallScreens="true"
android:normalScreens="true"
android:largeScreens="true"
android:xlargeScreens="true"/>
<uses-feature android:name="android.hardware.camera" android:required="false"/>
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/>
<!-- https://developer.android.com/about/versions/11/privacy/package-visibility -->
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"
tools:ignore="QueryAllPackagesPermission" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
tools:ignore="QueryAllPackagesPermission" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!-- <useapplications-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> -->
<!-- <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="com.android.vending.BILLING" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<!-- <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> -->
@@ -31,19 +34,23 @@
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppThemeDayNight"
android:theme="@style/AppTheme"
android:usesCleartextTraffic="true"
tools:targetApi="m">
<activity
android:name=".ui.MainActivity"
android:exported="true"
android:launchMode="singleTask"
android:theme="@style/AppThemeDayNight.NoActionBar">
android:name=".ui.MainActivity"
android:theme="@style/AppTheme.NoActionBar"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
<intent-filter>
<action android:name="android.service.quicksettings.action.QS_TILE_PREFERENCES" />
</intent-filter>
@@ -78,10 +85,6 @@
<activity
android:exported="false"
android:name=".ui.SubSettingActivity" />
<activity
android:exported="false"
android:name=".ui.UserAssetActivity" />
<activity
android:exported="false"
android:name=".ui.SubEditActivity" />
@@ -95,23 +98,6 @@
android:process=":RunSoLibV2RayDaemon"
android:theme="@style/AppTheme.NoActionBar.Translucent" />
<activity
android:exported="true"
android:name=".ui.UrlSchemeActivity">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.BROWSABLE"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="v2rayng"
android:host="install-config" />
</intent-filter>
</activity>
<service
android:name=".service.V2RayVpnService"
android:enabled="true"
@@ -133,11 +119,6 @@
android:process=":RunSoLibV2RayDaemon">
</service>
<service android:name=".service.V2RayTestService"
android:exported="false"
android:process=":RunSoLibV2RayDaemon">
</service>
<receiver
android:exported="true"
android:name=".receiver.WidgetProvider"
@@ -155,7 +136,7 @@
<service
android:exported="true"
android:name=".service.QSTileService"
android:icon="@drawable/ic_stat_name"
android:icon="@drawable/ic_v"
android:label="@string/app_tile_name"
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE"
android:process=":RunSoLibV2RayDaemon">
@@ -0,0 +1,144 @@
/*
* Copyright (C) 2012 The Android Open Source Project
*
* 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.android.vending.billing;
import android.os.Bundle;
/**
* InAppBillingService is the service that provides in-app billing version 3 and beyond.
* This service provides the following features:
* 1. Provides a new API to get details of in-app items published for the app including
* price, type, title and description.
* 2. The purchase flow is synchronous and purchase information is available immediately
* after it completes.
* 3. Purchase information of in-app purchases is maintained within the Google Play system
* till the purchase is consumed.
* 4. An API to consume a purchase of an inapp item. All purchases of one-time
* in-app items are consumable and thereafter can be purchased again.
* 5. An API to get current purchases of the user immediately. This will not contain any
* consumed purchases.
*
* All calls will give a response code with the following possible values
* RESULT_OK = 0 - success
* RESULT_USER_CANCELED = 1 - user pressed back or canceled a dialog
* RESULT_BILLING_UNAVAILABLE = 3 - this billing API version is not supported for the type requested
* RESULT_ITEM_UNAVAILABLE = 4 - requested SKU is not available for purchase
* RESULT_DEVELOPER_ERROR = 5 - invalid arguments provided to the API
* RESULT_ERROR = 6 - Fatal error during the API action
* RESULT_ITEM_ALREADY_OWNED = 7 - Failure to purchase since item is already owned
* RESULT_ITEM_NOT_OWNED = 8 - Failure to consume since item is not owned
*/
interface IInAppBillingService {
/**
* Checks support for the requested billing API version, package and in-app type.
* Minimum API version supported by this interface is 3.
* @param apiVersion the billing version which the app is using
* @param packageName the package name of the calling app
* @param type type of the in-app item being purchased "inapp" for one-time purchases
* and "subs" for subscription.
* @return RESULT_OK(0) on success, corresponding result code on failures
*/
int isBillingSupported(int apiVersion, String packageName, String type);
/**
* Provides details of a list of SKUs
* Given a list of SKUs of a valid type in the skusBundle, this returns a bundle
* with a list JSON strings containing the productId, price, title and description.
* This API can be called with a maximum of 20 SKUs.
* @param apiVersion billing API version that the Third-party is using
* @param packageName the package name of the calling app
* @param skusBundle bundle containing a StringArrayList of SKUs with key "ITEM_ID_LIST"
* @return Bundle containing the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on
* failure as listed above.
* "DETAILS_LIST" with a StringArrayList containing purchase information
* in JSON format similar to:
* '{ "productId" : "exampleSku", "type" : "inapp", "price" : "$5.00",
* "title : "Example Title", "description" : "This is an example description" }'
*/
Bundle getSkuDetails(int apiVersion, String packageName, String type, in Bundle skusBundle);
/**
* Returns a pending intent to launch the purchase flow for an in-app item by providing a SKU,
* the type, a unique purchase token and an optional developer payload.
* @param apiVersion billing API version that the app is using
* @param packageName package name of the calling app
* @param sku the SKU of the in-app item as published in the developer console
* @param type the type of the in-app item ("inapp" for one-time purchases
* and "subs" for subscription).
* @param developerPayload optional argument to be sent back with the purchase information
* @return Bundle containing the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on
* failure as listed above.
* "BUY_INTENT" - PendingIntent to start the purchase flow
*
* The Pending intent should be launched with startIntentSenderForResult. When purchase flow
* has completed, the onActivityResult() will give a resultCode of OK or CANCELED.
* If the purchase is successful, the result data will contain the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on
* failure as listed above.
* "INAPP_PURCHASE_DATA" - String in JSON format similar to
* '{"orderId":"12999763169054705758.1371079406387615",
* "packageName":"com.example.app",
* "productId":"exampleSku",
* "purchaseTime":1345678900000,
* "purchaseToken" : "122333444455555",
* "developerPayload":"example developer payload" }'
* "INAPP_DATA_SIGNATURE" - String containing the signature of the purchase data that
* was signed with the private key of the developer
* TODO: change this to app-specific keys.
*/
Bundle getBuyIntent(int apiVersion, String packageName, String sku, String type,
String developerPayload);
/**
* Returns the current SKUs owned by the user of the type and package name specified along with
* purchase information and a signature of the data to be validated.
* This will return all SKUs that have been purchased in V3 and managed items purchased using
* V1 and V2 that have not been consumed.
* @param apiVersion billing API version that the app is using
* @param packageName package name of the calling app
* @param type the type of the in-app items being requested
* ("inapp" for one-time purchases and "subs" for subscription).
* @param continuationToken to be set as null for the first call, if the number of owned
* skus are too many, a continuationToken is returned in the response bundle.
* This method can be called again with the continuation token to get the next set of
* owned skus.
* @return Bundle containing the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on
* failure as listed above.
* "INAPP_PURCHASE_ITEM_LIST" - StringArrayList containing the list of SKUs
* "INAPP_PURCHASE_DATA_LIST" - StringArrayList containing the purchase information
* "INAPP_DATA_SIGNATURE_LIST"- StringArrayList containing the signatures
* of the purchase information
* "INAPP_CONTINUATION_TOKEN" - String containing a continuation token for the
* next set of in-app purchases. Only set if the
* user has more owned skus than the current list.
*/
Bundle getPurchases(int apiVersion, String packageName, String type, String continuationToken);
/**
* Consume the last purchase of the given SKU. This will result in this item being removed
* from all subsequent responses to getPurchases() and allow re-purchase of this item.
* @param apiVersion billing API version that the app is using
* @param packageName package name of the calling app
* @param purchaseToken token in the purchase information JSON that identifies the purchase
* to be consumed
* @return 0 if consumption succeeded. Appropriate error values for failures.
*/
int consumePurchase(int apiVersion, String packageName, String purchaseToken);
}
@@ -1 +0,0 @@
geosite:category-ads-all,
@@ -1,132 +0,0 @@
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,
@@ -1,33 +0,0 @@
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,
@@ -54,7 +54,6 @@
"users": [
{
"id": "a3482e88-686a-4a58-8126-99c9df64b7bf",
"alterId": 0,
"security": "auto",
"level": 8
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 28 KiB

@@ -0,0 +1,116 @@
package com.v2ray.ang.util;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.DecodeHintType;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.RGBLuminanceSource;
import com.google.zxing.Result;
import com.google.zxing.common.GlobalHistogramBinarizer;
import com.google.zxing.common.HybridBinarizer;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
/**
* 描述:解析二维码图片
*/
public class QRCodeDecoder {
public static final Map<DecodeHintType, Object> HINTS = new EnumMap<>(DecodeHintType.class);
static {
List<BarcodeFormat> allFormats = new ArrayList<>();
allFormats.add(BarcodeFormat.AZTEC);
allFormats.add(BarcodeFormat.CODABAR);
allFormats.add(BarcodeFormat.CODE_39);
allFormats.add(BarcodeFormat.CODE_93);
allFormats.add(BarcodeFormat.CODE_128);
allFormats.add(BarcodeFormat.DATA_MATRIX);
allFormats.add(BarcodeFormat.EAN_8);
allFormats.add(BarcodeFormat.EAN_13);
allFormats.add(BarcodeFormat.ITF);
allFormats.add(BarcodeFormat.MAXICODE);
allFormats.add(BarcodeFormat.PDF_417);
allFormats.add(BarcodeFormat.QR_CODE);
allFormats.add(BarcodeFormat.RSS_14);
allFormats.add(BarcodeFormat.RSS_EXPANDED);
allFormats.add(BarcodeFormat.UPC_A);
allFormats.add(BarcodeFormat.UPC_E);
allFormats.add(BarcodeFormat.UPC_EAN_EXTENSION);
HINTS.put(DecodeHintType.TRY_HARDER, BarcodeFormat.QR_CODE);
HINTS.put(DecodeHintType.POSSIBLE_FORMATS, allFormats);
HINTS.put(DecodeHintType.CHARACTER_SET, "utf-8");
}
private QRCodeDecoder() {
}
/**
* 同步解析本地图片二维码。该方法是耗时操作,请在子线程中调用。
*
* @param picturePath 要解析的二维码图片本地路径
* @return 返回二维码图片里的内容 或 null
*/
public static String syncDecodeQRCode(String picturePath) {
return syncDecodeQRCode(getDecodeAbleBitmap(picturePath));
}
/**
* 同步解析bitmap二维码。该方法是耗时操作,请在子线程中调用。
*
* @param bitmap 要解析的二维码图片
* @return 返回二维码图片里的内容 或 null
*/
public static String syncDecodeQRCode(Bitmap bitmap) {
Result result = null;
RGBLuminanceSource source = null;
try {
int width = bitmap.getWidth();
int height = bitmap.getHeight();
int[] pixels = new int[width * height];
bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
source = new RGBLuminanceSource(width, height, pixels);
result = new MultiFormatReader().decode(new BinaryBitmap(new HybridBinarizer(source)), HINTS);
return result.getText();
} catch (Exception e) {
e.printStackTrace();
if (source != null) {
try {
result = new MultiFormatReader().decode(new BinaryBitmap(new GlobalHistogramBinarizer(source)), HINTS);
return result.getText();
} catch (Throwable e2) {
e2.printStackTrace();
}
}
return null;
}
}
/**
* 将本地图片文件转换成可解码二维码的 Bitmap。为了避免图片太大,这里对图片进行了压缩。感谢 https://github.com/devilsen 提的 PR
*
* @param picturePath 本地图片文件路径
* @return
*/
private static Bitmap getDecodeAbleBitmap(String picturePath) {
try {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(picturePath, options);
int sampleSize = options.outHeight / 400;
if (sampleSize <= 0) {
sampleSize = 1;
}
options.inSampleSize = sampleSize;
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(picturePath, options);
} catch (Exception e) {
return null;
}
}
}
@@ -9,6 +9,7 @@ class AngApplication : MultiDexApplication() {
const val PREF_LAST_VERSION = "pref_last_version"
}
var curIndex = -1 //Current proxy that is opened. (Used to implement restart feature)
var firstRun = false
private set
@@ -6,7 +6,6 @@ package com.v2ray.ang
*/
object AppConfig {
const val ANG_PACKAGE = "com.v2ray.ang"
const val DIR_ASSETS = "assets"
// legacy
const val ANG_CONFIG = "ang_config"
@@ -24,12 +23,7 @@ object AppConfig {
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_FORWARD_IPV6 = "pref_forward_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"
@@ -38,7 +32,16 @@ object AppConfig {
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 PREF_BYPASS_MAINLAND = "pref_bypass_mainland"
// const val PREF_START_ON_BOOT = "pref_start_on_boot"
// const val PREF_MUX_ENAimport libv2ray.Libv2rayBLED = "pref_mux_enabled"
// const val PREF_SOCKS_PORT = "pref_socks_port"
// const val PREF_HTTP_PORT = "pref_http_port"
// const val PREF_DONATE = "pref_donate"
// const val PREF_LICENSES = "pref_licenses"
// const val PREF_FEEDBACK = "pref_feedback"
// const val PREF_TG_GROUP = "pref_tg_group"
// const val PREF_AUTO_RESTART = "pref_auto_restart"
const val HTTP_PROTOCOL: String = "http://"
const val HTTPS_PROTOCOL: String = "https://"
@@ -61,16 +64,11 @@ object AppConfig {
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 promotionUrl = "https://1.2345345.xyz/ads.html"
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
@@ -81,9 +79,4 @@ object AppConfig {
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
}
@@ -17,16 +17,12 @@ data class AngConfig(
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 = "")
var subid: String = "")
data class SubItemBean(var id: String = "",
var remarks: String = "",
var url: String = "",
var enabled: Boolean = true)
var url: String = "")
}
@@ -6,8 +6,7 @@ enum class EConfigType(val value: Int, val protocolScheme: String) {
SHADOWSOCKS(3, "ss://"),
SOCKS(4, "socks://"),
VLESS(5, "vless://"),
TROJAN(6, "trojan://"),
WIREGUARD(7, "wireguard://");
TROJAN(6, "trojan://");
companion object {
fun fromInt(value: Int) = values().firstOrNull { it.value == value }
@@ -1,9 +0,0 @@
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");
}
@@ -26,7 +26,7 @@ data class ServerConfig(
vnext = listOf(V2rayConfig.OutboundBean.OutSettingsBean.VnextBean(
users = listOf(V2rayConfig.OutboundBean.OutSettingsBean.VnextBean.UsersBean())))),
streamSettings = V2rayConfig.OutboundBean.StreamSettingsBean()))
EConfigType.CUSTOM, EConfigType.WIREGUARD ->
EConfigType.CUSTOM ->
return ServerConfig(configType = configType)
EConfigType.SHADOWSOCKS, EConfigType.SOCKS, EConfigType.TROJAN ->
return ServerConfig(
@@ -1,4 +0,0 @@
package com.v2ray.ang.dto
data class ServersCache(val guid: String,
val config: ServerConfig)
@@ -27,6 +27,7 @@ data class V2rayConfig(
const val DEFAULT_SECURITY = "auto"
const val DEFAULT_LEVEL = 8
const val DEFAULT_NETWORK = "tcp"
const val DEFAULT_FLOW = "xtls-rprx-splice"
const val TLS = "tls"
const val XTLS = "xtls"
@@ -74,25 +75,20 @@ data class V2rayConfig(
var response: Response? = null,
/*DNS*/
val network: String? = null,
val address: Any? = null,
val address: String? = 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<WireGuardBean>? = null,
) {
val inboundTag: String? = null) {
data class VnextBean(var address: String = "",
var port: Int = DEFAULT_PORT,
var users: List<UsersBean>) {
data class UsersBean(var id: String = "",
var alterId: Int? = null,
var security: String = DEFAULT_SECURITY,
var level: Int = DEFAULT_LEVEL,
var encryption: String = "",
@@ -106,7 +102,7 @@ data class V2rayConfig(
var port: Int = DEFAULT_PORT,
var level: Int = DEFAULT_LEVEL,
val email: String? = null,
var flow: String? = null,
val flow: String? = null,
val ivCheck: Boolean? = null,
var users: List<SocksUsersBean>? = null) {
@@ -117,9 +113,6 @@ data class V2rayConfig(
}
data class Response(var type: String)
data class WireGuardBean(var publicKey: String = "",
var endpoint: String = "")
}
data class StreamSettingsBean(var network: String = DEFAULT_NETWORK,
@@ -265,19 +258,15 @@ data class V2rayConfig(
return sni
}
fun populateTlsSettings(streamSecurity: String, allowInsecure: Boolean, sni: String, fingerprint: String?, alpns: String?) {
fun populateTlsSettings(streamSecurity: String, allowInsecure: Boolean, sni: 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() }
allowInsecure = allowInsecure,
serverName = sni
)
if (security == TLS) {
tlsSettings = tlsSetting
xtlsSettings = null
} else if (security == XTLS) {
tlsSettings = null
xtlsSettings = tlsSetting
}
}
@@ -293,8 +282,6 @@ data class V2rayConfig(
|| 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
}
@@ -307,8 +294,6 @@ data class V2rayConfig(
|| 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
}
@@ -322,8 +307,6 @@ data class V2rayConfig(
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
}
@@ -339,8 +322,7 @@ data class V2rayConfig(
fun getTransportSettingDetails(): List<String>? {
if (protocol.equals(EConfigType.VMESS.name, true)
|| protocol.equals(EConfigType.VLESS.name, true)
|| protocol.equals(EConfigType.TROJAN.name, true)) {
|| protocol.equals(EConfigType.VLESS.name, true)) {
val transport = streamSettings?.network ?: return null
return when (transport) {
"tcp" -> {
@@ -401,7 +383,7 @@ data class V2rayConfig(
}
data class RoutingBean(var domainStrategy: String,
var domainMatcher: String? = null,
val domainMatcher: String? = null,
var rules: ArrayList<RulesBean>,
val balancers: List<Any>? = null) {
@@ -439,10 +421,12 @@ data class V2rayConfig(
fun getProxyOutbound(): OutboundBean? {
outbounds.forEach { outbound ->
EConfigType.values().forEach {
if (outbound.protocol.equals(it.name, true)) {
return outbound
}
if (outbound.protocol.equals(EConfigType.VMESS.name, true) ||
outbound.protocol.equals(EConfigType.VLESS.name, true) ||
outbound.protocol.equals(EConfigType.SHADOWSOCKS.name, true) ||
outbound.protocol.equals(EConfigType.SOCKS.name, true) ||
outbound.protocol.equals(EConfigType.TROJAN.name, true)) {
return outbound
}
}
return null
@@ -6,12 +6,9 @@ data class VmessQRCode(var v: 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 = "",
var fp: String = "")
var sni: String = "")
@@ -74,7 +74,7 @@ private fun Float.toShortString(): String {
}
val URLConnection.responseLength: Long
get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) contentLengthLong else contentLength.toLong()
get() = if (Build.VERSION.SDK_INT >= 24) contentLengthLong else contentLength.toLong()
val URI.idnHost: String
get() = (host!!).replace("[", "").replace("]", "")
@@ -22,11 +22,11 @@ class QSTileService : TileService() {
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)
qsTile?.icon = Icon.createWithResource(applicationContext, R.drawable.ic_v_idle)
} 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?.icon = Icon.createWithResource(applicationContext, R.drawable.ic_v)
}
qsTile?.updateTile()
@@ -5,9 +5,10 @@ import android.app.Service
interface ServiceControl {
fun getService(): Service
fun startService()
fun startService(parameters: String)
fun stopService()
fun vpnProtect(socket: Int): Boolean
}
@@ -1,13 +1,8 @@
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 {
@@ -30,7 +25,7 @@ class V2RayProxyOnlyService : Service(), ServiceControl {
return this
}
override fun startService() {
override fun startService(parameters: String) {
// do nothing
}
@@ -45,12 +40,4 @@ class V2RayProxyOnlyService : Service(), ServiceControl {
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)
}
}
@@ -1,15 +1,18 @@
package com.v2ray.ang.service
import android.app.*
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
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 android.util.Log
import com.tencent.mmkv.MMKV
import com.v2ray.ang.AppConfig
import com.v2ray.ang.AppConfig.ANG_PACKAGE
@@ -41,7 +44,7 @@ object V2RayServiceManager {
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)
val v2rayPoint: V2RayPoint = Libv2ray.newV2RayPoint(V2RayCallback())
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) }
@@ -49,8 +52,12 @@ object V2RayServiceManager {
var serviceControl: SoftReference<ServiceControl>? = null
set(value) {
field = value
Seq.setContext(value?.get()?.getService()?.applicationContext)
Libv2ray.initV2Env(Utils.userAssetPath(value?.get()?.getService()))
val context = value?.get()?.getService()?.applicationContext
context?.let {
v2rayPoint.packageName = Utils.packagePath(context)
v2rayPoint.packageCodePath = context.applicationInfo.nativeLibraryDir + "/"
Seq.setContext(context)
}
}
var currentConfig: ServerConfig? = null
@@ -62,7 +69,7 @@ object V2RayServiceManager {
fun startV2Ray(context: Context) {
if (settingsStorage?.decodeBool(AppConfig.PREF_PROXY_SHARING) == true) {
context.toast(R.string.toast_warning_pref_proxysharing_short)
} else {
}else{
context.toast(R.string.toast_services_start)
}
val intent = if (settingsStorage?.decodeString(AppConfig.PREF_MODE) ?: "VPN" == "VPN") {
@@ -81,6 +88,7 @@ object V2RayServiceManager {
override fun shutdown(): Long {
val serviceControl = serviceControl?.get() ?: return -1
// called by go
// shutdown the whole vpn service
return try {
serviceControl.stopService()
0
@@ -94,9 +102,9 @@ object V2RayServiceManager {
return 0
}
override fun protect(l: Long): Boolean {
val serviceControl = serviceControl?.get() ?: return true
return serviceControl.vpnProtect(l.toInt())
override fun protect(l: Long): Long {
val serviceControl = serviceControl?.get() ?: return 0
return if (serviceControl.vpnProtect(l.toInt())) 0 else 1
}
override fun onEmitStatus(l: Long, s: String?): Long {
@@ -108,7 +116,7 @@ object V2RayServiceManager {
val serviceControl = serviceControl?.get() ?: return -1
//Logger.d(s)
return try {
serviceControl.startService()
serviceControl.startService(s)
lastQueryTime = System.currentTimeMillis()
startSpeedNotification()
0
@@ -117,6 +125,7 @@ object V2RayServiceManager {
-1
}
}
}
fun startV2rayPoint() {
@@ -141,9 +150,12 @@ object V2RayServiceManager {
v2rayPoint.configureFileContent = result.content
v2rayPoint.domainName = config.getV2rayPointDomainAndPort()
currentConfig = config
v2rayPoint.enableLocalDNS = settingsStorage?.decodeBool(AppConfig.PREF_LOCAL_DNS_ENABLED) ?: false
v2rayPoint.forwardIpv6 = settingsStorage?.decodeBool(AppConfig.PREF_FORWARD_IPV6) ?: false
v2rayPoint.proxyOnly = settingsStorage?.decodeString(AppConfig.PREF_MODE) ?: "VPN" != "VPN"
try {
v2rayPoint.runLoop(settingsStorage?.decodeBool(AppConfig.PREF_PREFER_IPV6) ?: false)
v2rayPoint.runLoop()
} catch (e: Exception) {
Log.d(ANG_PACKAGE, e.toString())
}
@@ -205,9 +217,6 @@ object V2RayServiceManager {
AppConfig.MSG_STATE_RESTART -> {
startV2rayPoint()
}
AppConfig.MSG_MEASURE_DELAY -> {
measureV2rayDelay()
}
}
when (intent?.action) {
@@ -223,29 +232,6 @@ object V2RayServiceManager {
}
}
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)
@@ -279,7 +265,7 @@ object V2RayServiceManager {
}
mBuilder = NotificationCompat.Builder(service, channelId)
.setSmallIcon(R.drawable.ic_stat_name)
.setSmallIcon(R.drawable.ic_v)
.setContentTitle(currentConfig?.remarks)
.setPriority(NotificationCompat.PRIORITY_MIN)
.setOngoing(true)
@@ -320,7 +306,7 @@ object V2RayServiceManager {
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)
mBuilder?.setSmallIcon(R.drawable.ic_v)
} else if (proxyTraffic > directTraffic) {
mBuilder?.setSmallIcon(R.drawable.ic_stat_proxy)
} else {
@@ -340,7 +326,7 @@ object V2RayServiceManager {
return mNotificationManager
}
private fun startSpeedNotification() {
fun startSpeedNotification() {
if (mSubscription == null &&
v2rayPoint.isRunning &&
settingsStorage?.decodeBool(AppConfig.PREF_SPEED_ENABLED) == true) {
@@ -389,7 +375,7 @@ object V2RayServiceManager {
text.append("${up.toLong().toSpeedString()}${down.toLong().toSpeedString()}\n")
}
private fun stopSpeedNotification() {
fun stopSpeedNotification() {
if (mSubscription != null) {
mSubscription?.unsubscribe() //stop queryStats
mSubscription = null
@@ -1,45 +0,0 @@
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<String, String>
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
}
}
@@ -1,6 +1,6 @@
package com.v2ray.ang.service
import android.app.Service
import android.app.*
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
@@ -8,14 +8,12 @@ import android.net.*
import android.os.Build
import android.os.ParcelFileDescriptor
import android.os.StrictMode
import android.util.Log
import androidx.annotation.RequiresApi
import android.util.Log
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
@@ -24,32 +22,19 @@ 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
*/
/**
* 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()
@@ -66,12 +51,10 @@ class V2RayVpnService : VpnService(), ServiceControl {
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)
}
@@ -90,17 +73,18 @@ class V2RayVpnService : VpnService(), ServiceControl {
stopV2Ray()
}
// override fun onLowMemory() {
// stopV2Ray()
// super.onLowMemory()
// }
override fun onLowMemory() {
stopV2Ray()
super.onLowMemory()
}
override fun onDestroy() {
super.onDestroy()
V2RayServiceManager.cancelNotification()
stopV2Ray()
}
private fun setup() {
private fun setup(parameters: String) {
val prepare = prepare(this)
if (prepare != null) {
return
@@ -109,34 +93,35 @@ class V2RayVpnService : VpnService(), ServiceControl {
// 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 enableLocalDns = settingsStorage?.decodeBool(AppConfig.PREF_LOCAL_DNS_ENABLED) ?: false
val routingMode = settingsStorage?.decodeString(AppConfig.PREF_ROUTING_MODE) ?: "0"
val routingMode = settingsStorage?.decodeString(AppConfig.PREF_ROUTING_MODE) ?: ERoutingMode.GLOBAL_PROXY.value
parameters.split(" ")
.map { it.split(",") }
.forEach {
when (it[0][0]) {
'm' -> builder.setMtu(java.lang.Short.parseShort(it[1]).toInt())
's' -> builder.addSearchDomain(it[1])
'a' -> builder.addAddress(it[1], Integer.parseInt(it[2]))
'r' -> {
if (routingMode == "1" || routingMode == "3") {
if (it[1] == "::") { //not very elegant, should move Vpn setting in Kotlin, simplify go code
builder.addRoute("2000::", 3)
} else {
resources.getStringArray(R.array.bypass_private_ip_address).forEach { cidr ->
val addr = cidr.split('/')
builder.addRoute(addr[0], addr[1].toInt())
}
}
} else {
builder.addRoute(it[1], Integer.parseInt(it[2]))
}
}
'd' -> builder.addDnsServer(it[1])
}
}
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 {
if(!enableLocalDns) {
Utils.getVpnDnsServers()
.forEach {
if (Utils.isPureIpAddress(it)) {
@@ -184,69 +169,23 @@ class V2RayVpnService : VpnService(), ServiceControl {
// 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())
}
sendFd()
}
private fun sendFd() {
val fd = mInterface.fileDescriptor
val path = File(applicationContext.filesDir, "sock_path").absolutePath
Log.d(packageName, path)
val path = File(Utils.packagePath(applicationContext), "sock_path").absolutePath
GlobalScope.launch(Dispatchers.IO) {
var tries = 0
while (true) try {
Thread.sleep(50L shl tries)
Thread.sleep(1000L shl tries)
Log.d(packageName, "sendFd tries: $tries")
LocalSocket().use { localSocket ->
localSocket.connect(LocalSocketAddress(path, LocalSocketAddress.Namespace.FILESYSTEM))
@@ -273,7 +212,6 @@ class V2RayVpnService : VpnService(), ServiceControl {
// 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)
@@ -282,13 +220,6 @@ class V2RayVpnService : VpnService(), ServiceControl {
}
}
try {
Log.d(packageName, "tun2socks destroy")
process.destroy()
} catch (e: Exception) {
Log.d(packageName, e.toString())
}
V2RayServiceManager.stopV2rayPoint()
if (isForced) {
@@ -304,6 +235,7 @@ class V2RayVpnService : VpnService(), ServiceControl {
} catch (ignored: Exception) {
// ignored
}
}
}
@@ -311,8 +243,8 @@ class V2RayVpnService : VpnService(), ServiceControl {
return this
}
override fun startService() {
setup()
override fun startService(parameters: String) {
setup(parameters)
}
override fun stopService() {
@@ -323,11 +255,4 @@ class V2RayVpnService : VpnService(), ServiceControl {
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)
}
}
@@ -1,12 +1,7 @@
package com.v2ray.ang.ui
import android.content.Context
import android.os.Build
import android.view.MenuItem
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity
import com.v2ray.ang.util.MyContextWrapper
import com.v2ray.ang.util.Utils
import android.view.MenuItem
abstract class BaseActivity : AppCompatActivity() {
override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
@@ -16,12 +11,4 @@ abstract class BaseActivity : AppCompatActivity() {
}
else -> super.onOptionsItemSelected(item)
}
@RequiresApi(Build.VERSION_CODES.N)
override fun attachBaseContext(newBase: Context?) {
val context = newBase?.let {
MyContextWrapper.wrap(newBase, Utils.getLocale(newBase))
}
super.attachBaseContext(context)
}
}
@@ -1,13 +1,12 @@
package com.v2ray.ang.ui
import android.os.Bundle
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
@@ -16,7 +15,6 @@ 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
@@ -25,9 +23,9 @@ class LogcatActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityLogcatBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
binding = ActivityLogcatBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
title = getString(R.string.title_logcat)
@@ -40,7 +38,7 @@ class LogcatActivity : BaseActivity() {
try {
binding.pbWaiting.visibility = View.VISIBLE
lifecycleScope.launch(Dispatchers.Default) {
GlobalScope.launch(Dispatchers.Default) {
if (shouldFlushLog) {
val lst = LinkedHashSet<String>()
lst.add("logcat")
@@ -83,7 +81,7 @@ class LogcatActivity : BaseActivity() {
toast(R.string.toast_success)
true
}
R.id.clear_all -> {
R.id.delete -> {
logcat(true)
true
}
@@ -1,47 +1,46 @@
package com.v2ray.ang.ui
import android.Manifest
import android.content.*
import android.content.Intent
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.LinearLayoutManager
import androidx.recyclerview.widget.ItemTouchHelper
import android.text.TextUtils
import android.util.Log
import android.view.KeyEvent
import android.view.Menu
import android.view.MenuItem
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.tbruyelle.rxpermissions.RxPermissions
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.databinding.ActivityMainBinding
import com.v2ray.ang.dto.EConfigType
import com.v2ray.ang.extension.toast
import com.v2ray.ang.helper.SimpleItemTouchHelperCallback
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 com.v2ray.ang.viewmodel.MainViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import libv2ray.Libv2ray
import me.drakeet.support.toast.ToastCompat
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
@@ -81,7 +80,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
}
binding.layoutTest.setOnClickListener {
if (mainViewModel.isRunning.value == true) {
setTestState(getString(R.string.connection_test_testing))
binding.tvTestState.text = getString(R.string.connection_test_testing)
mainViewModel.testCurrentServerRealPing()
} else {
// tv_test_state.text = getString(R.string.connection_test_fail)
@@ -102,31 +101,32 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
binding.drawerLayout.addDrawerListener(toggle)
toggle.syncState()
binding.navView.setNavigationItemSelectedListener(this)
binding.version.text = "v${BuildConfig.VERSION_NAME} (${SpeedtestUtil.getLibVersion()})"
binding.version.text = "v${BuildConfig.VERSION_NAME} (${Libv2ray.checkVersionX()})"
setupViewModel()
copyAssets()
setupViewModelObserver()
migrateLegacy()
}
private fun setupViewModel() {
mainViewModel.updateListAction.observe(this) { index ->
private fun setupViewModelObserver() {
mainViewModel.updateListAction.observe(this) {
val index = it ?: return@observe
if (index >= 0) {
adapter.notifyItemChanged(index)
} else {
adapter.notifyDataSetChanged()
}
}
mainViewModel.updateTestResultAction.observe(this) { setTestState(it) }
mainViewModel.isRunning.observe(this) { isRunning ->
mainViewModel.updateTestResultAction.observe(this) { binding.tvTestState.text = it }
mainViewModel.isRunning.observe(this) {
val isRunning = it ?: return@observe
adapter.isRunning = isRunning
if (isRunning) {
binding.fab.backgroundTintList = ColorStateList.valueOf(ContextCompat.getColor(this, R.color.colorSelected))
setTestState(getString(R.string.connection_connected))
binding.fab.setImageResource(R.drawable.ic_v)
binding.tvTestState.text = 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.fab.setImageResource(R.drawable.ic_v_idle)
binding.tvTestState.text = getString(R.string.connection_not_connected)
binding.layoutTest.isFocusable = false
}
hideCircle()
@@ -134,31 +134,8 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
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) {
GlobalScope.launch(Dispatchers.IO) {
val result = AngConfigManager.migrateLegacyConfig(this@MainActivity)
if (result != null) {
launch(Dispatchers.Main) {
@@ -183,17 +160,6 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
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()
@@ -218,23 +184,18 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
true
}
R.id.import_manually_vmess -> {
importManually(EConfigType.VMESS.value)
true
}
R.id.import_manually_vless -> {
importManually(EConfigType.VLESS.value)
startActivity(Intent().putExtra("createConfigType", EConfigType.VMESS.value).
setClass(this, ServerActivity::class.java))
true
}
R.id.import_manually_ss -> {
importManually(EConfigType.SHADOWSOCKS.value)
startActivity(Intent().putExtra("createConfigType", EConfigType.SHADOWSOCKS.value).
setClass(this, ServerActivity::class.java))
true
}
R.id.import_manually_socks -> {
importManually(EConfigType.SOCKS.value)
true
}
R.id.import_manually_trojan -> {
importManually(EConfigType.TROJAN.value)
startActivity(Intent().putExtra("createConfigType", EConfigType.SOCKS.value).
setClass(this, ServerActivity::class.java))
true
}
R.id.import_config_custom_clipboard -> {
@@ -278,56 +239,17 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
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
}
// R.id.settings -> {
// startActivity<SettingsActivity>("isRunning" to isRunning)
// true
// }
// R.id.logcat -> {
// startActivity<LogcatActivity>()
// 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
@@ -381,16 +303,9 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
}
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)
var count = AngConfigManager.importBatchConfig(server, subid)
if (count <= 0) {
count = AngConfigManager.importBatchConfig(Utils.decode(server!!), subid2, append)
count = AngConfigManager.importBatchConfig(Utils.decode(server!!), subid)
}
if (count > 0) {
toast(R.string.toast_success)
@@ -453,7 +368,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
toast(R.string.toast_invalid_url)
return false
}
lifecycleScope.launch(Dispatchers.IO) {
GlobalScope.launch(Dispatchers.IO) {
val configText = try {
Utils.getUrlContentWithCustomUserAgent(url)
} catch (e: Exception) {
@@ -485,15 +400,12 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
) {
return@forEach
}
if (!it.second.enabled) {
return@forEach
}
val url = Utils.idnToASCII(it.second.url)
val url = it.second.url
if (!Utils.isValidUrl(url)) {
return@forEach
}
Log.d(ANG_PACKAGE, url)
lifecycleScope.launch(Dispatchers.IO) {
GlobalScope.launch(Dispatchers.IO) {
val configText = try {
Utils.getUrlContentWithCustomUserAgent(url)
} catch (e: Exception) {
@@ -504,7 +416,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
return@launch
}
launch(Dispatchers.Main) {
importBatchConfig(configText, it.first)
importBatchConfig(Utils.decode(configText), it.first)
}
}
}
@@ -525,7 +437,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
try {
chooseFileForCustomConfig.launch(Intent.createChooser(intent, getString(R.string.title_file_chooser)))
} catch (ex: ActivityNotFoundException) {
} catch (ex: android.content.ActivityNotFoundException) {
toast(R.string.toast_require_file_manager)
}
}
@@ -567,9 +479,8 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
return
}
mainViewModel.appendCustomConfigServer(server)
mainViewModel.reloadServerList()
toast(R.string.toast_success)
//adapter.notifyItemInserted(mainViewModel.serverList.lastIndex)
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()
@@ -577,10 +488,6 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
}
}
fun setTestState(content: String?) {
binding.tvTestState.text = content
}
// val mConnection = object : ServiceConnection {
// override fun onServiceDisconnected(name: ComponentName?) {
// }
@@ -607,16 +514,11 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
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)
if (binding.fabProgressCircle.isShown) {
binding.fabProgressCircle.hide()
}
}
} catch (e: Exception) {
Log.d(ANG_PACKAGE, e.toString())
}
}
@@ -639,14 +541,14 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
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()}")
Utils.openUri(this, AppConfig.promotionUrl)
}
R.id.donate -> {
// startActivity<InappBuyActivity>()
}
R.id.logcat -> {
startActivity(Intent(this, LogcatActivity::class.java))
@@ -2,13 +2,12 @@ package com.v2ray.ang.ui
import android.content.Intent
import android.graphics.Color
import android.text.TextUtils
import androidx.core.content.ContextCompat
import androidx.appcompat.app.AlertDialog
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
@@ -39,43 +38,29 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
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<out String> by lazy {
mActivity.resources.getStringArray(R.array.share_method)
}
var isRunning = false
override fun getItemCount() = mActivity.mainViewModel.serversCache.size + 1
override fun getItemCount() = mActivity.mainViewModel.serverList.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 guid = mActivity.mainViewModel.serverList.getOrNull(position) ?: return
val config = mActivity.mainViewModel.serversCache.getOrElse(guid) { MmkvManager.decodeServerConfig(guid) } ?: return
val outbound = config.getProxyOutbound()
val aff = MmkvManager.decodeServerAffiliationInfo(guid)
holder.itemMainBinding.tvName.text = config.remarks
holder.itemMainBinding.btnRadio.isChecked = guid == mainStorage?.decodeString(MmkvManager.KEY_SELECTED_SERVER)
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))
if (aff?.testDelayMillis?:0L < 0L) {
holder.itemMainBinding.tvTestResult.setTextColor(ContextCompat.getColor(mActivity, android.R.color.holo_red_dark))
} 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()) {
@@ -138,15 +123,9 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
}
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)
}
mActivity.mainViewModel.removeServer(guid)
notifyItemRemoved(position)
notifyItemRangeChanged(position, mActivity.mainViewModel.serverList.size)
}
}
@@ -154,10 +133,8 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
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))
notifyItemChanged(mActivity.mainViewModel.serverList.indexOf(selected))
notifyItemChanged(mActivity.mainViewModel.serverList.indexOf(guid))
if (isRunning) {
mActivity.showCircle()
Utils.stopVService(mActivity)
@@ -191,12 +168,6 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
}
}
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 ->
@@ -207,7 +178,7 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
}
override fun getItemViewType(position: Int): Int {
return if (position == mActivity.mainViewModel.serversCache.size) {
return if (position == mActivity.mainViewModel.serverList.size) {
VIEW_TYPE_FOOTER
} else {
VIEW_TYPE_ITEM
@@ -231,7 +202,7 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
BaseViewHolder(itemFooterBinding.root), ItemTouchHelperViewHolder
override fun onItemDismiss(position: Int) {
val guid = mActivity.mainViewModel.serversCache.getOrNull(position)?.guid ?: return
val guid = mActivity.mainViewModel.serverList.getOrNull(position) ?: return
if (guid != mainStorage?.decodeString(MmkvManager.KEY_SELECTED_SERVER)) {
// mActivity.alert(R.string.del_config_comfirm) {
// positiveButton(android.R.string.ok) {
@@ -1,32 +1,39 @@
package com.v2ray.ang.ui
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.content.Context
import android.os.Bundle
import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
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 android.view.animation.AccelerateInterpolator
import android.view.animation.DecelerateInterpolator
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.*
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager
import com.v2ray.ang.AppConfig
import com.v2ray.ang.AppConfig.ANG_PACKAGE
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.Utils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import java.net.URL
class PerAppProxyActivity : BaseActivity() {
private lateinit var binding: ActivityBypassListBinding
@@ -89,52 +96,51 @@ class PerAppProxyActivity : BaseActivity() {
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
}
binding.recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
var dst = 0
val threshold = resources.getDimensionPixelSize(R.dimen.bypass_list_header_height) * 3
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
dst += dy
if (dst > threshold) {
binding.headerView.hide()
dst = 0
} else if (dst < -20) {
binding.headerView.show()
dst = 0
}
}
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
}
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
}
})
showing = true
}
})
***/
binding.switchPerAppProxy.setOnCheckedChangeListener { _, isChecked ->
defaultSharedPreferences.edit().putBoolean(AppConfig.PREF_PER_APP_PROXY, isChecked).apply()
@@ -146,35 +152,33 @@ class PerAppProxyActivity : BaseActivity() {
}
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)
binding.etSearch.setOnEditorActionListener { v, actionId, _ ->
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<AppInfo>()
if (TextUtils.isEmpty(key)) {
appsAll?.forEach {
apps.add(it)
val key = v.text.toString().uppercase()
val apps = ArrayList<AppInfo>()
if (TextUtils.isEmpty(key)) {
appsAll?.forEach {
apps.add(it)
}
} else {
appsAll?.forEach {
if (it.appName.uppercase().indexOf(key) >= 0) {
apps.add(it)
}
}
}
adapter = PerAppProxyAdapter(this, apps, adapter?.blacklist)
binding.recyclerView.adapter = adapter
adapter?.notifyDataSetChanged()
true
} else {
false
}
}
} 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() {
@@ -186,23 +190,6 @@ class PerAppProxyActivity : BaseActivity() {
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)
}
@@ -219,20 +206,14 @@ class PerAppProxyActivity : BaseActivity() {
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)
@@ -241,41 +222,27 @@ class PerAppProxyActivity : BaseActivity() {
private fun selectProxyApp() {
toast(R.string.msg_downloading_content)
val url = AppConfig.androidpackagenamelistUrl
lifecycleScope.launch(Dispatchers.IO) {
val content = Utils.getUrlContext(url, 5000)
GlobalScope.launch(Dispatchers.IO) {
val content = try {
URL(url).readText()
} catch (e: Exception) {
e.printStackTrace()
""
}
launch(Dispatchers.Main) {
Log.d(ANG_PACKAGE, content)
selectProxyApp(content, true)
selectProxyApp(content)
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 {
private fun selectProxyApp(content: String): Boolean {
try {
val proxyApps = if (TextUtils.isEmpty(content)) {
Utils.readTextFromAssets(v2RayApplication, "proxy_packagename.txt")
} else {
content
var proxyApps = content
if (TextUtils.isEmpty(content)) {
val assets = Utils.readTextFromAssets(v2RayApplication, "proxy_packagename.txt")
proxyApps = assets.lines().toString()
}
if (TextUtils.isEmpty(proxyApps)) {
return false
@@ -288,7 +255,7 @@ class PerAppProxyActivity : BaseActivity() {
it.apps.forEach block@{
val packageName = it.packageName
Log.d(ANG_PACKAGE, packageName)
if (!inProxyApps(proxyApps, packageName, force)) {
if (proxyApps.indexOf(packageName) < 0) {
adapter?.blacklist!!.add(packageName)
println(packageName)
return@block
@@ -301,7 +268,7 @@ class PerAppProxyActivity : BaseActivity() {
it.apps.forEach block@{
val packageName = it.packageName
Log.d(ANG_PACKAGE, packageName)
if (inProxyApps(proxyApps, packageName, force)) {
if (proxyApps.indexOf(packageName) >= 0) {
adapter?.blacklist!!.add(packageName)
println(packageName)
return@block
@@ -316,40 +283,4 @@ class PerAppProxyActivity : BaseActivity() {
}
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<AppInfo>()
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
}
}
@@ -1,7 +1,8 @@
package com.v2ray.ang.ui
import android.view.LayoutInflater
import android.graphics.Color
import androidx.recyclerview.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.v2ray.ang.R
@@ -35,7 +36,7 @@ class PerAppProxyAdapter(val activity: BaseActivity, val apps: List<AppInfo>, bl
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)
ctx.resources.getDimensionPixelSize(R.dimen.bypass_list_header_height) * 3)
BaseViewHolder(view)
}
// VIEW_TYPE_ITEM -> AppViewHolder(ctx.layoutInflater
@@ -65,10 +66,10 @@ class PerAppProxyAdapter(val activity: BaseActivity, val apps: List<AppInfo>, bl
itemBypassBinding.packageName.text = appInfo.packageName
if (appInfo.isSystemApp) {
itemBypassBinding.name.text = String.format("** %1s", appInfo.appName)
//name.textColor = Color.RED
itemBypassBinding.name.setTextColor(Color.RED)
} else {
itemBypassBinding.name.text = appInfo.appName
//name.textColor = Color.DKGRAY
itemBypassBinding.name.setTextColor(Color.DKGRAY)
}
itemView.setOnClickListener(this)
@@ -1,5 +1,6 @@
package com.v2ray.ang.ui
import android.graphics.Color
import android.os.Bundle
import com.v2ray.ang.R
import androidx.fragment.app.Fragment
@@ -20,7 +21,7 @@ class RoutingSettingsActivity : BaseActivity() {
val view = binding.root
setContentView(view)
title = getString(R.string.title_pref_routing_custom)
title = getString(R.string.routing_settings_title)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
val fragments = ArrayList<Fragment>()
@@ -30,7 +31,7 @@ class RoutingSettingsActivity : BaseActivity() {
val adapter = FragmentAdapter(this, fragments)
binding.viewpager.adapter = adapter
//tablayout.setTabTextColors(Color.BLACK, Color.RED)
binding.tablayout.setTabTextColors(Color.BLACK, Color.RED)
TabLayoutMediator(binding.tablayout, binding.viewpager) { tab, position ->
tab.text = titles[position]
}.attach()
@@ -5,11 +5,11 @@ 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 android.view.*
import android.view.MenuInflater
import androidx.activity.result.contract.ActivityResultContracts
import com.tbruyelle.rxpermissions.RxPermissions
import com.v2ray.ang.AppConfig
import com.v2ray.ang.R
@@ -20,6 +20,7 @@ import com.v2ray.ang.util.Utils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import java.net.URL
class RoutingSettingsFragment : Fragment() {
private lateinit var binding: FragmentRoutingSettingsBinding
@@ -60,7 +61,9 @@ class RoutingSettingsFragment : Fragment() {
override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
R.id.save_routing -> {
saveRouting()
val content = binding.etRoutingContent.text.toString()
defaultSharedPreferences.edit().putString(requireArguments().getString(routing_arg), content).apply()
activity?.toast(R.string.toast_success)
true
}
R.id.del_routing -> {
@@ -125,23 +128,26 @@ class RoutingSettingsFragment : Fragment() {
fun setDefaultRules(): Boolean {
var url = AppConfig.v2rayCustomRoutingListUrl
var tag = ""
when (requireArguments().getString(routing_arg)) {
AppConfig.PREF_V2RAY_ROUTING_AGENT -> {
tag = AppConfig.TAG_AGENT
url += AppConfig.TAG_AGENT
}
AppConfig.PREF_V2RAY_ROUTING_DIRECT -> {
tag = AppConfig.TAG_DIRECT
url += AppConfig.TAG_DIRECT
}
AppConfig.PREF_V2RAY_ROUTING_BLOCKED -> {
tag = AppConfig.TAG_BLOCKED
url += AppConfig.TAG_BLOCKED
}
}
url += tag
activity?.toast(R.string.msg_downloading_content)
lifecycleScope.launch(Dispatchers.IO) {
val content = Utils.getUrlContext(url, 5000)
GlobalScope.launch(Dispatchers.IO) {
val content = try {
URL(url).readText()
} catch (e: Exception) {
e.printStackTrace()
""
}
launch(Dispatchers.Main) {
val routingList = if (TextUtils.isEmpty(content)) {
Utils.readTextFromAssets(activity?.v2RayApplication!!, "custom_routing_$tag")
@@ -32,7 +32,7 @@ class ScScannerActivity : BaseActivity() {
private val scanQRCode = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
if (it.resultCode == RESULT_OK) {
val count = AngConfigManager.importBatchConfig(it.data?.getStringExtra("SCAN_RESULT"), "", false)
val count = AngConfigManager.importBatchConfig(it.data?.getStringExtra("SCAN_RESULT"), "")
if (count > 0) {
toast(R.string.toast_success)
} else {
@@ -106,7 +106,7 @@ class ScannerActivity : BaseActivity(), ZXingScannerView.ResultHandler {
try {
val bitmap = BitmapFactory.decodeStream(contentResolver.openInputStream(uri))
val text = QRCodeDecoder.syncDecodeQRCode(bitmap)
finished(text!!)
finished(text)
} catch (e: Exception) {
e.printStackTrace()
toast(e.message.toString())
@@ -1,20 +1,19 @@
package com.v2ray.ang.ui
import android.os.Bundle
import androidx.appcompat.app.AlertDialog
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.dto.V2rayConfig.Companion.XTLS
import com.v2ray.ang.extension.toast
import com.v2ray.ang.util.MmkvManager
import com.v2ray.ang.util.MmkvManager.ID_MAIN
@@ -34,10 +33,6 @@ class ServerActivity : BaseActivity() {
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<out String> by lazy {
resources.getStringArray(R.array.securitys)
}
@@ -65,12 +60,7 @@ class ServerActivity : BaseActivity() {
private val allowinsecures: Array<out String> by lazy {
resources.getStringArray(R.array.allowinsecures)
}
private val uTlsItems: Array<out String> by lazy {
resources.getStringArray(R.array.streamsecurity_utls)
}
private val alpns: Array<out String> 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.
@@ -78,20 +68,18 @@ class ServerActivity : BaseActivity() {
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_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_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 et_sni: EditText? by lazy { findViewById(R.id.et_sni) }
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)
@@ -103,8 +91,8 @@ class ServerActivity : BaseActivity() {
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)
// 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) {
@@ -145,19 +133,13 @@ class ServerActivity : BaseActivity() {
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)
//sp_flow.setSelection(flow)
}
}
val securityEncryptions = if (config.configType == EConfigType.SHADOWSOCKS) shadowsocksSecuritys else securitys
@@ -174,17 +156,7 @@ class ServerActivity : BaseActivity() {
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)
}
et_request_host?.text = Utils.getEditable(tlsSetting.serverName)
}
}
val network = Utils.arrayFind(networks, streamSetting.network)
@@ -202,7 +174,6 @@ class ServerActivity : BaseActivity() {
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)
@@ -211,10 +182,9 @@ class ServerActivity : BaseActivity() {
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)
//sp_flow?.setSelection(0)
return true
}
@@ -240,19 +210,6 @@ class ServerActivity : BaseActivity() {
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 ->
@@ -264,9 +221,6 @@ class ServerActivity : BaseActivity() {
config.outboundBean?.streamSettings?.let {
saveStreamSettings(it)
}
if(config.subscriptionId.isEmpty() && !subscriptionId.isNullOrEmpty()) {
config.subscriptionId = subscriptionId!!
}
MmkvManager.encodeServerConfig(editGuid, config)
toast(R.string.toast_success)
@@ -279,11 +233,14 @@ class ServerActivity : BaseActivity() {
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]
if (streamSecuritys[sp_stream_security?.selectedItemPosition ?: 0] == XTLS) {
// vnext.users[0].flow = flows[sp_flow.selectedItemPosition].ifBlank { V2rayConfig.DEFAULT_FLOW }
} else {
vnext.users[0].flow = ""
}
}
}
@@ -304,12 +261,6 @@ class ServerActivity : BaseActivity() {
}
} 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 {
""
}
}
}
@@ -318,11 +269,9 @@ class ServerActivity : BaseActivity() {
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 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],
@@ -335,16 +284,16 @@ class ServerActivity : BaseActivity() {
mode = transportTypes(networks[network])[type],
serviceName = path
)
if (sniField.isNotBlank()) {
sni = sniField
}
//if (sniField.isNotBlank()) {
// sni = sniField
//}
val allowInsecure = if (allowinsecures[allowInsecureField].isBlank()) {
settingsStorage?.decodeBool(PREF_ALLOW_INSECURE) ?: false
false//settingsStorage?.decodeBool(PREF_ALLOW_INSECURE) ?: false
} else {
allowinsecures[allowInsecureField].toBoolean()
}
streamSetting.populateTlsSettings(streamSecuritys[streamSecurity], allowInsecure, sni)
streamSetting.populateTlsSettings(streamSecuritys[streamSecurity], allowInsecure, sni, uTlsItems[utlsIndex], alpns[alpnIndex])
}
private fun transportTypes(network: String?): Array<out String> {
@@ -364,19 +313,12 @@ class ServerActivity : BaseActivity() {
*/
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()
}
}
AlertDialog.Builder(this).setMessage(R.string.del_config_comfirm)
.setPositiveButton(android.R.string.ok) { _, _ ->
MmkvManager.removeServer(editGuid)
finish()
}
.show()
}
return true
}
@@ -7,7 +7,7 @@ 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.google.gson.Gson
import com.tencent.mmkv.MMKV
import com.v2ray.ang.R
import com.v2ray.ang.databinding.ActivityServerCustomConfigBinding
@@ -2,12 +2,14 @@ package com.v2ray.ang.ui
import android.content.Intent
import android.os.Bundle
import androidx.preference.*
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.AppConfig
import com.v2ray.ang.extension.toast
import com.v2ray.ang.service.V2RayServiceManager
import com.v2ray.ang.util.Utils
import com.v2ray.ang.viewmodel.SettingsViewModel
@@ -26,31 +28,146 @@ class SettingsActivity : BaseActivity() {
}
class SettingsFragment : PreferenceFragmentCompat() {
private val perAppProxy by lazy { findPreference<CheckBoxPreference>(AppConfig.PREF_PER_APP_PROXY) }
private val localDns by lazy { findPreference<CheckBoxPreference>(AppConfig.PREF_LOCAL_DNS_ENABLED) }
private val fakeDns by lazy { findPreference<CheckBoxPreference>(AppConfig.PREF_FAKE_DNS_ENABLED) }
private val localDnsPort by lazy { findPreference<EditTextPreference>(AppConfig.PREF_LOCAL_DNS_PORT) }
private val vpnDns by lazy { findPreference<EditTextPreference>(AppConfig.PREF_VPN_DNS) }
private val perAppProxy by lazy { findPreference(AppConfig.PREF_PER_APP_PROXY) as CheckBoxPreference }
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) }
private val sppedEnabled by lazy { findPreference(AppConfig.PREF_SPEED_ENABLED) as CheckBoxPreference }
private val sniffingEnabled by lazy { findPreference(AppConfig.PREF_SNIFFING_ENABLED) as CheckBoxPreference }
private val proxySharing by lazy { findPreference(AppConfig.PREF_PROXY_SHARING) as CheckBoxPreference }
private val domainStrategy by lazy { findPreference(AppConfig.PREF_ROUTING_DOMAIN_STRATEGY) as ListPreference }
private val routingMode by lazy { findPreference(AppConfig.PREF_ROUTING_MODE) as ListPreference }
private val forwardIpv6 by lazy { findPreference(AppConfig.PREF_FORWARD_IPV6) as CheckBoxPreference }
private val enableLocalDns by lazy { findPreference(AppConfig.PREF_LOCAL_DNS_ENABLED) as CheckBoxPreference }
private val domesticDns by lazy { findPreference(AppConfig.PREF_DOMESTIC_DNS) as EditTextPreference }
private val remoteDns by lazy { findPreference(AppConfig.PREF_REMOTE_DNS) as EditTextPreference }
// val autoRestart by lazy { findPreference(PREF_AUTO_RESTART) as CheckBoxPreference }
private val remoteDns by lazy { findPreference<EditTextPreference>(AppConfig.PREF_REMOTE_DNS) }
private val domesticDns by lazy { findPreference<EditTextPreference>(AppConfig.PREF_DOMESTIC_DNS) }
private val socksPort by lazy { findPreference<EditTextPreference>(AppConfig.PREF_SOCKS_PORT) }
private val httpPort by lazy { findPreference<EditTextPreference>(AppConfig.PREF_HTTP_PORT) }
private val routingCustom by lazy { findPreference<Preference>(AppConfig.PREF_ROUTING_CUSTOM) }
// val socksPort by lazy { findPreference(PREF_SOCKS_PORT) as EditTextPreference }
// val httpPort by lazy { findPreference(PREF_HTTP_PORT) as EditTextPreference }
private val routingCustom: Preference by lazy { findPreference(AppConfig.PREF_ROUTING_CUSTOM) }
// val donate: Preference by lazy { findPreference(PREF_DONATE) }
// 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<ListPreference>(AppConfig.PREF_MODE) }
private val mode by lazy { findPreference(AppConfig.PREF_MODE) as ListPreference }
private fun restartProxy() {
Utils.stopVService(requireContext())
V2RayServiceManager.startV2Ray(requireContext())
}
private fun isRunning(): Boolean {
return false //TODO no point of adding logic now since Settings will be changed soon
}
override fun onCreatePreferences(bundle: Bundle?, s: String?) {
addPreferencesFromResource(R.xml.pref_settings)
routingCustom?.setOnPreferenceClickListener {
perAppProxy.setOnPreferenceClickListener {
if (isRunning()) {
Utils.stopVService(requireContext())
}
startActivity(Intent(activity, PerAppProxyActivity::class.java))
perAppProxy.isChecked = true
true
}
sppedEnabled.setOnPreferenceClickListener {
if (isRunning())
restartProxy()
true
}
sniffingEnabled.setOnPreferenceClickListener {
if (isRunning())
restartProxy()
true
}
proxySharing.setOnPreferenceClickListener {
if (proxySharing.isChecked)
activity?.toast(R.string.toast_warning_pref_proxysharing)
if (isRunning())
restartProxy()
true
}
domainStrategy.setOnPreferenceChangeListener { _, _ ->
if (isRunning())
restartProxy()
true
}
routingMode.setOnPreferenceChangeListener { _, _ ->
if (isRunning())
restartProxy()
true
}
routingCustom.setOnPreferenceClickListener {
if (isRunning())
Utils.stopVService(requireContext())
startActivity(Intent(activity, RoutingSettingsActivity::class.java))
false
}
forwardIpv6.setOnPreferenceClickListener {
if (isRunning())
restartProxy()
true
}
enableLocalDns.setOnPreferenceClickListener {
if (isRunning())
restartProxy()
true
}
domesticDns.setOnPreferenceChangeListener { _, any ->
// domesticDns.summary = any as String
val nval = any as String
domesticDns.summary = if (nval == "") AppConfig.DNS_DIRECT else nval
if (isRunning())
restartProxy()
true
}
remoteDns.setOnPreferenceChangeListener { _, any ->
// remoteDns.summary = any as String
val nval = any as String
remoteDns.summary = if (nval == "") AppConfig.DNS_AGENT else nval
if (isRunning())
restartProxy()
true
}
localDns?.setOnPreferenceChangeListener{ _, any ->
updateLocalDns(any as Boolean)
true
}
localDnsPort?.setOnPreferenceChangeListener { _, any ->
val nval = any as String
localDnsPort?.summary = if (TextUtils.isEmpty(nval)) "10807" else nval
true
}
vpnDns?.setOnPreferenceChangeListener { _, any ->
vpnDns?.summary = any as String
true
}
mode.setOnPreferenceChangeListener { _, newValue ->
updateMode(newValue.toString())
true
}
mode.dialogLayoutResource = R.layout.preference_with_help_link
// donate.onClick {
// startActivity<InappBuyActivity>()
// }
// licenses.onClick {
// val fragment = LicensesDialogFragment.Builder(act)
// .setNotices(R.raw.licenses)
@@ -73,54 +190,15 @@ class SettingsActivity : BaseActivity() {
// }
// }
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"
// socksPort.setOnPreferenceChangeListener { preference, any ->
// socksPort.summary = any as String
// true
// }
// httpPort.setOnPreferenceChangeListener { preference, any ->
// httpPort.summary = any as String
// true
// }
}
override fun onStart() {
@@ -128,37 +206,26 @@ class SettingsActivity : BaseActivity() {
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)
domesticDns.summary = defaultSharedPreferences.getString(AppConfig.PREF_DOMESTIC_DNS, "")
if (TextUtils.isEmpty(remoteDnsString)) {
remoteDnsString = AppConfig.DNS_AGENT
}
if (TextUtils.isEmpty(domesticDns?.summary)) {
domesticDns?.summary = AppConfig.DNS_DIRECT
if ( domesticDns.summary == "") {
domesticDns.summary = AppConfig.DNS_DIRECT
}
remoteDns?.summary = remoteDnsString
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
}
// socksPort.summary = defaultSharedPreferences.getString(PREF_SOCKS_PORT, "10808")
// lanconnPort.summary = defaultSharedPreferences.getString(PREF_HTTP_PORT, "")
}
private fun updateMode(mode: String?) {
val defaultSharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireActivity())
val vpn = mode == "VPN"
perAppProxy?.isEnabled = vpn
perAppProxy?.isChecked = PreferenceManager.getDefaultSharedPreferences(requireActivity())
perAppProxy.isEnabled = vpn
perAppProxy.isChecked = PreferenceManager.getDefaultSharedPreferences(requireActivity())
.getBoolean(AppConfig.PREF_PER_APP_PROXY, false)
localDns?.isEnabled = vpn
fakeDns?.isEnabled = vpn
@@ -1,10 +1,10 @@
package com.v2ray.ang.ui
import android.os.Bundle
import androidx.appcompat.app.AlertDialog
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
@@ -45,7 +45,7 @@ class SubEditActivity : BaseActivity() {
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
}
@@ -55,7 +55,7 @@ class SubEditActivity : BaseActivity() {
private fun clearServer(): Boolean {
binding.etRemarks.text = null
binding.etUrl.text = null
binding.chkEnable.isChecked = true
return true
}
@@ -75,16 +75,15 @@ class SubEditActivity : BaseActivity() {
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
// }
if (TextUtils.isEmpty(subItem.url)) {
toast(R.string.sub_setting_url)
return false
}
subStorage?.encode(subId, Gson().toJson(subItem))
toast(R.string.toast_success)
@@ -1,11 +1,11 @@
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 androidx.recyclerview.widget.LinearLayoutManager
import com.v2ray.ang.databinding.ActivitySubSettingBinding
import com.v2ray.ang.dto.SubscriptionItem
import com.v2ray.ang.util.MmkvManager
@@ -2,19 +2,14 @@ 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.LayoutInflater
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<SubSettingRecyclerAdapter.MainViewHolder>() {
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
@@ -23,11 +18,6 @@ class SubSettingRecyclerAdapter(val activity: SubSettingActivity) : RecyclerView
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 {
@@ -35,11 +25,6 @@ class SubSettingRecyclerAdapter(val activity: SubSettingActivity) : RecyclerView
.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 {
@@ -114,4 +114,3 @@ class TaskerActivity : BaseActivity() {
}
}
@@ -1,51 +0,0 @@
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()
}
}
}
@@ -1,216 +0,0 @@
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<UserAssetViewHolder>() {
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)
}
@@ -60,9 +60,9 @@ object AngConfigManager {
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_SOCKS_PORT,
// AppConfig.PREF_HTTP_PORT,
// AppConfig.PREF_LOGLEVEL,
AppConfig.PREF_ROUTING_DOMAIN_STRATEGY,
AppConfig.PREF_ROUTING_MODE,
AppConfig.PREF_V2RAY_ROUTING_AGENT,
@@ -75,8 +75,8 @@ object AngConfigManager {
AppConfig.PREF_SPEED_ENABLED,
AppConfig.PREF_PROXY_SHARING,
AppConfig.PREF_LOCAL_DNS_ENABLED,
AppConfig.PREF_ALLOW_INSECURE,
AppConfig.PREF_PREFER_IPV6,
// AppConfig.PREF_ALLOW_INSECURE,
// AppConfig.PREF_PREFER_IPV6,
AppConfig.PREF_PER_APP_PROXY,
AppConfig.PREF_BYPASS_APPS,
).forEach { key ->
@@ -108,11 +108,10 @@ object AngConfigManager {
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
// vnext.users[0].flow = vmessBean.flow
}
}
config.outboundBean?.settings?.servers?.get(0)?.let { server ->
@@ -138,14 +137,13 @@ object AngConfigManager {
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 allowInsecure = if (vmessBean.allowInsecure.isBlank()) {
// settingsStorage?.decodeBool(AppConfig.PREF_ALLOW_INSECURE) ?: false
// } else {
// vmessBean.allowInsecure.toBoolean()
// }
streamSetting.populateTlsSettings(vmessBean.streamSecurity, false,
sni)//vmessBean.sni.ifBlank { sni })
}
}
val key = MmkvManager.encodeServerConfig(vmessBean.guid, config)
@@ -160,7 +158,7 @@ object AngConfigManager {
val subItem = SubscriptionItem()
subItem.remarks = it.remarks
subItem.url = it.url
subItem.enabled = it.enabled
//subItem.enabled = it.enabled
subStorage?.encode(it.id, Gson().toJson(subItem))
}
}
@@ -181,12 +179,11 @@ object AngConfigManager {
}
var config: ServerConfig? = null
val allowInsecure = settingsStorage?.decodeBool(AppConfig.PREF_ALLOW_INSECURE) ?: false
val allowInsecure = false//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
if (!tryParseNewVmess(str, config, allowInsecure)) {
if (str.indexOf("?") > 0) {
if (!tryResolveVmess4Kitsunebi(str, config)) {
@@ -213,15 +210,12 @@ object AngConfigManager {
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)
vnext.users[0].encryption = DEFAULT_SECURITY
}
val sni = streamSetting.populateTransportSettings(vmessQRCode.net, vmessQRCode.type, vmessQRCode.host,
vmessQRCode.path, vmessQRCode.path, vmessQRCode.host, vmessQRCode.path, vmessQRCode.type, vmessQRCode.path)
val fingerprint = vmessQRCode.fp ?: streamSetting.tlsSettings?.fingerprint
streamSetting.populateTlsSettings(vmessQRCode.tls, allowInsecure,
if (TextUtils.isEmpty(vmessQRCode.sni)) sni else vmessQRCode.sni, fingerprint, vmessQRCode.alpn)
if (TextUtils.isEmpty(vmessQRCode.sni)) sni else vmessQRCode.sni)
}
}
} else if (str.startsWith(EConfigType.SHADOWSOCKS.protocolScheme)) {
@@ -274,7 +268,7 @@ object AngConfigManager {
//part decode
val indexS = result.indexOf("@")
if (indexS > 0) {
result = Utils.decode(result.substring(0, indexS)) + result.substring(indexS, result.length)
//result = Utils.decode(result.substring(0, indexS)) + result.substring(indexS, result.length)
} else {
result = Utils.decode(result)
}
@@ -291,41 +285,27 @@ object AngConfigManager {
server.users = listOf(socksUsersBean)
}
} else if (str.startsWith(EConfigType.TROJAN.protocolScheme)) {
val uri = URI(Utils.fixIllegalUrl(str))
val uri = URI(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"])
fingerprint = queryParam["fp"] ?: ""
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
}
var sni = ""
uri.rawQuery?.let { rawQuery ->
val queryParam = rawQuery.split("&")
.associate { it.split("=").let { (k, v) -> k to Utils.urlDecode(v) } }
sni = queryParam["sni"] ?: ""
}
config.outboundBean?.streamSettings?.populateTlsSettings(TLS, allowInsecure, sni)
} else if (str.startsWith(EConfigType.VLESS.protocolScheme)) {
val uri = URI(Utils.fixIllegalUrl(str))
val uri = URI(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
@@ -338,8 +318,7 @@ object AngConfigManager {
val sni = streamSetting.populateTransportSettings(queryParam["type"] ?: "tcp", queryParam["headerType"],
queryParam["host"], queryParam["path"], queryParam["seed"], queryParam["quicSecurity"], queryParam["key"],
queryParam["mode"], queryParam["serviceName"])
fingerprint = queryParam["fp"] ?: ""
streamSetting.populateTlsSettings(queryParam["security"] ?: "", allowInsecure, queryParam["sni"] ?: sni, fingerprint, queryParam["alpn"])
streamSetting.populateTlsSettings(queryParam["security"] ?: "", allowInsecure, queryParam["sni"] ?: sni)
}
if (config == null){
return R.string.toast_incorrect_protocol
@@ -362,7 +341,7 @@ object AngConfigManager {
return runCatching {
val uri = URI(uriString)
check(uri.scheme == "vmess")
val (_, protocol, tlsStr, uuid, alterId) =
val (_, protocol, tlsStr, uuid) =
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.")
@@ -376,15 +355,14 @@ object AngConfigManager {
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()
vnext.users[0].encryption = DEFAULT_SECURITY
}
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)
streamSetting.populateTlsSettings(if (tls) TLS else "", allowInsecure, sni)
true
}.getOrElse { false }
}
@@ -413,15 +391,14 @@ object AngConfigManager {
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
vnext.users[0].encryption = arr21[0]
}
return true
}
private fun tryResolveResolveSip002(str: String, config: ServerConfig): Boolean {
try {
val uri = URI(Utils.fixIllegalUrl(str))
val uri = URI(str.replace(" ", "%20"))
config.remarks = Utils.urlDecode(uri.fragment ?: "")
val method: String
@@ -472,13 +449,9 @@ object AngConfigManager {
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()
vmessQRCode.fp = streamSetting.tlsSettings?.fingerprint.orEmpty()
outbound.getTransportSettingDetails()?.let { transportDetails ->
vmessQRCode.type = transportDetails[0]
vmessQRCode.host = transportDetails[1]
@@ -487,58 +460,41 @@ object AngConfigManager {
val json = Gson().toJson(vmessQRCode)
Utils.encode(json)
}
EConfigType.CUSTOM, EConfigType.WIREGUARD -> ""
EConfigType.CUSTOM -> ""
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()!!),
val url = String.format("%s:%s@%s:%s",
outbound.getSecurityEncryption(),
outbound.getPassword(),
outbound.getServerAddress(),
outbound.getServerPort())
url + remark
Utils.encode(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
val url = String.format("%s:%s@%s:%s",
outbound.settings?.servers?.get(0)?.users?.get(0)?.user,
outbound.getPassword(),
outbound.getServerAddress(),
outbound.getServerPort())
Utils.encode(url) + remark
}
EConfigType.VLESS,
EConfigType.TROJAN -> {
EConfigType.VLESS -> {
val remark = "#" + Utils.urlEncode(config.remarks)
val dicQuery = HashMap<String, String>()
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
}
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()
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()
}
if (!TextUtils.isEmpty(tlsSetting.fingerprint)) {
dicQuery["fp"] = tlsSetting.fingerprint!!
}
}
dicQuery["type"] = streamSetting.network.ifEmpty { V2rayConfig.DEFAULT_NETWORK }
@@ -594,6 +550,20 @@ object AngConfigManager {
outbound.getServerPort())
url + query + remark
}
EConfigType.TROJAN -> {
val remark = "#" + Utils.urlEncode(config.remarks)
var query = ""
(streamSetting.tlsSettings?: streamSetting.xtlsSettings)?.let { tlsSetting ->
if (!TextUtils.isEmpty(tlsSetting.serverName)) {
query = "?sni=${tlsSetting.serverName}"
}
}
val url = String.format("%s@%s:%s",
outbound.getPassword(),
Utils.getIpv6Address(outbound.getServerAddress()!!),
outbound.getServerPort())
url + query + remark
}
}
} catch (e: Exception) {
e.printStackTrace()
@@ -713,13 +683,13 @@ object AngConfigManager {
}
}
fun importBatchConfig(servers: String?, subid: String, append: Boolean): Int {
fun importBatchConfig(servers: String?, subid: String): Int {
try {
if (servers == null) {
return 0
}
val removedSelectedServer =
if (!TextUtils.isEmpty(subid) && !append) {
if (!TextUtils.isEmpty(subid)) {
MmkvManager.decodeServerConfig(mainStorage?.decodeString(KEY_SELECTED_SERVER) ?: "")?.let {
if (it.subscriptionId == subid) {
return@let it
@@ -729,9 +699,8 @@ object AngConfigManager {
} else {
null
}
if(!append) {
MmkvManager.removeServerViaSubid(subid)
}
MmkvManager.removeServerViaSubid(subid)
// var servers = server
// if (server.indexOf("vmess") >= 0 && server.indexOf("vmess") == server.lastIndexOf("vmess")) {
// servers = server.replace("\n", "")
@@ -1,36 +1,21 @@
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) {
fun sendMsg2Service(ctx: Context, what: Int, content: String) {
sendMsg(ctx, AppConfig.BROADCAST_ACTION_SERVICE, what, content)
}
fun sendMsg2UI(ctx: Context, what: Int, content: Serializable) {
fun sendMsg2UI(ctx: Context, what: Int, content: String) {
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) {
private fun sendMsg(ctx: Context, action: String, what: Int, content: String) {
try {
val intent = Intent()
intent.action = action
@@ -42,4 +27,4 @@ object MessageUtil {
e.printStackTrace()
}
}
}
}
@@ -44,7 +44,7 @@ object MmkvManager {
fun encodeServerConfig(guid: String, config: ServerConfig): String {
val key = guid.ifBlank { Utils.getUuid() }
serverStorage?.encode(key, Gson().toJson(config))
val serverList = decodeServerList()
val serverList= decodeServerList()
if (!serverList.contains(key)) {
serverList.add(key)
mainStorage?.encode(KEY_ANG_CONFIGS, Gson().toJson(serverList))
@@ -62,7 +62,7 @@ object MmkvManager {
if (mainStorage?.decodeString(KEY_SELECTED_SERVER) == guid) {
mainStorage?.remove(KEY_SELECTED_SERVER)
}
val serverList = decodeServerList()
val serverList= decodeServerList()
serverList.remove(guid)
mainStorage?.encode(KEY_ANG_CONFIGS, Gson().toJson(serverList))
serverStorage?.remove(guid)
@@ -141,39 +141,4 @@ object MmkvManager {
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<ServerDelay>()
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))
}
}
@@ -1,33 +0,0 @@
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)
}
}
}
@@ -1,100 +0,0 @@
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<DecodeHintType, Any?> = 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<BarcodeFormat> = 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"
}
}
@@ -1,142 +0,0 @@
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<Socket?>()
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()
}
}
@@ -13,28 +13,28 @@ 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.os.SystemClock
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.responseLength
import com.v2ray.ang.extension.toast
import java.net.*
import com.v2ray.ang.service.V2RayServiceManager
import kotlinx.coroutines.isActive
import java.io.IOException
import java.net.*
import kotlin.coroutines.coroutineContext
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) }
private val tcpTestingSockets = ArrayList<Socket?>()
/**
* convert string to editalbe for kotlin
@@ -62,16 +62,11 @@ object Utils {
* 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
0
}
}
@@ -117,12 +112,12 @@ object Utils {
try {
return Base64.decode(text, Base64.NO_WRAP).toString(charset("UTF-8"))
} catch (e: Exception) {
Log.i(ANG_PACKAGE, "Parse base64 standard failed $e")
Log.i(AppConfig.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")
Log.i(AppConfig.ANG_PACKAGE, "Parse base64 url safe failed $e")
}
return null
}
@@ -229,7 +224,7 @@ object Utils {
// addr = addr.toLowerCase()
val octets = addr.split('.').toTypedArray()
if (octets.size == 4) {
if (octets[3].indexOf(":") > 0) {
if(octets[3].indexOf(":") > 0) {
addr = addr.substring(0, addr.indexOf(":"))
}
return isIpv4Address(addr)
@@ -317,7 +312,7 @@ object Utils {
fun urlDecode(url: String): String {
return try {
URLDecoder.decode(URLDecoder.decode(url), "utf-8")
URLDecoder.decode(url, "UTF-8")
} catch (e: Exception) {
e.printStackTrace()
url
@@ -333,6 +328,58 @@ object Utils {
}
}
fun testConnection(context: Context, port: Int): String {
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 + 1))) 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
}
/**
* package path
*/
fun packagePath(context: Context): String {
var path = context.filesDir.toString()
path = path.replace("files", "")
//path += "tun2socks"
return path
}
/**
* readTextFromAssets
@@ -344,33 +391,75 @@ object Utils {
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
/**
* ping
*/
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 (allText.isNotBlank()) {
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 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()
/**
* tcping
*/
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 socketConnectTime(url: String, port: Int): Long {
try {
val socket = Socket()
synchronized(this) {
tcpTestingSockets.add(socket)
}
val start = System.currentTimeMillis()
socket.connect(InetSocketAddress(url, port))
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()
}
return result
}
@Throws(IOException::class)
@@ -389,11 +478,6 @@ object Utils {
}
}
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)
@@ -401,39 +485,5 @@ object Utils {
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()
}
}
@@ -9,7 +9,6 @@ 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
@@ -59,8 +58,7 @@ object V2rayConfigUtil {
//转成Json
val v2rayConfig = Gson().fromJson(assets, V2rayConfig::class.java) ?: return result
v2rayConfig.log.loglevel = settingsStorage?.decodeString(AppConfig.PREF_LOGLEVEL)
?: "warning"
//v2rayConfig.log.loglevel = settingsStorage?.decodeString(AppConfig.PREF_LOGLEVEL) ?: "warning"
inbounds(v2rayConfig)
@@ -91,8 +89,8 @@ object V2rayConfigUtil {
*/
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())
//val socksPort = Utils.parseInt(settingsStorage?.decodeString(AppConfig.PREF_SOCKS_PORT) ?: AppConfig.PORT_SOCKS)
//val httpPort = Utils.parseInt(settingsStorage?.decodeString(AppConfig.PREF_HTTP_PORT) ?: AppConfig.PORT_HTTP)
v2rayConfig.inbounds.forEach { curInbound ->
if (settingsStorage?.decodeBool(AppConfig.PREF_PROXY_SHARING) != true) {
@@ -100,7 +98,7 @@ object V2rayConfigUtil {
curInbound.listen = "127.0.0.1"
}
}
v2rayConfig.inbounds[0].port = socksPort
v2rayConfig.inbounds[0].port = 10808
val fakedns = settingsStorage?.decodeBool(AppConfig.PREF_FAKE_DNS_ENABLED)
?: false
val sniffAllTlsAndHttp = settingsStorage?.decodeBool(AppConfig.PREF_SNIFFING_ENABLED, true)
@@ -113,7 +111,7 @@ object V2rayConfigUtil {
v2rayConfig.inbounds[0].sniffing?.destOverride?.add("fakedns")
}
v2rayConfig.inbounds[1].port = httpPort
//v2rayConfig.inbounds[1].port = httpPort
// if (httpPort > 0) {
// val httpCopy = v2rayConfig.inbounds[0].copy()
@@ -151,8 +149,7 @@ object V2rayConfigUtil {
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
val routingMode = settingsStorage?.decodeString(AppConfig.PREF_ROUTING_MODE) ?: "0"
// Hardcode googleapis.cn
val googleapisRoute = V2rayConfig.RoutingBean.RulesBean(
@@ -162,26 +159,18 @@ object V2rayConfigUtil {
)
when (routingMode) {
ERoutingMode.BYPASS_LAN.value -> {
"1" -> {
routingGeo("ip", "private", AppConfig.TAG_DIRECT, v2rayConfig)
}
ERoutingMode.BYPASS_MAINLAND.value -> {
"2" -> {
routingGeo("", "cn", AppConfig.TAG_DIRECT, v2rayConfig)
v2rayConfig.routing.rules.add(0, googleapisRoute)
}
ERoutingMode.BYPASS_LAN_MAINLAND.value -> {
"3" -> {
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()
@@ -292,11 +281,11 @@ object V2rayConfigUtil {
port = 53,
network = "tcp,udp")
val localDnsPort = Utils.parseInt(settingsStorage?.decodeString(AppConfig.PREF_LOCAL_DNS_PORT), AppConfig.PORT_LOCAL_DNS.toInt())
//val localDnsPort = Utils.parseInt(settingsStorage?.decodeString(AppConfig.PREF_LOCAL_DNS_PORT) ?: AppConfig.PORT_LOCAL_DNS)
v2rayConfig.inbounds.add(
V2rayConfig.InboundBean(
tag = "dns-in",
port = localDnsPort,
port = 10807,
listen = "127.0.0.1",
protocol = "dokodemo-door",
settings = dnsInboundSettings,
@@ -346,15 +335,15 @@ object V2rayConfigUtil {
// 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 routingMode = settingsStorage?.decodeString(AppConfig.PREF_ROUTING_MODE) ?: "0"
if (directDomain.size > 0 || routingMode == "2" || routingMode == "3") {
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) {
if (routingMode == "2" || routingMode == "3") {
servers.add(V2rayConfig.DnsBean.ServersBean(domesticDns.first(), 53, geositeCn, geoipCn))
}
if (Utils.isPureIpAddress(domesticDns.first())) {
@@ -1,37 +1,35 @@
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 android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.util.Log
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.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.*
import com.v2ray.ang.util.MmkvManager.KEY_ANG_CONFIGS
import kotlinx.coroutines.*
import java.util.*
import java.util.concurrent.ConcurrentHashMap
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<ServersCache>()
val serversCache = ConcurrentHashMap<String, ServerConfig>()
val isRunning by lazy { MutableLiveData<Boolean>() }
val updateListAction by lazy { MutableLiveData<Int>() }
val updateTestResultAction by lazy { MutableLiveData<String>() }
@@ -47,8 +45,8 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
override fun onCleared() {
getApplication<AngApplication>().unregisterReceiver(mMsgReceiver)
tcpingTestScope.coroutineContext[Job]?.cancelChildren()
SpeedtestUtil.closeAllTcpSockets()
Log.i(ANG_PACKAGE, "Main ViewModel is cleared")
Utils.closeAllTcpSockets()
Log.i(AppConfig.ANG_PACKAGE, "Main ViewModel is cleared")
super.onCleared()
}
@@ -61,61 +59,51 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
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))
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))
GlobalScope.launch(Dispatchers.Default) {
serverList.forEach { guid ->
MmkvManager.decodeServerConfig(guid)?.let {
serversCache[guid] = it
}
}
}
}
fun testAllTcping() {
tcpingTestScope.coroutineContext[Job]?.cancelChildren()
SpeedtestUtil.closeAllTcpSockets()
Utils.closeAllTcpSockets()
MmkvManager.clearAllTestDelayResults()
updateListAction.value = -1 // update all
getApplication<AngApplication>().toast(R.string.connection_test_testing)
for (item in serversCache) {
item.config.getProxyOutbound()?.let { outbound ->
for (guid in serverList) {
serversCache.getOrElse(guid) { MmkvManager.decodeServerConfig(guid) }?.getProxyOutbound()?.let { outbound ->
val serverAddress = outbound.getServerAddress()
val serverPort = outbound.getServerPort()
if (serverAddress != null && serverPort != null) {
tcpingTestScope.launch {
val testResult = SpeedtestUtil.tcping(serverAddress, serverPort)
val testResult = Utils.tcping(serverAddress, serverPort)
launch(Dispatchers.Main) {
MmkvManager.encodeServerTestDelayMillis(item.guid, testResult)
updateListAction.value = getPosition(item.guid)
MmkvManager.encodeServerTestDelayMillis(guid, testResult)
updateListAction.value = serverList.indexOf(guid)
}
}
}
@@ -123,82 +111,14 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
}
}
fun testAllRealPing() {
MessageUtil.sendMsg2TestService(getApplication(), AppConfig.MSG_MEASURE_CONFIG_CANCEL, "")
MmkvManager.clearAllTestDelayResults()
updateListAction.value = -1 // update all
getApplication<AngApplication>().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<String>( 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()
val socksPort = 10808//Utils.parseInt(defaultDPreference.getPrefString(SettingsActivity.PREF_SOCKS_PORT, "10808"))
GlobalScope.launch(Dispatchers.IO) {
val result = Utils.testConnection(getApplication(), socksPort)
launch(Dispatchers.Main) {
updateTestResultAction.value = result
}
}
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() {
@@ -221,14 +141,6 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
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<String, Long>
MmkvManager.encodeServerTestDelayMillis(resultPair.first, resultPair.second)
updateListAction.value = getPosition(resultPair.first)
}
}
}
}
@@ -1,10 +1,10 @@
package com.v2ray.ang.viewmodel
import android.app.Application
import android.content.SharedPreferences
import android.util.Log
import androidx.lifecycle.AndroidViewModel
import android.content.SharedPreferences
import androidx.preference.PreferenceManager
import android.util.Log
import com.tencent.mmkv.MMKV
import com.v2ray.ang.AppConfig
import com.v2ray.ang.util.MmkvManager
@@ -30,11 +30,6 @@ class SettingsViewModel(application: Application) : AndroidViewModel(application
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,
@@ -46,11 +41,9 @@ class SettingsViewModel(application: Application) : AndroidViewModel(application
AppConfig.PREF_PROXY_SHARING,
AppConfig.PREF_LOCAL_DNS_ENABLED,
AppConfig.PREF_FAKE_DNS_ENABLED,
AppConfig.PREF_ALLOW_INSECURE,
AppConfig.PREF_PREFER_IPV6,
AppConfig.PREF_FORWARD_IPV6,
AppConfig.PREF_PER_APP_PROXY,
AppConfig.PREF_BYPASS_APPS,
AppConfig.PREF_CONFIRM_REMOVE, -> {
AppConfig.PREF_BYPASS_APPS, -> {
settingsStorage?.encode(key, sharedPreferences.getBoolean(key, false))
}
AppConfig.PREF_SNIFFING_ENABLED -> {
Binary file not shown.

Before

Width:  |  Height:  |  Size: 964 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 389 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 739 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 279 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 882 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 494 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 698 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 867 B

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true"
android:drawable="@color/secondary_text" />
<item android:state_focused="true"
android:drawable="@color/secondary_text" />
<item android:state_hovered="true"
android:drawable="@color/secondary_text" />
<item android:drawable="@color/colorPrimary_text" />
</selector>
@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M19.35,10.04C18.67,6.59 15.64,4 12,4 9.11,4 6.6,5.64 5.35,8.04 2.34,8.36 0,10.91 0,14c0,3.31 2.69,6 6,6h13c2.76,0 5,-2.24 5,-5 0,-2.64 -2.05,-4.78 -4.65,-4.96zM17,13l-5,5 -5,-5h3V9h4v4h3z" />
</vector>

Some files were not shown because too many files have changed in this diff Show More