Compare commits

...

66 Commits
1.6.9 ... 1.7.3

Author SHA1 Message Date
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
81 changed files with 1205 additions and 3833 deletions

View File

@@ -1 +1,16 @@
# AndroidLibV2rayLite # AndroidLibV2rayLite
进入`AndroidLibV2rayLite`文件夹
```
go mod download
```
编译aar
```
gomobile bind -v -o android.aar -target=android ./
```

View File

@@ -3,7 +3,7 @@ module github.com/2dust/AndroidLibV2rayLite
go 1.16 go 1.16
require ( require (
github.com/xtls/xray-core v1.4.2 github.com/xtls/xray-core v1.5.2
golang.org/x/mobile v0.0.0-20210220033013-bdb1ca9a1e08 golang.org/x/mobile v0.0.0-20210220033013-bdb1ca9a1e08
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57 golang.org/x/sys v0.0.0-20211214234402-4825e8c3871d
) )

View File

@@ -1,52 +1,110 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 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.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.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.37.0 h1:69FNAINiZfsEuwH3fKq8QrAAnHz+2m4XL4kVYi5BX0Q=
cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo= cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo=
dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3 h1:hJiie5Bf3QucGRa4ymsAUOxyhYwGEz1xrsVk0P8erlw=
dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU= 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 h1:SPOUaucgtVls75mg+X7CXigS71EnsfVUK/2CgVrwqgw=
dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU= 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 h1:GvWw74lx5noHocd+f6HBMXK6DuggBB1dhVkuGZbv7qM=
dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4= 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 h1:ivON6cwHK1OH26MZyWDCnbTRZZf0IhNsENoNAKFS1g4=
dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999 h1:OR8VhtwhcAI3U48/rzBsVOuHi0zDPzYI1xASVcdSgR8=
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/antihax/optional v1.0.0 h1:xK2lYat7ZLaVVcIuj82J8kIro4V6kDe0AUDFboUCwcg=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 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 h1:ckJgFhFWywOx+YLEMIJsTb+NV6NexWICk5+AMSuz3ss=
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= 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 h1:D21IyuvjDCshj1/qq+pCNd3VZOAEI9jy6Bi131YlXgI=
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
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/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 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-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4 h1:hzAQntlaYRkVSFEfj9OTWlVV1H155FMD8BTKktLv0QI=
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1 h1:zH8ljVhhq7yC0MIeUL/IviMtY8hx2mK8cN9wEYb8ggw=
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 h1:t5Wuyh53qYyg9eqn4BbnlIT+vmhyww0TatL+zT3uWgI=
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 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.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/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-20200812162917-85c65e2d0165/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw=
github.com/dgryski/go-metro v0.0.0-20211015221634-2661b20a2446 h1:QnWGyQI3H080vbC9E4jlr6scOYEnALtvV/69oATYzOo=
github.com/dgryski/go-metro v0.0.0-20211015221634-2661b20a2446/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= 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.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.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.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021 h1:fP+fF0up6oPY49OrjPrhIJ8yQfdIM85NXMLkMg1EXVs=
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 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 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= 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/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.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.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 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/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I=
github.com/gliderlabs/ssh v0.1.1 h1:j3L6gSLQalDETeEg/Jg0mGY0/y/N6zI2xX1978P0Uqw=
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= 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 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7 h1:2hRPrmiwPrp3fQX967rNJIhQPtiGXdlQWAxKbKw3VHA=
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= 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.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.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.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= 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.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.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.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/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.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.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
@@ -54,8 +112,11 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 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.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 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.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/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c h1:964Od4U6p2jUkFxvCydnIczKteheJEzHRToSGK3Bnlw=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 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.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.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
@@ -64,99 +125,209 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.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.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.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY=
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57 h1:eqyIo2HjKhKe/mJzTG8n4VqvLXIOEG+SLdDqX7xGtkY=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go v2.0.0+incompatible h1:j0GKcs05QVmm7yesiZq2+9cxHkNK9YM6zKx4D2qucQU=
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
github.com/googleapis/gax-go/v2 v2.0.3 h1:siORttZ36U2R/WjiJuDz8znElWBiAlO9rVt+mqJt0Cc=
github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= 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.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/h12w/go-socks5 v0.0.0-20200522160539-76189e178364 h1:5XxdakFhqd9dnXoAZy1Mb2R/DZ6D1e+0bGC/JhucGYI=
github.com/h12w/go-socks5 v0.0.0-20200522160539-76189e178364/go.mod h1:eDJQioIyy4Yn3MVivT7rv/39gAJTrA7lgmYr8EW950c= github.com/h12w/go-socks5 v0.0.0-20200522160539-76189e178364/go.mod h1:eDJQioIyy4Yn3MVivT7rv/39gAJTrA7lgmYr8EW950c=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1 h1:ujPKutqRlJtcfWk6toYVYagwra7HQHbXOaS171b4Tg8=
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024 h1:rBMNdlhTLzJjJSDIjNEXX1Pz3Hmwmz91v+zycvx9PJc=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
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/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.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.3 h1:/Um6a/ZmD5tF7peoOJ5oN5KMQ0DrGVQSXLNwyckutPk=
github.com/kr/pty v1.1.3/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/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lucas-clemente/quic-go v0.20.0/go.mod h1:fZq/HUDIM+mW6X6wtzORjC0E/WDBMKe5Hf9bgjISwLk= github.com/lucas-clemente/quic-go v0.24.0 h1:ToR7SIIEdrgOhgVTHvPgdVRJfgVy+N0wQAagH7L4d5g=
github.com/lucas-clemente/quic-go v0.24.0/go.mod h1:paZuzjXCE5mj6sikVLMvqXk8lJV2AsqtJ6bDhjEfxx0=
github.com/lunixbochs/vtclean v1.0.0 h1:xu2sLAri4lGiovBDQKxl5mrXyESr3gUr5m5SM5+LVb8=
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe h1:W/GaMY0y69G4cFlmsC6B9sbuo2fP8OFP1ABjt4kPz+w=
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/marten-seemann/qpack v0.2.1 h1:jvTsT/HpCn2UZJdP+UUB53FfUUgeOyG5K1ns0OJOGVs=
github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc= github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc=
github.com/marten-seemann/qtls-go1-15 v0.1.4 h1:RehYMOyRW8hPVEja1KBVsFVNSm35Jj9Mvs5yNoZZ28A=
github.com/marten-seemann/qtls-go1-15 v0.1.4/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I= github.com/marten-seemann/qtls-go1-15 v0.1.4/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I=
github.com/marten-seemann/qtls-go1-16 v0.1.3/go.mod h1:gNpI2Ol+lRS3WwSOtIUUtRwZEQMXjYK+dQSBFbethAk= github.com/marten-seemann/qtls-go1-16 v0.1.4 h1:xbHbOGGhrenVtII6Co8akhLEdrawwB2iHl5yhJRpnco=
github.com/marten-seemann/qtls-go1-16 v0.1.4/go.mod h1:gNpI2Ol+lRS3WwSOtIUUtRwZEQMXjYK+dQSBFbethAk=
github.com/marten-seemann/qtls-go1-17 v0.1.0 h1:P9ggrs5xtwiqXv/FHNwntmuLMNq3KaSIG93AtAZ48xk=
github.com/marten-seemann/qtls-go1-17 v0.1.0/go.mod h1:fz4HIxByo+LlWcreM4CZOYNuz3taBQ8rN2X6FqvaWo8=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/microcosm-cc/bluemonday v1.0.1 h1:SIYunPjnlXcW+gVfvm0IlSeR5U3WZUOLfVmqg85Go44=
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg=
github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86 h1:D6paGObi5Wud7xg83MaEFyjxQB1W5bz5d0IFppr+ymk=
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab h1:eFXv9Nu1lGbrNbj619aWwZfVF5HBrm9Plte8aNptuTI=
github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= 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.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.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.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.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.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.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 h1:A/ADD6HaPnAKj3yS7HjGHRK77qi41Hi0DirOOIQAeIw=
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM=
github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 h1:JhzVVoYvbOACxoUmOs6V/G4D5nPVUW73rKvXxP4XUJc=
github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE=
github.com/pires/go-proxyproto v0.5.0/go.mod h1:Odh9VFOZJCf9G8cLW5o435Xf1J95Jw9Gw5rnCjcwzAY= github.com/pires/go-proxyproto v0.6.1 h1:EBupykFmo22SDjv4fQVQd2J9NOoLPmyZA/15ldOGkPw=
github.com/pires/go-proxyproto v0.6.1/go.mod h1:Odh9VFOZJCf9G8cLW5o435Xf1J95Jw9Gw5rnCjcwzAY=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 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/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.8.0 h1:1921Yw9Gc3iSc4VQh3PIoOqgPCZS7G/4xQNVUp8Mda8=
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 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-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e h1:n/3MEhJQjQxrOUCzh1Y3Re6aJUUWRp2M9+Oc3eVn/54=
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273 h1:agujYaXJSxSo18YNX3jzl+4G6Bstwt+kqv47GS12uL0=
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/refraction-networking/utls v0.0.0-20201210053706-2179f286686b/go.mod h1:tz9gX959MEFfFN5whTIocCLUG57WiILqtdVxI8c6Wj0= github.com/refraction-networking/utls v1.0.0 h1:6XQHSjDmeBCF9sPq8p2zMVGq7Ud3rTD2q88Fw8Tz1tA=
github.com/refraction-networking/utls v1.0.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 h1:Ppwyp6VYCF1nvBTXL3trRso7mXMlRrw9ooo375wvi2s=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/seiflotfy/cuckoofilter v0.0.0-20201222105146-bc6005554a0c h1:pqy40B3MQWYrza7YZXOXgl0Nf0QGFqrOC0BKae1UNAA=
github.com/seiflotfy/cuckoofilter v0.0.0-20201222105146-bc6005554a0c/go.mod h1:bR6DqgcAl1zTcOX8/pE2Qkj9XO00eCNqmKb7lXP8EAg= github.com/seiflotfy/cuckoofilter v0.0.0-20201222105146-bc6005554a0c/go.mod h1:bR6DqgcAl1zTcOX8/pE2Qkj9XO00eCNqmKb7lXP8EAg=
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4 h1:Fth6mevc5rX7glNLpbAMJnqKlfIkcTjZCSHEeqvKbcI=
github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY= github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY=
github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48 h1:vabduItPAIz9px5iryD5peyx7O3Ya8TBThapgXim98o=
github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM= 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 h1:qb9IthCFBmROJ6YBS31BEMeSYjOscSiG+EO+JVNTz64=
github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0= 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 h1:MZM7FHLqUHYI0Y/mQAt3d2aYa0SiNms/hFqC9qJYolM=
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041 h1:llrF3Fs4018ePo4+G/HV/uQUqEI1HMDjCeOf2V6puPc=
github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= 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 h1:Yoy/IzG4lULT6qZg62sVC+qyBL8DQkmD2zv6i7OImrc=
github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw= github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw=
github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c h1:UOk+nlt1BJtTcH15CT7iNO7YVWTfTv/DNwEAQHLIaDQ=
github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI= github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI=
github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b h1:vYEG87HxbU6dXj5npkeulCS96Dtz5xg3jcfCgpcvbIw=
github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU= 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 h1:7pDq9pAMCQgRohFmd25X8hIH8VxmT3TaDm+r9LHxgBk=
github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag= github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag=
github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9 h1:MPblCbqA5+z6XARjScMfz1TqtJC7TuTRj0U9VqIBs6k=
github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg= github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg=
github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50 h1:crYRwvwjdVh1biHzzciFHe8DrZcYrVcZFlJtykhRctg=
github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw= github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw=
github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc h1:eHRtZoIi6n9Wo1uR+RU44C247msLWwyA89hVKwRLkMk=
github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y= 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 h1:SWV2fHctRpRrp49VXJ6UZja7gU9QLHwRpIPBN89SKEo=
github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9 h1:fxoFD0in0/CBzXoyNhMTjvBZYW6ilSnTw7N7y/8vkmM=
github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q= 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 h1:T4wuULTrzCKMFlg3HmKHgXAF8oStFb/+lOIupLV2v+o=
github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ= github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ=
github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241 h1:Y+TeIabU8sJD10Qwd/zMty2/LEaT9GNDaA6nyZf+jgo=
github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I= github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I=
github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122 h1:TQVQrsyNaimGwF7bIhzoVC9QkKm4KsWd8cECGzFx8gI=
github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0= github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0=
github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2 h1:bu666BQci+y4S0tVRVjsHUeRon6vUXmsGBwdowgMrg4=
github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ= github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ=
github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82 h1:LneqU9PHDsg/AkPDU3AkqMxnMYL+imaqkpflHu73us8=
github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk= github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk=
github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95 h1:/vdW8Cb7EXrkqWGufVMES1OH2sU9gKVb2n9/1y5NMBY=
github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 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 h1:YGaxtkYjb8mnTvtufv2LKLwCQu2/C7qFB7UtrOlTWOY=
github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4=
github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133 h1:JtcyT0rk/9PKOdnKQzuDR+FSjh7SGtJwpgVpfZBRKlQ=
github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw=
github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d h1:yKm7XZV6j9Ev6lojP2XaIshpT4ymkqhMeSghO5Ps00E=
github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e h1:qpG93cPwA5f7s/ZPBJnGOYQNK/vKsaDaseuKT5Asee8=
github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.3.0 h1:NGXK3lHquSN08v5vWalVI/L8XU9hdzE/G6xsrze47As=
github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 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.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07 h1:UyzmZLoiDWMRywV4DUYb9Fbt8uiOSooupjTq10vpvnU=
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= 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 h1:5x1GzBaRteIwTr5RAGFVG14uNeRFxVNbXPWrK2qAgpc=
github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
github.com/viant/toolbox v0.24.0 h1:6TteTDQ68CjgcCe8wH3D3ZhUQQOJXMTbj/D9rkk2a1k=
github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=
github.com/xtls/go v0.0.0-20201118062508-3632bf3b7499/go.mod h1:5TB2+k58gx4A4g2Nf5miSHNDF6CuAzHKpWBooLAshTs= github.com/xtls/go v0.0.0-20210920065950-d4af136d3672 h1:4mkzGhKqt3JO1BWYjtD3iRFyAx4ow67hmSqOcGjuxqQ=
github.com/xtls/xray-core v1.4.2 h1:D0Le+Qy9L/eY5LbUQfrk7WJ8wbODpQSW/ZRCg+BRe7c= github.com/xtls/go v0.0.0-20210920065950-d4af136d3672/go.mod h1:YGGVbz9cOxyKFUmhW7LGaLZaMA0cPlHJinvAmVxEMSU=
github.com/xtls/xray-core v1.4.2/go.mod h1:DmL/9rOCliev/a6HciWEvSJVEhUF6C0EpD3clW8v0pc= github.com/xtls/xray-core v1.5.2 h1:gaA3vHwXdfF5y8q++l/l94fgHzx5Yj2anonDG/16t3A=
github.com/xtls/xray-core v1.5.2/go.mod h1:mqmSR72VHg0JUCnF8DWnAOTTrmnmrAYR8NWS+Cyg+F4=
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.1 h1:/vn0k+RBvwlxEmP5E7SZMqNxPhfMVFEJiykr15/0XKM=
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.opencensus.io v0.18.0 h1:Mk5rgZcggtbvtAun5aJzAtjKKN/t0R3jJPlWILlv938=
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
go.starlark.net v0.0.0-20210312235212-74c10e2c17dc/go.mod h1:t3mmBBPzAVvK0L0n1drDmrQsJ8FoIx4INCqVMTr/Zo0= go.opentelemetry.io/proto/otlp v0.7.0 h1:rwOQPCuKAKmwGKq2aVNnYIibI6wnV7EvzgfTCzcdGg8=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.starlark.net v0.0.0-20211203141949-70c0e40ae128 h1:bxH+EXOo87zEOwKDdZ8Tevgi6irRbqheRm/fr293c58=
go.starlark.net v0.0.0-20211203141949-70c0e40ae128/go.mod h1:t3mmBBPzAVvK0L0n1drDmrQsJ8FoIx4INCqVMTr/Zo0=
go4.org v0.0.0-20180809161055-417644f6feb5 h1:+hE86LblG4AyDgwMCLTE6FOlM9+qjHSYS+rKqxUVdsM=
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d h1:E2M5QgjZ/Jg+ObCQAudsXxuTsLj7Nl5RV/lZcQZmKSo=
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= 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-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-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
@@ -165,14 +336,19 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/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-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-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211209193657-4570a0811e8b h1:QAqMVf3pSa6eeTsuklijukjXBlj7Es2QQplab+/RbQ4=
golang.org/x/crypto v0.0.0-20211209193657-4570a0811e8b/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-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 h1:estk1glOnSVeJ9tdEZZc5mAMDZk5lNJNyJ6DvrBkTEU=
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= 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-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 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-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-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-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3 h1:XQyxROzUlZH+WIQwySDgnISgOivlhjIEwaQaJEJrrN0=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 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-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20210220033013-bdb1ca9a1e08 h1:h+GZ3ubjuWaQjGe8owMGcmMVCqs0xYJtRG5y2bpHaqU= golang.org/x/mobile v0.0.0-20210220033013-bdb1ca9a1e08 h1:h+GZ3ubjuWaQjGe8owMGcmMVCqs0xYJtRG5y2bpHaqU=
@@ -181,6 +357,9 @@ golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 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.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38=
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 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-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-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -194,19 +373,30 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 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-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-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-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210330230544-e57232859fb2/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= 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-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211209124913-491a49abca63 h1:iocB37TsdFuN6IBRZ+ry36wrkoV51/tl5vOWqkcPGvY=
golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 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-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-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-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852 h1:xYq6+9AtI+xP3M4r0N1hCkHrInHDBohhquRgx9Kk6gI=
golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= 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-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-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-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-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-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/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 h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 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-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -222,16 +412,29 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/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-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-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201231184435-2d18734c6014/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/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-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211214234402-4825e8c3871d h1:1oIt9o40TWWI9FUaveVpUvBe13FNqBNVXy3ue2fcfkw=
golang.org/x/sys v0.0.0-20211214234402-4825e8c3871d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 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.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.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.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.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-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c h1:fqgJT0MGcGpPgpWU7VRdRjuArfcOvC4AoJmILihzhDg=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/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-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-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -243,18 +446,24 @@ golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 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-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-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69 h1:yBHHx+XZqXJBm6Exke3N7V9gnlsyXxoCPEb1yVenjfk=
golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
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 h1:P1HhGGuLW4aAclzjtmJdf0mJOjVUZUzOTqkAkWL+l6w=
golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 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-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-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= 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.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.1.0 h1:K6z2u68e86TPdSdefXdzvXgR1zEMa+459vBSfWYAZkI=
google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= 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.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.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.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
google.golang.org/appengine v1.4.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-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-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
@@ -262,7 +471,10 @@ google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoA
google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= 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-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-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-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa h1:I0YcKz0I7OAhddo7ya8kMnvprhcWM045PmkBdMO9zN0=
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= 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.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.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
@@ -270,7 +482,11 @@ google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZi
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 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.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 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.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
google.golang.org/grpc v1.43.0 h1:Eeu7bZtDZ2DpRCsLhUlcrLnvYaMK1Gz86a+hMVvELmM=
google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 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-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 v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -282,22 +498,38 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 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-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.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 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/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= 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/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.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.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.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.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-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.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919 h1:tmXTu+dfa+d9Evp8NpJdgOy6+rt8/x4yG7qPBrtNfLY=
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
h12.io/socks v1.0.2/go.mod h1:AIhxy1jOId/XCz9BO+EIgNL2rQiPTBNnOfnVnQ+3Eck= h12.io/socks v1.0.3 h1:Ka3qaQewws4j4/eDQnOdpr4wXsC//dXtWvftlIcCQUo=
h12.io/socks v1.0.3/go.mod h1:AIhxy1jOId/XCz9BO+EIgNL2rQiPTBNnOfnVnQ+3Eck=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 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-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-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc h1:/hemPrYIhOhy8zYrNj+069zDB68us2sMGsfkFJO0iZs=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
sourcegraph.com/sourcegraph/go-diff v0.5.0 h1:eTiIR0CoWjGzJcnQ3OkhIl/b9GJovq4lSAVRt0ZFEG8=
sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck=
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4 h1:JPJh2pk3+X4lXAkZIk2RuE/7/FoK9maXw+TNPJhVS/c=
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=

View File

@@ -2,8 +2,8 @@
A V2Ray client for Android, support [Xray core](https://github.com/XTLS/Xray-core) and [v2fly core](https://github.com/v2fly/v2ray-core) 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-17%2B-yellow.svg?style=flat)](https://developer.android.com/about/versions/jelly-bean#android-4.2) [![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.4.10-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) [![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) [![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) [![GitHub Releases](https://img.shields.io/github/downloads/2dust/v2rayNG/latest/total?logo=github)](https://github.com/2dust/v2rayNG/releases)
@@ -16,15 +16,12 @@ A V2Ray client for Android, support [Xray core](https://github.com/XTLS/Xray-cor
### Usage ### Usage
#### Geoip and Geosite #### Geoip and Geosite
v2rayNG release already embedded domain file `geoip.dat` and `geosite.dat`. However it is (probably) not the latest and not the most complete list. - geoip.dat and geosite.dat files are in `Android/data/com.v2ray.ang/files/assets` (path may differ on some Android device)
For power user, the embedded files can be easily replaced with the following steps: - download feature will get enhanced version in this [repo](https://github.com/Loyalsoldier/v2ray-rules-dat) (Note it need a working proxy)
1. Launch v2rayNG (v1.4.9+) - latest official [domain list](https://github.com/v2fly/domain-list-community) and [ip list](https://github.com/v2fly/geoip) can be imported manually
2. Find existing geoip.dat and geosite.dat in `Android/data/com.v2ray.ang/files/assets` (path may differ on some Android device) - possible to use third party dat file in the same folder, like [h2y](https://guide.v2fly.org/routing/sitedata.html#%E5%A4%96%E7%BD%AE%E7%9A%84%E5%9F%9F%E5%90%8D%E6%96%87%E4%BB%B6)
3. Replace them with the latest [domain list](https://github.com/v2fly/domain-list-community) and [ip list](https://github.com/v2fly/geoip)
4. Enhanced version can be found in this [repo](https://github.com/Loyalsoldier/v2ray-rules-dat) (recommend to use `geosite:geolocation-!cn` for proxy dns and routing)
5. It is also possible to use third party dat file in the same folder, like [h2y](https://guide.v2fly.org/routing/sitedata.html#%E5%A4%96%E7%BD%AE%E7%9A%84%E5%9F%9F%E5%90%8D%E6%96%87%E4%BB%B6)
#### See more in our [wiki](https://github.com/2dust/v2rayNG/wiki) ### More in our [wiki](https://github.com/2dust/v2rayNG/wiki)
### Development guide ### Development guide
@@ -32,4 +29,5 @@ Android project under V2rayNG folder can be compiled directly in Android Studio,
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) 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/) and [Makefiles for Go Developers](https://tutorialedge.net/golang/makefiles-for-go-developers/)
v2rayNG can run on Android Emulators, with minimum Android 5.0 v2rayNG can run on Android Emulators. For WSA, VPN permission need to be granted via
`appops set [package name] ACTIVATE_VPN allow`

View File

@@ -1,6 +1,5 @@
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android { android {
compileSdkVersion Integer.parseInt("$compileSdkVer") compileSdkVersion Integer.parseInt("$compileSdkVer")
@@ -13,7 +12,7 @@ android {
defaultConfig { defaultConfig {
applicationId "com.v2ray.ang" applicationId "com.v2ray.ang"
minSdkVersion 17 minSdkVersion 21
targetSdkVersion Integer.parseInt("$targetSdkVer") targetSdkVersion Integer.parseInt("$targetSdkVer")
multiDexEnabled true multiDexEnabled true
versionCode 212 versionCode 212
@@ -59,38 +58,45 @@ android {
android.applicationVariants.all { variant -> android.applicationVariants.all { variant ->
// assign different version code for each output // assign different version code for each output
variant.outputs.each { output -> variant.outputs.each { output ->
output.versionCodeOverride = output.outputFileName = "v2rayNG_" + variant.versionName + "_" + output.getFilter(com.android.build.OutputFile.ABI) + ".apk"
output.versionCodeOverride =
project.ext.versionCodes.get(output.getFilter(com.android.build.OutputFile.ABI), 0) * project.ext.versionCodes.get(output.getFilter(com.android.build.OutputFile.ABI), 0) *
1000000 + android.defaultConfig.versionCode 1000000 + android.defaultConfig.versionCode
} }
} }
buildFeatures {
viewBinding true
}
} }
dependencies { dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs') implementation fileTree(dir: 'libs', include: ['*.aar', '*.jar'], exclude: [])
testImplementation 'junit:junit:4.13' testImplementation 'junit:junit:4.13.2'
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion" // Androidx
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion" implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9" implementation "androidx.legacy:legacy-support-v4:1.0.0"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9" 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 // Androidx ktx
implementation 'android.arch.lifecycle:extensions:1.1.1' implementation 'androidx.activity:activity-ktx:1.4.0'
implementation 'android.arch.lifecycle:livedata:1.1.1' implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.4.1'
// Android support library //kotlin
implementation "com.android.support:support-v4:$supportLibVersion" implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion"
implementation "com.android.support:appcompat-v7:$supportLibVersion" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2"
implementation "com.android.support:design:$supportLibVersion" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2"
implementation "com.android.support:cardview-v7:$supportLibVersion"
implementation "com.android.support:preference-v7:$supportLibVersion"
implementation "com.android.support:recyclerview-v7:$supportLibVersion"
implementation "com.android.support:multidex:1.0.3"
implementation 'com.android.support.constraint:constraint-layout:2.0.1'
// DSL implementation 'com.tencent:mmkv-static:1.2.12'
implementation 'com.tencent:mmkv-static:1.0.19'
implementation 'com.google.code.gson:gson:2.8.6' implementation 'com.google.code.gson:gson:2.8.6'
implementation 'io.reactivex:rxjava:1.3.4' implementation 'io.reactivex:rxjava:1.3.4'
implementation 'io.reactivex:rxandroid:1.2.1' implementation 'io.reactivex:rxandroid:1.2.1'
@@ -99,23 +105,17 @@ dependencies {
implementation 'me.dm7.barcodescanner:zxing:1.9.8' implementation 'me.dm7.barcodescanner:zxing:1.9.8'
implementation 'com.github.jorgecastilloprz:fabprogresscircle:1.01@aar' implementation 'com.github.jorgecastilloprz:fabprogresscircle:1.01@aar'
implementation 'me.drakeet.support:toastcompat:1.1.0' implementation 'me.drakeet.support:toastcompat:1.1.0'
implementation 'com.blacksquircle.ui:editorkit:2.1.1'
implementation(name: 'libv2ray', ext: 'aar') implementation 'com.blacksquircle.ui:language-base:2.1.1'
//implementation(name: 'tun2socks', ext: 'aar') implementation 'com.blacksquircle.ui:language-json:2.1.1'
} }
buildscript { buildscript {
repositories { repositories {
google() google()
jcenter() mavenCentral()
maven { url 'https://maven.google.com' } maven { url 'https://maven.google.com' }
} maven { url 'https://jitpack.io' }
dependencies { jcenter()
classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlinVersion"
}
}
repositories {
flatDir {
dirs 'libs'
} }
} }

View File

@@ -34,9 +34,11 @@
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:label="@string/app_name" android:label="@string/app_name"
android:supportsRtl="true" android:supportsRtl="true"
android:extractNativeLibs="true" android:theme="@style/AppTheme"
android:theme="@style/AppTheme"> android:usesCleartextTraffic="true"
tools:targetApi="m">
<activity <activity
android:exported="true"
android:name=".ui.MainActivity" android:name=".ui.MainActivity"
android:theme="@style/AppTheme.NoActionBar" android:theme="@style/AppTheme.NoActionBar"
android:launchMode="singleTask"> android:launchMode="singleTask">
@@ -57,24 +59,40 @@
android:resource="@xml/shortcuts" /> android:resource="@xml/shortcuts" />
</activity> </activity>
<activity <activity
android:exported="false"
android:name=".ui.ServerActivity" android:name=".ui.ServerActivity"
android:windowSoftInputMode="stateUnchanged" /> android:windowSoftInputMode="stateUnchanged" />
<activity <activity
android:exported="false"
android:name=".ui.ServerCustomConfigActivity" android:name=".ui.ServerCustomConfigActivity"
android:windowSoftInputMode="stateUnchanged" /> android:windowSoftInputMode="stateUnchanged" />
<activity android:name=".ui.SettingsActivity" />
<activity android:name=".ui.PerAppProxyActivity" />
<activity android:name=".ui.ScannerActivity" />
<!-- <activity android:name=".InappBuyActivity" />-->
<activity android:name=".ui.LogcatActivity" />
<activity <activity
android:exported="false"
android:name=".ui.SettingsActivity" />
<activity
android:exported="false"
android:name=".ui.PerAppProxyActivity" />
<activity
android:exported="false"
android:name=".ui.ScannerActivity" />
<activity
android:exported="false"
android:name=".ui.LogcatActivity" />
<activity
android:exported="false"
android:name=".ui.RoutingSettingsActivity" android:name=".ui.RoutingSettingsActivity"
android:windowSoftInputMode="stateUnchanged" /> android:windowSoftInputMode="stateUnchanged" />
<activity android:name=".ui.SubSettingActivity" />
<activity android:name=".ui.SubEditActivity" />
<activity android:name=".ui.ScScannerActivity" />
<activity <activity
android:exported="false"
android:name=".ui.SubSettingActivity" />
<activity
android:exported="false"
android:name=".ui.SubEditActivity" />
<activity
android:exported="false"
android:name=".ui.ScScannerActivity" />
<activity
android:exported="false"
android:name=".ui.ScSwitchActivity" android:name=".ui.ScSwitchActivity"
android:excludeFromRecents="true" android:excludeFromRecents="true"
android:process=":RunSoLibV2RayDaemon" android:process=":RunSoLibV2RayDaemon"
@@ -101,7 +119,9 @@
android:process=":RunSoLibV2RayDaemon"> android:process=":RunSoLibV2RayDaemon">
</service> </service>
<receiver android:name=".receiver.WidgetProvider" <receiver
android:exported="false"
android:name=".receiver.WidgetProvider"
android:process=":RunSoLibV2RayDaemon"> android:process=":RunSoLibV2RayDaemon">
<meta-data <meta-data
android:name="android.appwidget.provider" android:name="android.appwidget.provider"
@@ -114,6 +134,7 @@
</receiver> </receiver>
<service <service
android:exported="true"
android:name=".service.QSTileService" android:name=".service.QSTileService"
android:icon="@drawable/ic_v" android:icon="@drawable/ic_v"
android:label="@string/app_tile_name" android:label="@string/app_tile_name"
@@ -125,6 +146,7 @@
</service> </service>
<!-- =====================Tasker===================== --> <!-- =====================Tasker===================== -->
<activity <activity
android:exported="true"
android:name=".ui.TaskerActivity" android:name=".ui.TaskerActivity"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"> android:label="@string/app_name">
@@ -133,7 +155,9 @@
</intent-filter> </intent-filter>
</activity> </activity>
<receiver android:name=".receiver.TaskerReceiver" <receiver
android:exported="true"
android:name=".receiver.TaskerReceiver"
android:process=":RunSoLibV2RayDaemon"> android:process=":RunSoLibV2RayDaemon">
<intent-filter> <intent-filter>
<action android:name="com.twofortyfouram.locale.intent.action.FIRE_SETTING" /> <action android:name="com.twofortyfouram.locale.intent.action.FIRE_SETTING" />

View File

@@ -54,7 +54,6 @@
"users": [ "users": [
{ {
"id": "a3482e88-686a-4a58-8126-99c9df64b7bf", "id": "a3482e88-686a-4a58-8126-99c9df64b7bf",
"alterId": 64,
"security": "auto", "security": "auto",
"level": 8 "level": 8
} }

View File

@@ -16,8 +16,8 @@
package com.v2ray.ang.helper; package com.v2ray.ang.helper;
import android.support.v7.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper; import androidx.recyclerview.widget.ItemTouchHelper;
/** /**
* Interface to listen for a move or dismissal event from a {@link ItemTouchHelper.Callback}. * Interface to listen for a move or dismissal event from a {@link ItemTouchHelper.Callback}.

View File

@@ -16,7 +16,7 @@
package com.v2ray.ang.helper; package com.v2ray.ang.helper;
import android.support.v7.widget.helper.ItemTouchHelper; import androidx.recyclerview.widget.ItemTouchHelper;
/** /**
* Interface to notify an item ViewHolder of relevant callbacks from {@link * Interface to notify an item ViewHolder of relevant callbacks from {@link

View File

@@ -16,7 +16,7 @@
package com.v2ray.ang.helper; package com.v2ray.ang.helper;
import android.support.v7.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
/** /**
* Listener for manual initiation of a drag. * Listener for manual initiation of a drag.

View File

@@ -17,9 +17,11 @@
package com.v2ray.ang.helper; package com.v2ray.ang.helper;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.support.v7.widget.GridLayoutManager; import androidx.recyclerview.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper; import androidx.recyclerview.widget.ItemTouchHelper;
import org.jetbrains.annotations.NotNull;
/** /**
* An implementation of {@link ItemTouchHelper.Callback} that enables basic drag & drop and * An implementation of {@link ItemTouchHelper.Callback} that enables basic drag & drop and
@@ -52,7 +54,7 @@ public class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback {
} }
@Override @Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { public int getMovementFlags(RecyclerView recyclerView, @NotNull RecyclerView.ViewHolder viewHolder) {
// Set movement flags based on the layout manager // Set movement flags based on the layout manager
if (recyclerView.getLayoutManager() instanceof GridLayoutManager) { if (recyclerView.getLayoutManager() instanceof GridLayoutManager) {
final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT; final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
@@ -66,24 +68,25 @@ public class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback {
} }
@Override @Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder source, RecyclerView.ViewHolder target) { public boolean onMove(@NotNull RecyclerView recyclerView, RecyclerView.ViewHolder source, RecyclerView.ViewHolder target) {
if (source.getItemViewType() != target.getItemViewType()) { if (source.getItemViewType() != target.getItemViewType()) {
return false; return false;
} }
// Notify the adapter of the move // Notify the adapter of the move
mAdapter.onItemMove(source.getAdapterPosition(), target.getAdapterPosition()); mAdapter.onItemMove(source.getBindingAdapterPosition(), target.getBindingAdapterPosition());
return true; return true;
} }
@Override @Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int i) { public void onSwiped(RecyclerView.ViewHolder viewHolder, int i) {
// Notify the adapter of the dismissal // Notify the adapter of the dismissal
mAdapter.onItemDismiss(viewHolder.getAdapterPosition()); mAdapter.onItemDismiss(viewHolder.getBindingAdapterPosition());
} }
@Override @Override
public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) { public void onChildDraw(@NotNull Canvas c, @NotNull RecyclerView recyclerView, @NotNull RecyclerView.ViewHolder viewHolder, float dX,
float dY, int actionState, boolean isCurrentlyActive) {
if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) { if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
// Fade out the view as it is swiped out of the parent's bounds // Fade out the view as it is swiped out of the parent's bounds
final float alpha = ALPHA_FULL - Math.abs(dX) / (float) viewHolder.itemView.getWidth(); final float alpha = ALPHA_FULL - Math.abs(dX) / (float) viewHolder.itemView.getWidth();
@@ -109,7 +112,7 @@ public class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback {
} }
@Override @Override
public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { public void clearView(@NotNull RecyclerView recyclerView, @NotNull RecyclerView.ViewHolder viewHolder) {
super.clearView(recyclerView, viewHolder); super.clearView(recyclerView, viewHolder);
mAdapter.onItemMoveCompleted(); mAdapter.onItemMoveCompleted();

View File

@@ -1,121 +0,0 @@
package com.v2ray.ang.util;
import static android.content.Context.MODE_PRIVATE;
import android.content.Context;
import android.content.res.AssetManager;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
public class AssetsUtil {
public static boolean copyAssetFolder(AssetManager assetManager,
String fromAssetPath, String toPath) {
try {
String[] files = assetManager.list(fromAssetPath);
new File(toPath).mkdirs();
boolean res = true;
for (String file : files)
if (file.contains("."))
res &= copyAsset(assetManager,
fromAssetPath + "/" + file,
toPath + "/" + file);
else
res &= copyAssetFolder(assetManager,
fromAssetPath + "/" + file,
toPath + "/" + file);
return res;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
public static boolean copyAsset(AssetManager assetManager,
String fromAssetPath, String toPath) {
InputStream in = null;
OutputStream out = null;
try {
in = assetManager.open(fromAssetPath);
new File(toPath).createNewFile();
out = new FileOutputStream(toPath);
copyFile(in, out);
in.close();
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
} finally {
try {
if (out != null) {
out.close();
}
if (in != null) {
in.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static String readTextFromAssets(AssetManager assetManager, String fileName) {
try {
InputStreamReader inputReader = new InputStreamReader(assetManager.open(fileName));
BufferedReader bufReader = new BufferedReader(inputReader);
String line;
String Result = "";
while ((line = bufReader.readLine()) != null)
Result += line;
return Result;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static String getAssetPath(Context context, String assetPath) {
InputStream in = null;
OutputStream out = null;
try {
context.deleteFile(assetPath);
in = context.getAssets().open(assetPath);
out = context.openFileOutput(assetPath, MODE_PRIVATE);
copyFile(in, out);
in.close();
String path = context.getFilesDir().toString();
return path + "/" + assetPath;
} catch (Exception e) {
e.printStackTrace();
return "";
} finally {
try {
if (out != null) {
out.close();
}
if (in != null) {
in.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
private static void copyFile(InputStream in, OutputStream out) throws IOException {
byte[] buffer = new byte[1024];
int read;
while ((read = in.read(buffer)) != -1) {
out.write(buffer, 0, read);
}
}
}

View File

@@ -1,570 +0,0 @@
// Portions copyright 2002, Google, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.v2ray.ang.util;
// This code was converted from code at http://iharder.sourceforge.net/base64/
// Lots of extraneous features were removed.
/* The original code said:
* <p>
* I am placing this code in the Public Domain. Do with it as you will.
* This software comes with no guarantees or warranties but with
* plenty of well-wishing instead!
* Please visit
* <a href="http://iharder.net/xmlizable">http://iharder.net/xmlizable</a>
* periodically to check for updates or to contribute improvements.
* </p>
*
* @author Robert Harder
* @author rharder@usa.net
* @version 1.3
*/
/**
* Base64 converter class. This code is not a complete MIME encoder;
* it simply converts binary data to base64 data and back.
*
* <p>Note {@link CharBase64} is a GWT-compatible implementation of this
* class.
*/
public class Base64 {
/** Specify encoding (value is {@code true}). */
public final static boolean ENCODE = true;
/** Specify decoding (value is {@code false}). */
public final static boolean DECODE = false;
/** The equals sign (=) as a byte. */
private final static byte EQUALS_SIGN = (byte) '=';
/** The new line character (\n) as a byte. */
private final static byte NEW_LINE = (byte) '\n';
/**
* The 64 valid Base64 values.
*/
private final static byte[] ALPHABET =
{(byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F',
(byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K',
(byte) 'L', (byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P',
(byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U',
(byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z',
(byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e',
(byte) 'f', (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j',
(byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', (byte) 'o',
(byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't',
(byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x', (byte) 'y',
(byte) 'z', (byte) '0', (byte) '1', (byte) '2', (byte) '3',
(byte) '4', (byte) '5', (byte) '6', (byte) '7', (byte) '8',
(byte) '9', (byte) '+', (byte) '/'};
/**
* The 64 valid web safe Base64 values.
*/
private final static byte[] WEBSAFE_ALPHABET =
{(byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F',
(byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K',
(byte) 'L', (byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P',
(byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U',
(byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z',
(byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e',
(byte) 'f', (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j',
(byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', (byte) 'o',
(byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't',
(byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x', (byte) 'y',
(byte) 'z', (byte) '0', (byte) '1', (byte) '2', (byte) '3',
(byte) '4', (byte) '5', (byte) '6', (byte) '7', (byte) '8',
(byte) '9', (byte) '-', (byte) '_'};
/**
* Translates a Base64 value to either its 6-bit reconstruction value
* or a negative number indicating some other meaning.
**/
private final static byte[] DECODABET = {-9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 0 - 8
-5, -5, // Whitespace: Tab and Linefeed
-9, -9, // Decimal 11 - 12
-5, // Whitespace: Carriage Return
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - 26
-9, -9, -9, -9, -9, // Decimal 27 - 31
-5, // Whitespace: Space
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42
62, // Plus sign at decimal 43
-9, -9, -9, // Decimal 44 - 46
63, // Slash at decimal 47
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine
-9, -9, -9, // Decimal 58 - 60
-1, // Equals sign at decimal 61
-9, -9, -9, // Decimal 62 - 64
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A' through 'N'
14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O' through 'Z'
-9, -9, -9, -9, -9, -9, // Decimal 91 - 96
26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a' through 'm'
39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n' through 'z'
-9, -9, -9, -9, -9 // Decimal 123 - 127
/* ,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 128 - 139
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */
};
/** The web safe decodabet */
private final static byte[] WEBSAFE_DECODABET =
{-9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 0 - 8
-5, -5, // Whitespace: Tab and Linefeed
-9, -9, // Decimal 11 - 12
-5, // Whitespace: Carriage Return
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - 26
-9, -9, -9, -9, -9, // Decimal 27 - 31
-5, // Whitespace: Space
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 44
62, // Dash '-' sign at decimal 45
-9, -9, // Decimal 46-47
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine
-9, -9, -9, // Decimal 58 - 60
-1, // Equals sign at decimal 61
-9, -9, -9, // Decimal 62 - 64
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A' through 'N'
14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O' through 'Z'
-9, -9, -9, -9, // Decimal 91-94
63, // Underscore '_' at decimal 95
-9, // Decimal 96
26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a' through 'm'
39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n' through 'z'
-9, -9, -9, -9, -9 // Decimal 123 - 127
/* ,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 128 - 139
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */
};
// Indicates white space in encoding
private final static byte WHITE_SPACE_ENC = -5;
// Indicates equals sign in encoding
private final static byte EQUALS_SIGN_ENC = -1;
/** Defeats instantiation. */
private Base64() {
}
/* ******** E N C O D I N G M E T H O D S ******** */
/**
* Encodes up to three bytes of the array <var>source</var>
* and writes the resulting four Base64 bytes to <var>destination</var>.
* The source and destination arrays can be manipulated
* anywhere along their length by specifying
* <var>srcOffset</var> and <var>destOffset</var>.
* This method does not check to make sure your arrays
* are large enough to accommodate <var>srcOffset</var> + 3 for
* the <var>source</var> array or <var>destOffset</var> + 4 for
* the <var>destination</var> array.
* The actual number of significant bytes in your array is
* given by <var>numSigBytes</var>.
*
* @param source the array to convert
* @param srcOffset the index where conversion begins
* @param numSigBytes the number of significant bytes in your array
* @param destination the array to hold the conversion
* @param destOffset the index where output will be put
* @param alphabet is the encoding alphabet
* @return the <var>destination</var> array
* @since 1.3
*/
private static byte[] encode3to4(byte[] source, int srcOffset,
int numSigBytes, byte[] destination, int destOffset, byte[] alphabet) {
// 1 2 3
// 01234567890123456789012345678901 Bit position
// --------000000001111111122222222 Array position from threeBytes
// --------| || || || | Six bit groups to index alphabet
// >>18 >>12 >> 6 >> 0 Right shift necessary
// 0x3f 0x3f 0x3f Additional AND
// Create buffer with zero-padding if there are only one or two
// significant bytes passed in the array.
// We have to shift left 24 in order to flush out the 1's that appear
// when Java treats a value as negative that is cast from a byte to an int.
int inBuff =
(numSigBytes > 0 ? ((source[srcOffset] << 24) >>> 8) : 0)
| (numSigBytes > 1 ? ((source[srcOffset + 1] << 24) >>> 16) : 0)
| (numSigBytes > 2 ? ((source[srcOffset + 2] << 24) >>> 24) : 0);
switch (numSigBytes) {
case 3:
destination[destOffset] = alphabet[(inBuff >>> 18)];
destination[destOffset + 1] = alphabet[(inBuff >>> 12) & 0x3f];
destination[destOffset + 2] = alphabet[(inBuff >>> 6) & 0x3f];
destination[destOffset + 3] = alphabet[(inBuff) & 0x3f];
return destination;
case 2:
destination[destOffset] = alphabet[(inBuff >>> 18)];
destination[destOffset + 1] = alphabet[(inBuff >>> 12) & 0x3f];
destination[destOffset + 2] = alphabet[(inBuff >>> 6) & 0x3f];
destination[destOffset + 3] = EQUALS_SIGN;
return destination;
case 1:
destination[destOffset] = alphabet[(inBuff >>> 18)];
destination[destOffset + 1] = alphabet[(inBuff >>> 12) & 0x3f];
destination[destOffset + 2] = EQUALS_SIGN;
destination[destOffset + 3] = EQUALS_SIGN;
return destination;
default:
return destination;
} // end switch
} // end encode3to4
/**
* Encodes a byte array into Base64 notation.
* Equivalent to calling
* {@code encodeBytes(source, 0, source.length)}
*
* @param source The data to convert
* @since 1.4
*/
public static String encode(byte[] source) {
return encode(source, 0, source.length, ALPHABET, true);
}
/**
* Encodes a byte array into web safe Base64 notation.
*
* @param source The data to convert
* @param doPadding is {@code true} to pad result with '=' chars
* if it does not fall on 3 byte boundaries
*/
public static String encodeWebSafe(byte[] source, boolean doPadding) {
return encode(source, 0, source.length, WEBSAFE_ALPHABET, doPadding);
}
/**
* Encodes a byte array into Base64 notation.
*
* @param source the data to convert
* @param off offset in array where conversion should begin
* @param len length of data to convert
* @param alphabet the encoding alphabet
* @param doPadding is {@code true} to pad result with '=' chars
* if it does not fall on 3 byte boundaries
* @since 1.4
*/
public static String encode(byte[] source, int off, int len, byte[] alphabet,
boolean doPadding) {
byte[] outBuff = encode(source, off, len, alphabet, Integer.MAX_VALUE);
int outLen = outBuff.length;
// If doPadding is false, set length to truncate '='
// padding characters
while (doPadding == false && outLen > 0) {
if (outBuff[outLen - 1] != '=') {
break;
}
outLen -= 1;
}
return new String(outBuff, 0, outLen);
}
/**
* Encodes a byte array into Base64 notation.
*
* @param source the data to convert
* @param off offset in array where conversion should begin
* @param len length of data to convert
* @param alphabet is the encoding alphabet
* @param maxLineLength maximum length of one line.
* @return the BASE64-encoded byte array
*/
public static byte[] encode(byte[] source, int off, int len, byte[] alphabet,
int maxLineLength) {
int lenDiv3 = (len + 2) / 3; // ceil(len / 3)
int len43 = lenDiv3 * 4;
byte[] outBuff = new byte[len43 // Main 4:3
+ (len43 / maxLineLength)]; // New lines
int d = 0;
int e = 0;
int len2 = len - 2;
int lineLength = 0;
for (; d < len2; d += 3, e += 4) {
// The following block of code is the same as
// encode3to4( source, d + off, 3, outBuff, e, alphabet );
// but inlined for faster encoding (~20% improvement)
int inBuff =
((source[d + off] << 24) >>> 8)
| ((source[d + 1 + off] << 24) >>> 16)
| ((source[d + 2 + off] << 24) >>> 24);
outBuff[e] = alphabet[(inBuff >>> 18)];
outBuff[e + 1] = alphabet[(inBuff >>> 12) & 0x3f];
outBuff[e + 2] = alphabet[(inBuff >>> 6) & 0x3f];
outBuff[e + 3] = alphabet[(inBuff) & 0x3f];
lineLength += 4;
if (lineLength == maxLineLength) {
outBuff[e + 4] = NEW_LINE;
e++;
lineLength = 0;
} // end if: end of line
} // end for: each piece of array
if (d < len) {
encode3to4(source, d + off, len - d, outBuff, e, alphabet);
lineLength += 4;
if (lineLength == maxLineLength) {
// Add a last newline
outBuff[e + 4] = NEW_LINE;
e++;
}
e += 4;
}
assert (e == outBuff.length);
return outBuff;
}
/* ******** D E C O D I N G M E T H O D S ******** */
/**
* Decodes four bytes from array <var>source</var>
* and writes the resulting bytes (up to three of them)
* to <var>destination</var>.
* The source and destination arrays can be manipulated
* anywhere along their length by specifying
* <var>srcOffset</var> and <var>destOffset</var>.
* This method does not check to make sure your arrays
* are large enough to accommodate <var>srcOffset</var> + 4 for
* the <var>source</var> array or <var>destOffset</var> + 3 for
* the <var>destination</var> array.
* This method returns the actual number of bytes that
* were converted from the Base64 encoding.
*
*
* @param source the array to convert
* @param srcOffset the index where conversion begins
* @param destination the array to hold the conversion
* @param destOffset the index where output will be put
* @param decodabet the decodabet for decoding Base64 content
* @return the number of decoded bytes converted
* @since 1.3
*/
private static int decode4to3(byte[] source, int srcOffset,
byte[] destination, int destOffset, byte[] decodabet) {
// Example: Dk==
if (source[srcOffset + 2] == EQUALS_SIGN) {
int outBuff =
((decodabet[source[srcOffset]] << 24) >>> 6)
| ((decodabet[source[srcOffset + 1]] << 24) >>> 12);
destination[destOffset] = (byte) (outBuff >>> 16);
return 1;
} else if (source[srcOffset + 3] == EQUALS_SIGN) {
// Example: DkL=
int outBuff =
((decodabet[source[srcOffset]] << 24) >>> 6)
| ((decodabet[source[srcOffset + 1]] << 24) >>> 12)
| ((decodabet[source[srcOffset + 2]] << 24) >>> 18);
destination[destOffset] = (byte) (outBuff >>> 16);
destination[destOffset + 1] = (byte) (outBuff >>> 8);
return 2;
} else {
// Example: DkLE
int outBuff =
((decodabet[source[srcOffset]] << 24) >>> 6)
| ((decodabet[source[srcOffset + 1]] << 24) >>> 12)
| ((decodabet[source[srcOffset + 2]] << 24) >>> 18)
| ((decodabet[source[srcOffset + 3]] << 24) >>> 24);
destination[destOffset] = (byte) (outBuff >> 16);
destination[destOffset + 1] = (byte) (outBuff >> 8);
destination[destOffset + 2] = (byte) (outBuff);
return 3;
}
} // end decodeToBytes
/**
* Decodes data from Base64 notation.
*
* @param s the string to decode (decoded in default encoding)
* @return the decoded data
* @since 1.4
*/
public static byte[] decode(String s) throws Base64DecoderException {
byte[] bytes = s.getBytes();
return decode(bytes, 0, bytes.length);
}
/**
* Decodes data from web safe Base64 notation.
* Web safe encoding uses '-' instead of '+', '_' instead of '/'
*
* @param s the string to decode (decoded in default encoding)
* @return the decoded data
*/
public static byte[] decodeWebSafe(String s) throws Base64DecoderException {
byte[] bytes = s.getBytes();
return decodeWebSafe(bytes, 0, bytes.length);
}
/**
* Decodes Base64 content in byte array format and returns
* the decoded byte array.
*
* @param source The Base64 encoded data
* @return decoded data
* @since 1.3
* @throws Base64DecoderException
*/
public static byte[] decode(byte[] source) throws Base64DecoderException {
return decode(source, 0, source.length);
}
/**
* Decodes web safe Base64 content in byte array format and returns
* the decoded data.
* Web safe encoding uses '-' instead of '+', '_' instead of '/'
*
* @param source the string to decode (decoded in default encoding)
* @return the decoded data
*/
public static byte[] decodeWebSafe(byte[] source)
throws Base64DecoderException {
return decodeWebSafe(source, 0, source.length);
}
/**
* Decodes Base64 content in byte array format and returns
* the decoded byte array.
*
* @param source the Base64 encoded data
* @param off the offset of where to begin decoding
* @param len the length of characters to decode
* @return decoded data
* @since 1.3
* @throws Base64DecoderException
*/
public static byte[] decode(byte[] source, int off, int len)
throws Base64DecoderException {
return decode(source, off, len, DECODABET);
}
/**
* Decodes web safe Base64 content in byte array format and returns
* the decoded byte array.
* Web safe encoding uses '-' instead of '+', '_' instead of '/'
*
* @param source the Base64 encoded data
* @param off the offset of where to begin decoding
* @param len the length of characters to decode
* @return decoded data
*/
public static byte[] decodeWebSafe(byte[] source, int off, int len)
throws Base64DecoderException {
return decode(source, off, len, WEBSAFE_DECODABET);
}
/**
* Decodes Base64 content using the supplied decodabet and returns
* the decoded byte array.
*
* @param source the Base64 encoded data
* @param off the offset of where to begin decoding
* @param len the length of characters to decode
* @param decodabet the decodabet for decoding Base64 content
* @return decoded data
*/
public static byte[] decode(byte[] source, int off, int len, byte[] decodabet)
throws Base64DecoderException {
int len34 = len * 3 / 4;
byte[] outBuff = new byte[2 + len34]; // Upper limit on size of output
int outBuffPosn = 0;
byte[] b4 = new byte[4];
int b4Posn = 0;
int i = 0;
byte sbiCrop = 0;
byte sbiDecode = 0;
for (i = 0; i < len; i++) {
sbiCrop = (byte) (source[i + off] & 0x7f); // Only the low seven bits
sbiDecode = decodabet[sbiCrop];
if (sbiDecode >= WHITE_SPACE_ENC) { // White space Equals sign or better
if (sbiDecode >= EQUALS_SIGN_ENC) {
// An equals sign (for padding) must not occur at position 0 or 1
// and must be the last byte[s] in the encoded value
if (sbiCrop == EQUALS_SIGN) {
int bytesLeft = len - i;
byte lastByte = (byte) (source[len - 1 + off] & 0x7f);
if (b4Posn == 0 || b4Posn == 1) {
throw new Base64DecoderException(
"invalid padding byte '=' at byte offset " + i);
} else if ((b4Posn == 3 && bytesLeft > 2)
|| (b4Posn == 4 && bytesLeft > 1)) {
throw new Base64DecoderException(
"padding byte '=' falsely signals end of encoded value "
+ "at offset " + i);
} else if (lastByte != EQUALS_SIGN && lastByte != NEW_LINE) {
throw new Base64DecoderException(
"encoded value has invalid trailing byte");
}
break;
}
b4[b4Posn++] = sbiCrop;
if (b4Posn == 4) {
outBuffPosn += decode4to3(b4, 0, outBuff, outBuffPosn, decodabet);
b4Posn = 0;
}
}
} else {
throw new Base64DecoderException("Bad Base64 input character at " + i
+ ": " + source[i + off] + "(decimal)");
}
}
// Because web safe encoding allows non padding base64 encodes, we
// need to pad the rest of the b4 buffer with equal signs when
// b4Posn != 0. There can be at most 2 equal signs at the end of
// four characters, so the b4 buffer must have two or three
// characters. This also catches the case where the input is
// padded with EQUALS_SIGN
if (b4Posn != 0) {
if (b4Posn == 1) {
throw new Base64DecoderException("single trailing character at offset "
+ (len - 1));
}
b4[b4Posn++] = EQUALS_SIGN;
outBuffPosn += decode4to3(b4, 0, outBuff, outBuffPosn, decodabet);
}
byte[] out = new byte[outBuffPosn];
System.arraycopy(outBuff, 0, out, 0, outBuffPosn);
return out;
}
}

View File

@@ -1,32 +0,0 @@
// Copyright 2002, Google, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.v2ray.ang.util;
/**
* Exception thrown when encountering an invalid Base64 input character.
*
* @author nelson
*/
public class Base64DecoderException extends Exception {
public Base64DecoderException() {
super();
}
public Base64DecoderException(String s) {
super(s);
}
private static final long serialVersionUID = 1L;
}

View File

@@ -1,43 +0,0 @@
/* Copyright (c) 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.v2ray.ang.util;
/**
* Exception thrown when something went wrong with in-app billing.
* An IabException has an associated IabResult (an error).
* To get the IAB result that caused this exception to be thrown,
* call {@link #getResult()}.
*/
public class IabException extends Exception {
IabResult mResult;
public IabException(IabResult r) {
this(r, null);
}
public IabException(int response, String message) {
this(new IabResult(response, message));
}
public IabException(IabResult r, Exception cause) {
super(r.getMessage(), cause);
mResult = r;
}
public IabException(int response, String message, Exception cause) {
this(new IabResult(response, message), cause);
}
/** Returns the IAB result (error) that this exception signals. */
public IabResult getResult() { return mResult; }
}

View File

@@ -1,979 +0,0 @@
/* Copyright (c) 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.v2ray.ang.util;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender.SendIntentException;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.text.TextUtils;
import android.util.Log;
import com.android.vending.billing.IInAppBillingService;
import org.json.JSONException;
import java.util.ArrayList;
import java.util.List;
/**
* Provides convenience methods for in-app billing. You can create one instance of this
* class for your application and use it to process in-app billing operations.
* It provides synchronous (blocking) and asynchronous (non-blocking) methods for
* many common in-app billing operations, as well as automatic signature
* verification.
* <p>
* After instantiating, you must perform setup in order to start using the object.
* To perform setup, call the {@link #startSetup} method and provide a listener;
* that listener will be notified when setup is complete, after which (and not before)
* you may call other methods.
* <p>
* After setup is complete, you will typically want to request an inventory of owned
* items and subscriptions. See {@link #queryInventory}, {@link #queryInventoryAsync}
* and related methods.
* <p>
* When you are done with this object, don't forget to call {@link #dispose}
* to ensure proper cleanup. This object holds a binding to the in-app billing
* service, which will leak unless you dispose of it correctly. If you created
* the object on an Activity's onCreate method, then the recommended
* place to dispose of it is the Activity's onDestroy method.
* <p>
* A note about threading: When using this object from a background thread, you may
* call the blocking versions of methods; when using from a UI thread, call
* only the asynchronous versions and handle the results via callbacks.
* Also, notice that you can only call one asynchronous operation at a time;
* attempting to start a second asynchronous operation while the first one
* has not yet completed will result in an exception being thrown.
*
* @author Bruno Oliveira (Google)
*/
public class IabHelper {
// Is debug logging enabled?
boolean mDebugLog = false;
String mDebugTag = "IabHelper";
// Is setup done?
boolean mSetupDone = false;
// Has this object been disposed of? (If so, we should ignore callbacks, etc)
boolean mDisposed = false;
// Are subscriptions supported?
boolean mSubscriptionsSupported = false;
// Is an asynchronous operation in progress?
// (only one at a time can be in progress)
boolean mAsyncInProgress = false;
// (for logging/debugging)
// if mAsyncInProgress == true, what asynchronous operation is in progress?
String mAsyncOperation = "";
// Context we were passed during initialization
Context mContext;
// Connection to the service
IInAppBillingService mService;
ServiceConnection mServiceConn;
// The request code used to launch purchase flow
int mRequestCode;
// The item type of the current purchase flow
String mPurchasingItemType;
// Public key for verifying signature, in base64 encoding
String mSignatureBase64 = null;
// Billing response codes
public static final int BILLING_RESPONSE_RESULT_OK = 0;
public static final int BILLING_RESPONSE_RESULT_USER_CANCELED = 1;
public static final int BILLING_RESPONSE_RESULT_BILLING_UNAVAILABLE = 3;
public static final int BILLING_RESPONSE_RESULT_ITEM_UNAVAILABLE = 4;
public static final int BILLING_RESPONSE_RESULT_DEVELOPER_ERROR = 5;
public static final int BILLING_RESPONSE_RESULT_ERROR = 6;
public static final int BILLING_RESPONSE_RESULT_ITEM_ALREADY_OWNED = 7;
public static final int BILLING_RESPONSE_RESULT_ITEM_NOT_OWNED = 8;
// IAB Helper error codes
public static final int IABHELPER_ERROR_BASE = -1000;
public static final int IABHELPER_REMOTE_EXCEPTION = -1001;
public static final int IABHELPER_BAD_RESPONSE = -1002;
public static final int IABHELPER_VERIFICATION_FAILED = -1003;
public static final int IABHELPER_SEND_INTENT_FAILED = -1004;
public static final int IABHELPER_USER_CANCELLED = -1005;
public static final int IABHELPER_UNKNOWN_PURCHASE_RESPONSE = -1006;
public static final int IABHELPER_MISSING_TOKEN = -1007;
public static final int IABHELPER_UNKNOWN_ERROR = -1008;
public static final int IABHELPER_SUBSCRIPTIONS_NOT_AVAILABLE = -1009;
public static final int IABHELPER_INVALID_CONSUMPTION = -1010;
// Keys for the responses from InAppBillingService
public static final String RESPONSE_CODE = "RESPONSE_CODE";
public static final String RESPONSE_GET_SKU_DETAILS_LIST = "DETAILS_LIST";
public static final String RESPONSE_BUY_INTENT = "BUY_INTENT";
public static final String RESPONSE_INAPP_PURCHASE_DATA = "INAPP_PURCHASE_DATA";
public static final String RESPONSE_INAPP_SIGNATURE = "INAPP_DATA_SIGNATURE";
public static final String RESPONSE_INAPP_ITEM_LIST = "INAPP_PURCHASE_ITEM_LIST";
public static final String RESPONSE_INAPP_PURCHASE_DATA_LIST = "INAPP_PURCHASE_DATA_LIST";
public static final String RESPONSE_INAPP_SIGNATURE_LIST = "INAPP_DATA_SIGNATURE_LIST";
public static final String INAPP_CONTINUATION_TOKEN = "INAPP_CONTINUATION_TOKEN";
// Item types
public static final String ITEM_TYPE_INAPP = "inapp";
public static final String ITEM_TYPE_SUBS = "subs";
// some fields on the getSkuDetails response bundle
public static final String GET_SKU_DETAILS_ITEM_LIST = "ITEM_ID_LIST";
public static final String GET_SKU_DETAILS_ITEM_TYPE_LIST = "ITEM_TYPE_LIST";
/**
* Creates an instance. After creation, it will not yet be ready to use. You must perform
* setup by calling {@link #startSetup} and wait for setup to complete. This constructor does not
* block and is safe to call from a UI thread.
*
* @param ctx Your application or Activity context. Needed to bind to the in-app billing service.
* @param base64PublicKey Your application's public key, encoded in base64.
* This is used for verification of purchase signatures. You can find your app's base64-encoded
* public key in your application's page on Google Play Developer Console. Note that this
* is NOT your "developer public key".
*/
public IabHelper(Context ctx, String base64PublicKey) {
mContext = ctx.getApplicationContext();
mSignatureBase64 = base64PublicKey;
logDebug("IAB helper created.");
}
/**
* Enables or disable debug logging through LogCat.
*/
public void enableDebugLogging(boolean enable, String tag) {
checkNotDisposed();
mDebugLog = enable;
mDebugTag = tag;
}
public void enableDebugLogging(boolean enable) {
checkNotDisposed();
mDebugLog = enable;
}
/**
* Callback for setup process. This listener's {@link #onIabSetupFinished} method is called
* when the setup process is complete.
*/
public interface OnIabSetupFinishedListener {
/**
* Called to notify that setup is complete.
*
* @param result The result of the setup process.
*/
void onIabSetupFinished(IabResult result);
}
/**
* Starts the setup process. This will start up the setup process asynchronously.
* You will be notified through the listener when the setup process is complete.
* This method is safe to call from a UI thread.
*
* @param listener The listener to notify when the setup process is complete.
*/
public void startSetup(final OnIabSetupFinishedListener listener) {
// If already set up, can't do it again.
checkNotDisposed();
if (mSetupDone) throw new IllegalStateException("IAB helper is already set up.");
// Connection to IAB service
logDebug("Starting in-app billing setup.");
mServiceConn = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
logDebug("Billing service disconnected.");
mService = null;
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
if (mDisposed) return;
logDebug("Billing service connected.");
mService = IInAppBillingService.Stub.asInterface(service);
String packageName = mContext.getPackageName();
try {
logDebug("Checking for in-app billing 3 support.");
// check for in-app billing v3 support
int response = mService.isBillingSupported(3, packageName, ITEM_TYPE_INAPP);
if (response != BILLING_RESPONSE_RESULT_OK) {
if (listener != null) listener.onIabSetupFinished(new IabResult(response,
"Error checking for billing v3 support."));
// if in-app purchases aren't supported, neither are subscriptions.
mSubscriptionsSupported = false;
return;
}
logDebug("In-app billing version 3 supported for " + packageName);
// check for v3 subscriptions support
response = mService.isBillingSupported(3, packageName, ITEM_TYPE_SUBS);
if (response == BILLING_RESPONSE_RESULT_OK) {
logDebug("Subscriptions AVAILABLE.");
mSubscriptionsSupported = true;
} else {
logDebug("Subscriptions NOT AVAILABLE. Response: " + response);
}
mSetupDone = true;
} catch (RemoteException e) {
if (listener != null) {
listener.onIabSetupFinished(new IabResult(IABHELPER_REMOTE_EXCEPTION,
"RemoteException while setting up in-app billing."));
}
e.printStackTrace();
return;
}
if (listener != null) {
listener.onIabSetupFinished(new IabResult(BILLING_RESPONSE_RESULT_OK, "Setup successful."));
}
}
};
// Intent serviceIntent = new Intent("ir.cafebazaar.pardakht.InAppBillingService.BIND");
// serviceIntent.setPackage("com.farsitel.bazaar");
Intent serviceIntent = new Intent("com.android.vending.billing.InAppBillingService.BIND");
serviceIntent.setPackage("com.android.vending");
if (!mContext.getPackageManager().queryIntentServices(serviceIntent, 0).isEmpty()) {
// service available to handle that Intent
mContext.bindService(serviceIntent, mServiceConn, Context.BIND_AUTO_CREATE);
} else {
// no service available to handle that Intent
if (listener != null) {
listener.onIabSetupFinished(
new IabResult(BILLING_RESPONSE_RESULT_BILLING_UNAVAILABLE,
"Billing service unavailable on device."));
}
}
}
/**
* Dispose of object, releasing resources. It's very important to call this
* method when you are done with this object. It will release any resources
* used by it such as service connections. Naturally, once the object is
* disposed of, it can't be used again.
*/
public void dispose() {
logDebug("Disposing.");
mSetupDone = false;
if (mServiceConn != null) {
logDebug("Unbinding from service.");
if (mContext != null) mContext.unbindService(mServiceConn);
}
mDisposed = true;
mContext = null;
mServiceConn = null;
mService = null;
mPurchaseListener = null;
}
private void checkNotDisposed() {
if (mDisposed)
throw new IllegalStateException("IabHelper was disposed of, so it cannot be used.");
}
/**
* Returns whether subscriptions are supported.
*/
public boolean subscriptionsSupported() {
checkNotDisposed();
return mSubscriptionsSupported;
}
/**
* Callback that notifies when a purchase is finished.
*/
public interface OnIabPurchaseFinishedListener {
/**
* Called to notify that an in-app purchase finished. If the purchase was successful,
* then the sku parameter specifies which item was purchased. If the purchase failed,
* the sku and extraData parameters may or may not be null, depending on how far the purchase
* process went.
*
* @param result The result of the purchase.
* @param info The purchase information (null if purchase failed)
*/
void onIabPurchaseFinished(IabResult result, Purchase info);
}
// The listener registered on launchPurchaseFlow, which we have to call back when
// the purchase finishes
OnIabPurchaseFinishedListener mPurchaseListener;
public void launchPurchaseFlow(Activity act, String sku, int requestCode, OnIabPurchaseFinishedListener listener) {
launchPurchaseFlow(act, sku, requestCode, listener, "");
}
public void launchPurchaseFlow(Activity act, String sku, int requestCode,
OnIabPurchaseFinishedListener listener, String extraData) {
launchPurchaseFlow(act, sku, ITEM_TYPE_INAPP, requestCode, listener, extraData);
}
public void launchSubscriptionPurchaseFlow(Activity act, String sku, int requestCode,
OnIabPurchaseFinishedListener listener) {
launchSubscriptionPurchaseFlow(act, sku, requestCode, listener, "");
}
public void launchSubscriptionPurchaseFlow(Activity act, String sku, int requestCode,
OnIabPurchaseFinishedListener listener, String extraData) {
launchPurchaseFlow(act, sku, ITEM_TYPE_SUBS, requestCode, listener, extraData);
}
/**
* Initiate the UI flow for an in-app purchase. Call this method to initiate an in-app purchase,
* which will involve bringing up the Google Play screen. The calling activity will be paused while
* the user interacts with Google Play, and the result will be delivered via the activity's
* {@link android.app.Activity#onActivityResult} method, at which point you must call
* this object's {@link #handleActivityResult} method to continue the purchase flow. This method
* MUST be called from the UI thread of the Activity.
*
* @param act The calling activity.
* @param sku The sku of the item to purchase.
* @param itemType indicates if it's a product or a subscription (ITEM_TYPE_INAPP or ITEM_TYPE_SUBS)
* @param requestCode A request code (to differentiate from other responses --
* as in {@link android.app.Activity#startActivityForResult}).
* @param listener The listener to notify when the purchase process finishes
* @param extraData Extra data (developer payload), which will be returned with the purchase data
* when the purchase completes. This extra data will be permanently bound to that purchase
* and will always be returned when the purchase is queried.
*/
public void launchPurchaseFlow(Activity act, String sku, String itemType, int requestCode,
OnIabPurchaseFinishedListener listener, String extraData) {
checkNotDisposed();
checkSetupDone("launchPurchaseFlow");
flagStartAsync("launchPurchaseFlow");
IabResult result;
if (itemType.equals(ITEM_TYPE_SUBS) && !mSubscriptionsSupported) {
IabResult r = new IabResult(IABHELPER_SUBSCRIPTIONS_NOT_AVAILABLE,
"Subscriptions are not available.");
flagEndAsync();
if (listener != null) listener.onIabPurchaseFinished(r, null);
return;
}
try {
logDebug("Constructing buy intent for " + sku + ", item type: " + itemType);
Bundle buyIntentBundle = mService.getBuyIntent(3, mContext.getPackageName(), sku, itemType, extraData);
int response = getResponseCodeFromBundle(buyIntentBundle);
if (response != BILLING_RESPONSE_RESULT_OK) {
logError("Unable to buy item, Error response: " + getResponseDesc(response));
flagEndAsync();
result = new IabResult(response, "Unable to buy item");
if (listener != null) listener.onIabPurchaseFinished(result, null);
return;
}
PendingIntent pendingIntent = buyIntentBundle.getParcelable(RESPONSE_BUY_INTENT);
logDebug("Launching buy intent for " + sku + ". Request code: " + requestCode);
mRequestCode = requestCode;
mPurchaseListener = listener;
mPurchasingItemType = itemType;
act.startIntentSenderForResult(pendingIntent.getIntentSender(),
requestCode, new Intent(),
Integer.valueOf(0), Integer.valueOf(0),
Integer.valueOf(0));
} catch (SendIntentException e) {
logError("SendIntentException while launching purchase flow for sku " + sku);
e.printStackTrace();
flagEndAsync();
result = new IabResult(IABHELPER_SEND_INTENT_FAILED, "Failed to send intent.");
if (listener != null) listener.onIabPurchaseFinished(result, null);
} catch (RemoteException e) {
logError("RemoteException while launching purchase flow for sku " + sku);
e.printStackTrace();
flagEndAsync();
result = new IabResult(IABHELPER_REMOTE_EXCEPTION, "Remote exception while starting purchase flow");
if (listener != null) listener.onIabPurchaseFinished(result, null);
}
}
/**
* Handles an activity result that's part of the purchase flow in in-app billing. If you
* are calling {@link #launchPurchaseFlow}, then you must call this method from your
* Activity's {@link android.app.Activity@onActivityResult} method. This method
* MUST be called from the UI thread of the Activity.
*
* @param requestCode The requestCode as you received it.
* @param resultCode The resultCode as you received it.
* @param data The data (Intent) as you received it.
* @return Returns true if the result was related to a purchase flow and was handled;
* false if the result was not related to a purchase, in which case you should
* handle it normally.
*/
public boolean handleActivityResult(int requestCode, int resultCode, Intent data) {
IabResult result;
if (requestCode != mRequestCode) return false;
checkNotDisposed();
checkSetupDone("handleActivityResult");
// end of async purchase operation that started on launchPurchaseFlow
flagEndAsync();
if (data == null) {
logError("Null data in IAB activity result.");
result = new IabResult(IABHELPER_BAD_RESPONSE, "Null data in IAB result");
if (mPurchaseListener != null) mPurchaseListener.onIabPurchaseFinished(result, null);
return true;
}
int responseCode = getResponseCodeFromIntent(data);
String purchaseData = data.getStringExtra(RESPONSE_INAPP_PURCHASE_DATA);
String dataSignature = data.getStringExtra(RESPONSE_INAPP_SIGNATURE);
if (resultCode == Activity.RESULT_OK && responseCode == BILLING_RESPONSE_RESULT_OK) {
logDebug("Successful resultcode from purchase activity.");
logDebug("Purchase data: " + purchaseData);
logDebug("Data signature: " + dataSignature);
logDebug("Extras: " + data.getExtras());
logDebug("Expected item type: " + mPurchasingItemType);
if (purchaseData == null || dataSignature == null) {
logError("BUG: either purchaseData or dataSignature is null.");
logDebug("Extras: " + data.getExtras().toString());
result = new IabResult(IABHELPER_UNKNOWN_ERROR, "IAB returned null purchaseData or dataSignature");
if (mPurchaseListener != null)
mPurchaseListener.onIabPurchaseFinished(result, null);
return true;
}
Purchase purchase = null;
try {
purchase = new Purchase(mPurchasingItemType, purchaseData, dataSignature);
String sku = purchase.getSku();
// Verify signature
if (!Security.verifyPurchase(mSignatureBase64, purchaseData, dataSignature)) {
logError("Purchase signature verification FAILED for sku " + sku);
result = new IabResult(IABHELPER_VERIFICATION_FAILED, "Signature verification failed for sku " + sku);
if (mPurchaseListener != null)
mPurchaseListener.onIabPurchaseFinished(result, purchase);
return true;
}
logDebug("Purchase signature successfully verified.");
} catch (JSONException e) {
logError("Failed to parse purchase data.");
e.printStackTrace();
result = new IabResult(IABHELPER_BAD_RESPONSE, "Failed to parse purchase data.");
if (mPurchaseListener != null)
mPurchaseListener.onIabPurchaseFinished(result, null);
return true;
}
if (mPurchaseListener != null) {
mPurchaseListener.onIabPurchaseFinished(new IabResult(BILLING_RESPONSE_RESULT_OK, "Success"), purchase);
}
} else if (resultCode == Activity.RESULT_OK) {
// result code was OK, but in-app billing response was not OK.
logDebug("Result code was OK but in-app billing response was not OK: " + getResponseDesc(responseCode));
if (mPurchaseListener != null) {
result = new IabResult(responseCode, "Problem purchashing item.");
mPurchaseListener.onIabPurchaseFinished(result, null);
}
} else if (resultCode == Activity.RESULT_CANCELED) {
logDebug("Purchase canceled - Response: " + getResponseDesc(responseCode));
result = new IabResult(IABHELPER_USER_CANCELLED, "User canceled.");
if (mPurchaseListener != null) mPurchaseListener.onIabPurchaseFinished(result, null);
} else {
logError("Purchase failed. Result code: " + Integer.toString(resultCode)
+ ". Response: " + getResponseDesc(responseCode));
result = new IabResult(IABHELPER_UNKNOWN_PURCHASE_RESPONSE, "Unknown purchase response.");
if (mPurchaseListener != null) mPurchaseListener.onIabPurchaseFinished(result, null);
}
return true;
}
public Inventory queryInventory(boolean querySkuDetails, List<String> moreSkus) throws IabException {
return queryInventory(querySkuDetails, moreSkus, null);
}
/**
* Queries the inventory. This will query all owned items from the server, as well as
* information on additional skus, if specified. This method may block or take long to execute.
* Do not call from a UI thread. For that, use the non-blocking version {@link #refreshInventoryAsync}.
*
* @param querySkuDetails if true, SKU details (price, description, etc) will be queried as well
* as purchase information.
* @param moreItemSkus additional PRODUCT skus to query information on, regardless of ownership.
* Ignored if null or if querySkuDetails is false.
* @param moreSubsSkus additional SUBSCRIPTIONS skus to query information on, regardless of ownership.
* Ignored if null or if querySkuDetails is false.
* @throws IabException if a problem occurs while refreshing the inventory.
*/
public Inventory queryInventory(boolean querySkuDetails, List<String> moreItemSkus,
List<String> moreSubsSkus) throws IabException {
checkNotDisposed();
checkSetupDone("queryInventory");
try {
Inventory inv = new Inventory();
int r = queryPurchases(inv, ITEM_TYPE_INAPP);
if (r != BILLING_RESPONSE_RESULT_OK) {
throw new IabException(r, "Error refreshing inventory (querying owned items).");
}
if (querySkuDetails) {
r = querySkuDetails(ITEM_TYPE_INAPP, inv, moreItemSkus);
if (r != BILLING_RESPONSE_RESULT_OK) {
throw new IabException(r, "Error refreshing inventory (querying prices of items).");
}
}
// if subscriptions are supported, then also query for subscriptions
if (mSubscriptionsSupported) {
r = queryPurchases(inv, ITEM_TYPE_SUBS);
if (r != BILLING_RESPONSE_RESULT_OK) {
throw new IabException(r, "Error refreshing inventory (querying owned subscriptions).");
}
if (querySkuDetails) {
r = querySkuDetails(ITEM_TYPE_SUBS, inv, moreItemSkus);
if (r != BILLING_RESPONSE_RESULT_OK) {
throw new IabException(r, "Error refreshing inventory (querying prices of subscriptions).");
}
}
}
return inv;
} catch (RemoteException e) {
throw new IabException(IABHELPER_REMOTE_EXCEPTION, "Remote exception while refreshing inventory.", e);
} catch (JSONException e) {
throw new IabException(IABHELPER_BAD_RESPONSE, "Error parsing JSON response while refreshing inventory.", e);
}
}
/**
* Listener that notifies when an inventory query operation completes.
*/
public interface QueryInventoryFinishedListener {
/**
* Called to notify that an inventory query operation completed.
*
* @param result The result of the operation.
* @param inv The inventory.
*/
void onQueryInventoryFinished(IabResult result, Inventory inv);
}
/**
* Asynchronous wrapper for inventory query. This will perform an inventory
* query as described in {@link #queryInventory}, but will do so asynchronously
* and call back the specified listener upon completion. This method is safe to
* call from a UI thread.
*
* @param querySkuDetails as in {@link #queryInventory}
* @param moreSkus as in {@link #queryInventory}
* @param listener The listener to notify when the refresh operation completes.
*/
public void queryInventoryAsync(final boolean querySkuDetails,
final List<String> moreSkus,
final QueryInventoryFinishedListener listener) {
final Handler handler = new Handler();
checkNotDisposed();
checkSetupDone("queryInventory");
flagStartAsync("refresh inventory");
(new Thread(new Runnable() {
public void run() {
IabResult result = new IabResult(BILLING_RESPONSE_RESULT_OK, "Inventory refresh successful.");
Inventory inv = null;
try {
inv = queryInventory(querySkuDetails, moreSkus);
} catch (IabException ex) {
result = ex.getResult();
}
flagEndAsync();
final IabResult result_f = result;
final Inventory inv_f = inv;
if (!mDisposed && listener != null) {
handler.post(new Runnable() {
public void run() {
listener.onQueryInventoryFinished(result_f, inv_f);
}
});
}
}
})).start();
}
public void queryInventoryAsync(QueryInventoryFinishedListener listener) {
queryInventoryAsync(true, null, listener);
}
public void queryInventoryAsync(boolean querySkuDetails, QueryInventoryFinishedListener listener) {
queryInventoryAsync(querySkuDetails, null, listener);
}
/**
* Consumes a given in-app product. Consuming can only be done on an item
* that's owned, and as a result of consumption, the user will no longer own it.
* This method may block or take long to return. Do not call from the UI thread.
* For that, see {@link #consumeAsync}.
*
* @param itemInfo The PurchaseInfo that represents the item to consume.
* @throws IabException if there is a problem during consumption.
*/
void consume(Purchase itemInfo) throws IabException {
checkNotDisposed();
checkSetupDone("consume");
if (!itemInfo.mItemType.equals(ITEM_TYPE_INAPP)) {
throw new IabException(IABHELPER_INVALID_CONSUMPTION,
"Items of type '" + itemInfo.mItemType + "' can't be consumed.");
}
try {
String token = itemInfo.getToken();
String sku = itemInfo.getSku();
if (token == null || token.equals("")) {
logError("Can't consume " + sku + ". No token.");
throw new IabException(IABHELPER_MISSING_TOKEN, "PurchaseInfo is missing token for sku: "
+ sku + " " + itemInfo);
}
logDebug("Consuming sku: " + sku + ", token: " + token);
int response = mService.consumePurchase(3, mContext.getPackageName(), token);
if (response == BILLING_RESPONSE_RESULT_OK) {
logDebug("Successfully consumed sku: " + sku);
} else {
logDebug("Error consuming consuming sku " + sku + ". " + getResponseDesc(response));
throw new IabException(response, "Error consuming sku " + sku);
}
} catch (RemoteException e) {
throw new IabException(IABHELPER_REMOTE_EXCEPTION, "Remote exception while consuming. PurchaseInfo: " + itemInfo, e);
}
}
/**
* Callback that notifies when a consumption operation finishes.
*/
public interface OnConsumeFinishedListener {
/**
* Called to notify that a consumption has finished.
*
* @param purchase The purchase that was (or was to be) consumed.
* @param result The result of the consumption operation.
*/
void onConsumeFinished(Purchase purchase, IabResult result);
}
/**
* Callback that notifies when a multi-item consumption operation finishes.
*/
public interface OnConsumeMultiFinishedListener {
/**
* Called to notify that a consumption of multiple items has finished.
*
* @param purchases The purchases that were (or were to be) consumed.
* @param results The results of each consumption operation, corresponding to each
* sku.
*/
void onConsumeMultiFinished(List<Purchase> purchases, List<IabResult> results);
}
/**
* Asynchronous wrapper to item consumption. Works like {@link #consume}, but
* performs the consumption in the background and notifies completion through
* the provided listener. This method is safe to call from a UI thread.
*
* @param purchase The purchase to be consumed.
* @param listener The listener to notify when the consumption operation finishes.
*/
public void consumeAsync(Purchase purchase, OnConsumeFinishedListener listener) {
checkNotDisposed();
checkSetupDone("consume");
List<Purchase> purchases = new ArrayList<Purchase>();
purchases.add(purchase);
consumeAsyncInternal(purchases, listener, null);
}
/**
* Same as {@link consumeAsync}, but for multiple items at once.
*
* @param purchases The list of PurchaseInfo objects representing the purchases to consume.
* @param listener The listener to notify when the consumption operation finishes.
*/
public void consumeAsync(List<Purchase> purchases, OnConsumeMultiFinishedListener listener) {
checkNotDisposed();
checkSetupDone("consume");
consumeAsyncInternal(purchases, null, listener);
}
/**
* Returns a human-readable description for the given response code.
*
* @param code The response code
* @return A human-readable string explaining the result code.
* It also includes the result code numerically.
*/
public static String getResponseDesc(int code) {
String[] iab_msgs = ("0:OK/1:User Canceled/2:Unknown/" +
"3:Billing Unavailable/4:Item unavailable/" +
"5:Developer Error/6:Error/7:Item Already Owned/" +
"8:Item not owned").split("/");
String[] iabhelper_msgs = ("0:OK/-1001:Remote exception during initialization/" +
"-1002:Bad response received/" +
"-1003:Purchase signature verification failed/" +
"-1004:Send intent failed/" +
"-1005:User cancelled/" +
"-1006:Unknown purchase response/" +
"-1007:Missing token/" +
"-1008:Unknown error/" +
"-1009:Subscriptions not available/" +
"-1010:Invalid consumption attempt").split("/");
if (code <= IABHELPER_ERROR_BASE) {
int index = IABHELPER_ERROR_BASE - code;
if (index >= 0 && index < iabhelper_msgs.length) return iabhelper_msgs[index];
else return String.valueOf(code) + ":Unknown IAB Helper Error";
} else if (code < 0 || code >= iab_msgs.length)
return String.valueOf(code) + ":Unknown";
else
return iab_msgs[code];
}
// Checks that setup was done; if not, throws an exception.
void checkSetupDone(String operation) {
if (!mSetupDone) {
logError("Illegal state for operation (" + operation + "): IAB helper is not set up.");
throw new IllegalStateException("IAB helper is not set up. Can't perform operation: " + operation);
}
}
// Workaround to bug where sometimes response codes come as Long instead of Integer
int getResponseCodeFromBundle(Bundle b) {
Object o = b.get(RESPONSE_CODE);
if (o == null) {
logDebug("Bundle with null response code, assuming OK (known issue)");
return BILLING_RESPONSE_RESULT_OK;
} else if (o instanceof Integer) return ((Integer) o).intValue();
else if (o instanceof Long) return (int) ((Long) o).longValue();
else {
logError("Unexpected type for bundle response code.");
logError(o.getClass().getName());
throw new RuntimeException("Unexpected type for bundle response code: " + o.getClass().getName());
}
}
// Workaround to bug where sometimes response codes come as Long instead of Integer
int getResponseCodeFromIntent(Intent i) {
Object o = i.getExtras().get(RESPONSE_CODE);
if (o == null) {
logError("Intent with no response code, assuming OK (known issue)");
return BILLING_RESPONSE_RESULT_OK;
} else if (o instanceof Integer) return ((Integer) o).intValue();
else if (o instanceof Long) return (int) ((Long) o).longValue();
else {
logError("Unexpected type for intent response code.");
logError(o.getClass().getName());
throw new RuntimeException("Unexpected type for intent response code: " + o.getClass().getName());
}
}
void flagStartAsync(String operation) {
if (mAsyncInProgress) throw new IllegalStateException("Can't start async operation (" +
operation + ") because another async operation(" + mAsyncOperation + ") is in progress.");
mAsyncOperation = operation;
mAsyncInProgress = true;
logDebug("Starting async operation: " + operation);
}
void flagEndAsync() {
logDebug("Ending async operation: " + mAsyncOperation);
mAsyncOperation = "";
mAsyncInProgress = false;
}
int queryPurchases(Inventory inv, String itemType) throws JSONException, RemoteException {
// Query purchases
logDebug("Querying owned items, item type: " + itemType);
logDebug("Package name: " + mContext.getPackageName());
boolean verificationFailed = false;
String continueToken = null;
do {
logDebug("Calling getPurchases with continuation token: " + continueToken);
Bundle ownedItems = mService.getPurchases(3, mContext.getPackageName(),
itemType, continueToken);
int response = getResponseCodeFromBundle(ownedItems);
logDebug("Owned items response: " + String.valueOf(response));
if (response != BILLING_RESPONSE_RESULT_OK) {
logDebug("getPurchases() failed: " + getResponseDesc(response));
return response;
}
if (!ownedItems.containsKey(RESPONSE_INAPP_ITEM_LIST)
|| !ownedItems.containsKey(RESPONSE_INAPP_PURCHASE_DATA_LIST)
|| !ownedItems.containsKey(RESPONSE_INAPP_SIGNATURE_LIST)) {
logError("Bundle returned from getPurchases() doesn't contain required fields.");
return IABHELPER_BAD_RESPONSE;
}
ArrayList<String> ownedSkus = ownedItems.getStringArrayList(
RESPONSE_INAPP_ITEM_LIST);
ArrayList<String> purchaseDataList = ownedItems.getStringArrayList(
RESPONSE_INAPP_PURCHASE_DATA_LIST);
ArrayList<String> signatureList = ownedItems.getStringArrayList(
RESPONSE_INAPP_SIGNATURE_LIST);
for (int i = 0; i < purchaseDataList.size(); ++i) {
String purchaseData = purchaseDataList.get(i);
String signature = signatureList.get(i);
String sku = ownedSkus.get(i);
if (Security.verifyPurchase(mSignatureBase64, purchaseData, signature)) {
logDebug("Sku is owned: " + sku);
Purchase purchase = new Purchase(itemType, purchaseData, signature);
if (TextUtils.isEmpty(purchase.getToken())) {
logWarn("BUG: empty/null token!");
logDebug("Purchase data: " + purchaseData);
}
// Record ownership and token
inv.addPurchase(purchase);
} else {
logWarn("Purchase signature verification **FAILED**. Not adding item.");
logDebug(" Purchase data: " + purchaseData);
logDebug(" Signature: " + signature);
verificationFailed = true;
}
}
continueToken = ownedItems.getString(INAPP_CONTINUATION_TOKEN);
logDebug("Continuation token: " + continueToken);
} while (!TextUtils.isEmpty(continueToken));
return verificationFailed ? IABHELPER_VERIFICATION_FAILED : BILLING_RESPONSE_RESULT_OK;
}
int querySkuDetails(String itemType, Inventory inv, List<String> moreSkus)
throws RemoteException, JSONException {
logDebug("Querying SKU details.");
ArrayList<String> skuList = new ArrayList<String>();
skuList.addAll(inv.getAllOwnedSkus(itemType));
if (moreSkus != null) {
for (String sku : moreSkus) {
if (!skuList.contains(sku)) {
skuList.add(sku);
}
}
}
if (skuList.size() == 0) {
logDebug("queryPrices: nothing to do because there are no SKUs.");
return BILLING_RESPONSE_RESULT_OK;
}
Bundle querySkus = new Bundle();
querySkus.putStringArrayList(GET_SKU_DETAILS_ITEM_LIST, skuList);
Bundle skuDetails = mService.getSkuDetails(3, mContext.getPackageName(),
itemType, querySkus);
if (!skuDetails.containsKey(RESPONSE_GET_SKU_DETAILS_LIST)) {
int response = getResponseCodeFromBundle(skuDetails);
if (response != BILLING_RESPONSE_RESULT_OK) {
logDebug("getSkuDetails() failed: " + getResponseDesc(response));
return response;
} else {
logError("getSkuDetails() returned a bundle with neither an error nor a detail list.");
return IABHELPER_BAD_RESPONSE;
}
}
ArrayList<String> responseList = skuDetails.getStringArrayList(
RESPONSE_GET_SKU_DETAILS_LIST);
for (String thisResponse : responseList) {
SkuDetails d = new SkuDetails(itemType, thisResponse);
logDebug("Got sku details: " + d);
inv.addSkuDetails(d);
}
return BILLING_RESPONSE_RESULT_OK;
}
void consumeAsyncInternal(final List<Purchase> purchases,
final OnConsumeFinishedListener singleListener,
final OnConsumeMultiFinishedListener multiListener) {
final Handler handler = new Handler();
flagStartAsync("consume");
(new Thread(new Runnable() {
public void run() {
final List<IabResult> results = new ArrayList<IabResult>();
for (Purchase purchase : purchases) {
try {
consume(purchase);
results.add(new IabResult(BILLING_RESPONSE_RESULT_OK, "Successful consume of sku " + purchase.getSku()));
} catch (IabException ex) {
results.add(ex.getResult());
}
}
flagEndAsync();
if (!mDisposed && singleListener != null) {
handler.post(new Runnable() {
public void run() {
singleListener.onConsumeFinished(purchases.get(0), results.get(0));
}
});
}
if (!mDisposed && multiListener != null) {
handler.post(new Runnable() {
public void run() {
multiListener.onConsumeMultiFinished(purchases, results);
}
});
}
}
})).start();
}
void logDebug(String msg) {
if (mDebugLog) Log.d(mDebugTag, msg);
}
void logError(String msg) {
Log.e(mDebugTag, "In-app billing error: " + msg);
}
void logWarn(String msg) {
Log.w(mDebugTag, "In-app billing warning: " + msg);
}
}

View File

@@ -1,45 +0,0 @@
/* Copyright (c) 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.v2ray.ang.util;
/**
* Represents the result of an in-app billing operation.
* A result is composed of a response code (an integer) and possibly a
* message (String). You can get those by calling
* {@link #getResponse} and {@link #getMessage()}, respectively. You
* can also inquire whether a result is a success or a failure by
* calling {@link #isSuccess()} and {@link #isFailure()}.
*/
public class IabResult {
int mResponse;
String mMessage;
public IabResult(int response, String message) {
mResponse = response;
if (message == null || message.trim().length() == 0) {
mMessage = IabHelper.getResponseDesc(response);
}
else {
mMessage = message + " (response: " + IabHelper.getResponseDesc(response) + ")";
}
}
public int getResponse() { return mResponse; }
public String getMessage() { return mMessage; }
public boolean isSuccess() { return mResponse == IabHelper.BILLING_RESPONSE_RESULT_OK; }
public boolean isFailure() { return !isSuccess(); }
public String toString() { return "IabResult: " + getMessage(); }
}

View File

@@ -1,91 +0,0 @@
/* Copyright (c) 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.v2ray.ang.util;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Represents a block of information about in-app items.
* An Inventory is returned by such methods as {@link IabHelper#queryInventory}.
*/
public class Inventory {
Map<String,SkuDetails> mSkuMap = new HashMap<String,SkuDetails>();
Map<String,Purchase> mPurchaseMap = new HashMap<String,Purchase>();
Inventory() { }
/** Returns the listing details for an in-app product. */
public SkuDetails getSkuDetails(String sku) {
return mSkuMap.get(sku);
}
/** Returns purchase information for a given product, or null if there is no purchase. */
public Purchase getPurchase(String sku) {
return mPurchaseMap.get(sku);
}
/** Returns whether or not there exists a purchase of the given product. */
public boolean hasPurchase(String sku) {
return mPurchaseMap.containsKey(sku);
}
/** Return whether or not details about the given product are available. */
public boolean hasDetails(String sku) {
return mSkuMap.containsKey(sku);
}
/**
* Erase a purchase (locally) from the inventory, given its product ID. This just
* modifies the Inventory object locally and has no effect on the server! This is
* useful when you have an existing Inventory object which you know to be up to date,
* and you have just consumed an item successfully, which means that erasing its
* purchase data from the Inventory you already have is quicker than querying for
* a new Inventory.
*/
public void erasePurchase(String sku) {
if (mPurchaseMap.containsKey(sku)) mPurchaseMap.remove(sku);
}
/** Returns a list of all owned product IDs. */
List<String> getAllOwnedSkus() {
return new ArrayList<String>(mPurchaseMap.keySet());
}
/** Returns a list of all owned product IDs of a given type */
List<String> getAllOwnedSkus(String itemType) {
List<String> result = new ArrayList<String>();
for (Purchase p : mPurchaseMap.values()) {
if (p.getItemType().equals(itemType)) result.add(p.getSku());
}
return result;
}
/** Returns a list of all purchases. */
List<Purchase> getAllPurchases() {
return new ArrayList<Purchase>(mPurchaseMap.values());
}
void addSkuDetails(SkuDetails d) {
mSkuMap.put(d.getSku(), d);
}
void addPurchase(Purchase p) {
mPurchaseMap.put(p.getSku(), p);
}
}

View File

@@ -1,540 +0,0 @@
package com.v2ray.ang.util;
import android.content.Context;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.text.TextUtils;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* Reference to http://blog.csdn.net/way_ping_li/article/details/8487866
* and improved some features...
*/
public class LogRecorder {
public static final int LOG_LEVEL_NO_SET = 0;
public static final int LOG_BUFFER_MAIN = 1;
public static final int LOG_BUFFER_SYSTEM = 1 << 1;
public static final int LOG_BUFFER_RADIO = 1 << 2;
public static final int LOG_BUFFER_EVENTS = 1 << 3;
public static final int LOG_BUFFER_KERNEL = 1 << 4; // not be supported by now
public static final int LOG_BUFFER_DEFAULT = LOG_BUFFER_MAIN | LOG_BUFFER_SYSTEM;
public static final int INVALID_PID = -1;
public String mFileSuffix;
public String mFolderPath;
public int mFileSizeLimitation;
public int mLevel;
public List<String> mFilterTags = new ArrayList<>();
public int mPID = INVALID_PID;
public boolean mUseLogcatFileOut = false;
private LogDumper mLogDumper = null;
public static final int EVENT_RESTART_LOG = 1001;
private RestartHandler mHandler;
private static class RestartHandler extends Handler {
final LogRecorder logRecorder;
public RestartHandler(LogRecorder logRecorder) {
this.logRecorder = logRecorder;
}
@Override
public void handleMessage(Message msg) {
if (msg.what == EVENT_RESTART_LOG) {
logRecorder.stop();
logRecorder.start();
}
}
}
public LogRecorder() {
mHandler = new RestartHandler(this);
}
public void start() {
// make sure the out folder exist
// TODO support multi-phase path
File file = new File(mFolderPath);
if (!file.exists()) {
file.mkdirs();
}
String cmdStr = collectLogcatCommand();
if (mLogDumper != null) {
mLogDumper.stopDumping();
mLogDumper = null;
}
mLogDumper = new LogDumper(mFolderPath, mFileSuffix, mFileSizeLimitation, cmdStr, mHandler);
mLogDumper.start();
}
public void stop() {
// TODO maybe should clean the log buffer first?
if (mLogDumper != null) {
mLogDumper.stopDumping();
mLogDumper = null;
}
}
private String collectLogcatCommand() {
StringBuilder stringBuilder = new StringBuilder();
final String SPACE = " ";
stringBuilder.append("logcat");
// TODO select ring buffer, -b
// TODO set out format
stringBuilder.append(SPACE);
stringBuilder.append("-v time");
// append tag filters
String levelStr = getLevelStr();
if (!mFilterTags.isEmpty()) {
stringBuilder.append(SPACE);
stringBuilder.append("-s");
for (int i = 0; i < mFilterTags.size(); i++) {
String tag = mFilterTags.get(i) + ":" + levelStr;
stringBuilder.append(SPACE);
stringBuilder.append(tag);
}
} else {
if (!TextUtils.isEmpty(levelStr)) {
stringBuilder.append(SPACE);
stringBuilder.append("*:" + levelStr);
}
}
// logcat -f , but the rotated count default is 4?
// can`t be sure to use that feature
if (mPID != INVALID_PID) {
mUseLogcatFileOut = false;
String pidStr = adjustPIDStr();
if (!TextUtils.isEmpty(pidStr)) {
stringBuilder.append(SPACE);
stringBuilder.append("|");
stringBuilder.append(SPACE);
stringBuilder.append("grep (" + pidStr + ")");
}
}
return stringBuilder.toString();
}
private String getLevelStr() {
switch (mLevel) {
case 2:
return "V";
case 3:
return "D";
case 4:
return "I";
case 5:
return "W";
case 6:
return "E";
case 7:
return "F";
}
return "V";
}
/**
* Android`s user app pid is bigger than 1000.
*
* @return
*/
private String adjustPIDStr() {
if (mPID == INVALID_PID) {
return null;
}
String pidStr = String.valueOf(mPID);
int length = pidStr.length();
if (length < 4) {
pidStr = " 0" + pidStr;
}
if (length == 4) {
pidStr = " " + pidStr;
}
return pidStr;
}
private class LogDumper extends Thread {
final String logPath;
final String logFileSuffix;
final int logFileLimitation;
final String logCmd;
final RestartHandler restartHandler;
private Process logcatProc;
private BufferedReader mReader = null;
private FileOutputStream out = null;
private boolean mRunning = true;
final private Object mRunningLock = new Object();
private long currentFileSize;
public LogDumper(String folderPath, String suffix,
int fileSizeLimitation, String command,
RestartHandler handler) {
logPath = folderPath;
logFileSuffix = suffix;
logFileLimitation = fileSizeLimitation;
logCmd = command;
restartHandler = handler;
String date = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss")
.format(new Date(System.currentTimeMillis()));
String fileName = (TextUtils.isEmpty(logFileSuffix)) ? date : (logFileSuffix + "-"+ date);
try {
out = new FileOutputStream(new File(logPath, fileName + ".log"));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
public void stopDumping() {
synchronized (mRunningLock) {
mRunning = false;
}
}
@Override
public void run() {
try {
logcatProc = Runtime.getRuntime().exec(logCmd);
mReader = new BufferedReader(new InputStreamReader(
logcatProc.getInputStream()), 1024);
String line = null;
while (mRunning && (line = mReader.readLine()) != null) {
if (!mRunning) {
break;
}
if (line.length() == 0) {
continue;
}
if (out != null && !line.isEmpty()) {
byte[] data = (line + "\n").getBytes();
out.write(data);
if (logFileLimitation != 0) {
currentFileSize += data.length;
if (currentFileSize > logFileLimitation*1024) {
restartHandler.sendEmptyMessage(EVENT_RESTART_LOG);
break;
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (logcatProc != null) {
logcatProc.destroy();
logcatProc = null;
}
if (mReader != null) {
try {
mReader.close();
mReader = null;
} catch (IOException e) {
e.printStackTrace();
}
}
if (out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
out = null;
}
}
}
}
public static class Builder {
/**
* context object
*/
private Context mContext;
/**
* the folder name that we save log files to,
* just folder name, not the whole path,
* if set this, will save log files to /sdcard/$mLogFolderName folder,
* use /sdcard/$ApplicationName as default.
*/
private String mLogFolderName;
/**
* the whole folder path that we save log files to,
* this setting`s priority is bigger than folder name.
*/
private String mLogFolderPath;
/**
* the log file suffix,
* if this is sot, it will be appended to log file name automatically
*/
private String mLogFileNameSuffix = "";
/**
* single log file size limitation,
* in k-bytes, ex. set to 16, is 16KB limitation.
*/
private int mLogFileSizeLimitation = 0;
/**
* log level, see android.util.Log, 2 - 7,
* if not be set, will use verbose as default
*/
private int mLogLevel = LogRecorder.LOG_LEVEL_NO_SET;
/**
* can set several filter tags
* logcat -s ActivityManager:V SystemUI:V
*/
private List<String> mLogFilterTags = new ArrayList<>();
/**
* filter through pid, by setting this with your APP PID,
* the log recorder will just record the APP`s own log,
* use one call: android.os.Process.myPid().
*/
private int mPID = LogRecorder.INVALID_PID;
/**
* which log buffer to catch...
* <p/>
* Request alternate ring buffer, 'main', 'system', 'radio'
* or 'events'. Multiple -b parameters are allowed and the
* results are interleaved.
* <p/>
* The default is -b main -b system.
*/
private int mLogBuffersSelected = LogRecorder.LOG_BUFFER_DEFAULT;
/**
* log output format, don`t support config yet, use $time format as default.
* <p/>
* Log messages contain a number of metadata fields, in addition to the tag and priority.
* You can modify the output format for messages so that they display a specific metadata
* field. To do so, you use the -v option and specify one of the supported output formats
* listed below.
* <p/>
* brief — Display priority/tag and PID of the process issuing the message.
* process — Display PID only.
* tag — Display the priority/tag only.
* thread - Display the priority, tag, and the PID(process ID) and TID(thread ID)
* of the thread issuing the message.
* raw — Display the raw log message, with no other metadata fields.
* time — Display the date, invocation time, priority/tag, and PID of
* the process issuing the message.
* threadtime — Display the date, invocation time, priority, tag, and the PID(process ID)
* and TID(thread ID) of the thread issuing the message.
* long — Display all metadata fields and separate messages with blank lines.
*/
private int mLogOutFormat;
/**
* set log out folder name
*
* @param logFolderName folder name
* @return The same Builder.
*/
public Builder setLogFolderName(String logFolderName) {
this.mLogFolderName = logFolderName;
return this;
}
/**
* set log out folder path
*
* @param logFolderPath out folder absolute path
* @return the same Builder
*/
public Builder setLogFolderPath(String logFolderPath) {
this.mLogFolderPath = logFolderPath;
return this;
}
/**
* set log file name suffix
*
* @param logFileNameSuffix auto appened suffix
* @return the same Builder
*/
public Builder setLogFileNameSuffix(String logFileNameSuffix) {
this.mLogFileNameSuffix = logFileNameSuffix;
return this;
}
/**
* set the file size limitation
*
* @param fileSizeLimitation file size limitation in KB
* @return the same Builder
*/
public Builder setLogFileSizeLimitation(int fileSizeLimitation) {
this.mLogFileSizeLimitation = fileSizeLimitation;
return this;
}
/**
* set the log level
*
* @param logLevel log level, 2-7
* @return the same Builder
*/
public Builder setLogLevel(int logLevel) {
this.mLogLevel = logLevel;
return this;
}
/**
* add log filterspec tag name, can add multiple ones,
* they use the same log level set by setLogLevel()
*
* @param tag tag name
* @return the same Builder
*/
public Builder addLogFilterTag(String tag) {
mLogFilterTags.add(tag);
return this;
}
/**
* which process`s log
*
* @param mPID process id
* @return the same Builder
*/
public Builder setPID(int mPID) {
this.mPID = mPID;
return this;
}
/**
* -b radio, -b main, -b system, -b events
* -b main -b system as default
*
* @param logBuffersSelected one of
* LOG_BUFFER_MAIN = 1 << 0;
* LOG_BUFFER_SYSTEM = 1 << 1;
* LOG_BUFFER_RADIO = 1 << 2;
* LOG_BUFFER_EVENTS = 1 << 3;
* LOG_BUFFER_KERNEL = 1 << 4;
* @return the same Builder
*/
public Builder setLogBufferSelected(int logBuffersSelected) {
this.mLogBuffersSelected = logBuffersSelected;
return this;
}
/**
* sets log out format, -v parameter
*
* @param logOutFormat out format, like -v time
* @return the same Builder
*/
public Builder setLogOutFormat(int logOutFormat) {
this.mLogOutFormat = mLogOutFormat;
return this;
}
public Builder(Context context) {
mContext = context;
}
/**
* call this only if mLogFolderName and mLogFolderPath not
* be set both.
*
* @return
*/
private void applyAppNameAsOutfolderName() {
try {
String appName = mContext.getPackageName();
String versionName = mContext.getPackageManager().getPackageInfo(
appName, 0).versionName;
int versionCode = mContext.getPackageManager()
.getPackageInfo(appName, 0).versionCode;
mLogFolderName = appName + "-" + versionName + "-" + versionCode;
mLogFolderPath = applyOutfolderPath();
} catch (Exception e) {
}
}
private String applyOutfolderPath() {
String outPath = "";
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
outPath = Environment.getExternalStorageDirectory()
.getAbsolutePath() + File.separator + mLogFolderName;
}
return outPath;
}
/**
* Combine all of the options that have been set and return
* a new {@link LogRecorder} object.
*/
public LogRecorder build() {
LogRecorder logRecorder = new LogRecorder();
// no folder name & folder path be set
if (TextUtils.isEmpty(mLogFolderName)
&& TextUtils.isEmpty(mLogFolderPath)) {
applyAppNameAsOutfolderName();
}
// make sure out path be set
if (TextUtils.isEmpty(mLogFolderPath)) {
mLogFolderPath = applyOutfolderPath();
}
logRecorder.mFolderPath = mLogFolderPath;
logRecorder.mFileSuffix = mLogFileNameSuffix;
logRecorder.mFileSizeLimitation = mLogFileSizeLimitation;
logRecorder.mLevel = mLogLevel;
if (!mLogFilterTags.isEmpty()) {
for (int i = 0; i < mLogFilterTags.size(); i++) {
logRecorder.mFilterTags.add(mLogFilterTags.get(i));
}
}
logRecorder.mPID = mPID;
return logRecorder;
}
}
}

View File

@@ -1,63 +0,0 @@
/* Copyright (c) 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.v2ray.ang.util;
import org.json.JSONException;
import org.json.JSONObject;
/**
* Represents an in-app billing purchase.
*/
public class Purchase {
String mItemType; // ITEM_TYPE_INAPP or ITEM_TYPE_SUBS
String mOrderId;
String mPackageName;
String mSku;
long mPurchaseTime;
int mPurchaseState;
String mDeveloperPayload;
String mToken;
String mOriginalJson;
String mSignature;
public Purchase(String itemType, String jsonPurchaseInfo, String signature) throws JSONException {
mItemType = itemType;
mOriginalJson = jsonPurchaseInfo;
JSONObject o = new JSONObject(mOriginalJson);
mOrderId = o.optString("orderId");
mPackageName = o.optString("packageName");
mSku = o.optString("productId");
mPurchaseTime = o.optLong("purchaseTime");
mPurchaseState = o.optInt("purchaseState");
mDeveloperPayload = o.optString("developerPayload");
mToken = o.optString("token", o.optString("purchaseToken"));
mSignature = signature;
}
public String getItemType() { return mItemType; }
public String getOrderId() { return mOrderId; }
public String getPackageName() { return mPackageName; }
public String getSku() { return mSku; }
public long getPurchaseTime() { return mPurchaseTime; }
public int getPurchaseState() { return mPurchaseState; }
public String getDeveloperPayload() { return mDeveloperPayload; }
public String getToken() { return mToken; }
public String getOriginalJson() { return mOriginalJson; }
public String getSignature() { return mSignature; }
@Override
public String toString() { return "PurchaseInfo(type:" + mItemType + "):" + mOriginalJson; }
}

View File

@@ -1,119 +0,0 @@
/* Copyright (c) 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.v2ray.ang.util;
import android.text.TextUtils;
import android.util.Log;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
/**
* Security-related methods. For a secure implementation, all of this code
* should be implemented on a server that communicates with the
* application on the device. For the sake of simplicity and clarity of this
* example, this code is included here and is executed on the device. If you
* must verify the purchases on the phone, you should obfuscate this code to
* make it harder for an attacker to replace the code with stubs that treat all
* purchases as verified.
*/
public class Security {
private static final String TAG = "IABUtil/Security";
private static final String KEY_FACTORY_ALGORITHM = "RSA";
private static final String SIGNATURE_ALGORITHM = "SHA1withRSA";
/**
* Verifies that the data was signed with the given signature, and returns
* the verified purchase. The data is in JSON format and signed
* with a private key. The data also contains the {@link PurchaseState}
* and product ID of the purchase.
* @param base64PublicKey the base64-encoded public key to use for verifying.
* @param signedData the signed JSON string (signed, not encrypted)
* @param signature the signature for the data, signed with the private key
*/
public static boolean verifyPurchase(String base64PublicKey, String signedData, String signature) {
if (TextUtils.isEmpty(signedData) || TextUtils.isEmpty(base64PublicKey) ||
TextUtils.isEmpty(signature)) {
Log.e(TAG, "Purchase verification failed: missing data.");
return false;
}
PublicKey key = Security.generatePublicKey(base64PublicKey);
return Security.verify(key, signedData, signature);
}
/**
* Generates a PublicKey instance from a string containing the
* Base64-encoded public key.
*
* @param encodedPublicKey Base64-encoded public key
* @throws IllegalArgumentException if encodedPublicKey is invalid
*/
public static PublicKey generatePublicKey(String encodedPublicKey) {
try {
byte[] decodedKey = Base64.decode(encodedPublicKey);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_FACTORY_ALGORITHM);
return keyFactory.generatePublic(new X509EncodedKeySpec(decodedKey));
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
} catch (InvalidKeySpecException e) {
Log.e(TAG, "Invalid key specification.");
throw new IllegalArgumentException(e);
} catch (Base64DecoderException e) {
Log.e(TAG, "Base64 decoding failed.");
throw new IllegalArgumentException(e);
}
}
/**
* Verifies that the signature from the server matches the computed
* signature on the data. Returns true if the data is correctly signed.
*
* @param publicKey public key associated with the developer account
* @param signedData signed data from server
* @param signature server signature
* @return true if the data and signature match
*/
public static boolean verify(PublicKey publicKey, String signedData, String signature) {
Signature sig;
try {
sig = Signature.getInstance(SIGNATURE_ALGORITHM);
sig.initVerify(publicKey);
sig.update(signedData.getBytes());
if (!sig.verify(Base64.decode(signature))) {
Log.e(TAG, "Signature verification failed.");
return false;
}
return true;
} catch (NoSuchAlgorithmException e) {
Log.e(TAG, "NoSuchAlgorithmException.");
} catch (InvalidKeyException e) {
Log.e(TAG, "Invalid key specification.");
} catch (SignatureException e) {
Log.e(TAG, "Signature exception.");
} catch (Base64DecoderException e) {
Log.e(TAG, "Base64 decoding failed.");
}
return false;
}
}

View File

@@ -1,58 +0,0 @@
/* Copyright (c) 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.v2ray.ang.util;
import org.json.JSONException;
import org.json.JSONObject;
/**
* Represents an in-app product's listing details.
*/
public class SkuDetails {
String mItemType;
String mSku;
String mType;
String mPrice;
String mTitle;
String mDescription;
String mJson;
public SkuDetails(String jsonSkuDetails) throws JSONException {
this(IabHelper.ITEM_TYPE_INAPP, jsonSkuDetails);
}
public SkuDetails(String itemType, String jsonSkuDetails) throws JSONException {
mItemType = itemType;
mJson = jsonSkuDetails;
JSONObject o = new JSONObject(mJson);
mSku = o.optString("productId");
mType = o.optString("type");
mPrice = o.optString("price");
mTitle = o.optString("title");
mDescription = o.optString("description");
}
public String getSku() { return mSku; }
public String getType() { return mType; }
public String getPrice() { return mPrice; }
public String getTitle() { return mTitle; }
public String getDescription() { return mDescription; }
@Override
public String toString() {
return "SkuDetails:" + mJson;
}
}

View File

@@ -1,7 +1,7 @@
package com.v2ray.ang package com.v2ray.ang
import android.support.multidex.MultiDexApplication import androidx.multidex.MultiDexApplication
import android.support.v7.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.tencent.mmkv.MMKV import com.tencent.mmkv.MMKV
class AngApplication : MultiDexApplication() { class AngApplication : MultiDexApplication() {

View File

@@ -21,7 +21,7 @@ data class ServerConfig(
return ServerConfig( return ServerConfig(
configType = configType, configType = configType,
outboundBean = V2rayConfig.OutboundBean( outboundBean = V2rayConfig.OutboundBean(
protocol = configType.name.toLowerCase(), protocol = configType.name.lowercase(),
settings = V2rayConfig.OutboundBean.OutSettingsBean( settings = V2rayConfig.OutboundBean.OutSettingsBean(
vnext = listOf(V2rayConfig.OutboundBean.OutSettingsBean.VnextBean( vnext = listOf(V2rayConfig.OutboundBean.OutSettingsBean.VnextBean(
users = listOf(V2rayConfig.OutboundBean.OutSettingsBean.VnextBean.UsersBean())))), users = listOf(V2rayConfig.OutboundBean.OutSettingsBean.VnextBean.UsersBean())))),
@@ -32,7 +32,7 @@ data class ServerConfig(
return ServerConfig( return ServerConfig(
configType = configType, configType = configType,
outboundBean = V2rayConfig.OutboundBean( outboundBean = V2rayConfig.OutboundBean(
protocol = configType.name.toLowerCase(), protocol = configType.name.lowercase(),
settings = V2rayConfig.OutboundBean.OutSettingsBean( settings = V2rayConfig.OutboundBean.OutSettingsBean(
servers = listOf(V2rayConfig.OutboundBean.OutSettingsBean.ServersBean())), servers = listOf(V2rayConfig.OutboundBean.OutSettingsBean.ServersBean())),
streamSettings = V2rayConfig.OutboundBean.StreamSettingsBean())) streamSettings = V2rayConfig.OutboundBean.StreamSettingsBean()))

View File

@@ -20,7 +20,7 @@ data class V2rayConfig(
val api: Any? = null, val api: Any? = null,
val transport: Any? = null, val transport: Any? = null,
val reverse: Any? = null, val reverse: Any? = null,
var fakedns: FakednsBean? = null, var fakedns: Any? = null,
val browserForwarder: Any? = null) { val browserForwarder: Any? = null) {
companion object { companion object {
const val DEFAULT_PORT = 443 const val DEFAULT_PORT = 443
@@ -89,7 +89,6 @@ data class V2rayConfig(
var users: List<UsersBean>) { var users: List<UsersBean>) {
data class UsersBean(var id: String = "", data class UsersBean(var id: String = "",
var alterId: Int? = null,
var security: String = DEFAULT_SECURITY, var security: String = DEFAULT_SECURITY,
var level: Int = DEFAULT_LEVEL, var level: Int = DEFAULT_LEVEL,
var encryption: String = "", var encryption: String = "",
@@ -205,8 +204,8 @@ data class V2rayConfig(
tcpSetting.header.type = HTTP tcpSetting.header.type = HTTP
if (!TextUtils.isEmpty(host) || !TextUtils.isEmpty(path)) { if (!TextUtils.isEmpty(host) || !TextUtils.isEmpty(path)) {
val requestObj = TcpSettingsBean.HeaderBean.RequestBean() val requestObj = TcpSettingsBean.HeaderBean.RequestBean()
requestObj.headers.Host = (host ?: "").split(",").map { it.trim() } requestObj.headers.Host = (host ?: "").split(",").map { it.trim() }.filter { it.isNotEmpty() }
requestObj.path = (path ?: "").split(",").map { it.trim() } requestObj.path = (path ?: "").split(",").map { it.trim() }.filter { it.isNotEmpty() }
tcpSetting.header.request = requestObj tcpSetting.header.request = requestObj
sni = requestObj.headers.Host.getOrNull(0) ?: sni sni = requestObj.headers.Host.getOrNull(0) ?: sni
} }
@@ -236,7 +235,7 @@ data class V2rayConfig(
"h2", "http" -> { "h2", "http" -> {
network = "h2" network = "h2"
val h2Setting = HttpSettingsBean() val h2Setting = HttpSettingsBean()
h2Setting.host = (host ?: "").split(",").map { it.trim() } h2Setting.host = (host ?: "").split(",").map { it.trim() }.filter { it.isNotEmpty() }
sni = h2Setting.host.getOrNull(0) ?: sni sni = h2Setting.host.getOrNull(0) ?: sni
h2Setting.path = path ?: "/" h2Setting.path = path ?: "/"
httpSettings = h2Setting httpSettings = h2Setting
@@ -369,8 +368,8 @@ data class V2rayConfig(
} }
} }
data class DnsBean(var servers: List<Any>? = null, data class DnsBean(var servers: ArrayList<Any>? = null,
var hosts: Map<String, String>? = null, var hosts: Map<String, Any>? = null,
val clientIp: String? = null, val clientIp: String? = null,
val disableCache: Boolean? = null, val disableCache: Boolean? = null,
val queryStrategy: String? = null, val queryStrategy: String? = null,

View File

@@ -5,7 +5,7 @@ data class VmessQRCode(var v: String = "",
var add: String = "", var add: String = "",
var port: String = "", var port: String = "",
var id: String = "", var id: String = "",
var aid: String = "", var aid: String = "0",
var net: String = "", var net: String = "",
var type: String = "", var type: String = "",
var host: String = "", var host: String = "",

View File

@@ -1,244 +0,0 @@
package com.v2ray.ang.extension
import android.app.Fragment
import android.app.ProgressDialog
import android.content.Context
import android.content.DialogInterface
import android.database.Cursor
import android.graphics.drawable.Drawable
import android.support.v7.app.AlertDialog
import android.view.KeyEvent
import android.view.View
import android.widget.ListAdapter
fun Context.alertView(
title: String? = null,
view: View,
init: (KAlertDialogBuilder.() -> Unit)? = null
) = KAlertDialogBuilder(this).apply {
if (title != null) title(title)
if (title != null) customView(view)
if (init != null) init()
}
fun Fragment.alert(
message: String,
title: String? = null,
init: (KAlertDialogBuilder.() -> Unit)? = null
) = activity.alert(message, title, init)
fun Context.alert(
message: String,
title: String? = null,
init: (KAlertDialogBuilder.() -> Unit)? = null
) = KAlertDialogBuilder(this).apply {
if (title != null) title(title)
message(message)
if (init != null) init()
}
fun Fragment.alert(
message: Int,
title: Int? = null,
init: (KAlertDialogBuilder.() -> Unit)? = null
) = activity.alert(message, title, init)
fun Context.alert(
message: Int,
title: Int? = null,
init: (KAlertDialogBuilder.() -> Unit)? = null
) = KAlertDialogBuilder(this).apply {
if (title != null) title(title)
message(message)
if (init != null) init()
}
fun Fragment.alert(init: KAlertDialogBuilder.() -> Unit): KAlertDialogBuilder = activity.alert(init)
fun Context.alert(init: KAlertDialogBuilder.() -> Unit) = KAlertDialogBuilder(this).apply { init() }
fun Fragment.progressDialog(
message: Int? = null,
title: Int? = null,
init: (ProgressDialog.() -> Unit)? = null
) = activity.progressDialog(message, title, init)
fun Context.progressDialog(
message: Int? = null,
title: Int? = null,
init: (ProgressDialog.() -> Unit)? = null
) = progressDialog(false, message?.let { getString(it) }, title?.let { getString(it) }, init)
fun Fragment.indeterminateProgressDialog(
message: Int? = null,
title: Int? = null,
init: (ProgressDialog.() -> Unit)? = null
) = activity.progressDialog(message, title, init)
fun Context.indeterminateProgressDialog(
message: Int? = null,
title: Int? = null,
init: (ProgressDialog.() -> Unit)? = null
) = progressDialog(true, message?.let { getString(it) }, title?.let { getString(it) }, init)
fun Fragment.progressDialog(
message: String? = null,
title: String? = null,
init: (ProgressDialog.() -> Unit)? = null
) = activity.progressDialog(message, title, init)
fun Context.progressDialog(
message: String? = null,
title: String? = null,
init: (ProgressDialog.() -> Unit)? = null
) = progressDialog(false, message, title, init)
fun Fragment.indeterminateProgressDialog(
message: String? = null,
title: String? = null,
init: (ProgressDialog.() -> Unit)? = null
) = activity.indeterminateProgressDialog(message, title, init)
fun Context.indeterminateProgressDialog(
message: String? = null,
title: String? = null,
init: (ProgressDialog.() -> Unit)? = null
) = progressDialog(true, message, title, init)
private fun Context.progressDialog(
indeterminate: Boolean,
message: String? = null,
title: String? = null,
init: (ProgressDialog.() -> Unit)? = null
) = ProgressDialog(this).apply {
isIndeterminate = indeterminate
if (!indeterminate) setProgressStyle(ProgressDialog.STYLE_HORIZONTAL)
if (message != null) setMessage(message)
if (title != null) setTitle(title)
if (init != null) init()
show()
}
fun Fragment.selector(
title: CharSequence? = null,
items: List<CharSequence>,
onClick: (Int) -> Unit
): Unit = activity.selector(title, items, onClick)
fun Context.selector(
title: CharSequence? = null,
items: List<CharSequence>,
onClick: (Int) -> Unit
) {
with(KAlertDialogBuilder(this)) {
if (title != null) title(title)
items(items, onClick)
show()
}
}
class KAlertDialogBuilder(val ctx: Context) {
val builder: AlertDialog.Builder = AlertDialog.Builder(ctx)
protected var dialog: AlertDialog? = null
fun dismiss() {
dialog?.dismiss()
}
fun show(): KAlertDialogBuilder {
dialog = builder.create()
dialog!!.show()
return this
}
fun title(title: CharSequence) {
builder.setTitle(title)
}
fun title(resource: Int) {
builder.setTitle(resource)
}
fun message(title: CharSequence) {
builder.setMessage(title)
}
fun message(resource: Int) {
builder.setMessage(resource)
}
fun icon(icon: Int) {
builder.setIcon(icon)
}
fun icon(icon: Drawable) {
builder.setIcon(icon)
}
fun customTitle(title: View) {
builder.setCustomTitle(title)
}
fun customView(view: View) {
builder.setView(view)
}
fun cancellable(value: Boolean = true) {
builder.setCancelable(value)
}
fun onCancel(f: () -> Unit) {
builder.setOnCancelListener { f() }
}
fun onKey(f: (keyCode: Int, e: KeyEvent) -> Boolean) {
builder.setOnKeyListener({ dialog, keyCode, event -> f(keyCode, event) })
}
fun neutralButton(textResource: Int = android.R.string.ok, f: DialogInterface.() -> Unit = { dismiss() }) {
neutralButton(ctx.getString(textResource), f)
}
fun neutralButton(title: String, f: DialogInterface.() -> Unit = { dismiss() }) {
builder.setNeutralButton(title, { dialog, which -> dialog.f() })
}
fun positiveButton(textResource: Int = android.R.string.ok, f: DialogInterface.() -> Unit) {
positiveButton(ctx.getString(textResource), f)
}
fun positiveButton(title: String, f: DialogInterface.() -> Unit) {
builder.setPositiveButton(title, { dialog, which -> dialog.f() })
}
fun negativeButton(textResource: Int = android.R.string.cancel, f: DialogInterface.() -> Unit = { dismiss() }) {
negativeButton(ctx.getString(textResource), f)
}
fun negativeButton(title: String, f: DialogInterface.() -> Unit = { dismiss() }) {
builder.setNegativeButton(title, { dialog, which -> dialog.f() })
}
fun items(itemsId: Int, f: (which: Int) -> Unit) {
items(ctx.resources!!.getTextArray(itemsId), f)
}
fun items(items: List<CharSequence>, f: (which: Int) -> Unit) {
items(items.toTypedArray(), f)
}
fun items(items: Array<CharSequence>, f: (which: Int) -> Unit) {
builder.setItems(items, { dialog, which -> f(which) })
}
fun adapter(adapter: ListAdapter, f: (which: Int) -> Unit) {
builder.setAdapter(adapter, { dialog, which -> f(which) })
}
fun adapter(cursor: Cursor, labelColumn: String, f: (which: Int) -> Unit) {
builder.setCursor(cursor, { dialog, which -> f(which) }, labelColumn)
}
}

View File

@@ -15,13 +15,13 @@ import java.net.URLConnection
val Context.v2RayApplication: AngApplication val Context.v2RayApplication: AngApplication
get() = applicationContext as AngApplication get() = applicationContext as AngApplication
inline fun Context.toast(message: Int): Toast = ToastCompat fun Context.toast(message: Int): Toast = ToastCompat
.makeText(this, message, Toast.LENGTH_SHORT) .makeText(this, message, Toast.LENGTH_SHORT)
.apply { .apply {
show() show()
} }
inline fun Context.toast(message: CharSequence): Toast = ToastCompat fun Context.toast(message: CharSequence): Toast = ToastCompat
.makeText(this, message, Toast.LENGTH_SHORT) .makeText(this, message, Toast.LENGTH_SHORT)
.apply { .apply {
show() show()

View File

@@ -6,6 +6,7 @@ import android.appwidget.AppWidgetProvider
import android.content.ComponentName import android.content.ComponentName
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Build
import android.widget.RemoteViews import android.widget.RemoteViews
import com.v2ray.ang.R import com.v2ray.ang.R
import com.v2ray.ang.AppConfig import com.v2ray.ang.AppConfig
@@ -21,16 +22,33 @@ class WidgetProvider : AppWidgetProvider() {
updateWidgetBackground(context, appWidgetManager, appWidgetIds, V2RayServiceManager.v2rayPoint.isRunning) updateWidgetBackground(context, appWidgetManager, appWidgetIds, V2RayServiceManager.v2rayPoint.isRunning)
} }
private fun updateWidgetBackground(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray, isRunning: Boolean) { private fun updateWidgetBackground(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray, isRunning: Boolean) {
val remoteViews = RemoteViews(context.packageName, R.layout.widget_switch) val remoteViews = RemoteViews(context.packageName, R.layout.widget_switch)
val intent = Intent(context, WidgetProvider::class.java) val intent = Intent(context, WidgetProvider::class.java)
intent.action = AppConfig.BROADCAST_ACTION_WIDGET_CLICK intent.action = AppConfig.BROADCAST_ACTION_WIDGET_CLICK
val pendingIntent = PendingIntent.getBroadcast(context, R.id.layout_switch, intent, PendingIntent.FLAG_UPDATE_CURRENT) val pendingIntent = PendingIntent.getBroadcast(
context,
R.id.layout_switch,
intent,
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
} else {
PendingIntent.FLAG_UPDATE_CURRENT
})
remoteViews.setOnClickPendingIntent(R.id.layout_switch, pendingIntent) remoteViews.setOnClickPendingIntent(R.id.layout_switch, pendingIntent)
if (isRunning) { if (isRunning) {
remoteViews.setInt(R.id.layout_switch, "setBackgroundResource", R.drawable.ic_rounded_corner_theme) remoteViews.setInt(
R.id.layout_switch,
"setBackgroundResource",
R.drawable.ic_rounded_corner_theme
)
} else { } else {
remoteViews.setInt(R.id.layout_switch, "setBackgroundResource", R.drawable.ic_rounded_corner_grey) remoteViews.setInt(
R.id.layout_switch,
"setBackgroundResource",
R.drawable.ic_rounded_corner_grey
)
} }
for (appWidgetId in appWidgetIds) { for (appWidgetId in appWidgetIds) {

View File

@@ -10,11 +10,12 @@ import android.content.Intent
import android.content.IntentFilter import android.content.IntentFilter
import android.graphics.Color import android.graphics.Color
import android.os.Build import android.os.Build
import android.support.annotation.RequiresApi import androidx.annotation.RequiresApi
import android.support.v4.app.NotificationCompat import androidx.core.app.NotificationCompat
import android.util.Log import android.util.Log
import com.tencent.mmkv.MMKV import com.tencent.mmkv.MMKV
import com.v2ray.ang.AppConfig import com.v2ray.ang.AppConfig
import com.v2ray.ang.AppConfig.ANG_PACKAGE
import com.v2ray.ang.AppConfig.TAG_DIRECT import com.v2ray.ang.AppConfig.TAG_DIRECT
import com.v2ray.ang.R import com.v2ray.ang.R
import com.v2ray.ang.dto.ServerConfig import com.v2ray.ang.dto.ServerConfig
@@ -26,6 +27,9 @@ import com.v2ray.ang.util.MmkvManager
import com.v2ray.ang.util.Utils import com.v2ray.ang.util.Utils
import com.v2ray.ang.util.V2rayConfigUtil import com.v2ray.ang.util.V2rayConfigUtil
import go.Seq import go.Seq
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import libv2ray.Libv2ray import libv2ray.Libv2ray
import libv2ray.V2RayPoint import libv2ray.V2RayPoint
import libv2ray.V2RayVPNServiceSupportsSet import libv2ray.V2RayVPNServiceSupportsSet
@@ -89,7 +93,7 @@ object V2RayServiceManager {
serviceControl.stopService() serviceControl.stopService()
0 0
} catch (e: Exception) { } catch (e: Exception) {
Log.d(serviceControl.getService().packageName, e.toString()) Log.d(ANG_PACKAGE, e.toString())
-1 -1
} }
} }
@@ -117,7 +121,7 @@ object V2RayServiceManager {
startSpeedNotification() startSpeedNotification()
0 0
} catch (e: Exception) { } catch (e: Exception) {
Log.d(serviceControl.getService().packageName, e.toString()) Log.d(ANG_PACKAGE, e.toString())
-1 -1
} }
} }
@@ -140,7 +144,7 @@ object V2RayServiceManager {
mFilter.addAction(Intent.ACTION_USER_PRESENT) mFilter.addAction(Intent.ACTION_USER_PRESENT)
service.registerReceiver(mMsgReceive, mFilter) service.registerReceiver(mMsgReceive, mFilter)
} catch (e: Exception) { } catch (e: Exception) {
Log.d(service.packageName, e.toString()) Log.d(ANG_PACKAGE, e.toString())
} }
v2rayPoint.configureFileContent = result.content v2rayPoint.configureFileContent = result.content
@@ -153,7 +157,7 @@ object V2RayServiceManager {
try { try {
v2rayPoint.runLoop() v2rayPoint.runLoop()
} catch (e: Exception) { } catch (e: Exception) {
Log.d(service.packageName, e.toString()) Log.d(ANG_PACKAGE, e.toString())
} }
if (v2rayPoint.isRunning) { if (v2rayPoint.isRunning) {
@@ -170,10 +174,12 @@ object V2RayServiceManager {
val service = serviceControl?.get()?.getService() ?: return val service = serviceControl?.get()?.getService() ?: return
if (v2rayPoint.isRunning) { if (v2rayPoint.isRunning) {
try { GlobalScope.launch(Dispatchers.Default) {
v2rayPoint.stopLoop() try {
} catch (e: Exception) { v2rayPoint.stopLoop()
Log.d(service.packageName, e.toString()) } catch (e: Exception) {
Log.d(ANG_PACKAGE, e.toString())
}
} }
} }
@@ -183,7 +189,7 @@ object V2RayServiceManager {
try { try {
service.unregisterReceiver(mMsgReceive) service.unregisterReceiver(mMsgReceive)
} catch (e: Exception) { } catch (e: Exception) {
Log.d(service.packageName, e.toString()) Log.d(ANG_PACKAGE, e.toString())
} }
} }
@@ -215,11 +221,11 @@ object V2RayServiceManager {
when (intent?.action) { when (intent?.action) {
Intent.ACTION_SCREEN_OFF -> { Intent.ACTION_SCREEN_OFF -> {
Log.d(AppConfig.ANG_PACKAGE, "SCREEN_OFF, stop querying stats") Log.d(ANG_PACKAGE, "SCREEN_OFF, stop querying stats")
stopSpeedNotification() stopSpeedNotification()
} }
Intent.ACTION_SCREEN_ON -> { Intent.ACTION_SCREEN_ON -> {
Log.d(AppConfig.ANG_PACKAGE, "SCREEN_ON, start querying stats") Log.d(ANG_PACKAGE, "SCREEN_ON, start querying stats")
startSpeedNotification() startSpeedNotification()
} }
} }
@@ -231,15 +237,23 @@ object V2RayServiceManager {
val startMainIntent = Intent(service, MainActivity::class.java) val startMainIntent = Intent(service, MainActivity::class.java)
val contentPendingIntent = PendingIntent.getActivity(service, val contentPendingIntent = PendingIntent.getActivity(service,
NOTIFICATION_PENDING_INTENT_CONTENT, startMainIntent, NOTIFICATION_PENDING_INTENT_CONTENT, startMainIntent,
PendingIntent.FLAG_UPDATE_CURRENT) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
} else {
PendingIntent.FLAG_UPDATE_CURRENT
})
val stopV2RayIntent = Intent(AppConfig.BROADCAST_ACTION_SERVICE) val stopV2RayIntent = Intent(AppConfig.BROADCAST_ACTION_SERVICE)
stopV2RayIntent.`package` = AppConfig.ANG_PACKAGE stopV2RayIntent.`package` = ANG_PACKAGE
stopV2RayIntent.putExtra("key", AppConfig.MSG_STATE_STOP) stopV2RayIntent.putExtra("key", AppConfig.MSG_STATE_STOP)
val stopV2RayPendingIntent = PendingIntent.getBroadcast(service, val stopV2RayPendingIntent = PendingIntent.getBroadcast(service,
NOTIFICATION_PENDING_INTENT_STOP_V2RAY, stopV2RayIntent, NOTIFICATION_PENDING_INTENT_STOP_V2RAY, stopV2RayIntent,
PendingIntent.FLAG_UPDATE_CURRENT) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
} else {
PendingIntent.FLAG_UPDATE_CURRENT
})
val channelId = val channelId =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {

View File

@@ -8,7 +8,7 @@ import android.net.*
import android.os.Build import android.os.Build
import android.os.ParcelFileDescriptor import android.os.ParcelFileDescriptor
import android.os.StrictMode import android.os.StrictMode
import android.support.annotation.RequiresApi import androidx.annotation.RequiresApi
import android.util.Log import android.util.Log
import com.tencent.mmkv.MMKV import com.tencent.mmkv.MMKV
import com.v2ray.ang.AppConfig import com.v2ray.ang.AppConfig
@@ -132,8 +132,7 @@ class V2RayVpnService : VpnService(), ServiceControl {
builder.setSession(V2RayServiceManager.currentConfig?.remarks.orEmpty()) builder.setSession(V2RayServiceManager.currentConfig?.remarks.orEmpty())
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && if (settingsStorage?.decodeBool(AppConfig.PREF_PER_APP_PROXY) == true) {
settingsStorage?.decodeBool(AppConfig.PREF_PER_APP_PROXY) == true) {
val apps = settingsStorage?.decodeStringSet(AppConfig.PREF_PER_APP_PROXY_SET) val apps = settingsStorage?.decodeStringSet(AppConfig.PREF_PER_APP_PROXY_SET)
val bypassApps = settingsStorage?.decodeBool(AppConfig.PREF_BYPASS_APPS) ?: false val bypassApps = settingsStorage?.decodeBool(AppConfig.PREF_BYPASS_APPS) ?: false
apps?.forEach { apps?.forEach {

View File

@@ -1,6 +1,6 @@
package com.v2ray.ang.ui package com.v2ray.ang.ui
import android.support.v7.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import android.view.MenuItem import android.view.MenuItem
abstract class BaseActivity : AppCompatActivity() { abstract class BaseActivity : AppCompatActivity() {
@@ -11,4 +11,4 @@ abstract class BaseActivity : AppCompatActivity() {
} }
else -> super.onOptionsItemSelected(item) else -> super.onOptionsItemSelected(item)
} }
} }

View File

@@ -1,21 +1,17 @@
package com.v2ray.ang.ui package com.v2ray.ang.ui
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.viewpager2.adapter.FragmentStateAdapter
import android.support.v4.app.Fragment class FragmentAdapter(fragmentActivity: FragmentActivity, private val mFragments: List<Fragment>) :
import android.support.v4.app.FragmentManager FragmentStateAdapter(fragmentActivity) {
import android.support.v4.app.FragmentStatePagerAdapter
class FragmentAdapter(fm: FragmentManager, private val mFragments: List<Fragment>, private val mTitles: List<String>) : FragmentStatePagerAdapter(fm) { override fun createFragment(position: Int): Fragment {
override fun getItem(position: Int): Fragment {
return mFragments[position] return mFragments[position]
} }
override fun getCount(): Int { override fun getItemCount(): Int {
return mFragments.size return mFragments.size
} }
}
override fun getPageTitle(position: Int): CharSequence? {
return mTitles[position]
}
}

View File

@@ -7,10 +7,11 @@ import android.text.method.ScrollingMovementMethod
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import com.v2ray.ang.AppConfig.ANG_PACKAGE
import com.v2ray.ang.R import com.v2ray.ang.R
import com.v2ray.ang.databinding.ActivityLogcatBinding
import com.v2ray.ang.extension.toast import com.v2ray.ang.extension.toast
import com.v2ray.ang.util.Utils import com.v2ray.ang.util.Utils
import kotlinx.android.synthetic.main.activity_logcat.*
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@@ -18,10 +19,13 @@ import java.io.IOException
import java.util.LinkedHashSet import java.util.LinkedHashSet
class LogcatActivity : BaseActivity() { class LogcatActivity : BaseActivity() {
private lateinit var binding: ActivityLogcatBinding
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_logcat) binding = ActivityLogcatBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
title = getString(R.string.title_logcat) title = getString(R.string.title_logcat)
@@ -32,7 +36,7 @@ class LogcatActivity : BaseActivity() {
private fun logcat(shouldFlushLog: Boolean) { private fun logcat(shouldFlushLog: Boolean) {
try { try {
pb_waiting.visibility = View.VISIBLE binding.pbWaiting.visibility = View.VISIBLE
GlobalScope.launch(Dispatchers.Default) { GlobalScope.launch(Dispatchers.Default) {
if (shouldFlushLog) { if (shouldFlushLog) {
@@ -48,17 +52,17 @@ class LogcatActivity : BaseActivity() {
lst.add("-v") lst.add("-v")
lst.add("time") lst.add("time")
lst.add("-s") lst.add("-s")
lst.add("GoLog,tun2socks,com.v2ray.ang") lst.add("GoLog,tun2socks,${ANG_PACKAGE},AndroidRuntime,System.err")
val process = Runtime.getRuntime().exec(lst.toTypedArray()) val process = Runtime.getRuntime().exec(lst.toTypedArray())
// val bufferedReader = BufferedReader( // val bufferedReader = BufferedReader(
// InputStreamReader(process.inputStream)) // InputStreamReader(process.inputStream))
// val allText = bufferedReader.use(BufferedReader::readText) // val allText = bufferedReader.use(BufferedReader::readText)
val allText = process.inputStream.bufferedReader().use { it.readText() } val allText = process.inputStream.bufferedReader().use { it.readText() }
launch(Dispatchers.Main) { launch(Dispatchers.Main) {
tv_logcat.text = allText binding.tvLogcat.text = allText
tv_logcat.movementMethod = ScrollingMovementMethod() binding.tvLogcat.movementMethod = ScrollingMovementMethod()
pb_waiting.visibility = View.GONE binding.pbWaiting.visibility = View.GONE
Handler(Looper.getMainLooper()).post { sv_logcat.fullScroll(View.FOCUS_DOWN) } Handler(Looper.getMainLooper()).post { binding.svLogcat.fullScroll(View.FOCUS_DOWN) }
} }
} }
} catch (e: IOException) { } catch (e: IOException) {
@@ -66,14 +70,14 @@ class LogcatActivity : BaseActivity() {
} }
} }
override fun onCreateOptionsMenu(menu: Menu?): Boolean { override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.menu_logcat, menu) menuInflater.inflate(R.menu.menu_logcat, menu)
return super.onCreateOptionsMenu(menu) return super.onCreateOptionsMenu(menu)
} }
override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) { override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
R.id.copy_all -> { R.id.copy_all -> {
Utils.setClipboard(this, tv_logcat.text.toString()) Utils.setClipboard(this, binding.tvLogcat.text.toString())
toast(R.string.toast_success) toast(R.string.toast_success)
true true
} }

View File

@@ -1,27 +1,30 @@
package com.v2ray.ang.ui package com.v2ray.ang.ui
import android.Manifest import android.Manifest
import android.arch.lifecycle.ViewModelProviders
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import android.net.VpnService import android.net.VpnService
import android.os.Bundle import android.os.Bundle
import android.support.design.widget.NavigationView import com.google.android.material.navigation.NavigationView
import android.support.v4.view.GravityCompat import androidx.core.view.GravityCompat
import android.support.v7.app.ActionBarDrawerToggle import androidx.appcompat.app.ActionBarDrawerToggle
import android.support.v7.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import android.support.v7.widget.helper.ItemTouchHelper import androidx.recyclerview.widget.ItemTouchHelper
import android.text.TextUtils import android.text.TextUtils
import android.util.Log import android.util.Log
import android.view.KeyEvent import android.view.KeyEvent
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import android.widget.Toast import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels
import com.tbruyelle.rxpermissions.RxPermissions import com.tbruyelle.rxpermissions.RxPermissions
import com.tencent.mmkv.MMKV import com.tencent.mmkv.MMKV
import com.v2ray.ang.AppConfig import com.v2ray.ang.AppConfig
import com.v2ray.ang.AppConfig.ANG_PACKAGE
import com.v2ray.ang.BuildConfig import com.v2ray.ang.BuildConfig
import com.v2ray.ang.R import com.v2ray.ang.R
import com.v2ray.ang.databinding.ActivityMainBinding
import com.v2ray.ang.dto.EConfigType import com.v2ray.ang.dto.EConfigType
import com.v2ray.ang.extension.toast import com.v2ray.ang.extension.toast
import com.v2ray.ang.helper.SimpleItemTouchHelperCallback import com.v2ray.ang.helper.SimpleItemTouchHelperCallback
@@ -30,7 +33,6 @@ import com.v2ray.ang.util.AngConfigManager
import com.v2ray.ang.util.MmkvManager import com.v2ray.ang.util.MmkvManager
import com.v2ray.ang.util.Utils import com.v2ray.ang.util.Utils
import com.v2ray.ang.viewmodel.MainViewModel import com.v2ray.ang.viewmodel.MainViewModel
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@@ -38,30 +40,31 @@ import libv2ray.Libv2ray
import me.drakeet.support.toast.ToastCompat import me.drakeet.support.toast.ToastCompat
import rx.Observable import rx.Observable
import rx.android.schedulers.AndroidSchedulers import rx.android.schedulers.AndroidSchedulers
import java.net.URL
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedListener { class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedListener {
companion object { private lateinit var binding: ActivityMainBinding
private const val REQUEST_CODE_VPN_PREPARE = 0
private const val REQUEST_SCAN = 1
private const val REQUEST_FILE_CHOOSER = 2
private const val REQUEST_SCAN_URL = 3
}
private val adapter by lazy { MainRecyclerAdapter(this) } private val adapter by lazy { MainRecyclerAdapter(this) }
private val mainStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_MAIN, MMKV.MULTI_PROCESS_MODE) } 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 settingsStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SETTING, MMKV.MULTI_PROCESS_MODE) }
private val requestVpnPermission = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
if (it.resultCode == RESULT_OK) {
startV2Ray()
}
}
private var mItemTouchHelper: ItemTouchHelper? = null private var mItemTouchHelper: ItemTouchHelper? = null
val mainViewModel: MainViewModel by lazy { ViewModelProviders.of(this).get(MainViewModel::class.java) } val mainViewModel: MainViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main) binding = ActivityMainBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
title = getString(R.string.title_server) title = getString(R.string.title_server)
setSupportActionBar(toolbar) setSupportActionBar(binding.toolbar)
fab.setOnClickListener { binding.fab.setOnClickListener {
if (mainViewModel.isRunning.value == true) { if (mainViewModel.isRunning.value == true) {
Utils.stopVService(this) Utils.stopVService(this)
} else if (settingsStorage?.decodeString(AppConfig.PREF_MODE) ?: "VPN" == "VPN") { } else if (settingsStorage?.decodeString(AppConfig.PREF_MODE) ?: "VPN" == "VPN") {
@@ -69,65 +72,65 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
if (intent == null) { if (intent == null) {
startV2Ray() startV2Ray()
} else { } else {
startActivityForResult(intent, REQUEST_CODE_VPN_PREPARE) requestVpnPermission.launch(intent)
} }
} else { } else {
startV2Ray() startV2Ray()
} }
} }
layout_test.setOnClickListener { binding.layoutTest.setOnClickListener {
if (mainViewModel.isRunning.value == true) { if (mainViewModel.isRunning.value == true) {
tv_test_state.text = getString(R.string.connection_test_testing) binding.tvTestState.text = getString(R.string.connection_test_testing)
mainViewModel.testCurrentServerRealPing() mainViewModel.testCurrentServerRealPing()
} else { } else {
// tv_test_state.text = getString(R.string.connection_test_fail) // tv_test_state.text = getString(R.string.connection_test_fail)
} }
} }
recycler_view.setHasFixedSize(true) binding.recyclerView.setHasFixedSize(true)
recycler_view.layoutManager = LinearLayoutManager(this) binding.recyclerView.layoutManager = LinearLayoutManager(this)
recycler_view.adapter = adapter binding.recyclerView.adapter = adapter
val callback = SimpleItemTouchHelperCallback(adapter) val callback = SimpleItemTouchHelperCallback(adapter)
mItemTouchHelper = ItemTouchHelper(callback) mItemTouchHelper = ItemTouchHelper(callback)
mItemTouchHelper?.attachToRecyclerView(recycler_view) mItemTouchHelper?.attachToRecyclerView(binding.recyclerView)
val toggle = ActionBarDrawerToggle( val toggle = ActionBarDrawerToggle(
this, drawer_layout, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close) this, binding.drawerLayout, binding.toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close)
drawer_layout.addDrawerListener(toggle) binding.drawerLayout.addDrawerListener(toggle)
toggle.syncState() toggle.syncState()
nav_view.setNavigationItemSelectedListener(this) binding.navView.setNavigationItemSelectedListener(this)
version.text = "v${BuildConfig.VERSION_NAME} (${Libv2ray.checkVersionX()})" binding.version.text = "v${BuildConfig.VERSION_NAME} (${Libv2ray.checkVersionX()})"
setupViewModelObserver() setupViewModelObserver()
migrateLegacy() migrateLegacy()
} }
private fun setupViewModelObserver() { private fun setupViewModelObserver() {
mainViewModel.updateListAction.observe(this, { mainViewModel.updateListAction.observe(this) {
val index = it ?: return@observe val index = it ?: return@observe
if (index >= 0) { if (index >= 0) {
adapter.notifyItemChanged(index) adapter.notifyItemChanged(index)
} else { } else {
adapter.notifyDataSetChanged() adapter.notifyDataSetChanged()
} }
}) }
mainViewModel.updateTestResultAction.observe(this, { tv_test_state.text = it }) mainViewModel.updateTestResultAction.observe(this) { binding.tvTestState.text = it }
mainViewModel.isRunning.observe(this, { mainViewModel.isRunning.observe(this) {
val isRunning = it ?: return@observe val isRunning = it ?: return@observe
adapter.isRunning = isRunning adapter.isRunning = isRunning
if (isRunning) { if (isRunning) {
fab.setImageResource(R.drawable.ic_v) binding.fab.setImageResource(R.drawable.ic_v)
tv_test_state.text = getString(R.string.connection_connected) binding.tvTestState.text = getString(R.string.connection_connected)
layout_test.isFocusable = true binding.layoutTest.isFocusable = true
} else { } else {
fab.setImageResource(R.drawable.ic_v_idle) binding.fab.setImageResource(R.drawable.ic_v_idle)
tv_test_state.text = getString(R.string.connection_not_connected) binding.tvTestState.text = getString(R.string.connection_not_connected)
layout_test.isFocusable = false binding.layoutTest.isFocusable = false
} }
hideCircle() hideCircle()
}) }
mainViewModel.startListenBroadcast() mainViewModel.startListenBroadcast()
} }
@@ -166,38 +169,14 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
super.onPause() super.onPause()
} }
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { override fun onCreateOptionsMenu(menu: Menu): Boolean {
super.onActivityResult(requestCode, resultCode, data)
when (requestCode) {
REQUEST_CODE_VPN_PREPARE ->
if (resultCode == RESULT_OK) {
startV2Ray()
}
REQUEST_SCAN ->
if (resultCode == RESULT_OK) {
importBatchConfig(data?.getStringExtra("SCAN_RESULT"))
}
REQUEST_FILE_CHOOSER -> {
val uri = data?.data
if (resultCode == RESULT_OK && uri != null) {
readContentFromUri(uri)
}
}
REQUEST_SCAN_URL ->
if (resultCode == RESULT_OK) {
importConfigCustomUrl(data?.getStringExtra("SCAN_RESULT"))
}
}
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.menu_main, menu) menuInflater.inflate(R.menu.menu_main, menu)
return true return true
} }
override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) { override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
R.id.import_qrcode -> { R.id.import_qrcode -> {
importQRcode(REQUEST_SCAN) importQRcode(true)
true true
} }
R.id.import_clipboard -> { R.id.import_clipboard -> {
@@ -232,7 +211,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
true true
} }
R.id.import_config_custom_url_scan -> { R.id.import_config_custom_url_scan -> {
importQRcode(REQUEST_SCAN_URL) importQRcode(false)
true true
} }
@@ -275,7 +254,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
/** /**
* import config from qrcode * import config from qrcode
*/ */
fun importQRcode(requestCode: Int): Boolean { fun importQRcode(forConfig: Boolean): Boolean {
// try { // try {
// startActivityForResult(Intent("com.google.zxing.client.android.SCAN") // startActivityForResult(Intent("com.google.zxing.client.android.SCAN")
// .addCategory(Intent.CATEGORY_DEFAULT) // .addCategory(Intent.CATEGORY_DEFAULT)
@@ -285,7 +264,10 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
.request(Manifest.permission.CAMERA) .request(Manifest.permission.CAMERA)
.subscribe { .subscribe {
if (it) if (it)
startActivityForResult(Intent(this, ScannerActivity::class.java), requestCode) if (forConfig)
scanQRCodeForConfig.launch(Intent(this, ScannerActivity::class.java))
else
scanQRCodeForUrlToCustomConfig.launch(Intent(this, ScannerActivity::class.java))
else else
toast(R.string.toast_permission_denied) toast(R.string.toast_permission_denied)
} }
@@ -293,6 +275,18 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
return true return true
} }
private val scanQRCodeForConfig = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
if (it.resultCode == RESULT_OK) {
importBatchConfig(it.data?.getStringExtra("SCAN_RESULT"))
}
}
private val scanQRCodeForUrlToCustomConfig = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
if (it.resultCode == RESULT_OK) {
importConfigCustomUrl(it.data?.getStringExtra("SCAN_RESULT"))
}
}
/** /**
* import config from clipboard * import config from clipboard
*/ */
@@ -376,7 +370,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
} }
GlobalScope.launch(Dispatchers.IO) { GlobalScope.launch(Dispatchers.IO) {
val configText = try { val configText = try {
URL(url).readText() Utils.getUrlContentWithCustomUserAgent(url)
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
"" ""
@@ -410,13 +404,16 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
if (!Utils.isValidUrl(url)) { if (!Utils.isValidUrl(url)) {
return@forEach return@forEach
} }
Log.d("Main", url) Log.d(ANG_PACKAGE, url)
GlobalScope.launch(Dispatchers.IO) { GlobalScope.launch(Dispatchers.IO) {
val configText = try { val configText = try {
URL(url).readText() Utils.getUrlContentWithCustomUserAgent(url)
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
"" launch(Dispatchers.Main) {
toast("\"" + it.second.remarks + "\" " + getString(R.string.toast_failure))
}
return@launch
} }
launch(Dispatchers.Main) { launch(Dispatchers.Main) {
importBatchConfig(Utils.decode(configText), it.first) importBatchConfig(Utils.decode(configText), it.first)
@@ -439,14 +436,19 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
intent.addCategory(Intent.CATEGORY_OPENABLE) intent.addCategory(Intent.CATEGORY_OPENABLE)
try { try {
startActivityForResult( chooseFileForCustomConfig.launch(Intent.createChooser(intent, getString(R.string.title_file_chooser)))
Intent.createChooser(intent, getString(R.string.title_file_chooser)),
REQUEST_FILE_CHOOSER)
} catch (ex: android.content.ActivityNotFoundException) { } catch (ex: android.content.ActivityNotFoundException) {
toast(R.string.toast_require_file_manager) toast(R.string.toast_require_file_manager)
} }
} }
private val chooseFileForCustomConfig = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
val uri = it.data?.data
if (it.resultCode == RESULT_OK && uri != null) {
readContentFromUri(uri)
}
}
/** /**
* read content from uri * read content from uri
*/ */
@@ -504,7 +506,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
} }
fun showCircle() { fun showCircle() {
fabProgressCircle?.show() binding.fabProgressCircle.show()
} }
fun hideCircle() { fun hideCircle() {
@@ -512,8 +514,8 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
Observable.timer(300, TimeUnit.MILLISECONDS) Observable.timer(300, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe { .subscribe {
if (fabProgressCircle.isShown) { if (binding.fabProgressCircle.isShown) {
fabProgressCircle.hide() binding.fabProgressCircle.hide()
} }
} }
} catch (e: Exception) { } catch (e: Exception) {
@@ -521,8 +523,8 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
} }
override fun onBackPressed() { override fun onBackPressed() {
if (drawer_layout.isDrawerOpen(GravityCompat.START)) { if (binding.drawerLayout.isDrawerOpen(GravityCompat.START)) {
drawer_layout.closeDrawer(GravityCompat.START) binding.drawerLayout.closeDrawer(GravityCompat.START)
} else { } else {
super.onBackPressed() super.onBackPressed()
} }
@@ -552,7 +554,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
startActivity(Intent(this, LogcatActivity::class.java)) startActivity(Intent(this, LogcatActivity::class.java))
} }
} }
drawer_layout.closeDrawer(GravityCompat.START) binding.drawerLayout.closeDrawer(GravityCompat.START)
return true return true
} }
} }

View File

@@ -2,9 +2,9 @@ package com.v2ray.ang.ui
import android.content.Intent import android.content.Intent
import android.graphics.Color import android.graphics.Color
import android.support.v4.content.ContextCompat import androidx.core.content.ContextCompat
import android.support.v7.app.AlertDialog import androidx.appcompat.app.AlertDialog
import android.support.v7.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@@ -12,6 +12,9 @@ import com.google.gson.Gson
import com.tencent.mmkv.MMKV import com.tencent.mmkv.MMKV
import com.v2ray.ang.AppConfig import com.v2ray.ang.AppConfig
import com.v2ray.ang.R import com.v2ray.ang.R
import com.v2ray.ang.databinding.ItemQrcodeBinding
import com.v2ray.ang.databinding.ItemRecyclerFooterBinding
import com.v2ray.ang.databinding.ItemRecyclerMainBinding
import com.v2ray.ang.dto.EConfigType import com.v2ray.ang.dto.EConfigType
import com.v2ray.ang.dto.SubscriptionItem import com.v2ray.ang.dto.SubscriptionItem
import com.v2ray.ang.extension.toast import com.v2ray.ang.extension.toast
@@ -21,8 +24,6 @@ import com.v2ray.ang.service.V2RayServiceManager
import com.v2ray.ang.util.AngConfigManager import com.v2ray.ang.util.AngConfigManager
import com.v2ray.ang.util.MmkvManager import com.v2ray.ang.util.MmkvManager
import com.v2ray.ang.util.Utils import com.v2ray.ang.util.Utils
import kotlinx.android.synthetic.main.item_qrcode.view.*
import kotlinx.android.synthetic.main.item_recycler_main.view.*
import rx.Observable import rx.Observable
import rx.android.schedulers.AndroidSchedulers import rx.android.schedulers.AndroidSchedulers
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
@@ -47,38 +48,42 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
override fun onBindViewHolder(holder: BaseViewHolder, position: Int) { override fun onBindViewHolder(holder: BaseViewHolder, position: Int) {
if (holder is MainViewHolder) { if (holder is MainViewHolder) {
val guid = mActivity.mainViewModel.serverList.getOrNull(position) ?: return val guid = mActivity.mainViewModel.serverList.getOrNull(position) ?: return
val config = mActivity.mainViewModel.serversCache.getOrElse(guid, { MmkvManager.decodeServerConfig(guid) })?: return val config = mActivity.mainViewModel.serversCache.getOrElse(guid) { MmkvManager.decodeServerConfig(guid) } ?: return
val outbound = config.getProxyOutbound() val outbound = config.getProxyOutbound()
val aff = MmkvManager.decodeServerAffiliationInfo(guid) val aff = MmkvManager.decodeServerAffiliationInfo(guid)
holder.name.text = config.remarks holder.itemMainBinding.tvName.text = config.remarks
holder.radio.isChecked = guid == mainStorage?.decodeString(MmkvManager.KEY_SELECTED_SERVER) holder.itemMainBinding.btnRadio.isChecked = guid == mainStorage?.decodeString(MmkvManager.KEY_SELECTED_SERVER)
holder.itemView.setBackgroundColor(Color.TRANSPARENT) holder.itemView.setBackgroundColor(Color.TRANSPARENT)
holder.test_result.text = aff?.getTestDelayString() ?: "" holder.itemMainBinding.tvTestResult.text = aff?.getTestDelayString() ?: ""
if (aff?.testDelayMillis?:0L < 0L) { if (aff?.testDelayMillis?:0L < 0L) {
holder.test_result.setTextColor(ContextCompat.getColor(mActivity, android.R.color.holo_red_dark)) holder.itemMainBinding.tvTestResult.setTextColor(ContextCompat.getColor(mActivity, android.R.color.holo_red_dark))
} else { } else {
holder.test_result.setTextColor(ContextCompat.getColor(mActivity, R.color.colorPing)) holder.itemMainBinding.tvTestResult.setTextColor(ContextCompat.getColor(mActivity, R.color.colorPing))
} }
holder.subscription.text = "" holder.itemMainBinding.tvSubscription.text = ""
val json = subStorage?.decodeString(config.subscriptionId) val json = subStorage?.decodeString(config.subscriptionId)
if (!json.isNullOrBlank()) { if (!json.isNullOrBlank()) {
val sub = Gson().fromJson(json, SubscriptionItem::class.java) val sub = Gson().fromJson(json, SubscriptionItem::class.java)
holder.subscription.text = sub.remarks holder.itemMainBinding.tvSubscription.text = sub.remarks
} }
var shareOptions = share_method.asList() var shareOptions = share_method.asList()
if (config.configType == EConfigType.CUSTOM) { when (config.configType) {
holder.type.text = mActivity.getString(R.string.server_customize_config) EConfigType.CUSTOM -> {
shareOptions = shareOptions.takeLast(1) holder.itemMainBinding.tvType.text = mActivity.getString(R.string.server_customize_config)
} else if (config.configType == EConfigType.VLESS) { shareOptions = shareOptions.takeLast(1)
holder.type.text = config.configType.name }
} else { EConfigType.VLESS -> {
holder.type.text = config.configType.name.toLowerCase() holder.itemMainBinding.tvType.text = config.configType.name
}
else -> {
holder.itemMainBinding.tvType.text = config.configType.name.lowercase()
}
} }
holder.statistics.text = "${outbound?.getServerAddress()} : ${outbound?.getServerPort()}" holder.itemMainBinding.tvStatistics.text = "${outbound?.getServerAddress()} : ${outbound?.getServerPort()}"
holder.layout_share.setOnClickListener { holder.itemMainBinding.layoutShare.setOnClickListener {
AlertDialog.Builder(mActivity).setItems(shareOptions.toTypedArray()) { _, i -> AlertDialog.Builder(mActivity).setItems(shareOptions.toTypedArray()) { _, i ->
try { try {
when (i) { when (i) {
@@ -86,9 +91,9 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
if (config.configType == EConfigType.CUSTOM) { if (config.configType == EConfigType.CUSTOM) {
shareFullContent(guid) shareFullContent(guid)
} else { } else {
val iv = LayoutInflater.from(mActivity).inflate(R.layout.item_qrcode, null) val ivBinding = ItemQrcodeBinding.inflate(LayoutInflater.from(mActivity))
iv.iv_qcode.setImageBitmap(AngConfigManager.share2QRCode(guid)) ivBinding.ivQcode.setImageBitmap(AngConfigManager.share2QRCode(guid))
AlertDialog.Builder(mActivity).setView(iv).show() AlertDialog.Builder(mActivity).setView(ivBinding.root).show()
} }
} }
1 -> { 1 -> {
@@ -107,7 +112,7 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
}.show() }.show()
} }
holder.layout_edit.setOnClickListener { holder.itemMainBinding.layoutEdit.setOnClickListener {
val intent = Intent().putExtra("guid", guid) val intent = Intent().putExtra("guid", guid)
.putExtra("isRunning", isRunning) .putExtra("isRunning", isRunning)
if (config.configType == EConfigType.CUSTOM) { if (config.configType == EConfigType.CUSTOM) {
@@ -116,7 +121,7 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
mActivity.startActivity(intent.setClass(mActivity, ServerActivity::class.java)) mActivity.startActivity(intent.setClass(mActivity, ServerActivity::class.java))
} }
} }
holder.layout_remove.setOnClickListener { holder.itemMainBinding.layoutRemove.setOnClickListener {
if (guid != mainStorage?.decodeString(MmkvManager.KEY_SELECTED_SERVER)) { if (guid != mainStorage?.decodeString(MmkvManager.KEY_SELECTED_SERVER)) {
mActivity.mainViewModel.removeServer(guid) mActivity.mainViewModel.removeServer(guid)
notifyItemRemoved(position) notifyItemRemoved(position)
@@ -124,7 +129,7 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
} }
} }
holder.infoContainer.setOnClickListener { holder.itemMainBinding.infoContainer.setOnClickListener {
val selected = mainStorage?.decodeString(MmkvManager.KEY_SELECTED_SERVER) val selected = mainStorage?.decodeString(MmkvManager.KEY_SELECTED_SERVER)
if (guid != selected) { if (guid != selected) {
mainStorage?.encode(MmkvManager.KEY_SELECTED_SERVER, guid) mainStorage?.encode(MmkvManager.KEY_SELECTED_SERVER, guid)
@@ -146,9 +151,9 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
if (holder is FooterViewHolder) { if (holder is FooterViewHolder) {
//if (activity?.defaultDPreference?.getPrefBoolean(AppConfig.PREF_INAPP_BUY_IS_PREMIUM, false)) { //if (activity?.defaultDPreference?.getPrefBoolean(AppConfig.PREF_INAPP_BUY_IS_PREMIUM, false)) {
if (true) { if (true) {
holder.layout_edit.visibility = View.INVISIBLE holder.itemFooterBinding.layoutEdit.visibility = View.INVISIBLE
} else { } else {
holder.layout_edit.setOnClickListener { holder.itemFooterBinding.layoutEdit.setOnClickListener {
Utils.openUri(mActivity, "${Utils.decode(AppConfig.promotionUrl)}?t=${System.currentTimeMillis()}") Utils.openUri(mActivity, "${Utils.decode(AppConfig.promotionUrl)}?t=${System.currentTimeMillis()}")
} }
} }
@@ -166,11 +171,9 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder {
return when (viewType) { return when (viewType) {
VIEW_TYPE_ITEM -> VIEW_TYPE_ITEM ->
MainViewHolder(LayoutInflater.from(parent.context) MainViewHolder(ItemRecyclerMainBinding.inflate(LayoutInflater.from(parent.context), parent, false))
.inflate(R.layout.item_recycler_main, parent, false))
else -> else ->
FooterViewHolder(LayoutInflater.from(parent.context) FooterViewHolder(ItemRecyclerFooterBinding.inflate(LayoutInflater.from(parent.context), parent, false))
.inflate(R.layout.item_recycler_footer, parent, false))
} }
} }
@@ -182,40 +185,21 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
} }
} }
open class BaseViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) open class BaseViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun onItemSelected() {
class MainViewHolder(itemView: View) : BaseViewHolder(itemView), ItemTouchHelperViewHolder {
val subscription = itemView.tv_subscription!!
val radio = itemView.btn_radio!!
val name = itemView.tv_name!!
val test_result = itemView.tv_test_result!!
val type = itemView.tv_type!!
val statistics = itemView.tv_statistics!!
val infoContainer = itemView.info_container!!
val layout_edit = itemView.layout_edit!!
val layout_share = itemView.layout_share
val layout_remove = itemView.layout_remove!!
override fun onItemSelected() {
itemView.setBackgroundColor(Color.LTGRAY) itemView.setBackgroundColor(Color.LTGRAY)
} }
override fun onItemClear() { fun onItemClear() {
itemView.setBackgroundColor(0) itemView.setBackgroundColor(0)
} }
} }
class FooterViewHolder(itemView: View) : BaseViewHolder(itemView), ItemTouchHelperViewHolder { class MainViewHolder(val itemMainBinding: ItemRecyclerMainBinding) :
val layout_edit = itemView.layout_edit!! BaseViewHolder(itemMainBinding.root), ItemTouchHelperViewHolder
override fun onItemSelected() { class FooterViewHolder(val itemFooterBinding: ItemRecyclerFooterBinding) :
itemView.setBackgroundColor(Color.LTGRAY) BaseViewHolder(itemFooterBinding.root), ItemTouchHelperViewHolder
}
override fun onItemClear() {
itemView.setBackgroundColor(0)
}
}
override fun onItemDismiss(position: Int) { override fun onItemDismiss(position: Int) {
val guid = mActivity.mainViewModel.serverList.getOrNull(position) ?: return val guid = mActivity.mainViewModel.serverList.getOrNull(position) ?: return
@@ -233,7 +217,11 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
override fun onItemMove(fromPosition: Int, toPosition: Int): Boolean { override fun onItemMove(fromPosition: Int, toPosition: Int): Boolean {
mActivity.mainViewModel.swapServer(fromPosition, toPosition) mActivity.mainViewModel.swapServer(fromPosition, toPosition)
notifyItemMoved(fromPosition, toPosition) notifyItemMoved(fromPosition, toPosition)
//notifyItemRangeChanged(fromPosition, toPosition - fromPosition + 1) // position is changed, since position is used by click callbacks, need to update range
if (toPosition > fromPosition)
notifyItemRangeChanged(fromPosition, toPosition - fromPosition + 1)
else
notifyItemRangeChanged(toPosition, fromPosition - toPosition + 1)
return true return true
} }

View File

@@ -4,10 +4,10 @@ import android.animation.Animator
import android.animation.AnimatorListenerAdapter import android.animation.AnimatorListenerAdapter
import android.content.Context import android.content.Context
import android.os.Bundle import android.os.Bundle
import android.support.v7.preference.PreferenceManager import androidx.preference.PreferenceManager
import android.support.v7.widget.DividerItemDecoration import androidx.recyclerview.widget.DividerItemDecoration
import android.support.v7.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import android.text.TextUtils import android.text.TextUtils
import android.util.Log import android.util.Log
import android.view.Menu import android.view.Menu
@@ -17,7 +17,6 @@ import android.view.animation.AccelerateInterpolator
import android.view.animation.DecelerateInterpolator import android.view.animation.DecelerateInterpolator
import com.v2ray.ang.R import com.v2ray.ang.R
import com.v2ray.ang.util.AppManagerUtil import com.v2ray.ang.util.AppManagerUtil
import kotlinx.android.synthetic.main.activity_bypass_list.*
import rx.android.schedulers.AndroidSchedulers import rx.android.schedulers.AndroidSchedulers
import rx.schedulers.Schedulers import rx.schedulers.Schedulers
import java.text.Collator import java.text.Collator
@@ -25,6 +24,8 @@ import java.util.*
import android.view.inputmethod.EditorInfo import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
import com.v2ray.ang.AppConfig 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.dto.AppInfo
import com.v2ray.ang.extension.toast import com.v2ray.ang.extension.toast
import com.v2ray.ang.extension.v2RayApplication import com.v2ray.ang.extension.v2RayApplication
@@ -35,6 +36,7 @@ import kotlinx.coroutines.launch
import java.net.URL import java.net.URL
class PerAppProxyActivity : BaseActivity() { class PerAppProxyActivity : BaseActivity() {
private lateinit var binding: ActivityBypassListBinding
private var adapter: PerAppProxyAdapter? = null private var adapter: PerAppProxyAdapter? = null
private var appsAll: List<AppInfo>? = null private var appsAll: List<AppInfo>? = null
@@ -42,12 +44,14 @@ class PerAppProxyActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_bypass_list) binding = ActivityBypassListBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayHomeAsUpEnabled(true)
val dividerItemDecoration = DividerItemDecoration(this, LinearLayoutManager.VERTICAL) val dividerItemDecoration = DividerItemDecoration(this, LinearLayoutManager.VERTICAL)
recycler_view.addItemDecoration(dividerItemDecoration) binding.recyclerView.addItemDecoration(dividerItemDecoration)
val blacklist = defaultSharedPreferences.getStringSet(AppConfig.PREF_PER_APP_PROXY_SET, null) val blacklist = defaultSharedPreferences.getStringSet(AppConfig.PREF_PER_APP_PROXY_SET, null)
@@ -62,8 +66,8 @@ class PerAppProxyActivity : BaseActivity() {
one.isSelected = 0 one.isSelected = 0
} }
} }
val comparator = object : Comparator<AppInfo> { val comparator = Comparator<AppInfo> { p1, p2 ->
override fun compare(p1: AppInfo, p2: AppInfo): Int = when { when {
p1.isSelected > p2.isSelected -> -1 p1.isSelected > p2.isSelected -> -1
p1.isSelected == p2.isSelected -> 0 p1.isSelected == p2.isSelected -> 0
else -> 1 else -> 1
@@ -89,20 +93,20 @@ class PerAppProxyActivity : BaseActivity() {
.subscribe { .subscribe {
appsAll = it appsAll = it
adapter = PerAppProxyAdapter(this, it, blacklist) adapter = PerAppProxyAdapter(this, it, blacklist)
recycler_view.adapter = adapter binding.recyclerView.adapter = adapter
pb_waiting.visibility = View.GONE binding.pbWaiting.visibility = View.GONE
} }
recycler_view.addOnScrollListener(object : RecyclerView.OnScrollListener() { binding.recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
var dst = 0 var dst = 0
val threshold = resources.getDimensionPixelSize(R.dimen.bypass_list_header_height) * 3 val threshold = resources.getDimensionPixelSize(R.dimen.bypass_list_header_height) * 3
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
dst += dy dst += dy
if (dst > threshold) { if (dst > threshold) {
header_view.hide() binding.headerView.hide()
dst = 0 dst = 0
} else if (dst < -20) { } else if (dst < -20) {
header_view.show() binding.headerView.show()
dst = 0 dst = 0
} }
} }
@@ -138,23 +142,23 @@ class PerAppProxyActivity : BaseActivity() {
} }
}) })
switch_per_app_proxy.setOnCheckedChangeListener { _, isChecked -> binding.switchPerAppProxy.setOnCheckedChangeListener { _, isChecked ->
defaultSharedPreferences.edit().putBoolean(AppConfig.PREF_PER_APP_PROXY, isChecked).apply() defaultSharedPreferences.edit().putBoolean(AppConfig.PREF_PER_APP_PROXY, isChecked).apply()
} }
switch_per_app_proxy.isChecked = defaultSharedPreferences.getBoolean(AppConfig.PREF_PER_APP_PROXY, false) binding.switchPerAppProxy.isChecked = defaultSharedPreferences.getBoolean(AppConfig.PREF_PER_APP_PROXY, false)
switch_bypass_apps.setOnCheckedChangeListener { _, isChecked -> binding.switchBypassApps.setOnCheckedChangeListener { _, isChecked ->
defaultSharedPreferences.edit().putBoolean(AppConfig.PREF_BYPASS_APPS, isChecked).apply() defaultSharedPreferences.edit().putBoolean(AppConfig.PREF_BYPASS_APPS, isChecked).apply()
} }
switch_bypass_apps.isChecked = defaultSharedPreferences.getBoolean(AppConfig.PREF_BYPASS_APPS, false) binding.switchBypassApps.isChecked = defaultSharedPreferences.getBoolean(AppConfig.PREF_BYPASS_APPS, false)
et_search.setOnEditorActionListener { v, actionId, event -> binding.etSearch.setOnEditorActionListener { v, actionId, _ ->
if (actionId == EditorInfo.IME_ACTION_SEARCH) { if (actionId == EditorInfo.IME_ACTION_SEARCH) {
//hide //hide
var imm: InputMethodManager = v.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager var imm: InputMethodManager = v.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS) imm.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS)
val key = v.text.toString().toUpperCase() val key = v.text.toString().uppercase()
val apps = ArrayList<AppInfo>() val apps = ArrayList<AppInfo>()
if (TextUtils.isEmpty(key)) { if (TextUtils.isEmpty(key)) {
appsAll?.forEach { appsAll?.forEach {
@@ -162,13 +166,13 @@ class PerAppProxyActivity : BaseActivity() {
} }
} else { } else {
appsAll?.forEach { appsAll?.forEach {
if (it.appName.toUpperCase().indexOf(key) >= 0) { if (it.appName.uppercase().indexOf(key) >= 0) {
apps.add(it) apps.add(it)
} }
} }
} }
adapter = PerAppProxyAdapter(this, apps, adapter?.blacklist) adapter = PerAppProxyAdapter(this, apps, adapter?.blacklist)
recycler_view.adapter = adapter binding.recyclerView.adapter = adapter
adapter?.notifyDataSetChanged() adapter?.notifyDataSetChanged()
true true
} else { } else {
@@ -184,7 +188,7 @@ class PerAppProxyActivity : BaseActivity() {
} }
} }
override fun onCreateOptionsMenu(menu: Menu?): Boolean { override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.menu_bypass_list, menu) menuInflater.inflate(R.menu.menu_bypass_list, menu)
return super.onCreateOptionsMenu(menu) return super.onCreateOptionsMenu(menu)
} }
@@ -226,7 +230,7 @@ class PerAppProxyActivity : BaseActivity() {
"" ""
} }
launch(Dispatchers.Main) { launch(Dispatchers.Main) {
Log.d("selectProxyApp", content) Log.d(ANG_PACKAGE, content)
selectProxyApp(content) selectProxyApp(content)
toast(R.string.toast_success) toast(R.string.toast_success)
} }
@@ -246,11 +250,11 @@ class PerAppProxyActivity : BaseActivity() {
adapter?.blacklist!!.clear() adapter?.blacklist!!.clear()
if (switch_bypass_apps.isChecked) { if (binding.switchBypassApps.isChecked) {
adapter?.let { adapter?.let {
it.apps.forEach block@{ it.apps.forEach block@{
val packageName = it.packageName val packageName = it.packageName
Log.d("selectProxyApp2", packageName) Log.d(ANG_PACKAGE, packageName)
if (proxyApps.indexOf(packageName) < 0) { if (proxyApps.indexOf(packageName) < 0) {
adapter?.blacklist!!.add(packageName) adapter?.blacklist!!.add(packageName)
println(packageName) println(packageName)
@@ -263,7 +267,7 @@ class PerAppProxyActivity : BaseActivity() {
adapter?.let { adapter?.let {
it.apps.forEach block@{ it.apps.forEach block@{
val packageName = it.packageName val packageName = it.packageName
Log.d("selectProxyApp3", packageName) Log.d(ANG_PACKAGE, packageName)
if (proxyApps.indexOf(packageName) >= 0) { if (proxyApps.indexOf(packageName) >= 0) {
adapter?.blacklist!!.add(packageName) adapter?.blacklist!!.add(packageName)
println(packageName) println(packageName)

View File

@@ -1,13 +1,13 @@
package com.v2ray.ang.ui package com.v2ray.ang.ui
import android.graphics.Color import android.graphics.Color
import android.support.v7.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import com.v2ray.ang.R import com.v2ray.ang.R
import com.v2ray.ang.databinding.ItemRecyclerBypassListBinding
import com.v2ray.ang.dto.AppInfo import com.v2ray.ang.dto.AppInfo
import kotlinx.android.synthetic.main.item_recycler_bypass_list.view.*
import java.util.* import java.util.*
class PerAppProxyAdapter(val activity: BaseActivity, val apps: List<AppInfo>, blacklist: MutableSet<String>?) : class PerAppProxyAdapter(val activity: BaseActivity, val apps: List<AppInfo>, blacklist: MutableSet<String>?) :
@@ -18,8 +18,7 @@ class PerAppProxyAdapter(val activity: BaseActivity, val apps: List<AppInfo>, bl
private const val VIEW_TYPE_ITEM = 1 private const val VIEW_TYPE_ITEM = 1
} }
private var mActivity: BaseActivity = activity val blacklist = if (blacklist == null) HashSet() else HashSet(blacklist)
val blacklist = if (blacklist == null) HashSet<String>() else HashSet<String>(blacklist)
override fun onBindViewHolder(holder: BaseViewHolder, position: Int) { override fun onBindViewHolder(holder: BaseViewHolder, position: Int) {
if (holder is AppViewHolder) { if (holder is AppViewHolder) {
@@ -43,8 +42,7 @@ class PerAppProxyAdapter(val activity: BaseActivity, val apps: List<AppInfo>, bl
// VIEW_TYPE_ITEM -> AppViewHolder(ctx.layoutInflater // VIEW_TYPE_ITEM -> AppViewHolder(ctx.layoutInflater
// .inflate(R.layout.item_recycler_bypass_list, parent, false)) // .inflate(R.layout.item_recycler_bypass_list, parent, false))
else -> AppViewHolder(LayoutInflater.from(ctx) else -> AppViewHolder(ItemRecyclerBypassListBinding.inflate(LayoutInflater.from(ctx), parent, false))
.inflate(R.layout.item_recycler_bypass_list, parent, false))
} }
} }
@@ -53,30 +51,25 @@ class PerAppProxyAdapter(val activity: BaseActivity, val apps: List<AppInfo>, bl
open class BaseViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) open class BaseViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
inner class AppViewHolder(itemView: View) : BaseViewHolder(itemView), inner class AppViewHolder(private val itemBypassBinding: ItemRecyclerBypassListBinding) : BaseViewHolder(itemBypassBinding.root),
View.OnClickListener { View.OnClickListener {
private val inBlacklist: Boolean get() = blacklist.contains(appInfo.packageName) private val inBlacklist: Boolean get() = blacklist.contains(appInfo.packageName)
private lateinit var appInfo: AppInfo private lateinit var appInfo: AppInfo
val icon = itemView.icon!!
val name = itemView.name!!
val package_name = itemView.package_name!!
val checkBox = itemView.check_box!!
fun bind(appInfo: AppInfo) { fun bind(appInfo: AppInfo) {
this.appInfo = appInfo this.appInfo = appInfo
icon.setImageDrawable(appInfo.appIcon) itemBypassBinding.icon.setImageDrawable(appInfo.appIcon)
// name.text = appInfo.appName // name.text = appInfo.appName
checkBox.isChecked = inBlacklist itemBypassBinding.checkBox.isChecked = inBlacklist
package_name.text = appInfo.packageName itemBypassBinding.packageName.text = appInfo.packageName
if (appInfo.isSystemApp) { if (appInfo.isSystemApp) {
name.text = String.format("** %1s", appInfo.appName) itemBypassBinding.name.text = String.format("** %1s", appInfo.appName)
name.setTextColor(Color.RED) itemBypassBinding.name.setTextColor(Color.RED)
} else { } else {
name.text = appInfo.appName itemBypassBinding.name.text = appInfo.appName
name.setTextColor(Color.DKGRAY) itemBypassBinding.name.setTextColor(Color.DKGRAY)
} }
itemView.setOnClickListener(this) itemView.setOnClickListener(this)
@@ -85,10 +78,10 @@ class PerAppProxyAdapter(val activity: BaseActivity, val apps: List<AppInfo>, bl
override fun onClick(v: View?) { override fun onClick(v: View?) {
if (inBlacklist) { if (inBlacklist) {
blacklist.remove(appInfo.packageName) blacklist.remove(appInfo.packageName)
checkBox.isChecked = false itemBypassBinding.checkBox.isChecked = false
} else { } else {
blacklist.add(appInfo.packageName) blacklist.add(appInfo.packageName)
checkBox.isChecked = true itemBypassBinding.checkBox.isChecked = true
} }
} }
} }

View File

@@ -3,19 +3,23 @@ package com.v2ray.ang.ui
import android.graphics.Color import android.graphics.Color
import android.os.Bundle import android.os.Bundle
import com.v2ray.ang.R import com.v2ray.ang.R
import android.support.v4.app.Fragment import androidx.fragment.app.Fragment
import com.google.android.material.tabs.TabLayoutMediator
import com.v2ray.ang.AppConfig import com.v2ray.ang.AppConfig
import kotlinx.android.synthetic.main.activity_routing_settings.* import com.v2ray.ang.databinding.ActivityRoutingSettingsBinding
class RoutingSettingsActivity : BaseActivity() { class RoutingSettingsActivity : BaseActivity() {
private lateinit var binding: ActivityRoutingSettingsBinding
private val titles: Array<out String> by lazy { private val titles: Array<out String> by lazy {
resources.getStringArray(R.array.routing_tag) resources.getStringArray(R.array.routing_tag)
} }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_routing_settings) binding = ActivityRoutingSettingsBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
title = getString(R.string.routing_settings_title) title = getString(R.string.routing_settings_title)
supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayHomeAsUpEnabled(true)
@@ -25,9 +29,11 @@ class RoutingSettingsActivity : BaseActivity() {
fragments.add(RoutingSettingsFragment().newInstance(AppConfig.PREF_V2RAY_ROUTING_DIRECT)) fragments.add(RoutingSettingsFragment().newInstance(AppConfig.PREF_V2RAY_ROUTING_DIRECT))
fragments.add(RoutingSettingsFragment().newInstance(AppConfig.PREF_V2RAY_ROUTING_BLOCKED)) fragments.add(RoutingSettingsFragment().newInstance(AppConfig.PREF_V2RAY_ROUTING_BLOCKED))
val adapter = FragmentAdapter(supportFragmentManager, fragments, titles.toList()) val adapter = FragmentAdapter(this, fragments)
viewpager?.adapter = adapter binding.viewpager.adapter = adapter
tablayout.setTabTextColors(Color.BLACK, Color.RED) binding.tablayout.setTabTextColors(Color.BLACK, Color.RED)
tablayout.setupWithViewPager(viewpager) TabLayoutMediator(binding.tablayout, binding.viewpager) { tab, position ->
tab.text = titles[position]
}.attach()
} }
} }

View File

@@ -4,34 +4,37 @@ import android.Manifest
import android.app.Activity.RESULT_OK import android.app.Activity.RESULT_OK
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.support.v4.app.Fragment import android.text.TextUtils
import android.support.v7.preference.PreferenceManager import androidx.fragment.app.Fragment
import androidx.preference.PreferenceManager
import android.view.* import android.view.*
import com.v2ray.ang.R
import com.v2ray.ang.util.Utils
import kotlinx.android.synthetic.main.fragment_routing_settings.*
import android.view.MenuInflater import android.view.MenuInflater
import androidx.activity.result.contract.ActivityResultContracts
import com.tbruyelle.rxpermissions.RxPermissions import com.tbruyelle.rxpermissions.RxPermissions
import com.v2ray.ang.AppConfig import com.v2ray.ang.AppConfig
import com.v2ray.ang.R
import com.v2ray.ang.databinding.FragmentRoutingSettingsBinding
import com.v2ray.ang.extension.toast import com.v2ray.ang.extension.toast
import com.v2ray.ang.extension.v2RayApplication
import com.v2ray.ang.util.Utils
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.net.URL import java.net.URL
class RoutingSettingsFragment : Fragment() { class RoutingSettingsFragment : Fragment() {
private lateinit var binding: FragmentRoutingSettingsBinding
companion object { companion object {
private const val routing_arg = "routing_arg" private const val routing_arg = "routing_arg"
private const val REQUEST_SCAN_REPLACE = 11
private const val REQUEST_SCAN_APPEND = 12
} }
val defaultSharedPreferences by lazy { PreferenceManager.getDefaultSharedPreferences(context) } val defaultSharedPreferences by lazy { PreferenceManager.getDefaultSharedPreferences(requireContext()) }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? { savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment // Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_routing_settings, container, false) binding = FragmentRoutingSettingsBinding.inflate(layoutInflater)
return binding.root// inflater.inflate(R.layout.fragment_routing_settings, container, false)
} }
fun newInstance(arg: String): Fragment { fun newInstance(arg: String): Fragment {
@@ -45,8 +48,8 @@ class RoutingSettingsFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
val content = defaultSharedPreferences.getString(arguments!!.getString(routing_arg), "") val content = defaultSharedPreferences.getString(requireArguments().getString(routing_arg), "")
et_routing_content.text = Utils.getEditable(content!!) binding.etRoutingContent.text = Utils.getEditable(content!!)
setHasOptionsMenu(true) setHasOptionsMenu(true)
} }
@@ -58,21 +61,21 @@ class RoutingSettingsFragment : Fragment() {
override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) { override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
R.id.save_routing -> { R.id.save_routing -> {
val content = et_routing_content.text.toString() val content = binding.etRoutingContent.text.toString()
defaultSharedPreferences.edit().putString(arguments!!.getString(routing_arg), content).apply() defaultSharedPreferences.edit().putString(requireArguments().getString(routing_arg), content).apply()
activity?.toast(R.string.toast_success) activity?.toast(R.string.toast_success)
true true
} }
R.id.del_routing -> { R.id.del_routing -> {
et_routing_content.text = null binding.etRoutingContent.text = null
true true
} }
R.id.scan_replace -> { R.id.scan_replace -> {
scanQRcode(REQUEST_SCAN_REPLACE) scanQRcode(true)
true true
} }
R.id.scan_append -> { R.id.scan_append -> {
scanQRcode(REQUEST_SCAN_APPEND) scanQRcode(false)
true true
} }
R.id.default_rules -> { R.id.default_rules -> {
@@ -82,17 +85,26 @@ class RoutingSettingsFragment : Fragment() {
else -> super.onOptionsItemSelected(item) else -> super.onOptionsItemSelected(item)
} }
fun scanQRcode(requestCode: Int): Boolean { private fun saveRouting() {
val content = binding.etRoutingContent.text.toString()
defaultSharedPreferences.edit().putString(requireArguments().getString(routing_arg), content).apply()
activity?.toast(R.string.toast_success)
}
fun scanQRcode(forReplace: Boolean): Boolean {
// try { // try {
// startActivityForResult(Intent("com.google.zxing.client.android.SCAN") // startActivityForResult(Intent("com.google.zxing.client.android.SCAN")
// .addCategory(Intent.CATEGORY_DEFAULT) // .addCategory(Intent.CATEGORY_DEFAULT)
// .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP), requestCode) // .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP), requestCode)
// } catch (e: Exception) { // } catch (e: Exception) {
RxPermissions(activity!!) RxPermissions(requireActivity())
.request(Manifest.permission.CAMERA) .request(Manifest.permission.CAMERA)
.subscribe { .subscribe {
if (it) if (it)
startActivityForResult(Intent(activity, ScannerActivity::class.java), requestCode) if (forReplace)
scanQRCodeForReplace.launch(Intent(activity, ScannerActivity::class.java))
else
scanQRCodeForAppend.launch(Intent(activity, ScannerActivity::class.java))
else else
activity?.toast(R.string.toast_permission_denied) activity?.toast(R.string.toast_permission_denied)
} }
@@ -100,9 +112,23 @@ class RoutingSettingsFragment : Fragment() {
return true return true
} }
private val scanQRCodeForReplace = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
if (it.resultCode == RESULT_OK) {
val content = it.data?.getStringExtra("SCAN_RESULT")
binding.etRoutingContent.text = Utils.getEditable(content!!)
}
}
private val scanQRCodeForAppend = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
if (it.resultCode == RESULT_OK) {
val content = it.data?.getStringExtra("SCAN_RESULT")
binding.etRoutingContent.text = Utils.getEditable("${binding.etRoutingContent.text},$content")
}
}
fun setDefaultRules(): Boolean { fun setDefaultRules(): Boolean {
var url = AppConfig.v2rayCustomRoutingListUrl var url = AppConfig.v2rayCustomRoutingListUrl
when (arguments!!.getString(routing_arg)) { when (requireArguments().getString(routing_arg)) {
AppConfig.PREF_V2RAY_ROUTING_AGENT -> { AppConfig.PREF_V2RAY_ROUTING_AGENT -> {
url += AppConfig.TAG_AGENT url += AppConfig.TAG_AGENT
} }
@@ -123,28 +149,16 @@ class RoutingSettingsFragment : Fragment() {
"" ""
} }
launch(Dispatchers.Main) { launch(Dispatchers.Main) {
et_routing_content.text = Utils.getEditable(content) val routingList = if (TextUtils.isEmpty(content)) {
activity?.toast(R.string.toast_success) Utils.readTextFromAssets(activity?.v2RayApplication!!, "custom_routing_$tag")
} else {
content
}
binding.etRoutingContent.text = Utils.getEditable(routingList)
saveRouting()
//toast(R.string.toast_success)
} }
} }
return true return true
} }
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when (requestCode) {
REQUEST_SCAN_REPLACE ->
if (resultCode == RESULT_OK) {
val content = data?.getStringExtra("SCAN_RESULT")
et_routing_content.text = Utils.getEditable(content!!)
}
REQUEST_SCAN_APPEND ->
if (resultCode == RESULT_OK) {
val content = data?.getStringExtra("SCAN_RESULT")
et_routing_content.text = Utils.getEditable("${et_routing_content.text},$content")
}
}
}
} }

View File

@@ -6,25 +6,23 @@ import com.tbruyelle.rxpermissions.RxPermissions
import com.v2ray.ang.R import com.v2ray.ang.R
import com.v2ray.ang.util.AngConfigManager import com.v2ray.ang.util.AngConfigManager
import android.os.Bundle import android.os.Bundle
import androidx.activity.result.contract.ActivityResultContracts
import com.v2ray.ang.extension.toast import com.v2ray.ang.extension.toast
class ScScannerActivity : BaseActivity() { class ScScannerActivity : BaseActivity() {
companion object {
private const val REQUEST_SCAN = 1
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_none) setContentView(R.layout.activity_none)
importQRcode(REQUEST_SCAN) importQRcode()
} }
fun importQRcode(requestCode: Int): Boolean { fun importQRcode(): Boolean {
RxPermissions(this) RxPermissions(this)
.request(Manifest.permission.CAMERA) .request(Manifest.permission.CAMERA)
.subscribe { .subscribe {
if (it) if (it)
startActivityForResult(Intent(this, ScannerActivity::class.java), requestCode) scanQRCode.launch(Intent(this, ScannerActivity::class.java))
else else
toast(R.string.toast_permission_denied) toast(R.string.toast_permission_denied)
} }
@@ -32,21 +30,16 @@ class ScScannerActivity : BaseActivity() {
return true return true
} }
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { private val scanQRCode = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
super.onActivityResult(requestCode, resultCode, data) if (it.resultCode == RESULT_OK) {
when (requestCode) { val count = AngConfigManager.importBatchConfig(it.data?.getStringExtra("SCAN_RESULT"), "")
REQUEST_SCAN -> if (count > 0) {
if (resultCode == RESULT_OK) { toast(R.string.toast_success)
val count = AngConfigManager.importBatchConfig(data?.getStringExtra("SCAN_RESULT"), "") } else {
if (count > 0) { toast(R.string.toast_failure)
toast(R.string.toast_success) }
} else { startActivity(Intent(this, MainActivity::class.java))
toast(R.string.toast_failure)
}
startActivity(Intent(this, MainActivity::class.java))
}
} }
finish() finish()
} }
} }

View File

@@ -9,6 +9,7 @@ import android.content.Intent
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import androidx.activity.result.contract.ActivityResultContracts
import com.google.zxing.BarcodeFormat import com.google.zxing.BarcodeFormat
import com.tbruyelle.rxpermissions.RxPermissions import com.tbruyelle.rxpermissions.RxPermissions
import com.v2ray.ang.R import com.v2ray.ang.R
@@ -16,10 +17,6 @@ import com.v2ray.ang.extension.toast
import com.v2ray.ang.util.QRCodeDecoder import com.v2ray.ang.util.QRCodeDecoder
class ScannerActivity : BaseActivity(), ZXingScannerView.ResultHandler { class ScannerActivity : BaseActivity(), ZXingScannerView.ResultHandler {
companion object {
private const val REQUEST_FILE_CHOOSER = 2
}
private var mScannerView: ZXingScannerView? = null private var mScannerView: ZXingScannerView? = null
@@ -59,14 +56,14 @@ class ScannerActivity : BaseActivity(), ZXingScannerView.ResultHandler {
// mScannerView!!.resumeCameraPreview(this) // mScannerView!!.resumeCameraPreview(this)
} }
fun finished(text: String) { private fun finished(text: String) {
val intent = Intent() val intent = Intent()
intent.putExtra("SCAN_RESULT", text) intent.putExtra("SCAN_RESULT", text)
setResult(Activity.RESULT_OK, intent) setResult(Activity.RESULT_OK, intent)
finish() finish()
} }
override fun onCreateOptionsMenu(menu: Menu?): Boolean { override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.menu_scanner, menu) menuInflater.inflate(R.menu.menu_scanner, menu)
return true return true
} }
@@ -97,30 +94,22 @@ class ScannerActivity : BaseActivity(), ZXingScannerView.ResultHandler {
//intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true) //intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
try { try {
startActivityForResult( chooseFile.launch(Intent.createChooser(intent, getString(R.string.title_file_chooser)))
Intent.createChooser(intent, getString(R.string.title_file_chooser)),
REQUEST_FILE_CHOOSER)
} catch (ex: android.content.ActivityNotFoundException) { } catch (ex: android.content.ActivityNotFoundException) {
toast(R.string.toast_require_file_manager) toast(R.string.toast_require_file_manager)
} }
} }
private val chooseFile = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { val uri = it.data?.data
super.onActivityResult(requestCode, resultCode, data) if (it.resultCode == RESULT_OK && uri != null) {
when (requestCode) { try {
REQUEST_FILE_CHOOSER -> { val bitmap = BitmapFactory.decodeStream(contentResolver.openInputStream(uri))
val uri = data?.data val text = QRCodeDecoder.syncDecodeQRCode(bitmap)
if (resultCode == RESULT_OK && uri != null) { finished(text)
try { } catch (e: Exception) {
val bitmap = BitmapFactory.decodeStream(contentResolver.openInputStream(uri)) e.printStackTrace()
val text = QRCodeDecoder.syncDecodeQRCode(bitmap) toast(e.message.toString())
finished(text)
} catch (e: Exception) {
e.printStackTrace()
toast(e.message.toString())
}
}
} }
} }
} }

View File

@@ -1,19 +1,17 @@
package com.v2ray.ang.ui package com.v2ray.ang.ui
import android.os.Bundle import android.os.Bundle
import android.support.v7.app.AlertDialog import androidx.appcompat.app.AlertDialog
import android.text.TextUtils import android.text.TextUtils
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.widget.AdapterView import android.widget.*
import android.widget.ArrayAdapter
import com.tencent.mmkv.MMKV import com.tencent.mmkv.MMKV
import com.v2ray.ang.R import com.v2ray.ang.R
import com.v2ray.ang.dto.EConfigType import com.v2ray.ang.dto.EConfigType
import com.v2ray.ang.dto.ServerConfig import com.v2ray.ang.dto.ServerConfig
import com.v2ray.ang.dto.V2rayConfig import com.v2ray.ang.dto.V2rayConfig
import com.v2ray.ang.dto.V2rayConfig.Companion.DEFAULT_NETWORK
import com.v2ray.ang.dto.V2rayConfig.Companion.DEFAULT_PORT import com.v2ray.ang.dto.V2rayConfig.Companion.DEFAULT_PORT
import com.v2ray.ang.dto.V2rayConfig.Companion.XTLS import com.v2ray.ang.dto.V2rayConfig.Companion.XTLS
import com.v2ray.ang.extension.toast import com.v2ray.ang.extension.toast
@@ -21,19 +19,6 @@ import com.v2ray.ang.util.MmkvManager
import com.v2ray.ang.util.MmkvManager.ID_MAIN import com.v2ray.ang.util.MmkvManager.ID_MAIN
import com.v2ray.ang.util.MmkvManager.KEY_SELECTED_SERVER import com.v2ray.ang.util.MmkvManager.KEY_SELECTED_SERVER
import com.v2ray.ang.util.Utils import com.v2ray.ang.util.Utils
import kotlinx.android.synthetic.main.activity_server_socks.*
import kotlinx.android.synthetic.main.activity_server_vmess.*
import kotlinx.android.synthetic.main.activity_server_vmess.et_address
import kotlinx.android.synthetic.main.activity_server_vmess.et_id
import kotlinx.android.synthetic.main.activity_server_vmess.et_path
import kotlinx.android.synthetic.main.activity_server_vmess.et_port
import kotlinx.android.synthetic.main.activity_server_vmess.et_remarks
import kotlinx.android.synthetic.main.activity_server_vmess.et_request_host
import kotlinx.android.synthetic.main.activity_server_vmess.sp_allow_insecure
import kotlinx.android.synthetic.main.activity_server_vmess.sp_header_type
import kotlinx.android.synthetic.main.activity_server_vmess.sp_header_type_title
import kotlinx.android.synthetic.main.activity_server_vmess.sp_network
import kotlinx.android.synthetic.main.activity_server_vmess.sp_stream_security
class ServerActivity : BaseActivity() { class ServerActivity : BaseActivity() {
@@ -76,6 +61,26 @@ class ServerActivity : BaseActivity() {
resources.getStringArray(R.array.allowinsecures) resources.getStringArray(R.array.allowinsecures)
} }
// Kotlin synthetics was used, but since it is removed in 1.8. We switch to old manual approach.
// We don't use AndroidViewBinding because, it is better to share similar logics for different
// protocols. Use findViewById manually ensures the xml are de-coupled with the activity logic.
private val et_remarks: EditText by lazy { findViewById(R.id.et_remarks) }
private val et_address: EditText by lazy { findViewById(R.id.et_address) }
private val et_port: EditText by lazy { findViewById(R.id.et_port) }
private val et_id: EditText by lazy { findViewById(R.id.et_id) }
//private val et_alterId: EditText? by lazy { findViewById(R.id.et_alterId) }
private val et_security: EditText? by lazy { findViewById(R.id.et_security) }
//private val sp_flow: Spinner? by lazy { findViewById(R.id.sp_flow) }
private val sp_security: Spinner? by lazy { findViewById(R.id.sp_security) }
private val sp_stream_security: Spinner? by lazy { findViewById(R.id.sp_stream_security) }
private val sp_allow_insecure: Spinner? by lazy { findViewById(R.id.sp_allow_insecure) }
//private val et_sni: EditText? by lazy { findViewById(R.id.et_sni) }
private val sp_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) }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
title = getString(R.string.title_server) title = getString(R.string.title_server)
@@ -100,9 +105,9 @@ class ServerActivity : BaseActivity() {
getString(R.string.server_lab_mode_type) else getString(R.string.server_lab_mode_type) else
getString(R.string.server_lab_head_type) getString(R.string.server_lab_head_type)
config?.getProxyOutbound()?.getTransportSettingDetails()?.let { transportDetails -> config?.getProxyOutbound()?.getTransportSettingDetails()?.let { transportDetails ->
sp_header_type.setSelection(Utils.arrayFind(types, transportDetails[0])) sp_header_type?.setSelection(Utils.arrayFind(types, transportDetails[0]))
et_request_host.text = Utils.getEditable(transportDetails[1]) et_request_host?.text = Utils.getEditable(transportDetails[1])
et_path.text = Utils.getEditable(transportDetails[2]) et_path?.text = Utils.getEditable(transportDetails[2])
} }
} }
override fun onNothingSelected(parent: AdapterView<*>?) { override fun onNothingSelected(parent: AdapterView<*>?) {
@@ -128,11 +133,10 @@ class ServerActivity : BaseActivity() {
et_address.text = Utils.getEditable(outbound.getServerAddress().orEmpty()) et_address.text = Utils.getEditable(outbound.getServerAddress().orEmpty())
et_port.text = Utils.getEditable(outbound.getServerPort()?.toString() ?: DEFAULT_PORT.toString()) et_port.text = Utils.getEditable(outbound.getServerPort()?.toString() ?: DEFAULT_PORT.toString())
et_id.text = Utils.getEditable(outbound.getPassword().orEmpty()) 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) { if (config.configType == EConfigType.SOCKS) {
et_security.text = Utils.getEditable(outbound.settings?.servers?.get(0)?.users?.get(0)?.user.orEmpty()) et_security?.text = Utils.getEditable(outbound.settings?.servers?.get(0)?.users?.get(0)?.user.orEmpty())
} else if (config.configType == EConfigType.VLESS) { } else if (config.configType == EConfigType.VLESS) {
et_security.text = Utils.getEditable(outbound.getSecurityEncryption().orEmpty()) et_security?.text = Utils.getEditable(outbound.getSecurityEncryption().orEmpty())
val flow = Utils.arrayFind(flows, outbound.settings?.vnext?.get(0)?.users?.get(0)?.flow.orEmpty()) val flow = Utils.arrayFind(flows, outbound.settings?.vnext?.get(0)?.users?.get(0)?.flow.orEmpty())
if (flow >= 0) { if (flow >= 0) {
//sp_flow.setSelection(flow) //sp_flow.setSelection(flow)
@@ -152,7 +156,7 @@ class ServerActivity : BaseActivity() {
if (allowinsecure >= 0) { if (allowinsecure >= 0) {
sp_allow_insecure?.setSelection(allowinsecure) sp_allow_insecure?.setSelection(allowinsecure)
} }
et_request_host.text = Utils.getEditable(tlsSetting.serverName) et_request_host?.text = Utils.getEditable(tlsSetting.serverName)
} }
} }
val network = Utils.arrayFind(networks, streamSetting.network) val network = Utils.arrayFind(networks, streamSetting.network)
@@ -170,7 +174,6 @@ class ServerActivity : BaseActivity() {
et_address.text = null et_address.text = null
et_port.text = Utils.getEditable(DEFAULT_PORT.toString()) et_port.text = Utils.getEditable(DEFAULT_PORT.toString())
et_id.text = null et_id.text = null
et_alterId?.text = Utils.getEditable("0")
sp_security?.setSelection(0) sp_security?.setSelection(0)
sp_network?.setSelection(0) sp_network?.setSelection(0)
@@ -207,13 +210,6 @@ class ServerActivity : BaseActivity() {
toast(R.string.server_lab_id) toast(R.string.server_lab_id)
return false return false
} }
et_alterId?.let {
val alterId = Utils.parseInt(et_alterId.text.toString())
if (alterId < 0) {
toast(R.string.server_lab_alterid)
return false
}
}
config.remarks = et_remarks.text.toString().trim() config.remarks = et_remarks.text.toString().trim()
config.outboundBean?.settings?.vnext?.get(0)?.let { vnext -> config.outboundBean?.settings?.vnext?.get(0)?.let { vnext ->
@@ -223,7 +219,7 @@ class ServerActivity : BaseActivity() {
saveServers(server, port, config) saveServers(server, port, config)
} }
config.outboundBean?.streamSettings?.let { config.outboundBean?.streamSettings?.let {
saveStreamSettings(it, config) saveStreamSettings(it)
} }
MmkvManager.encodeServerConfig(editGuid, config) MmkvManager.encodeServerConfig(editGuid, config)
@@ -237,13 +233,11 @@ class ServerActivity : BaseActivity() {
vnext.port = port vnext.port = port
vnext.users[0].id = et_id.text.toString().trim() vnext.users[0].id = et_id.text.toString().trim()
if (config.configType == EConfigType.VMESS) { if (config.configType == EConfigType.VMESS) {
vnext.users[0].alterId = Utils.parseInt(et_alterId.text.toString()) vnext.users[0].security = securitys[sp_security?.selectedItemPosition ?: 0]
vnext.users[0].security = securitys[sp_security.selectedItemPosition]
} else if (config.configType == EConfigType.VLESS) { } else if (config.configType == EConfigType.VLESS) {
vnext.users[0].encryption = et_security.text.toString().trim() vnext.users[0].encryption = et_security?.text.toString().trim()
if (streamSecuritys[sp_stream_security.selectedItemPosition] == XTLS) { if (streamSecuritys[sp_stream_security?.selectedItemPosition ?: 0] == XTLS) {
// vnext.users[0].flow = if (flows[sp_flow.selectedItemPosition].isBlank()) V2rayConfig.DEFAULT_FLOW // vnext.users[0].flow = flows[sp_flow.selectedItemPosition].ifBlank { V2rayConfig.DEFAULT_FLOW }
// else flows[sp_flow.selectedItemPosition]
} else { } else {
vnext.users[0].flow = "" vnext.users[0].flow = ""
} }
@@ -255,13 +249,13 @@ class ServerActivity : BaseActivity() {
server.port = port server.port = port
if (config.configType == EConfigType.SHADOWSOCKS) { if (config.configType == EConfigType.SHADOWSOCKS) {
server.password = et_id.text.toString().trim() server.password = et_id.text.toString().trim()
server.method = shadowsocksSecuritys[sp_security.selectedItemPosition] server.method = shadowsocksSecuritys[sp_security?.selectedItemPosition ?: 0]
} else if (config.configType == EConfigType.SOCKS) { } else if (config.configType == EConfigType.SOCKS) {
if (TextUtils.isEmpty(et_security.text) && TextUtils.isEmpty(et_id.text)) { if (TextUtils.isEmpty(et_security?.text) && TextUtils.isEmpty(et_id.text)) {
server.users = null server.users = null
} else { } else {
val socksUsersBean = V2rayConfig.OutboundBean.OutSettingsBean.ServersBean.SocksUsersBean() val socksUsersBean = V2rayConfig.OutboundBean.OutSettingsBean.ServersBean.SocksUsersBean()
socksUsersBean.user = et_security.text.toString().trim() socksUsersBean.user = et_security?.text.toString().trim()
socksUsersBean.pass = et_id.text.toString().trim() socksUsersBean.pass = et_id.text.toString().trim()
server.users = listOf(socksUsersBean) server.users = listOf(socksUsersBean)
} }
@@ -270,33 +264,36 @@ class ServerActivity : BaseActivity() {
} }
} }
private fun saveStreamSettings(streamSetting: V2rayConfig.OutboundBean.StreamSettingsBean, config: ServerConfig) { private fun saveStreamSettings(streamSetting: V2rayConfig.OutboundBean.StreamSettingsBean) {
val network = if (sp_network != null) networks[sp_network.selectedItemPosition] else DEFAULT_NETWORK val network = sp_network?.selectedItemPosition ?: return
val type = if (sp_header_type != null) transportTypes(network)[sp_header_type.selectedItemPosition] else ""; val type = sp_header_type?.selectedItemPosition ?: return
val requestHost = if (et_request_host != null) et_request_host.text.toString().trim() else "" val requestHost = et_request_host?.text?.toString()?.trim() ?: return
val path = if (et_path != null) et_path.text.toString().trim() else "" val path = et_path?.text?.toString()?.trim() ?: return
//val sniField = et_sni?.text?.toString()?.trim() ?: return
val allowInsecureField = sp_allow_insecure?.selectedItemPosition ?: return
val streamSecurity = sp_stream_security?.selectedItemPosition ?: return
var sni = streamSetting.populateTransportSettings( var sni = streamSetting.populateTransportSettings(
transport = network, transport = networks[network],
headerType = type, headerType = transportTypes(networks[network])[type],
host = requestHost, host = requestHost,
path = path, path = path,
seed = path, seed = path,
quicSecurity = requestHost, quicSecurity = requestHost,
key = path, key = path,
mode = type, mode = transportTypes(networks[network])[type],
serviceName = path serviceName = path
) )
val allowInsecure = if (sp_allow_insecure == null || allowinsecures[sp_allow_insecure.selectedItemPosition].isBlank()) { //if (sniField.isNotBlank()) {
// sni = sniField
//}
val allowInsecure = if (allowinsecures[allowInsecureField].isBlank()) {
false//settingsStorage?.decodeBool(PREF_ALLOW_INSECURE) ?: false false//settingsStorage?.decodeBool(PREF_ALLOW_INSECURE) ?: false
} else { } else {
allowinsecures[sp_allow_insecure.selectedItemPosition].toBoolean() allowinsecures[allowInsecureField].toBoolean()
} }
val defaultTls = if (config.configType == EConfigType.TROJAN) V2rayConfig.TLS else "" streamSetting.populateTlsSettings(streamSecuritys[streamSecurity], allowInsecure, sni)
streamSetting.populateTlsSettings(
if (sp_stream_security != null) streamSecuritys[sp_stream_security.selectedItemPosition] else defaultTls,
allowInsecure,
sni
)
} }
private fun transportTypes(network: String?): Array<out String> { private fun transportTypes(network: String?): Array<out String> {
@@ -326,10 +323,10 @@ class ServerActivity : BaseActivity() {
return true return true
} }
override fun onCreateOptionsMenu(menu: Menu?): Boolean { override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.action_server, menu) menuInflater.inflate(R.menu.action_server, menu)
val delButton = menu?.findItem(R.id.del_config) val delButton = menu.findItem(R.id.del_config)
val saveButton = menu?.findItem(R.id.save_config) val saveButton = menu.findItem(R.id.save_config)
if (editGuid.isNotEmpty()) { if (editGuid.isNotEmpty()) {
if (isRunning) { if (isRunning) {

View File

@@ -1,24 +1,26 @@
package com.v2ray.ang.ui package com.v2ray.ang.ui
import android.os.Bundle import android.os.Bundle
import android.support.v7.app.AlertDialog
import android.text.TextUtils import android.text.TextUtils
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import com.blacksquircle.ui.language.json.JsonLanguage
import com.google.gson.Gson import com.google.gson.Gson
import com.tencent.mmkv.MMKV import com.tencent.mmkv.MMKV
import com.v2ray.ang.R import com.v2ray.ang.R
import com.v2ray.ang.databinding.ActivityServerCustomConfigBinding
import com.v2ray.ang.dto.EConfigType import com.v2ray.ang.dto.EConfigType
import com.v2ray.ang.dto.ServerConfig import com.v2ray.ang.dto.ServerConfig
import com.v2ray.ang.dto.V2rayConfig import com.v2ray.ang.dto.V2rayConfig
import com.v2ray.ang.extension.toast import com.v2ray.ang.extension.toast
import com.v2ray.ang.util.MmkvManager import com.v2ray.ang.util.MmkvManager
import com.v2ray.ang.util.Utils import com.v2ray.ang.util.Utils
import kotlinx.android.synthetic.main.activity_server_custom_config.*
import me.drakeet.support.toast.ToastCompat import me.drakeet.support.toast.ToastCompat
class ServerCustomConfigActivity : BaseActivity() { class ServerCustomConfigActivity : BaseActivity() {
private lateinit var binding: ActivityServerCustomConfigBinding
private val mainStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_MAIN, MMKV.MULTI_PROCESS_MODE) } private val mainStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_MAIN, MMKV.MULTI_PROCESS_MODE) }
private val serverRawStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SERVER_RAW, MMKV.MULTI_PROCESS_MODE) } private val serverRawStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SERVER_RAW, MMKV.MULTI_PROCESS_MODE) }
@@ -31,9 +33,12 @@ class ServerCustomConfigActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_server_custom_config) binding = ActivityServerCustomConfigBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
title = getString(R.string.title_server) title = getString(R.string.title_server)
binding.editor.language = JsonLanguage()
val config = MmkvManager.decodeServerConfig(editGuid) val config = MmkvManager.decodeServerConfig(editGuid)
if (config != null) { if (config != null) {
bindingServer(config) bindingServer(config)
@@ -47,12 +52,12 @@ class ServerCustomConfigActivity : BaseActivity() {
* bingding seleced server config * bingding seleced server config
*/ */
private fun bindingServer(config: ServerConfig): Boolean { private fun bindingServer(config: ServerConfig): Boolean {
et_remarks.text = Utils.getEditable(config.remarks) binding.etRemarks.text = Utils.getEditable(config.remarks)
val raw = serverRawStorage?.decodeString(editGuid) val raw = serverRawStorage?.decodeString(editGuid)
if (raw.isNullOrBlank()) { if (raw.isNullOrBlank()) {
tv_content.text = Utils.getEditable(config.fullConfig?.toPrettyPrinting().orEmpty()) binding.editor.setTextContent(Utils.getEditable(config.fullConfig?.toPrettyPrinting().orEmpty()))
} else { } else {
tv_content.text = Utils.getEditable(raw) binding.editor.setTextContent(Utils.getEditable(raw))
} }
return true return true
} }
@@ -61,7 +66,7 @@ class ServerCustomConfigActivity : BaseActivity() {
* clear or init server config * clear or init server config
*/ */
private fun clearServer(): Boolean { private fun clearServer(): Boolean {
et_remarks.text = null binding.etRemarks.text = null
return true return true
} }
@@ -69,13 +74,13 @@ class ServerCustomConfigActivity : BaseActivity() {
* save server config * save server config
*/ */
private fun saveServer(): Boolean { private fun saveServer(): Boolean {
if (TextUtils.isEmpty(et_remarks.text.toString())) { if (TextUtils.isEmpty(binding.etRemarks.text.toString())) {
toast(R.string.server_lab_remarks) toast(R.string.server_lab_remarks)
return false return false
} }
val v2rayConfig = try { val v2rayConfig = try {
Gson().fromJson(tv_content.text.toString(), V2rayConfig::class.java) Gson().fromJson(binding.editor.text.toString(), V2rayConfig::class.java)
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
ToastCompat.makeText(this, "${getString(R.string.toast_malformed_josn)} ${e.cause?.message}", Toast.LENGTH_LONG).show() ToastCompat.makeText(this, "${getString(R.string.toast_malformed_josn)} ${e.cause?.message}", Toast.LENGTH_LONG).show()
@@ -83,11 +88,11 @@ class ServerCustomConfigActivity : BaseActivity() {
} }
val config = MmkvManager.decodeServerConfig(editGuid) ?: ServerConfig.create(EConfigType.CUSTOM) val config = MmkvManager.decodeServerConfig(editGuid) ?: ServerConfig.create(EConfigType.CUSTOM)
config.remarks = et_remarks.text.toString().trim() config.remarks = binding.etRemarks.text.toString().trim()
config.fullConfig = v2rayConfig config.fullConfig = v2rayConfig
MmkvManager.encodeServerConfig(editGuid, config) MmkvManager.encodeServerConfig(editGuid, config)
serverRawStorage?.encode(editGuid, tv_content.text.toString()) serverRawStorage?.encode(editGuid, binding.editor.text.toString())
toast(R.string.toast_success) toast(R.string.toast_success)
finish() finish()
return true return true
@@ -108,10 +113,10 @@ class ServerCustomConfigActivity : BaseActivity() {
return true return true
} }
override fun onCreateOptionsMenu(menu: Menu?): Boolean { override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.action_server, menu) menuInflater.inflate(R.menu.action_server, menu)
val delButton = menu?.findItem(R.id.del_config) val delButton = menu.findItem(R.id.del_config)
val saveButton = menu?.findItem(R.id.save_config) val saveButton = menu.findItem(R.id.save_config)
if (editGuid.isNotEmpty()) { if (editGuid.isNotEmpty()) {
if (isRunning) { if (isRunning) {

View File

@@ -1,11 +1,11 @@
package com.v2ray.ang.ui package com.v2ray.ang.ui
import android.arch.lifecycle.ViewModelProviders
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.support.v7.preference.* import androidx.preference.*
import android.text.TextUtils import android.text.TextUtils
import android.view.View import android.view.View
import androidx.activity.viewModels
import com.v2ray.ang.R import com.v2ray.ang.R
import com.v2ray.ang.AppConfig import com.v2ray.ang.AppConfig
import com.v2ray.ang.extension.toast import com.v2ray.ang.extension.toast
@@ -14,8 +14,7 @@ import com.v2ray.ang.util.Utils
import com.v2ray.ang.viewmodel.SettingsViewModel import com.v2ray.ang.viewmodel.SettingsViewModel
class SettingsActivity : BaseActivity() { class SettingsActivity : BaseActivity() {
private val settingsViewModel: SettingsViewModel by viewModels()
private val settingsViewModel by lazy { ViewModelProviders.of(this).get(SettingsViewModel::class.java) }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@@ -204,7 +203,7 @@ class SettingsActivity : BaseActivity() {
override fun onStart() { override fun onStart() {
super.onStart() super.onStart()
val defaultSharedPreferences = PreferenceManager.getDefaultSharedPreferences(activity) val defaultSharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireActivity())
updateMode(defaultSharedPreferences.getString(AppConfig.PREF_MODE, "VPN")) updateMode(defaultSharedPreferences.getString(AppConfig.PREF_MODE, "VPN"))
var remoteDnsString = defaultSharedPreferences.getString(AppConfig.PREF_REMOTE_DNS, "") var remoteDnsString = defaultSharedPreferences.getString(AppConfig.PREF_REMOTE_DNS, "")
domesticDns.summary = defaultSharedPreferences.getString(AppConfig.PREF_DOMESTIC_DNS, "") domesticDns.summary = defaultSharedPreferences.getString(AppConfig.PREF_DOMESTIC_DNS, "")
@@ -223,10 +222,10 @@ class SettingsActivity : BaseActivity() {
} }
private fun updateMode(mode: String?) { private fun updateMode(mode: String?) {
val defaultSharedPreferences = PreferenceManager.getDefaultSharedPreferences(activity) val defaultSharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireActivity())
val vpn = mode == "VPN" val vpn = mode == "VPN"
perAppProxy.isEnabled = vpn perAppProxy.isEnabled = vpn
perAppProxy.isChecked = PreferenceManager.getDefaultSharedPreferences(activity) perAppProxy.isChecked = PreferenceManager.getDefaultSharedPreferences(requireActivity())
.getBoolean(AppConfig.PREF_PER_APP_PROXY, false) .getBoolean(AppConfig.PREF_PER_APP_PROXY, false)
localDns?.isEnabled = vpn localDns?.isEnabled = vpn
fakeDns?.isEnabled = vpn fakeDns?.isEnabled = vpn

View File

@@ -1,20 +1,21 @@
package com.v2ray.ang.ui package com.v2ray.ang.ui
import android.os.Bundle import android.os.Bundle
import android.support.v7.app.AlertDialog import androidx.appcompat.app.AlertDialog
import android.text.TextUtils import android.text.TextUtils
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import com.google.gson.Gson import com.google.gson.Gson
import com.tencent.mmkv.MMKV import com.tencent.mmkv.MMKV
import com.v2ray.ang.R import com.v2ray.ang.R
import com.v2ray.ang.databinding.ActivitySubEditBinding
import com.v2ray.ang.dto.SubscriptionItem import com.v2ray.ang.dto.SubscriptionItem
import com.v2ray.ang.extension.toast import com.v2ray.ang.extension.toast
import com.v2ray.ang.util.MmkvManager import com.v2ray.ang.util.MmkvManager
import com.v2ray.ang.util.Utils import com.v2ray.ang.util.Utils
import kotlinx.android.synthetic.main.activity_sub_edit.*
class SubEditActivity : BaseActivity() { class SubEditActivity : BaseActivity() {
private lateinit var binding: ActivitySubEditBinding
var del_config: MenuItem? = null var del_config: MenuItem? = null
var save_config: MenuItem? = null var save_config: MenuItem? = null
@@ -24,7 +25,9 @@ class SubEditActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_sub_edit) binding = ActivitySubEditBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
title = getString(R.string.title_sub_setting) title = getString(R.string.title_sub_setting)
val json = subStorage?.decodeString(editSubId) val json = subStorage?.decodeString(editSubId)
@@ -40,8 +43,8 @@ class SubEditActivity : BaseActivity() {
* bingding seleced server config * bingding seleced server config
*/ */
private fun bindingServer(subItem: SubscriptionItem): Boolean { private fun bindingServer(subItem: SubscriptionItem): Boolean {
et_remarks.text = Utils.getEditable(subItem.remarks) binding.etRemarks.text = Utils.getEditable(subItem.remarks)
et_url.text = Utils.getEditable(subItem.url) binding.etUrl.text = Utils.getEditable(subItem.url)
return true return true
} }
@@ -50,8 +53,8 @@ class SubEditActivity : BaseActivity() {
* clear or init server config * clear or init server config
*/ */
private fun clearServer(): Boolean { private fun clearServer(): Boolean {
et_remarks.text = null binding.etRemarks.text = null
et_url.text = null binding.etUrl.text = null
return true return true
} }
@@ -70,8 +73,8 @@ class SubEditActivity : BaseActivity() {
subItem = SubscriptionItem() subItem = SubscriptionItem()
} }
subItem.remarks = et_remarks.text.toString() subItem.remarks = binding.etRemarks.text.toString()
subItem.url = et_url.text.toString() subItem.url = binding.etUrl.text.toString()
if (TextUtils.isEmpty(subItem.remarks)) { if (TextUtils.isEmpty(subItem.remarks)) {
toast(R.string.sub_setting_remarks) toast(R.string.sub_setting_remarks)
@@ -103,10 +106,10 @@ class SubEditActivity : BaseActivity() {
return true return true
} }
override fun onCreateOptionsMenu(menu: Menu?): Boolean { override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.action_server, menu) menuInflater.inflate(R.menu.action_server, menu)
del_config = menu?.findItem(R.id.del_config) del_config = menu.findItem(R.id.del_config)
save_config = menu?.findItem(R.id.save_config) save_config = menu.findItem(R.id.save_config)
if (editSubId.isEmpty()) { if (editSubId.isEmpty()) {
del_config?.isVisible = false del_config?.isVisible = false

View File

@@ -4,26 +4,29 @@ import android.content.Intent
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import com.v2ray.ang.R import com.v2ray.ang.R
import kotlinx.android.synthetic.main.activity_sub_setting.*
import android.os.Bundle import android.os.Bundle
import android.support.v7.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.v2ray.ang.databinding.ActivitySubSettingBinding
import com.v2ray.ang.dto.SubscriptionItem import com.v2ray.ang.dto.SubscriptionItem
import com.v2ray.ang.util.MmkvManager import com.v2ray.ang.util.MmkvManager
class SubSettingActivity : BaseActivity() { class SubSettingActivity : BaseActivity() {
private lateinit var binding: ActivitySubSettingBinding
var subscriptions:List<Pair<String, SubscriptionItem>> = listOf() var subscriptions:List<Pair<String, SubscriptionItem>> = listOf()
private val adapter by lazy { SubSettingRecyclerAdapter(this) } private val adapter by lazy { SubSettingRecyclerAdapter(this) }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_sub_setting) binding = ActivitySubSettingBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
title = getString(R.string.title_sub_setting) title = getString(R.string.title_sub_setting)
recycler_view.setHasFixedSize(true) binding.recyclerView.setHasFixedSize(true)
recycler_view.layoutManager = LinearLayoutManager(this) binding.recyclerView.layoutManager = LinearLayoutManager(this)
recycler_view.adapter = adapter binding.recyclerView.adapter = adapter
supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayHomeAsUpEnabled(true)
} }
@@ -34,10 +37,10 @@ class SubSettingActivity : BaseActivity() {
adapter.notifyDataSetChanged() adapter.notifyDataSetChanged()
} }
override fun onCreateOptionsMenu(menu: Menu?): Boolean { override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.action_sub_setting, menu) menuInflater.inflate(R.menu.action_sub_setting, menu)
menu?.findItem(R.id.del_config)?.isVisible = false menu.findItem(R.id.del_config)?.isVisible = false
menu?.findItem(R.id.save_config)?.isVisible = false menu.findItem(R.id.save_config)?.isVisible = false
return super.onCreateOptionsMenu(menu) return super.onCreateOptionsMenu(menu)
} }

View File

@@ -2,46 +2,34 @@ package com.v2ray.ang.ui
import android.content.Intent import android.content.Intent
import android.graphics.Color import android.graphics.Color
import android.support.v7.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import com.v2ray.ang.R import com.v2ray.ang.databinding.ItemRecyclerSubSettingBinding
import kotlinx.android.synthetic.main.item_recycler_sub_setting.view.*
class SubSettingRecyclerAdapter(val activity: SubSettingActivity) : RecyclerView.Adapter<SubSettingRecyclerAdapter.BaseViewHolder>() { class SubSettingRecyclerAdapter(val activity: SubSettingActivity) : RecyclerView.Adapter<SubSettingRecyclerAdapter.MainViewHolder>() {
private var mActivity: SubSettingActivity = activity private var mActivity: SubSettingActivity = activity
override fun getItemCount() = mActivity.subscriptions.size override fun getItemCount() = mActivity.subscriptions.size
override fun onBindViewHolder(holder: BaseViewHolder, position: Int) { override fun onBindViewHolder(holder: MainViewHolder, position: Int) {
if (holder is MainViewHolder) { val subId = mActivity.subscriptions[position].first
val subId = mActivity.subscriptions[position].first val subItem = mActivity.subscriptions[position].second
val subItem = mActivity.subscriptions[position].second holder.itemSubSettingBinding.tvName.text = subItem.remarks
holder.name.text = subItem.remarks holder.itemSubSettingBinding.tvUrl.text = subItem.url
holder.url.text = subItem.url holder.itemView.setBackgroundColor(Color.TRANSPARENT)
holder.itemView.setBackgroundColor(Color.TRANSPARENT)
holder.layout_edit.setOnClickListener { holder.itemSubSettingBinding.layoutEdit.setOnClickListener {
mActivity.startActivity(Intent(mActivity, SubEditActivity::class.java) mActivity.startActivity(Intent(mActivity, SubEditActivity::class.java)
.putExtra("subId", subId) .putExtra("subId", subId)
) )
}
} }
} }
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MainViewHolder {
return MainViewHolder(LayoutInflater.from(parent.context) return MainViewHolder(ItemRecyclerSubSettingBinding.inflate(LayoutInflater.from(parent.context), parent, false))
.inflate(R.layout.item_recycler_sub_setting, parent, false))
}
open class BaseViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
class MainViewHolder(itemView: View) : BaseViewHolder(itemView) {
val name = itemView.tv_name!!
val url = itemView.tv_url!!
val layout_edit = itemView.layout_edit!!
} }
class MainViewHolder(val itemSubSettingBinding: ItemRecyclerSubSettingBinding) : RecyclerView.ViewHolder(itemSubSettingBinding.root)
} }

View File

@@ -14,10 +14,12 @@ import android.view.MenuItem
import com.google.zxing.WriterException import com.google.zxing.WriterException
import com.tencent.mmkv.MMKV import com.tencent.mmkv.MMKV
import com.v2ray.ang.AppConfig import com.v2ray.ang.AppConfig
import com.v2ray.ang.databinding.ActivityTaskerBinding
import com.v2ray.ang.util.MmkvManager import com.v2ray.ang.util.MmkvManager
import kotlinx.android.synthetic.main.activity_tasker.*
class TaskerActivity : BaseActivity() { class TaskerActivity : BaseActivity() {
private lateinit var binding: ActivityTaskerBinding
private var listview: ListView? = null private var listview: ListView? = null
private var lstData: ArrayList<String> = ArrayList() private var lstData: ArrayList<String> = ArrayList()
private var lstGuid: ArrayList<String> = ArrayList() private var lstGuid: ArrayList<String> = ArrayList()
@@ -26,7 +28,9 @@ class TaskerActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_tasker) binding = ActivityTaskerBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
//add def value //add def value
lstData.add("Default") lstData.add("Default")
@@ -55,7 +59,7 @@ class TaskerActivity : BaseActivity() {
if (switch == null || TextUtils.isEmpty(guid)) { if (switch == null || TextUtils.isEmpty(guid)) {
return return
} else { } else {
switch_start_service.isChecked = switch binding.switchStartService.isChecked = switch
val pos = lstGuid.indexOf(guid.toString()) val pos = lstGuid.indexOf(guid.toString())
if (pos >= 0) { if (pos >= 0) {
listview?.setItemChecked(pos, true) listview?.setItemChecked(pos, true)
@@ -74,12 +78,12 @@ class TaskerActivity : BaseActivity() {
} }
val extraBundle = Bundle() val extraBundle = Bundle()
extraBundle.putBoolean(AppConfig.TASKER_EXTRA_BUNDLE_SWITCH, switch_start_service.isChecked) extraBundle.putBoolean(AppConfig.TASKER_EXTRA_BUNDLE_SWITCH, binding.switchStartService.isChecked)
extraBundle.putString(AppConfig.TASKER_EXTRA_BUNDLE_GUID, lstGuid[position]) extraBundle.putString(AppConfig.TASKER_EXTRA_BUNDLE_GUID, lstGuid[position])
val intent = Intent() val intent = Intent()
val remarks = lstData[position] val remarks = lstData[position]
val blurb = if (switch_start_service.isChecked) { val blurb = if (binding.switchStartService.isChecked) {
"Start $remarks" "Start $remarks"
} else { } else {
"Stop $remarks" "Stop $remarks"
@@ -91,9 +95,9 @@ class TaskerActivity : BaseActivity() {
finish() finish()
} }
override fun onCreateOptionsMenu(menu: Menu?): Boolean { override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.action_server, menu) menuInflater.inflate(R.menu.action_server, menu)
val del_config = menu?.findItem(R.id.del_config) val del_config = menu.findItem(R.id.del_config)
del_config?.isVisible = false del_config?.isVisible = false
return super.onCreateOptionsMenu(menu) return super.onCreateOptionsMenu(menu)
} }

View File

@@ -3,7 +3,7 @@ package com.v2ray.ang.util
import android.content.Context import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences
import android.graphics.Bitmap import android.graphics.Bitmap
import android.support.v7.preference.PreferenceManager import androidx.preference.PreferenceManager
import android.text.TextUtils import android.text.TextUtils
import com.google.gson.Gson import com.google.gson.Gson
import com.tencent.mmkv.MMKV import com.tencent.mmkv.MMKV
@@ -17,7 +17,6 @@ import com.v2ray.ang.dto.V2rayConfig.Companion.DEFAULT_SECURITY
import com.v2ray.ang.dto.V2rayConfig.Companion.TLS import com.v2ray.ang.dto.V2rayConfig.Companion.TLS
import com.v2ray.ang.util.MmkvManager.KEY_SELECTED_SERVER import com.v2ray.ang.util.MmkvManager.KEY_SELECTED_SERVER
import java.net.URI import java.net.URI
import java.net.URLDecoder
import java.util.* import java.util.*
object AngConfigManager { object AngConfigManager {
@@ -103,7 +102,6 @@ object AngConfigManager {
vnext.port = vmessBean.port vnext.port = vmessBean.port
vnext.users[0].id = vmessBean.id vnext.users[0].id = vmessBean.id
if (config.configType == EConfigType.VMESS) { if (config.configType == EConfigType.VMESS) {
vnext.users[0].alterId = vmessBean.alterId
vnext.users[0].security = vmessBean.security vnext.users[0].security = vmessBean.security
} else if (config.configType == EConfigType.VLESS) { } else if (config.configType == EConfigType.VLESS) {
vnext.users[0].encryption = vmessBean.security vnext.users[0].encryption = vmessBean.security
@@ -139,7 +137,7 @@ object AngConfigManager {
// vmessBean.allowInsecure.toBoolean() // vmessBean.allowInsecure.toBoolean()
// } // }
streamSetting.populateTlsSettings(vmessBean.streamSecurity, false, streamSetting.populateTlsSettings(vmessBean.streamSecurity, false,
sni)//if (vmessBean.sni.isNotBlank()) vmessBean.sni else sni) sni)//vmessBean.sni.ifBlank { sni })
} }
} }
val key = MmkvManager.encodeServerConfig(vmessBean.guid, config) val key = MmkvManager.encodeServerConfig(vmessBean.guid, config)
@@ -169,7 +167,7 @@ object AngConfigManager {
} }
//maybe sub //maybe sub
if (str.startsWith(HTTP_PROTOCOL) || str.startsWith(HTTPS_PROTOCOL)) { if (TextUtils.isEmpty(subid) && (str.startsWith(HTTP_PROTOCOL) || str.startsWith(HTTPS_PROTOCOL))) {
MmkvManager.importUrlAsSubscription(str) MmkvManager.importUrlAsSubscription(str)
return 0 return 0
} }
@@ -192,10 +190,10 @@ object AngConfigManager {
return R.string.toast_decoding_failed return R.string.toast_decoding_failed
} }
val vmessQRCode = Gson().fromJson(result, VmessQRCode::class.java) val vmessQRCode = Gson().fromJson(result, VmessQRCode::class.java)
// Although VmessQRCode fields are non null, looks like Gson may still create null fields
if (TextUtils.isEmpty(vmessQRCode.add) if (TextUtils.isEmpty(vmessQRCode.add)
|| TextUtils.isEmpty(vmessQRCode.port) || TextUtils.isEmpty(vmessQRCode.port)
|| TextUtils.isEmpty(vmessQRCode.id) || TextUtils.isEmpty(vmessQRCode.id)
|| TextUtils.isEmpty(vmessQRCode.aid)
|| TextUtils.isEmpty(vmessQRCode.net) || TextUtils.isEmpty(vmessQRCode.net)
) { ) {
return R.string.toast_incorrect_protocol return R.string.toast_incorrect_protocol
@@ -207,12 +205,11 @@ object AngConfigManager {
vnext.port = Utils.parseInt(vmessQRCode.port) vnext.port = Utils.parseInt(vmessQRCode.port)
vnext.users[0].id = vmessQRCode.id vnext.users[0].id = vmessQRCode.id
vnext.users[0].encryption = DEFAULT_SECURITY vnext.users[0].encryption = DEFAULT_SECURITY
vnext.users[0].alterId = Utils.parseInt(vmessQRCode.aid)
} }
val sni = streamSetting.populateTransportSettings(vmessQRCode.net, vmessQRCode.type, vmessQRCode.host, val sni = streamSetting.populateTransportSettings(vmessQRCode.net, vmessQRCode.type, vmessQRCode.host,
vmessQRCode.path, vmessQRCode.path, vmessQRCode.host, vmessQRCode.path, vmessQRCode.type, vmessQRCode.path) vmessQRCode.path, vmessQRCode.path, vmessQRCode.host, vmessQRCode.path, vmessQRCode.type, vmessQRCode.path)
streamSetting.populateTlsSettings(vmessQRCode.tls, allowInsecure, streamSetting.populateTlsSettings(vmessQRCode.tls, allowInsecure,
if (vmessQRCode.sni.isNotBlank()) vmessQRCode.sni else sni) if (TextUtils.isEmpty(vmessQRCode.sni)) sni else vmessQRCode.sni)
} }
} }
} else if (str.startsWith(EConfigType.SHADOWSOCKS.protocolScheme)) { } else if (str.startsWith(EConfigType.SHADOWSOCKS.protocolScheme)) {
@@ -244,7 +241,7 @@ object AngConfigManager {
server.address = match.groupValues[3].removeSurrounding("[", "]") server.address = match.groupValues[3].removeSurrounding("[", "]")
server.port = match.groupValues[4].toInt() server.port = match.groupValues[4].toInt()
server.password = match.groupValues[2] server.password = match.groupValues[2]
server.method = match.groupValues[1].toLowerCase() server.method = match.groupValues[1].lowercase()
} }
} else if (str.startsWith(EConfigType.SOCKS.protocolScheme)) { } else if (str.startsWith(EConfigType.SOCKS.protocolScheme)) {
var result = str.replace(EConfigType.SOCKS.protocolScheme, "") var result = str.replace(EConfigType.SOCKS.protocolScheme, "")
@@ -275,7 +272,7 @@ object AngConfigManager {
server.address = match.groupValues[3].removeSurrounding("[", "]") server.address = match.groupValues[3].removeSurrounding("[", "]")
server.port = match.groupValues[4].toInt() server.port = match.groupValues[4].toInt()
val socksUsersBean = V2rayConfig.OutboundBean.OutSettingsBean.ServersBean.SocksUsersBean() val socksUsersBean = V2rayConfig.OutboundBean.OutSettingsBean.ServersBean.SocksUsersBean()
socksUsersBean.user = match.groupValues[1].toLowerCase() socksUsersBean.user = match.groupValues[1].lowercase()
socksUsersBean.pass = match.groupValues[2] socksUsersBean.pass = match.groupValues[2]
server.users = listOf(socksUsersBean) server.users = listOf(socksUsersBean)
} }
@@ -291,16 +288,14 @@ object AngConfigManager {
var sni = "" var sni = ""
uri.rawQuery?.let { rawQuery -> uri.rawQuery?.let { rawQuery ->
val queryParam = rawQuery.split("&") val queryParam = rawQuery.split("&")
.map { it.split("=").let { (k, v) -> k to URLDecoder.decode(v, "utf-8")!! } } .associate { it.split("=").let { (k, v) -> k to Utils.urlDecode(v) } }
.toMap()
sni = queryParam["sni"] ?: "" sni = queryParam["sni"] ?: ""
} }
config.outboundBean?.streamSettings?.populateTlsSettings(TLS, allowInsecure, sni) config.outboundBean?.streamSettings?.populateTlsSettings(TLS, allowInsecure, sni)
} else if (str.startsWith(EConfigType.VLESS.protocolScheme)) { } else if (str.startsWith(EConfigType.VLESS.protocolScheme)) {
val uri = URI(str) val uri = URI(str)
val queryParam = uri.rawQuery.split("&") val queryParam = uri.rawQuery.split("&")
.map { it.split("=").let { (k, v) -> k to URLDecoder.decode(v, "utf-8")!! } } .associate { it.split("=").let { (k, v) -> k to Utils.urlDecode(v) } }
.toMap()
config = ServerConfig.create(EConfigType.VLESS) config = ServerConfig.create(EConfigType.VLESS)
val streamSetting = config.outboundBean?.streamSettings ?: return -1 val streamSetting = config.outboundBean?.streamSettings ?: return -1
config.remarks = uri.fragment ?: "" config.remarks = uri.fragment ?: ""
@@ -338,14 +333,13 @@ object AngConfigManager {
return runCatching { return runCatching {
val uri = URI(uriString) val uri = URI(uriString)
check(uri.scheme == "vmess") 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})-([0-9]+)") 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 .matchEntire(uri.userInfo)?.groupValues
?: error("parse user info fail.") ?: error("parse user info fail.")
val tls = tlsStr.isNotBlank() val tls = tlsStr.isNotBlank()
val queryParam = uri.rawQuery.split("&") val queryParam = uri.rawQuery.split("&")
.map { it.split("=").let { (k, v) -> k to URLDecoder.decode(v, "utf-8")!! } } .associate { it.split("=").let { (k, v) -> k to Utils.urlDecode(v) } }
.toMap()
val streamSetting = config.outboundBean?.streamSettings ?: return false val streamSetting = config.outboundBean?.streamSettings ?: return false
config.remarks = uri.fragment config.remarks = uri.fragment
@@ -354,7 +348,6 @@ object AngConfigManager {
vnext.port = uri.port vnext.port = uri.port
vnext.users[0].id = uuid vnext.users[0].id = uuid
vnext.users[0].encryption = DEFAULT_SECURITY vnext.users[0].encryption = DEFAULT_SECURITY
vnext.users[0].alterId = alterId.toInt()
} }
val sni = streamSetting.populateTransportSettings(protocol, queryParam["type"], val sni = streamSetting.populateTransportSettings(protocol, queryParam["type"],
@@ -391,7 +384,6 @@ object AngConfigManager {
vnext.port = Utils.parseInt(arr22[1]) vnext.port = Utils.parseInt(arr22[1])
vnext.users[0].id = arr21[1] vnext.users[0].id = arr21[1]
vnext.users[0].encryption = arr21[0] vnext.users[0].encryption = arr21[0]
vnext.users[0].alterId = 0
} }
return true return true
} }
@@ -412,7 +404,6 @@ object AngConfigManager {
vmessQRCode.add = outbound.getServerAddress().orEmpty() vmessQRCode.add = outbound.getServerAddress().orEmpty()
vmessQRCode.port = outbound.getServerPort().toString() vmessQRCode.port = outbound.getServerPort().toString()
vmessQRCode.id = outbound.getPassword().orEmpty() vmessQRCode.id = outbound.getPassword().orEmpty()
vmessQRCode.aid = outbound.settings?.vnext?.get(0)?.users?.get(0)?.alterId.toString()
vmessQRCode.net = streamSetting.network vmessQRCode.net = streamSetting.network
vmessQRCode.tls = streamSetting.security vmessQRCode.tls = streamSetting.security
vmessQRCode.sni = streamSetting.tlsSettings?.serverName.orEmpty() vmessQRCode.sni = streamSetting.tlsSettings?.serverName.orEmpty()
@@ -454,28 +445,24 @@ object AngConfigManager {
} }
dicQuery["encryption"] = if (outbound.getSecurityEncryption().isNullOrEmpty()) "none" dicQuery["encryption"] = if (outbound.getSecurityEncryption().isNullOrEmpty()) "none"
else outbound.getSecurityEncryption().orEmpty() else outbound.getSecurityEncryption().orEmpty()
dicQuery["security"] = if (streamSetting.security.isEmpty()) "none" dicQuery["security"] = streamSetting.security.ifEmpty { "none" }
else streamSetting.security
(streamSetting.tlsSettings?: streamSetting.xtlsSettings)?.let { tlsSetting -> (streamSetting.tlsSettings?: streamSetting.xtlsSettings)?.let { tlsSetting ->
if (!TextUtils.isEmpty(tlsSetting.serverName)) { if (!TextUtils.isEmpty(tlsSetting.serverName)) {
dicQuery["sni"] = tlsSetting.serverName dicQuery["sni"] = tlsSetting.serverName
} }
} }
dicQuery["type"] = if (streamSetting.network.isEmpty()) V2rayConfig.DEFAULT_NETWORK dicQuery["type"] = streamSetting.network.ifEmpty { V2rayConfig.DEFAULT_NETWORK }
else streamSetting.network
outbound.getTransportSettingDetails()?.let { transportDetails -> outbound.getTransportSettingDetails()?.let { transportDetails ->
when (streamSetting.network) { when (streamSetting.network) {
"tcp" -> { "tcp" -> {
dicQuery["headerType"] = if (transportDetails[0].isEmpty()) "none" dicQuery["headerType"] = transportDetails[0].ifEmpty { "none" }
else transportDetails[0]
if (!TextUtils.isEmpty(transportDetails[1])) { if (!TextUtils.isEmpty(transportDetails[1])) {
dicQuery["host"] = Utils.urlEncode(transportDetails[1]) dicQuery["host"] = Utils.urlEncode(transportDetails[1])
} }
} }
"kcp" -> { "kcp" -> {
dicQuery["headerType"] = if (transportDetails[0].isEmpty()) "none" dicQuery["headerType"] = transportDetails[0].ifEmpty { "none" }
else transportDetails[0]
if (!TextUtils.isEmpty(transportDetails[2])) { if (!TextUtils.isEmpty(transportDetails[2])) {
dicQuery["seed"] = Utils.urlEncode(transportDetails[2]) dicQuery["seed"] = Utils.urlEncode(transportDetails[2])
} }
@@ -498,8 +485,7 @@ object AngConfigManager {
} }
} }
"quic" -> { "quic" -> {
dicQuery["headerType"] = if (transportDetails[0].isEmpty()) "none" dicQuery["headerType"] = transportDetails[0].ifEmpty { "none" }
else transportDetails[0]
dicQuery["quicSecurity"] = Utils.urlEncode(transportDetails[1]) dicQuery["quicSecurity"] = Utils.urlEncode(transportDetails[1])
dicQuery["key"] = Utils.urlEncode(transportDetails[2]) dicQuery["key"] = Utils.urlEncode(transportDetails[2])
} }

View File

@@ -42,11 +42,7 @@ object MmkvManager {
} }
fun encodeServerConfig(guid: String, config: ServerConfig): String { fun encodeServerConfig(guid: String, config: ServerConfig): String {
val key = if (guid.isBlank()) { val key = guid.ifBlank { Utils.getUuid() }
Utils.getUuid()
} else {
guid
}
serverStorage?.encode(key, Gson().toJson(config)) serverStorage?.encode(key, Gson().toJson(config))
val serverList= decodeServerList() val serverList= decodeServerList()
if (!serverList.contains(key)) { if (!serverList.contains(key)) {

View File

@@ -20,6 +20,7 @@ import android.util.Patterns
import android.webkit.URLUtil import android.webkit.URLUtil
import com.tencent.mmkv.MMKV import com.tencent.mmkv.MMKV
import com.v2ray.ang.AppConfig import com.v2ray.ang.AppConfig
import com.v2ray.ang.BuildConfig
import com.v2ray.ang.R import com.v2ray.ang.R
import com.v2ray.ang.extension.responseLength import com.v2ray.ang.extension.responseLength
import com.v2ray.ang.extension.toast import com.v2ray.ang.extension.toast
@@ -61,11 +62,11 @@ object Utils {
* parseInt * parseInt
*/ */
fun parseInt(str: String): Int { fun parseInt(str: String): Int {
try { return try {
return Integer.parseInt(str) Integer.parseInt(str)
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
return 0 0
} }
} }
@@ -73,12 +74,12 @@ object Utils {
* get text from clipboard * get text from clipboard
*/ */
fun getClipboard(context: Context): String { fun getClipboard(context: Context): String {
try { return try {
val cmb = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager val cmb = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
return cmb.primaryClip?.getItemAt(0)?.text.toString() cmb.primaryClip?.getItemAt(0)?.text.toString()
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
return "" ""
} }
} }
@@ -99,23 +100,37 @@ object Utils {
* base64 decode * base64 decode
*/ */
fun decode(text: String): String { fun decode(text: String): String {
tryDecodeBase64(text)?.let { return it }
if (text.endsWith('=')) {
// try again for some loosely formatted base64
tryDecodeBase64(text.trimEnd('='))?.let { return it }
}
return ""
}
fun tryDecodeBase64(text: String): String? {
try { try {
return Base64.decode(text, Base64.NO_WRAP).toString(charset("UTF-8")) return Base64.decode(text, Base64.NO_WRAP).toString(charset("UTF-8"))
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() Log.i(AppConfig.ANG_PACKAGE, "Parse base64 standard failed $e")
return ""
} }
try {
return Base64.decode(text, Base64.NO_WRAP.or(Base64.URL_SAFE)).toString(charset("UTF-8"))
} catch (e: Exception) {
Log.i(AppConfig.ANG_PACKAGE, "Parse base64 url safe failed $e")
}
return null
} }
/** /**
* base64 encode * base64 encode
*/ */
fun encode(text: String): String { fun encode(text: String): String {
try { return try {
return Base64.encodeToString(text.toByteArray(charset("UTF-8")), Base64.NO_WRAP) Base64.encodeToString(text.toByteArray(charset("UTF-8")), Base64.NO_WRAP)
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
return "" ""
} }
} }
@@ -124,7 +139,7 @@ object Utils {
*/ */
fun getRemoteDnsServers(): List<String> { fun getRemoteDnsServers(): List<String> {
val remoteDns = settingsStorage?.decodeString(AppConfig.PREF_REMOTE_DNS) ?: AppConfig.DNS_AGENT val remoteDns = settingsStorage?.decodeString(AppConfig.PREF_REMOTE_DNS) ?: AppConfig.DNS_AGENT
val ret = remoteDns.split(",").filter { isPureIpAddress(it) || it.startsWith("https") } val ret = remoteDns.split(",").filter { isPureIpAddress(it) || isCoreDNSAddress(it) }
if (ret.isEmpty()) { if (ret.isEmpty()) {
return listOf(AppConfig.DNS_AGENT) return listOf(AppConfig.DNS_AGENT)
} }
@@ -144,7 +159,7 @@ object Utils {
*/ */
fun getDomesticDnsServers(): List<String> { fun getDomesticDnsServers(): List<String> {
val domesticDns = settingsStorage?.decodeString(AppConfig.PREF_DOMESTIC_DNS) ?: AppConfig.DNS_DIRECT val domesticDns = settingsStorage?.decodeString(AppConfig.PREF_DOMESTIC_DNS) ?: AppConfig.DNS_DIRECT
val ret = domesticDns.split(",").filter { isPureIpAddress(it) || it.startsWith("https") } val ret = domesticDns.split(",").filter { isPureIpAddress(it) || isCoreDNSAddress(it) }
if (ret.isEmpty()) { if (ret.isEmpty()) {
return listOf(AppConfig.DNS_DIRECT) return listOf(AppConfig.DNS_DIRECT)
} }
@@ -157,12 +172,12 @@ object Utils {
fun createQRCode(text: String, size: Int = 800): Bitmap? { fun createQRCode(text: String, size: Int = 800): Bitmap? {
try { try {
val hints = HashMap<EncodeHintType, String>() val hints = HashMap<EncodeHintType, String>()
hints.put(EncodeHintType.CHARACTER_SET, "utf-8") hints[EncodeHintType.CHARACTER_SET] = "utf-8"
val bitMatrix = QRCodeWriter().encode(text, val bitMatrix = QRCodeWriter().encode(text,
BarcodeFormat.QR_CODE, size, size, hints) BarcodeFormat.QR_CODE, size, size, hints)
val pixels = IntArray(size * size) val pixels = IntArray(size * size)
for (y in 0..size - 1) { for (y in 0 until size) {
for (x in 0..size - 1) { for (x in 0 until size) {
if (bitMatrix.get(x, y)) { if (bitMatrix.get(x, y)) {
pixels[y * size + x] = 0xff000000.toInt() pixels[y * size + x] = 0xff000000.toInt()
} else { } else {
@@ -207,7 +222,7 @@ object Utils {
} }
// addr = addr.toLowerCase() // addr = addr.toLowerCase()
var octets = addr.split('.').toTypedArray() val octets = addr.split('.').toTypedArray()
if (octets.size == 4) { if (octets.size == 4) {
if(octets[3].indexOf(":") > 0) { if(octets[3].indexOf(":") > 0) {
addr = addr.substring(0, addr.indexOf(":")) addr = addr.substring(0, addr.indexOf(":"))
@@ -242,6 +257,10 @@ object Utils {
return regV6.matches(addr) return regV6.matches(addr)
} }
private fun isCoreDNSAddress(s: String): Boolean {
return s.startsWith("https") || s.startsWith("tcp") || s.startsWith("quic")
}
/** /**
* is valid url * is valid url
*/ */
@@ -283,29 +302,29 @@ object Utils {
* uuid * uuid
*/ */
fun getUuid(): String { fun getUuid(): String {
try { return try {
return UUID.randomUUID().toString().replace("-", "") UUID.randomUUID().toString().replace("-", "")
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
return "" ""
} }
} }
fun urlDecode(url: String): String { fun urlDecode(url: String): String {
try { return try {
return URLDecoder.decode(url, "UTF-8") URLDecoder.decode(url, "UTF-8")
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
return url url
} }
} }
fun urlEncode(url: String): String { fun urlEncode(url: String): String {
try { return try {
return URLEncoder.encode(url, "UTF-8") URLEncoder.encode(url, "UTF-8")
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
return url url
} }
} }
@@ -382,7 +401,7 @@ object Utils {
val allText = process.inputStream.bufferedReader().use { it.readText() } val allText = process.inputStream.bufferedReader().use { it.readText() }
if (allText.isNotBlank()) { if (allText.isNotBlank()) {
val tempInfo = allText.substring(allText.indexOf("min/avg/max/mdev") + 19) val tempInfo = allText.substring(allText.indexOf("min/avg/max/mdev") + 19)
val temps = tempInfo.split("/".toRegex()).dropLastWhile({ it.isEmpty() }).toTypedArray() val temps = tempInfo.split("/".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
if (temps.count() > 0 && temps[0].length < 10) { if (temps.count() > 0 && temps[0].length < 10) {
return temps[0].toFloat().toInt().toString() + "ms" return temps[0].toFloat().toInt().toString() + "ms"
} }
@@ -442,5 +461,21 @@ object Utils {
tcpTestingSockets.clear() tcpTestingSockets.clear()
} }
} }
@Throws(IOException::class)
fun getUrlContentWithCustomUserAgent(urlStr: String?): String {
val url = URL(urlStr)
val conn = url.openConnection()
conn.setRequestProperty("Connection", "close")
conn.setRequestProperty("User-agent", "v2rayNG/${BuildConfig.VERSION_NAME}")
url.userInfo?.let {
conn.setRequestProperty("Authorization",
"Basic ${encode(urlDecode(it))}")
}
conn.useCaches = false
return conn.inputStream.use {
it.bufferedReader().readText()
}
}
} }

View File

@@ -6,6 +6,7 @@ import android.util.Log
import com.google.gson.* import com.google.gson.*
import com.tencent.mmkv.MMKV import com.tencent.mmkv.MMKV
import com.v2ray.ang.AppConfig import com.v2ray.ang.AppConfig
import com.v2ray.ang.AppConfig.ANG_PACKAGE
import com.v2ray.ang.dto.V2rayConfig import com.v2ray.ang.dto.V2rayConfig
import com.v2ray.ang.dto.EConfigType import com.v2ray.ang.dto.EConfigType
import com.v2ray.ang.dto.V2rayConfig.Companion.DEFAULT_NETWORK import com.v2ray.ang.dto.V2rayConfig.Companion.DEFAULT_NETWORK
@@ -30,12 +31,12 @@ object V2rayConfigUtil {
} else { } else {
raw raw
} }
Log.d("V2rayConfigUtilGoLog", customConfig) Log.d(ANG_PACKAGE, customConfig)
return Result(true, customConfig) return Result(true, customConfig)
} }
val outbound = config.getProxyOutbound() ?: return Result(false, "") val outbound = config.getProxyOutbound() ?: return Result(false, "")
val result = getV2rayNonCustomConfig(context, outbound) val result = getV2rayNonCustomConfig(context, outbound)
Log.d("V2rayConfigUtilGoLog", result.content) Log.d(ANG_PACKAGE, result.content)
return result return result
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
@@ -69,10 +70,10 @@ object V2rayConfigUtil {
fakedns(v2rayConfig) fakedns(v2rayConfig)
dns(v2rayConfig)
if (settingsStorage?.decodeBool(AppConfig.PREF_LOCAL_DNS_ENABLED) == true) { if (settingsStorage?.decodeBool(AppConfig.PREF_LOCAL_DNS_ENABLED) == true) {
customLocalDns(v2rayConfig) customLocalDns(v2rayConfig)
} else {
customRemoteDns(v2rayConfig)
} }
if (settingsStorage?.decodeBool(AppConfig.PREF_SPEED_ENABLED) != true) { if (settingsStorage?.decodeBool(AppConfig.PREF_SPEED_ENABLED) != true) {
v2rayConfig.stats = null v2rayConfig.stats = null
@@ -92,7 +93,7 @@ object V2rayConfigUtil {
//val httpPort = Utils.parseInt(settingsStorage?.decodeString(AppConfig.PREF_HTTP_PORT) ?: AppConfig.PORT_HTTP) //val httpPort = Utils.parseInt(settingsStorage?.decodeString(AppConfig.PREF_HTTP_PORT) ?: AppConfig.PORT_HTTP)
v2rayConfig.inbounds.forEach { curInbound -> v2rayConfig.inbounds.forEach { curInbound ->
if (!(settingsStorage?.decodeBool(AppConfig.PREF_PROXY_SHARING) ?: false)) { if (settingsStorage?.decodeBool(AppConfig.PREF_PROXY_SHARING) != true) {
//bind all inbounds to localhost if the user requests //bind all inbounds to localhost if the user requests
curInbound.listen = "127.0.0.1" curInbound.listen = "127.0.0.1"
} }
@@ -100,7 +101,7 @@ object V2rayConfigUtil {
v2rayConfig.inbounds[0].port = 10808 v2rayConfig.inbounds[0].port = 10808
val fakedns = settingsStorage?.decodeBool(AppConfig.PREF_FAKE_DNS_ENABLED) val fakedns = settingsStorage?.decodeBool(AppConfig.PREF_FAKE_DNS_ENABLED)
?: false ?: false
val sniffAllTlsAndHttp = settingsStorage?.decodeBool(AppConfig.PREF_SNIFFING_ENABLED) val sniffAllTlsAndHttp = settingsStorage?.decodeBool(AppConfig.PREF_SNIFFING_ENABLED, true)
?: true ?: true
v2rayConfig.inbounds[0].sniffing?.enabled = fakedns || sniffAllTlsAndHttp v2rayConfig.inbounds[0].sniffing?.enabled = fakedns || sniffAllTlsAndHttp
if (!sniffAllTlsAndHttp) { if (!sniffAllTlsAndHttp) {
@@ -127,7 +128,7 @@ object V2rayConfigUtil {
private fun fakedns(v2rayConfig: V2rayConfig) { private fun fakedns(v2rayConfig: V2rayConfig) {
if (settingsStorage?.decodeBool(AppConfig.PREF_FAKE_DNS_ENABLED) == true) { if (settingsStorage?.decodeBool(AppConfig.PREF_FAKE_DNS_ENABLED) == true) {
v2rayConfig.fakedns = V2rayConfig.FakednsBean() v2rayConfig.fakedns = listOf(V2rayConfig.FakednsBean())
v2rayConfig.outbounds.filter { it.protocol == "freedom" }.forEach { v2rayConfig.outbounds.filter { it.protocol == "freedom" }.forEach {
it.settings?.domainStrategy = "UseIP" it.settings?.domainStrategy = "UseIP"
} }
@@ -186,7 +187,7 @@ object V2rayConfigUtil {
val rulesIP = V2rayConfig.RoutingBean.RulesBean() val rulesIP = V2rayConfig.RoutingBean.RulesBean()
rulesIP.type = "field" rulesIP.type = "field"
rulesIP.outboundTag = tag rulesIP.outboundTag = tag
rulesIP.ip = ArrayList<String>() rulesIP.ip = ArrayList()
rulesIP.ip?.add("geoip:$code") rulesIP.ip?.add("geoip:$code")
v2rayConfig.routing.rules.add(rulesIP) v2rayConfig.routing.rules.add(rulesIP)
} }
@@ -196,7 +197,7 @@ object V2rayConfigUtil {
val rulesDomain = V2rayConfig.RoutingBean.RulesBean() val rulesDomain = V2rayConfig.RoutingBean.RulesBean()
rulesDomain.type = "field" rulesDomain.type = "field"
rulesDomain.outboundTag = tag rulesDomain.outboundTag = tag
rulesDomain.domain = ArrayList<String>() rulesDomain.domain = ArrayList()
rulesDomain.domain?.add("geosite:$code") rulesDomain.domain?.add("geosite:$code")
v2rayConfig.routing.rules.add(rulesDomain) v2rayConfig.routing.rules.add(rulesDomain)
} }
@@ -213,13 +214,13 @@ object V2rayConfigUtil {
val rulesDomain = V2rayConfig.RoutingBean.RulesBean() val rulesDomain = V2rayConfig.RoutingBean.RulesBean()
rulesDomain.type = "field" rulesDomain.type = "field"
rulesDomain.outboundTag = tag rulesDomain.outboundTag = tag
rulesDomain.domain = ArrayList<String>() rulesDomain.domain = ArrayList()
//IP //IP
val rulesIP = V2rayConfig.RoutingBean.RulesBean() val rulesIP = V2rayConfig.RoutingBean.RulesBean()
rulesIP.type = "field" rulesIP.type = "field"
rulesIP.outboundTag = tag rulesIP.outboundTag = tag
rulesIP.ip = ArrayList<String>() rulesIP.ip = ArrayList()
userRule.split(",").map { it.trim() }.forEach { userRule.split(",").map { it.trim() }.forEach {
if (Utils.isIpAddress(it) || it.startsWith("geoip:")) { if (Utils.isIpAddress(it) || it.startsWith("geoip:")) {
@@ -261,53 +262,22 @@ object V2rayConfigUtil {
*/ */
private fun customLocalDns(v2rayConfig: V2rayConfig): Boolean { private fun customLocalDns(v2rayConfig: V2rayConfig): Boolean {
try { try {
val hosts = mutableMapOf<String, String>()
val servers = ArrayList<Any>()
val remoteDns = Utils.getRemoteDnsServers()
val domesticDns = Utils.getDomesticDnsServers()
val geositeCn = arrayListOf("geosite:cn")
val geoipCn = arrayListOf("geoip:cn")
val proxyDomain = userRule2Domian(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_AGENT)
?: "")
val directDomain = userRule2Domian(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_DIRECT)
?: "")
if (settingsStorage?.decodeBool(AppConfig.PREF_FAKE_DNS_ENABLED) == true) { if (settingsStorage?.decodeBool(AppConfig.PREF_FAKE_DNS_ENABLED) == true) {
val geositeCn = arrayListOf("geosite:cn")
val proxyDomain = userRule2Domian(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_AGENT)
?: "")
val directDomain = userRule2Domian(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_DIRECT)
?: "")
// fakedns with all domains to make it always top priority // fakedns with all domains to make it always top priority
servers.add(V2rayConfig.DnsBean.ServersBean(address = "fakedns", domains = geositeCn.plus(proxyDomain).plus(directDomain))) v2rayConfig.dns.servers?.add(0,
V2rayConfig.DnsBean.ServersBean(address = "fakedns", domains = geositeCn.plus(proxyDomain).plus(directDomain)))
} }
remoteDns.forEach {
servers.add(it)
}
if (proxyDomain.size > 0) {
servers.add(V2rayConfig.DnsBean.ServersBean(remoteDns.first(), 53, proxyDomain, null))
}
if (directDomain.size > 0) {
servers.add(V2rayConfig.DnsBean.ServersBean(domesticDns.first(), 53, directDomain, geoipCn))
}
val routingMode = settingsStorage?.decodeString(AppConfig.PREF_ROUTING_MODE) ?: "0"
if (routingMode == "2" || routingMode == "3") {
servers.add(V2rayConfig.DnsBean.ServersBean(domesticDns.first(), 53, geositeCn, geoipCn))
}
val blkDomain = userRule2Domian(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_BLOCKED)
?: "")
if (blkDomain.size > 0) {
hosts.putAll(blkDomain.map { it to "127.0.0.1" })
}
// hardcode googleapi rule to fix play store problems
hosts.put("domain:googleapis.cn", "googleapis.com")
// DNS dns对象
v2rayConfig.dns = V2rayConfig.DnsBean(
servers = servers,
hosts = hosts)
// DNS inbound对象 // DNS inbound对象
val remoteDns = Utils.getRemoteDnsServers()
if (v2rayConfig.inbounds.none { e -> e.protocol == "dokodemo-door" && e.tag == "dns-in" }) { if (v2rayConfig.inbounds.none { e -> e.protocol == "dokodemo-door" && e.tag == "dns-in" }) {
val dnsInboundSettings = V2rayConfig.InboundBean.InSettingsBean( val dnsInboundSettings = V2rayConfig.InboundBean.InSettingsBean(
address = if (remoteDns.first().startsWith("https")) "1.1.1.1" else remoteDns.first(), address = if (Utils.isPureIpAddress(remoteDns.first())) remoteDns.first() else "1.1.1.1",
port = 53, port = 53,
network = "tcp,udp") network = "tcp,udp")
@@ -333,17 +303,76 @@ object V2rayConfigUtil {
mux = null)) mux = null))
} }
// DNS routing // DNS routing tag
if (!domesticDns.first().startsWith("https")) { v2rayConfig.routing.rules.add(0, V2rayConfig.RoutingBean.RulesBean(
v2rayConfig.routing.rules.add(0, V2rayConfig.RoutingBean.RulesBean( type = "field",
type = "field", inboundTag = arrayListOf("dns-in"),
outboundTag = AppConfig.TAG_DIRECT, outboundTag = "dns-out",
port = "53", domain = null)
ip = arrayListOf(domesticDns.first()), )
domain = null) } catch (e: Exception) {
) e.printStackTrace()
return false
}
return true
}
private fun dns(v2rayConfig: V2rayConfig): Boolean {
try {
val hosts = mutableMapOf<String, String>()
val servers = ArrayList<Any>()
val remoteDns = Utils.getRemoteDnsServers()
val proxyDomain = userRule2Domian(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_AGENT)
?: "")
remoteDns.forEach {
servers.add(it)
} }
if (!remoteDns.first().startsWith("https")) { if (proxyDomain.size > 0) {
servers.add(V2rayConfig.DnsBean.ServersBean(remoteDns.first(), 53, proxyDomain, null))
}
// domestic DNS
val directDomain = userRule2Domian(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_DIRECT)
?: "")
val routingMode = settingsStorage?.decodeString(AppConfig.PREF_ROUTING_MODE) ?: "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 == "2" || routingMode == "3") {
servers.add(V2rayConfig.DnsBean.ServersBean(domesticDns.first(), 53, geositeCn, geoipCn))
}
if (Utils.isPureIpAddress(domesticDns.first())) {
v2rayConfig.routing.rules.add(0, V2rayConfig.RoutingBean.RulesBean(
type = "field",
outboundTag = AppConfig.TAG_DIRECT,
port = "53",
ip = arrayListOf(domesticDns.first()),
domain = null)
)
}
}
val blkDomain = userRule2Domian(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_BLOCKED)
?: "")
if (blkDomain.size > 0) {
hosts.putAll(blkDomain.map { it to "127.0.0.1" })
}
// hardcode googleapi rule to fix play store problems
hosts["domain:googleapis.cn"] = "googleapis.com"
// DNS dns对象
v2rayConfig.dns = V2rayConfig.DnsBean(
servers = servers,
hosts = hosts)
// DNS routing
if (Utils.isPureIpAddress(remoteDns.first())) {
v2rayConfig.routing.rules.add(0, V2rayConfig.RoutingBean.RulesBean( v2rayConfig.routing.rules.add(0, V2rayConfig.RoutingBean.RulesBean(
type = "field", type = "field",
outboundTag = AppConfig.TAG_AGENT, outboundTag = AppConfig.TAG_AGENT,
@@ -352,37 +381,6 @@ object V2rayConfigUtil {
domain = null) domain = null)
) )
} }
// DNS routing tag
v2rayConfig.routing.rules.add(0, V2rayConfig.RoutingBean.RulesBean(
type = "field",
inboundTag = arrayListOf("dns-in"),
outboundTag = "dns-out",
domain = null)
)
} catch (e: Exception) {
e.printStackTrace()
return false
}
return true
}
/**
* Custom Remote Dns
*/
private fun customRemoteDns(v2rayConfig: V2rayConfig): Boolean {
try {
val servers = ArrayList<Any>()
val hosts = mutableMapOf<String, String>()
Utils.getRemoteDnsServers().forEach {
servers.add(it)
}
// hardcode googleapi rule to fix play store problems
hosts.put("domain:googleapis.cn", "googleapis.com")
v2rayConfig.dns = V2rayConfig.DnsBean(servers = servers, hosts = hosts)
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
return false return false
@@ -395,14 +393,22 @@ object V2rayConfigUtil {
if (outbound.streamSettings?.network == DEFAULT_NETWORK if (outbound.streamSettings?.network == DEFAULT_NETWORK
&& outbound.streamSettings?.tcpSettings?.header?.type == HTTP) { && outbound.streamSettings?.tcpSettings?.header?.type == HTTP) {
val path = outbound.streamSettings?.tcpSettings?.header?.request?.path val path = outbound.streamSettings?.tcpSettings?.header?.request?.path
val Host = outbound.streamSettings?.tcpSettings?.header?.request?.headers?.Host val host = outbound.streamSettings?.tcpSettings?.header?.request?.headers?.Host
val requestString: String by lazy { val requestString: String by lazy {
"""{"version":"1.1","method":"GET","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36","Mozilla/5.0 (iPhone; CPU iPhone OS 10_0_2 like Mac OS X) AppleWebKit/601.1 (KHTML, like Gecko) CriOS/53.0.2785.109 Mobile/14A456 Safari/601.1.46"],"Accept-Encoding":["gzip, deflate"],"Connection":["keep-alive"],"Pragma":"no-cache"}}""" """{"version":"1.1","method":"GET","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36","Mozilla/5.0 (iPhone; CPU iPhone OS 10_0_2 like Mac OS X) AppleWebKit/601.1 (KHTML, like Gecko) CriOS/53.0.2785.109 Mobile/14A456 Safari/601.1.46"],"Accept-Encoding":["gzip, deflate"],"Connection":["keep-alive"],"Pragma":"no-cache"}}"""
} }
outbound.streamSettings?.tcpSettings?.header?.request = Gson().fromJson(requestString, V2rayConfig.OutboundBean.StreamSettingsBean.TcpSettingsBean.HeaderBean.RequestBean::class.java) outbound.streamSettings?.tcpSettings?.header?.request = Gson().fromJson(
outbound.streamSettings?.tcpSettings?.header?.request?.path = path!! requestString,
outbound.streamSettings?.tcpSettings?.header?.request?.headers?.Host = Host!! V2rayConfig.OutboundBean.StreamSettingsBean.TcpSettingsBean.HeaderBean.RequestBean::class.java
)
outbound.streamSettings?.tcpSettings?.header?.request?.path =
if (path.isNullOrEmpty()) {
listOf("/")
} else {
path
}
outbound.streamSettings?.tcpSettings?.header?.request?.headers?.Host = host!!
} }
} catch (e: Exception) { } catch (e: Exception) {

View File

@@ -1,8 +1,8 @@
package com.v2ray.ang.viewmodel package com.v2ray.ang.viewmodel
import android.app.Application import android.app.Application
import android.arch.lifecycle.AndroidViewModel import androidx.lifecycle.AndroidViewModel
import android.arch.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import android.content.BroadcastReceiver import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
@@ -95,7 +95,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
getApplication<AngApplication>().toast(R.string.connection_test_testing) getApplication<AngApplication>().toast(R.string.connection_test_testing)
for (guid in serverList) { for (guid in serverList) {
serversCache.getOrElse(guid, { MmkvManager.decodeServerConfig(guid) })?.getProxyOutbound()?.let { outbound -> serversCache.getOrElse(guid) { MmkvManager.decodeServerConfig(guid) }?.getProxyOutbound()?.let { outbound ->
val serverAddress = outbound.getServerAddress() val serverAddress = outbound.getServerAddress()
val serverPort = outbound.getServerPort() val serverPort = outbound.getServerPort()
if (serverAddress != null && serverPort != null) { if (serverAddress != null && serverPort != null) {

View File

@@ -1,9 +1,9 @@
package com.v2ray.ang.viewmodel package com.v2ray.ang.viewmodel
import android.app.Application import android.app.Application
import android.arch.lifecycle.AndroidViewModel import androidx.lifecycle.AndroidViewModel
import android.content.SharedPreferences import android.content.SharedPreferences
import android.support.v7.preference.PreferenceManager import androidx.preference.PreferenceManager
import android.util.Log import android.util.Log
import com.tencent.mmkv.MMKV import com.tencent.mmkv.MMKV
import com.v2ray.ang.AppConfig import com.v2ray.ang.AppConfig
@@ -46,7 +46,7 @@ class SettingsViewModel(application: Application) : AndroidViewModel(application
AppConfig.PREF_BYPASS_APPS, -> { AppConfig.PREF_BYPASS_APPS, -> {
settingsStorage?.encode(key, sharedPreferences.getBoolean(key, false)) settingsStorage?.encode(key, sharedPreferences.getBoolean(key, false))
} }
AppConfig.PREF_SNIFFING_ENABLED, -> { AppConfig.PREF_SNIFFING_ENABLED -> {
settingsStorage?.encode(key, sharedPreferences.getBoolean(key, true)) settingsStorage?.encode(key, sharedPreferences.getBoolean(key, true))
} }
AppConfig.PREF_PER_APP_PROXY_SET -> { AppConfig.PREF_PER_APP_PROXY_SET -> {

View File

@@ -13,17 +13,17 @@
android:layout_centerHorizontal="true" android:layout_centerHorizontal="true"
android:layout_centerVertical="true" /> android:layout_centerVertical="true" />
<android.support.v7.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view" android:id="@+id/recycler_view"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_alignParentEnd="true" android:layout_alignParentEnd="true"
android:layout_alignParentRight="true" android:layout_alignParentRight="true"
android:layout_alignParentTop="true" android:layout_alignParentTop="true"
app:layoutManager="android.support.v7.widget.LinearLayoutManager" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:context=".ui.PerAppProxyActivity" /> tools:context=".ui.PerAppProxyActivity" />
<android.support.v7.widget.LinearLayoutCompat <androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/header_view" android:id="@+id/header_view"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@@ -37,7 +37,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="@dimen/bypass_list_header_height"> android:layout_height="@dimen/bypass_list_header_height">
<android.support.v7.widget.SwitchCompat <androidx.appcompat.widget.SwitchCompat
android:id="@+id/switch_per_app_proxy" android:id="@+id/switch_per_app_proxy"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@@ -45,7 +45,7 @@
android:layout_alignParentRight="true" android:layout_alignParentRight="true"
android:layout_centerVertical="true" /> android:layout_centerVertical="true" />
<android.support.v7.widget.AppCompatTextView <androidx.appcompat.widget.AppCompatTextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentLeft="true" android:layout_alignParentLeft="true"
@@ -63,7 +63,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="@dimen/bypass_list_header_height"> android:layout_height="@dimen/bypass_list_header_height">
<android.support.v7.widget.SwitchCompat <androidx.appcompat.widget.SwitchCompat
android:id="@+id/switch_bypass_apps" android:id="@+id/switch_bypass_apps"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@@ -71,7 +71,7 @@
android:layout_alignParentRight="true" android:layout_alignParentRight="true"
android:layout_centerVertical="true" /> android:layout_centerVertical="true" />
<android.support.v7.widget.AppCompatTextView <androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_bypass_apps" android:id="@+id/tv_bypass_apps"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@@ -89,11 +89,11 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="@dimen/bypass_list_header_height"> android:layout_height="@dimen/bypass_list_header_height">
<android.support.design.widget.TextInputLayout <com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
<android.support.v7.widget.AppCompatEditText <androidx.appcompat.widget.AppCompatEditText
android:id="@+id/et_search" android:id="@+id/et_search"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@@ -103,11 +103,11 @@
android:maxLines="1" android:maxLines="1"
android:singleLine="true" android:singleLine="true"
android:textAppearance="@style/TextAppearance.AppCompat.Small" /> android:textAppearance="@style/TextAppearance.AppCompat.Small" />
</android.support.design.widget.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
</RelativeLayout> </RelativeLayout>
</android.support.v7.widget.LinearLayoutCompat> </androidx.appcompat.widget.LinearLayoutCompat>
</RelativeLayout> </RelativeLayout>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.drawerlayout.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/drawer_layout" android:id="@+id/drawer_layout"
@@ -12,19 +12,19 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical"> android:orientation="vertical">
<android.support.design.widget.AppBarLayout <com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay"> android:theme="@style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar <androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar" android:id="@+id/toolbar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize" android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary" android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay" /> app:popupTheme="@style/AppTheme.PopupOverlay" />
</android.support.design.widget.AppBarLayout> </com.google.android.material.appbar.AppBarLayout>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
@@ -34,7 +34,7 @@
android:fitsSystemWindows="true" android:fitsSystemWindows="true"
tools:context=".ui.MainActivity"> tools:context=".ui.MainActivity">
<android.support.design.widget.CoordinatorLayout <androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/main_content" android:id="@+id/main_content"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
@@ -44,7 +44,7 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical"> android:orientation="vertical">
<android.support.v7.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view" android:id="@+id/recycler_view"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"
@@ -83,7 +83,7 @@
android:layout_marginBottom="24dp" android:layout_marginBottom="24dp"
android:layout_gravity="bottom|end"> android:layout_gravity="bottom|end">
<android.support.design.widget.FloatingActionButton <com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab" android:id="@+id/fab"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@@ -101,11 +101,11 @@
android:nextFocusLeft="@+id/recycler_view" /> android:nextFocusLeft="@+id/recycler_view" />
</com.github.jorgecastilloprz.FABProgressCircle> </com.github.jorgecastilloprz.FABProgressCircle>
</android.support.design.widget.CoordinatorLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>
</RelativeLayout> </RelativeLayout>
</LinearLayout> </LinearLayout>
<android.support.design.widget.NavigationView <com.google.android.material.navigation.NavigationView
android:id="@+id/nav_view" android:id="@+id/nav_view"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="match_parent" android:layout_height="match_parent"
@@ -129,7 +129,7 @@
android:gravity="center" android:gravity="center"
android:textColor="@color/accent" /> android:textColor="@color/accent" />
</LinearLayout> </LinearLayout>
</android.support.design.widget.NavigationView> </com.google.android.material.navigation.NavigationView>
</android.support.v4.widget.DrawerLayout> </androidx.drawerlayout.widget.DrawerLayout>

View File

@@ -4,14 +4,14 @@
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"> android:orientation="vertical">
<android.support.design.widget.TabLayout <com.google.android.material.tabs.TabLayout
android:id="@+id/tablayout" android:id="@+id/tablayout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" /> android:layout_height="wrap_content" />
<android.support.v4.view.ViewPager <androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewpager" android:id="@+id/viewpager"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" /> android:layout_height="match_parent" />
</LinearLayout> </LinearLayout>

View File

@@ -8,14 +8,13 @@
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" android:orientation="vertical">
android:padding="@dimen/layout_margin_top_height">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" android:orientation="vertical"
android:paddingBottom="@dimen/layout_margin_top_height"> android:layout_margin="@dimen/layout_margin_spacing">
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
@@ -28,7 +27,8 @@
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"> android:orientation="vertical"
android:layout_margin="@dimen/layout_margin_spacing">
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
@@ -40,7 +40,7 @@
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_margin_top_height" android:layout_margin="@dimen/layout_margin_spacing"
android:orientation="vertical"> android:orientation="vertical">
<TextView <TextView
@@ -59,24 +59,24 @@
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_margin_top_height"
android:orientation="vertical"> android:orientation="vertical">
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="@dimen/layout_margin_spacing"
android:text="@string/server_lab_content" android:text="@string/server_lab_content"
android:textAppearance="@style/TextAppearance.AppCompat.Subhead" /> android:textAppearance="@style/TextAppearance.AppCompat.Subhead" />
<EditText <com.blacksquircle.ui.editorkit.widget.TextProcessor
android:id="@+id/tv_content" android:layout_width="match_parent"
android:layout_width="match_parent" android:layout_height="match_parent"
android:layout_height="match_parent" android:gravity="top|start"
android:layout_marginTop="@dimen/layout_margin_top_height" android:id="@+id/editor"
android:text="" /> android:layout_marginTop="@dimen/layout_margin_top_height" />
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
</ScrollView> </ScrollView>

View File

@@ -98,26 +98,6 @@
</LinearLayout> </LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_margin_top_height"
android:orientation="vertical">
<TextView
android:id="@+id/tv_alterId"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/server_lab_alterid" />
<EditText
android:id="@+id/et_alterId"
android:layout_width="match_parent"
android:layout_height="@dimen/edit_height"
android:inputType="number" />
</LinearLayout>
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"

View File

@@ -8,15 +8,15 @@
android:fitsSystemWindows="true" android:fitsSystemWindows="true"
tools:context=".ui.MainActivity"> tools:context=".ui.MainActivity">
<android.support.design.widget.CoordinatorLayout <androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/main_content" android:id="@+id/main_content"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view" android:id="@+id/recycler_view"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" /> android:layout_height="match_parent" />
</android.support.design.widget.CoordinatorLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>
</RelativeLayout> </RelativeLayout>

View File

@@ -10,7 +10,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="@dimen/bypass_list_header_height"> android:layout_height="@dimen/bypass_list_header_height">
<android.support.v7.widget.SwitchCompat <androidx.appcompat.widget.SwitchCompat
android:id="@+id/switch_start_service" android:id="@+id/switch_start_service"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@@ -19,7 +19,7 @@
android:layout_centerVertical="true" android:layout_centerVertical="true"
android:checked="true" /> android:checked="true" />
<android.support.v7.widget.AppCompatTextView <androidx.appcompat.widget.AppCompatTextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentLeft="true" android:layout_alignParentLeft="true"
@@ -45,4 +45,4 @@
android:choiceMode="singleChoice" /> android:choiceMode="singleChoice" />
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>

View File

@@ -7,7 +7,7 @@
android:clickable="true" android:clickable="true"
android:focusable="true"> android:focusable="true">
<android.support.v7.widget.AppCompatImageView <androidx.appcompat.widget.AppCompatImageView
android:id="@+id/icon" android:id="@+id/icon"
android:layout_width="46dp" android:layout_width="46dp"
android:layout_height="46dp" android:layout_height="46dp"
@@ -25,12 +25,12 @@
android:paddingStart="@dimen/layout_margin_right_height" android:paddingStart="@dimen/layout_margin_right_height"
android:paddingEnd="@dimen/layout_margin_right_height"> android:paddingEnd="@dimen/layout_margin_right_height">
<android.support.v7.widget.AppCompatTextView <androidx.appcompat.widget.AppCompatTextView
android:id="@+id/name" android:id="@+id/name"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" /> android:layout_height="wrap_content" />
<android.support.v7.widget.AppCompatTextView <androidx.appcompat.widget.AppCompatTextView
android:id="@+id/package_name" android:id="@+id/package_name"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@@ -38,7 +38,7 @@
</LinearLayout> </LinearLayout>
<android.support.v7.widget.AppCompatCheckBox <androidx.appcompat.widget.AppCompatCheckBox
android:id="@+id/check_box" android:id="@+id/check_box"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@@ -49,4 +49,4 @@
android:paddingEnd="6dp" android:paddingEnd="6dp"
android:paddingRight="6dp" /> android:paddingRight="6dp" />
</LinearLayout> </LinearLayout>

View File

@@ -7,7 +7,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center_vertical"> android:gravity="center_vertical">
<android.support.v7.widget.CardView <androidx.cardview.widget.CardView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="3dp" android:layout_margin="3dp"
@@ -24,7 +24,7 @@
android:focusable="true" android:focusable="true"
android:nextFocusRight="@+id/layout_share"> android:nextFocusRight="@+id/layout_share">
<android.support.v7.widget.AppCompatRadioButton <androidx.appcompat.widget.AppCompatRadioButton
android:id="@+id/btn_radio" android:id="@+id/btn_radio"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@@ -182,5 +182,5 @@
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
</android.support.v7.widget.CardView> </androidx.cardview.widget.CardView>
</LinearLayout> </LinearLayout>

View File

@@ -5,7 +5,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center_vertical"> android:gravity="center_vertical">
<android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto" <androidx.cardview.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:id="@+id/item_cardview" android:id="@+id/item_cardview"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@@ -102,5 +102,5 @@
</LinearLayout> </LinearLayout>
</android.support.v7.widget.CardView> </androidx.cardview.widget.CardView>
</LinearLayout> </LinearLayout>

View File

@@ -1,15 +1,15 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.AppBarLayout xmlns:android="http://schemas.android.com/apk/res/android" <com.google.android.material.appbar.AppBarLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="?android:attr/actionBarSize" android:layout_height="?android:attr/actionBarSize"
android:theme="@style/AppTheme.AppBarOverlay"> android:theme="@style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar <androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar" android:id="@+id/toolbar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
app:layout_scrollFlags="scroll|enterAlways" app:layout_scrollFlags="scroll|enterAlways"
app:popupTheme="@style/AppTheme.PopupOverlay" /> app:popupTheme="@style/AppTheme.PopupOverlay" />
</android.support.design.widget.AppBarLayout> </com.google.android.material.appbar.AppBarLayout>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.NavigationView xmlns:android="http://schemas.android.com/apk/res/android" <com.google.android.material.navigation.NavigationView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/nav_view" android:id="@+id/nav_view"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@@ -8,4 +8,4 @@
app:headerLayout="@layout/nav_header" app:headerLayout="@layout/nav_header"
app:itemTextColor="@color/colorPrimary" app:itemTextColor="@color/colorPrimary"
app:itemIconTint="@color/colorPrimary_dark" app:itemIconTint="@color/colorPrimary_dark"
app:menu="@menu/menu_drawer" /> app:menu="@menu/menu_drawer" />

View File

@@ -1,9 +1,11 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Button xmlns:android="http://schemas.android.com/apk/res/android" <Button xmlns:android="http://schemas.android.com/apk/res/android"
style="@style/Widget.AppCompat.Button.Borderless" xmlns:tools="http://schemas.android.com/tools"
style="@style/Widget.AppCompat.Button.Borderless"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:onClick="onModeHelpClicked" android:onClick="onModeHelpClicked"
android:text="@string/title_mode_help" android:text="@string/title_mode_help"
android:textAlignment="textStart" android:textAlignment="textStart"
android:textStyle="italic" /> android:textStyle="italic"
tools:ignore="UsingOnClickInXml" />

View File

@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="summary_pref_per_app_proxy">Set proxy for selected apps</string>
</resources>

View File

@@ -1,8 +0,0 @@
<resources>
<style name="AppTheme.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="android:statusBarColor">@android:color/transparent</item>
</style>
</resources>

View File

@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="summary_pref_per_app_proxy">为应用程序分别设置代理</string>
</resources>

View File

@@ -38,7 +38,6 @@
<string name="server_lab_address">地址(address)</string> <string name="server_lab_address">地址(address)</string>
<string name="server_lab_port">端口(port)</string> <string name="server_lab_port">端口(port)</string>
<string name="server_lab_id">用户ID(id)</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_security">加密方式(security)</string>
<string name="server_lab_network">传输协议(network)</string> <string name="server_lab_network">传输协议(network)</string>
<string name="server_lab_more_function">功能设置(不清楚则保持默认值)</string> <string name="server_lab_more_function">功能设置(不清楚则保持默认值)</string>
@@ -79,7 +78,7 @@
<string name="title_advanced">进阶设置</string> <string name="title_advanced">进阶设置</string>
<string name="title_vpn_settings">VPN 设置</string> <string name="title_vpn_settings">VPN 设置</string>
<string name="title_pref_per_app_proxy">分应用代理</string> <string name="title_pref_per_app_proxy">分应用代理</string>
<string name="summary_pref_per_app_proxy">分应用代理仅支持 Android 5.0 Lollipop 及更高</string> <string name="summary_pref_per_app_proxy">常规:勾选的App被代理,未勾选的直连;\n绕行模式:勾选的App直连,未勾选的被代理.\n不明白者在菜单中选择自动选中需代理应用</string>
<string name="title_pref_mux_enabled">启用Mux多路复用</string> <string name="title_pref_mux_enabled">启用Mux多路复用</string>
<string name="summary_pref_mux_enabled">开启可能会加速,关闭可能会减少断流</string> <string name="summary_pref_mux_enabled">开启可能会加速,关闭可能会减少断流</string>

View File

@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="summary_pref_per_app_proxy">為選擇的應用程式設定 Proxy</string>
</resources>

View File

@@ -1,13 +1,13 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<string name="app_name">v2rayNG</string> <string name="app_name">v2rayNG</string>
<string name="app_widget_name">切換</string> <string name="app_widget_name">開關</string>
<string name="app_tile_name">切換</string> <string name="app_tile_name">開關</string>
<string name="app_tile_first_use">首次使用此功能,請使用此應用程式新增組態</string> <string name="app_tile_first_use">首次使用此功能,請使用此應用程式新增組態</string>
<string name="navigation_drawer_open">Open navigation drawer</string> <string name="navigation_drawer_open">Open navigation drawer</string>
<string name="navigation_drawer_close">Close navigation drawer</string> <string name="navigation_drawer_close">Close navigation drawer</string>
<string name="migration_success">數據遷移成功!</string> <string name="migration_success">資料遷移成功</string>
<string name="migration_fail">數據遷移失敗啦!</string> <string name="migration_fail">資料遷移失敗</string>
<!-- Notifications --> <!-- Notifications -->
<string name="notification_action_stop_v2ray">停止</string> <string name="notification_action_stop_v2ray">停止</string>
@@ -23,11 +23,11 @@
<string name="menu_item_add_config">新增組態</string> <string name="menu_item_add_config">新增組態</string>
<string name="menu_item_save_config">儲存組態</string> <string name="menu_item_save_config">儲存組態</string>
<string name="menu_item_del_config">刪除組態</string> <string name="menu_item_del_config">刪除組態</string>
<string name="menu_item_import_config_qrcode">自 QR 匯入組態</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_clipboard">自剪貼簿匯入組態</string>
<string name="menu_item_import_config_manually_vmess">手動鍵入[Vmess]</string> <string name="menu_item_import_config_manually_vmess">手動鍵入 [Vmess]</string>
<string name="menu_item_import_config_manually_ss">手動鍵入[Shadowsocks]</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_socks">手動鍵入 [Socks]</string>
<string name="menu_item_import_config_custom">自訂組態</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_clipboard">自剪貼簿匯入自訂組態</string>
<string name="menu_item_import_config_custom_local">自本機匯入自訂組態</string> <string name="menu_item_import_config_custom_local">自本機匯入自訂組態</string>
@@ -37,17 +37,16 @@
<string name="server_lab_remarks">備註</string> <string name="server_lab_remarks">備註</string>
<string name="server_lab_address">位址</string> <string name="server_lab_address">位址</string>
<string name="server_lab_port"></string> <string name="server_lab_port"></string>
<string name="server_lab_id">使用者識別碼</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_security">安全性</string>
<string name="server_lab_network">網路</string> <string name="server_lab_network">網路</string>
<string name="server_lab_more_function">更多功能</string> <string name="server_lab_more_function">更多功能</string>
<string name="server_lab_head_type">標頭類型</string> <string name="server_lab_head_type">標頭類型</string>
<string name="server_lab_mode_type">gRPC 傳輸模式 (mode)</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_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_path">path(ws path/h2 path)/QUIC 加密鑰/kcp seed/gRPC serviceName</string>
<string name="server_lab_stream_security">傳輸層安全性(tls)</string> <string name="server_lab_stream_security">傳輸層安全性 (tls)</string>
<string name="server_lab_allow_insecure">跳過證驗證(allowInsecure)</string> <string name="server_lab_allow_insecure">跳過證驗證 (allowInsecure)</string>
<string name="server_lab_address3">伺服器位址</string> <string name="server_lab_address3">伺服器位址</string>
<string name="server_lab_port3">伺服器埠</string> <string name="server_lab_port3">伺服器埠</string>
<string name="server_lab_id3">密碼</string> <string name="server_lab_id3">密碼</string>
@@ -64,121 +63,121 @@
<string name="server_lab_content">內容</string> <string name="server_lab_content">內容</string>
<string name="toast_none_data_clipboard">剪貼簿內無資料</string> <string name="toast_none_data_clipboard">剪貼簿內無資料</string>
<string name="toast_invalid_url">網址無效</string> <string name="toast_invalid_url">網址無效</string>
<string name="server_lab_need_inbound">確保inboundsocks=10808,http=10809</string> <string name="server_lab_need_inbound">​​確保 inboundsocks=10808,http=10809</string>
<!-- PerAppProxyActivity --> <!-- PerAppProxyActivity -->
<string name="msg_dialog_progress">載入</string> <string name="msg_dialog_progress">載入</string>
<string name="menu_item_select_all">全選</string> <string name="menu_item_select_all">全選</string>
<string name="msg_enter_keywords">輸入關鍵字</string> <string name="msg_enter_keywords">輸入關鍵字</string>
<string name="switch_bypass_apps_mode">略過模式</string> <string name="switch_bypass_apps_mode">略過模式</string>
<string name="menu_item_select_proxy_app">自動選中需代理應用</string> <string name="menu_item_select_proxy_app">自動選中需要 Proxy 的應用</string>
<string name="msg_downloading_content">正在下載內容</string> <string name="msg_downloading_content">正在下載內容</string>
<!-- Preferences --> <!-- Preferences -->
<string name="title_settings">設定</string> <string name="title_settings">設定</string>
<string name="title_advanced">進階設定</string> <string name="title_advanced">進階</string>
<string name="title_vpn_settings">VPN 設定</string> <string name="title_vpn_settings">VPN 設定</string>
<string name="title_pref_per_app_proxy">Proxy 個別應用程式</string> <string name="title_pref_per_app_proxy">Proxy 個別應用程式</string>
<string name="summary_pref_per_app_proxy">Proxy 個別應用程式模式只支援 Android 5.0 (Lollipop) 或更高</string> <string name="summary_pref_per_app_proxy">常規:勾選的App被代理,未勾選的直連;\n繞行模式:勾選的App直連,未勾選的被代理.\n不明白者在菜單中選擇自動選中需代理應用</string>
<string name="title_pref_mux_enabled">啟用 Mux</string> <string name="title_pref_mux_enabled">啟用 Mux</string>
<string name="summary_pref_mux_enabled">啟用或許會加快網路速度,切換或許會閃爍</string> <string name="summary_pref_mux_enabled">啟用或許會加快網路速度,關閉或許會閃爍</string>
<string name="title_pref_speed_enabled">啟用速度顯示</string> <string name="title_pref_speed_enabled">啟用速度顯示</string>
<string name="summary_pref_speed_enabled">在通知中顯示當前速度\n小圖顯示流量的路由情況</string> <string name="summary_pref_speed_enabled">在通知中顯示當前速度\n小圖顯示流量的轉送情況</string>
<string name="title_pref_sniffing_enabled">啟用流量探</string> <string name="title_pref_sniffing_enabled">啟用網路監</string>
<string name="summary_pref_sniffing_enabled">流量中探測域名 (默認啟用)</string> <string name="summary_pref_sniffing_enabled">封包中監測網域 (預設啟用)</string>
<string name="title_pref_local_dns_enabled">啟用本地DNS</string> <string name="title_pref_local_dns_enabled">啟用本地 DNS</string>
<string name="summary_pref_local_dns_enabled">DNS 請求入 core 由 DNS 模塊處理(推薦啟用 如果需要路由略過局域網及中國大陸</string> <string name="summary_pref_local_dns_enabled">DNS 請求入 core 由 DNS 模塊處理 (建議啟用,可能需要轉送略過局域網及中國大陸)</string>
<string name="title_pref_fake_dns_enabled">啟用虛擬DNS</string> <string name="title_pref_fake_dns_enabled">啟用DNS</string>
<string name="summary_pref_fake_dns_enabled">本地返回虛構解析結果 (減低延時 但個別應用可能無法使用)</string> <string name="summary_pref_fake_dns_enabled">本地返回偽造解析結果 (減低延時但個別應用可能無法使用)</string>
<string name="title_pref_forward_ipv6">IPv6 路由</string> <string name="title_pref_forward_ipv6">IPv6 路由</string>
<string name="summary_pref_forward_ipv6">向遠端重新導向 IPv6 流量測試</string> <string name="summary_pref_forward_ipv6">向遠端重新導向 IPv6 流量 (測試)</string>
<string name="title_pref_routing">路由設置</string> <string name="title_pref_routing">轉送設定</string>
<string name="title_pref_routing_domain_strategy">策略</string> <string name="title_pref_routing_domain_strategy">域策略</string>
<string name="title_pref_routing_mode">路由模式</string> <string name="title_pref_routing_mode">轉送模式</string>
<string name="title_pref_routing_custom">自訂路由</string> <string name="title_pref_routing_custom">自訂轉送</string>
<string name="title_pref_donate"></string> <string name="title_pref_donate"></string>
<string name="summary_pref_donate">向開發人員捐</string> <string name="summary_pref_donate">向開發人員捐</string>
<string name="donate_detail">新增一些實驗性進階功能</string> <string name="donate_detail">新增一些實驗性進階功能</string>
<string name="title_pref_remote_dns">遠端DNS (可選)</string> <string name="title_pref_remote_dns">遠端 DNS (可選)</string>
<string name="summary_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_vpn_dns">VPN DNS (僅支 IPv4/v6)</string>
<string name="title_pref_domestic_dns">境内DNS (可選)</string> <string name="title_pref_domestic_dns">境内 DNS (可選)</string>
<string name="summary_pref_domestic_dns">DNS</string> <string name="summary_pref_domestic_dns">DNS</string>
<string name="title_pref_socks_port">SOCKS5代理連接</string> <string name="title_pref_socks_port">SOCKS5 Proxy </string>
<string name="summary_pref_socks_port">SOCKS5代理連接</string> <string name="summary_pref_socks_port">SOCKS5 Proxy </string>
<string name="title_pref_http_port">HTTP代理連接</string> <string name="title_pref_http_port">HTTP Proxy </string>
<string name="summary_pref_http_port">HTTP代理連接</string> <string name="summary_pref_http_port">HTTP Proxy </string>
<string name="title_pref_local_dns_port">本地DNS端口</string> <string name="title_pref_local_dns_port">本地 DNS</string>
<string name="summary_pref_local_dns_port">本地DNS端口</string> <string name="summary_pref_local_dns_port">本地 DNS</string>
<string name="title_pref_feedback">回饋</string> <string name="title_pref_feedback">意見回饋</string>
<string name="summary_pref_feedback">回饋提升或前往 GitHub 回報 Bug</string> <string name="summary_pref_feedback">前往 GitHub 回報錯誤</string>
<string name="summary_pref_tg_group">加入Telegram Group</string> <string name="summary_pref_tg_group">加入 Telegram 群組</string>
<string name="toast_tg_app_not_found">未找到Telegram app</string> <string name="toast_tg_app_not_found">未找到 Telegram 應用程式</string>
<string name="title_pref_promotion">推廣</string> <string name="title_pref_promotion">推廣</string>
<string name="summary_pref_promotion">一些推廣,點擊查看詳情(捐可去除)</string> <string name="summary_pref_promotion">一些推廣,輕觸以檢視 (捐可去除)</string>
<string name="title_mode">模式</string> <string name="title_mode">模式</string>
<string name="title_mode_help">點此查看更多幫助</string> <string name="title_mode_help">點此查看更多幫助</string>
<string name="donate_error_setup">錯誤設定:</string> <string name="donate_error_setup">錯誤設定</string>
<string name="donate_error_inventory">Error querying inventory</string> <string name="donate_error_inventory">未查詢到庫存</string>
<string name="donate_error_purchase1">購買錯誤:</string> <string name="donate_error_purchase1">購買錯誤</string>
<string name="donate_error_purchase2">購買錯誤,真實性驗證失敗。</string> <string name="donate_error_purchase2">購買錯誤,真實性驗證失敗。</string>
<string name="donate_success_purchase">感謝您的捐</string> <string name="donate_success_purchase">感謝您的捐</string>
<string name="donate_item_click"></string> <string name="donate_item_click"></string>
<string name="donate_item_paypal">Paypal</string> <string name="donate_item_paypal">Paypal</string>
<string name="title_logcat">Logcat</string> <string name="title_logcat">Logcat</string>
<string name="logcat_copy">複製</string> <string name="logcat_copy">複製</string>
<string name="logcat_delete">刪除</string> <string name="logcat_delete">刪除</string>
<string name="title_export_all">匯出全部(非自訂)配置至剪貼簿</string> <string name="title_export_all">匯出全部 (非自訂) 組態至剪貼簿</string>
<string name="title_sub_setting">訂閱設定</string> <string name="title_sub_setting">訂閱設定</string>
<string name="sub_setting_remarks">備註</string> <string name="sub_setting_remarks">備註</string>
<string name="sub_setting_url">位址(url)</string> <string name="sub_setting_url">位址 (url)</string>
<string name="title_sub_update">更新訂閱</string> <string name="title_sub_update">更新訂閱</string>
<string name="title_ping_all_server">測試所有配置延遲(Tcping)</string> <string name="title_ping_all_server">測試所有組態延遲 (Tcping)</string>
<string name="tasker_start_service">啟動服務</string> <string name="tasker_start_service">啟動服務</string>
<string name="tasker_setting_confirm">確定</string> <string name="tasker_setting_confirm">確定</string>
<string name="routing_settings_title">路由設定</string> <string name="routing_settings_title">轉送設定</string>
<string name="routing_settings_tips">英文逗號「,」分隔,記得儲存唷</string> <string name="routing_settings_tips">半形逗號「,」分隔,記得儲存唷</string>
<string name="routing_settings_save">儲存</string> <string name="routing_settings_save">儲存</string>
<string name="routing_settings_delete">清除</string> <string name="routing_settings_delete">清除</string>
<string name="routing_settings_scan_replace">掃描並取代</string> <string name="routing_settings_scan_replace">掃描並取代</string>
<string name="routing_settings_scan_append">掃描並附加</string> <string name="routing_settings_scan_append">掃描並附加</string>
<string name="routing_settings_default_rules">預設路由規則</string> <string name="routing_settings_default_rules">預設轉送規則</string>
<string name="connection_test_pending">"檢查連線能力"</string> <string name="connection_test_pending">"檢查連線能力"</string>
<string name="connection_test_testing">"測試中……"</string> <string name="connection_test_testing">"測試中……"</string>
<string name="connection_test_available">"成功: %d 毫秒延遲"</string> <string name="connection_test_available">"成功 %d 毫秒延遲"</string>
<string name="connection_test_error">"偵測出網際網路連線失敗: %s"</string> <string name="connection_test_error">"偵測出網際網路連線失敗%s"</string>
<string name="connection_test_fail">"無法使用網際網路"</string> <string name="connection_test_fail">"無法使用網際網路"</string>
<string name="connection_test_error_status_code">"錯誤碼: #%d"</string> <string name="connection_test_error_status_code">"錯誤碼(#%d)"</string>
<string name="connection_connected">"已連線,輕觸以檢查連線能力"</string> <string name="connection_connected">"已連線,輕觸以檢查連線能力"</string>
<string name="connection_not_connected">"未連線"</string> <string name="connection_not_connected">"未連線"</string>
<string-array name="share_method"> <string-array name="share_method">
<item>QR </item> <item>QR Code</item>
<item>匯出至剪貼簿</item> <item>匯出至剪貼簿</item>
<item>匯出完整配置至剪貼簿</item> <item>匯出完整組態至剪貼簿</item>
</string-array> </string-array>
<string-array name="routing_tag"> <string-array name="routing_tag">
@@ -188,20 +187,20 @@
</string-array> </string-array>
<string-array name="routing_mode"> <string-array name="routing_mode">
<item></item> <item></item>
<item>略過區域網路</item> <item>略過區域網路</item>
<item>略過中國大陸</item> <item>略過中國大陸</item>
<item>略過區域網路及中國大陸</item> <item>略過區域網路及中國大陸</item>
</string-array> </string-array>
<string name="title_pref_proxy_sharing_enabled">代理共享</string> <string name="title_pref_proxy_sharing_enabled">Proxy 共用</string>
<string name="summary_pref_proxy_sharing_enabled">綁定代理入口ip到0.0.0.0</string> <string name="summary_pref_proxy_sharing_enabled">綁定 Proxy 入口 IP 到 0.0.0.0</string>
<string name="toast_warning_pref_proxysharing">其他設備可以使用socks/http協定通過您的IP地址連接到代理\nHttp 代理: http://您的ip:10809\nSocks 代理: socks(4/5)://您的ip:10808\n僅在受信任的網路中啟用以避免未經授權的連</string> <string name="toast_warning_pref_proxysharing">其他裝置可以使用 socks/http 協定通過您的 IP 位址連線到 Proxy \nHttp Proxyhttp://您的ip:10809\nSocks Proxysocks(4/5)://您的ip:10808\n僅在受信任的網路中啟用以避免未經授權的連</string>
<string name="toast_warning_pref_proxysharing_short">代理共享已啟用,請確保處於受信網路</string> <string name="toast_warning_pref_proxysharing_short">Proxy 共用已啟用,請確保處於受信網路</string>
<string name="toast_malformed_josn">配置格式錯誤</string> <string name="toast_malformed_josn">組態格式錯誤</string>
<string-array name="mode_entries"> <string-array name="mode_entries">
<item>VPN</item> <item>VPN</item>
<item>代理</item> <item> Proxy</item>
</string-array> </string-array>
</resources> </resources>

View File

@@ -132,6 +132,6 @@
<item>196.0.0.0/6</item> <item>196.0.0.0/6</item>
<item>200.0.0.0/5</item> <item>200.0.0.0/5</item>
<item>208.0.0.0/4</item> <item>208.0.0.0/4</item>
<item>224.0.0.0/3</item> <item>240.0.0.0/4</item>
</string-array> </string-array>
</resources> </resources>

View File

@@ -38,7 +38,6 @@
<string name="server_lab_address">address</string> <string name="server_lab_address">address</string>
<string name="server_lab_port">port</string> <string name="server_lab_port">port</string>
<string name="server_lab_id">id</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_security">security</string>
<string name="server_lab_network">Network</string> <string name="server_lab_network">Network</string>
<string name="server_lab_more_function">more function</string> <string name="server_lab_more_function">more function</string>
@@ -80,7 +79,7 @@
<string name="title_advanced">Advanced Settings</string> <string name="title_advanced">Advanced Settings</string>
<string name="title_vpn_settings">VPN Settings</string> <string name="title_vpn_settings">VPN Settings</string>
<string name="title_pref_per_app_proxy">Per-app proxy</string> <string name="title_pref_per_app_proxy">Per-app proxy</string>
<string name="summary_pref_per_app_proxy">Per-app proxy mode only support Android 5.0 Lollipop or higher</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="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="summary_pref_mux_enabled">Enable maybe speed up network and switch network maybe flash</string>

View File

@@ -11,6 +11,7 @@
<style name="AppTheme.NoActionBar"> <style name="AppTheme.NoActionBar">
<item name="windowActionBar">false</item> <item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item> <item name="windowNoTitle">true</item>
<item name="android:statusBarColor">@android:color/transparent</item>
</style> </style>
<style name="AppTheme.NoActionBar.Translucent"> <style name="AppTheme.NoActionBar.Translucent">

View File

@@ -3,11 +3,13 @@
buildscript { buildscript {
repositories { repositories {
google() google()
jcenter() mavenCentral()
maven { url 'https://maven.google.com' } maven { url 'https://maven.google.com' }
maven { url 'https://jitpack.io' }
jcenter()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:4.1.0' classpath 'com.android.tools.build:gradle:7.1.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong
@@ -18,8 +20,10 @@ buildscript {
allprojects { allprojects {
repositories { repositories {
google() google()
jcenter() mavenCentral()
maven { url 'https://maven.google.com' } maven { url 'https://maven.google.com' }
maven { url 'https://jitpack.io' }
jcenter()
} }
} }

View File

@@ -13,9 +13,10 @@ org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryErro
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true # org.gradle.parallel=true
#Fri Jun 02 14:08:42 CST 2017 #Fri Jun 02 14:08:42 CST 2017
kotlinVersion=1.4.10 kotlinVersion=1.6.10
supportLibVersion=28.0.0 buildToolsVer=31.0.0
buildToolsVer=30.0.2 compileSdkVer=31
compileSdkVer=30 targetSdkVer=31
kotlin.incremental=true kotlin.incremental=true
targetSdkVer=30 android.useAndroidX=true
android.enableJetifier=true

View File

@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5.1-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-all.zip