Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
50bf9c8da0 | ||
|
|
78fe4e4bc4 | ||
|
|
962519854d | ||
|
|
841629f9a3 | ||
|
|
d32ccc817a | ||
|
|
19cc665f2d | ||
|
|
031e9105e2 | ||
|
|
35c5d64863 | ||
|
|
0b05756d12 | ||
|
|
3548dcbb67 | ||
|
|
b897b2a0e9 | ||
|
|
eb60e4a0e4 | ||
|
|
c3dfa8cedc | ||
|
|
f58bf85b6d | ||
|
|
906d0714b5 | ||
|
|
701fed2525 | ||
|
|
e83208465f | ||
|
|
2b031033d3 | ||
|
|
e0e16b5934 | ||
|
|
0748f994ef | ||
|
|
233b34bda6 | ||
|
|
6ece3385fe | ||
|
|
9b8a810445 | ||
|
|
f63242d147 | ||
|
|
7024fabb82 | ||
|
|
459f52fec6 | ||
|
|
90f2d33d97 | ||
|
|
115008a8a4 |
2
AndroidLibV2rayLite/.gitignore
vendored
Normal file
2
AndroidLibV2rayLite/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
*.jar
|
||||||
|
*.aar
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
sudo: required
|
|
||||||
language: go
|
|
||||||
go:
|
|
||||||
- "1.12"
|
|
||||||
go_import_path: github.com/2dust/AndroidLibV2rayLite
|
|
||||||
git:
|
|
||||||
depth: 5
|
|
||||||
addons:
|
|
||||||
apt:
|
|
||||||
update: true
|
|
||||||
before_script:
|
|
||||||
- sudo ntpdate -u time.google.com
|
|
||||||
- date
|
|
||||||
- make all
|
|
||||||
- make downloadGoMobile
|
|
||||||
script:
|
|
||||||
- make BuildMobile
|
|
||||||
after_success:
|
|
||||||
deploy:
|
|
||||||
provider: releases
|
|
||||||
api_key: ${GH_TOKEN}
|
|
||||||
file:
|
|
||||||
- libv2ray.aar
|
|
||||||
skip_cleanup: true
|
|
||||||
on:
|
|
||||||
tags: true
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package CoreI
|
package CoreI
|
||||||
|
|
||||||
import (
|
import (
|
||||||
v2core "v2ray.com/core"
|
v2core "github.com/xtls/xray-core/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Status struct {
|
type Status struct {
|
||||||
@@ -14,7 +14,7 @@ type Status struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func CheckVersion() int {
|
func CheckVersion() int {
|
||||||
return 22
|
return 23
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *Status) GetDataDir() string {
|
func (v *Status) GetDataDir() string {
|
||||||
|
|||||||
@@ -8,24 +8,19 @@ asset:
|
|||||||
# cd assets;curl https://raw.githubusercontent.com/2dust/AndroidLibV2rayLite/master/data/geosite.dat > geosite.dat
|
# cd assets;curl https://raw.githubusercontent.com/2dust/AndroidLibV2rayLite/master/data/geosite.dat > geosite.dat
|
||||||
# cd assets;curl https://raw.githubusercontent.com/2dust/AndroidLibV2rayLite/master/data/geoip.dat > geoip.dat
|
# cd assets;curl https://raw.githubusercontent.com/2dust/AndroidLibV2rayLite/master/data/geoip.dat > geoip.dat
|
||||||
|
|
||||||
fetchDep:
|
|
||||||
-go get github.com/2dust/AndroidLibV2rayLite
|
|
||||||
go get github.com/2dust/AndroidLibV2rayLite
|
|
||||||
|
|
||||||
ANDROID_HOME=$(HOME)/android-sdk-linux
|
ANDROID_HOME=$(HOME)/android-sdk-linux
|
||||||
export ANDROID_HOME
|
export ANDROID_HOME
|
||||||
PATH:=$(PATH):$(GOPATH)/bin
|
PATH:=$(PATH):$(GOPATH)/bin
|
||||||
export PATH
|
export PATH
|
||||||
downloadGoMobile:
|
downloadGoMobile:
|
||||||
go get golang.org/x/mobile/cmd/...
|
|
||||||
sudo apt-get install -qq libstdc++6:i386 lib32z1 expect
|
sudo apt-get install -qq libstdc++6:i386 lib32z1 expect
|
||||||
cd ~ ;curl -L https://raw.githubusercontent.com/2dust/AndroidLibV2rayLite/master/ubuntu-cli-install-android-sdk.sh | sudo bash - > /dev/null
|
cd ~ ;curl -L https://raw.githubusercontent.com/2dust/AndroidLibV2rayLite/master/ubuntu-cli-install-android-sdk.sh | sudo bash - > /dev/null
|
||||||
ls ~
|
ls ~
|
||||||
ls ~/android-sdk-linux/
|
ls ~/android-sdk-linux/
|
||||||
gomobile init ;gomobile bind -v -tags json github.com/2dust/AndroidLibV2rayLite
|
|
||||||
|
|
||||||
BuildMobile:
|
BuildMobile:
|
||||||
@echo Stub
|
gomobile init
|
||||||
|
gomobile bind -v -ldflags='-s -w' github.com/2dust/AndroidLibV2rayLite
|
||||||
|
|
||||||
all: asset pb fetchDep
|
all: asset pb
|
||||||
@echo DONE
|
@echo DONE
|
||||||
|
|||||||
@@ -12,8 +12,10 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
v2net "v2ray.com/core/common/net"
|
v2net "github.com/xtls/xray-core/common/net"
|
||||||
v2internet "v2ray.com/core/transport/internet"
|
"github.com/xtls/xray-core/features/dns"
|
||||||
|
"github.com/xtls/xray-core/features/outbound"
|
||||||
|
v2internet "github.com/xtls/xray-core/transport/internet"
|
||||||
)
|
)
|
||||||
|
|
||||||
type protectSet interface {
|
type protectSet interface {
|
||||||
@@ -190,6 +192,11 @@ func (d *ProtectedDialer) getFd(network v2net.Network) (fd int, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Init implement internet.SystemDialer
|
||||||
|
func (d *ProtectedDialer) Init(_ dns.Client, _ outbound.Manager) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
// Dial exported as the protected dial method
|
// Dial exported as the protected dial method
|
||||||
func (d *ProtectedDialer) Dial(ctx context.Context,
|
func (d *ProtectedDialer) Dial(ctx context.Context,
|
||||||
src v2net.Address, dest v2net.Destination, sockopt *v2internet.SocketConfig) (net.Conn, error) {
|
src v2net.Address, dest v2net.Destination, sockopt *v2internet.SocketConfig) (net.Conn, error) {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
v2net "v2ray.com/core/common/net"
|
v2net "github.com/xtls/xray-core/common/net"
|
||||||
)
|
)
|
||||||
|
|
||||||
type fakeSupportSet struct{}
|
type fakeSupportSet struct{}
|
||||||
|
|||||||
4
AndroidLibV2rayLite/compile-tun2socks.sh
Normal file → Executable file
4
AndroidLibV2rayLite/compile-tun2socks.sh
Normal file → Executable file
@@ -23,8 +23,8 @@ trap 'echo -e "Aborted, error $? in command: $BASH_COMMAND"; trap ERR; clear_tmp
|
|||||||
install -m644 $__dir/tun2socks.mk $TMPDIR/
|
install -m644 $__dir/tun2socks.mk $TMPDIR/
|
||||||
|
|
||||||
pushd $TMPDIR
|
pushd $TMPDIR
|
||||||
git clone --depth=1 https://github.com/shadowsocks/badvpn.git
|
git clone --depth=1 https://github.com/XTLS/badvpn.git
|
||||||
git clone --depth=1 https://github.com/shadowsocks/libancillary.git
|
git clone --depth=1 https://github.com/XTLS/libancillary.git
|
||||||
$NDK_HOME/ndk-build \
|
$NDK_HOME/ndk-build \
|
||||||
NDK_PROJECT_PATH=. \
|
NDK_PROJECT_PATH=. \
|
||||||
APP_BUILD_SCRIPT=./tun2socks.mk \
|
APP_BUILD_SCRIPT=./tun2socks.mk \
|
||||||
|
|||||||
9
AndroidLibV2rayLite/go.mod
Normal file
9
AndroidLibV2rayLite/go.mod
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
module github.com/2dust/AndroidLibV2rayLite
|
||||||
|
|
||||||
|
go 1.16
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/xtls/xray-core v1.4.2
|
||||||
|
golang.org/x/mobile v0.0.0-20210220033013-bdb1ca9a1e08
|
||||||
|
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57
|
||||||
|
)
|
||||||
303
AndroidLibV2rayLite/go.sum
Normal file
303
AndroidLibV2rayLite/go.sum
Normal file
@@ -0,0 +1,303 @@
|
|||||||
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo=
|
||||||
|
dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU=
|
||||||
|
dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU=
|
||||||
|
dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=
|
||||||
|
dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
|
||||||
|
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
|
||||||
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
|
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||||
|
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||||
|
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||||
|
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
|
||||||
|
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
|
||||||
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
|
github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
|
||||||
|
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||||
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||||
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
|
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||||
|
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/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/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||||
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
|
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||||
|
github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=
|
||||||
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
|
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||||
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
|
github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I=
|
||||||
|
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||||
|
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
||||||
|
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
|
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
|
||||||
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
|
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
|
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||||
|
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
|
||||||
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||||
|
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||||
|
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||||
|
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
|
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||||
|
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
|
||||||
|
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||||
|
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||||
|
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||||
|
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
|
||||||
|
github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
|
||||||
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
|
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
|
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||||
|
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
|
||||||
|
github.com/h12w/go-socks5 v0.0.0-20200522160539-76189e178364/go.mod h1:eDJQioIyy4Yn3MVivT7rv/39gAJTrA7lgmYr8EW950c=
|
||||||
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
|
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
|
||||||
|
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
|
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||||
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/text v0.1.0/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/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
|
||||||
|
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||||
|
github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc=
|
||||||
|
github.com/marten-seemann/qtls-go1-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/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||||
|
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
|
||||||
|
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
|
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
|
||||||
|
github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
|
||||||
|
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||||
|
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
|
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||||
|
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
||||||
|
github.com/onsi/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/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/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/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||||
|
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||||
|
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
|
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||||
|
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||||
|
github.com/refraction-networking/utls v0.0.0-20201210053706-2179f286686b/go.mod h1:tz9gX959MEFfFN5whTIocCLUG57WiILqtdVxI8c6Wj0=
|
||||||
|
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||||
|
github.com/seiflotfy/cuckoofilter v0.0.0-20201222105146-bc6005554a0c/go.mod h1:bR6DqgcAl1zTcOX8/pE2Qkj9XO00eCNqmKb7lXP8EAg=
|
||||||
|
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||||
|
github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY=
|
||||||
|
github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM=
|
||||||
|
github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0=
|
||||||
|
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
|
||||||
|
github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
|
||||||
|
github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw=
|
||||||
|
github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI=
|
||||||
|
github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU=
|
||||||
|
github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag=
|
||||||
|
github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg=
|
||||||
|
github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw=
|
||||||
|
github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y=
|
||||||
|
github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
|
||||||
|
github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q=
|
||||||
|
github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ=
|
||||||
|
github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I=
|
||||||
|
github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0=
|
||||||
|
github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ=
|
||||||
|
github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk=
|
||||||
|
github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||||
|
github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4=
|
||||||
|
github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw=
|
||||||
|
github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
|
||||||
|
github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
|
||||||
|
github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
|
||||||
|
github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=
|
||||||
|
github.com/xtls/go v0.0.0-20201118062508-3632bf3b7499/go.mod h1:5TB2+k58gx4A4g2Nf5miSHNDF6CuAzHKpWBooLAshTs=
|
||||||
|
github.com/xtls/xray-core v1.4.2 h1:D0Le+Qy9L/eY5LbUQfrk7WJ8wbODpQSW/ZRCg+BRe7c=
|
||||||
|
github.com/xtls/xray-core v1.4.2/go.mod h1:DmL/9rOCliev/a6HciWEvSJVEhUF6C0EpD3clW8v0pc=
|
||||||
|
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
|
||||||
|
go.starlark.net v0.0.0-20210312235212-74c10e2c17dc/go.mod h1:t3mmBBPzAVvK0L0n1drDmrQsJ8FoIx4INCqVMTr/Zo0=
|
||||||
|
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
|
||||||
|
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
|
||||||
|
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||||
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
|
||||||
|
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||||
|
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
|
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
|
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||||
|
golang.org/x/mobile v0.0.0-20210220033013-bdb1ca9a1e08 h1:h+GZ3ubjuWaQjGe8owMGcmMVCqs0xYJtRG5y2bpHaqU=
|
||||||
|
golang.org/x/mobile v0.0.0-20210220033013-bdb1ca9a1e08/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4=
|
||||||
|
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.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20210330230544-e57232859fb2/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
|
||||||
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/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-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201231184435-2d18734c6014/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-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||||
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69 h1:yBHHx+XZqXJBm6Exke3N7V9gnlsyXxoCPEb1yVenjfk=
|
||||||
|
golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||||
|
google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||||
|
google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=
|
||||||
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
|
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
|
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
|
google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
|
google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
|
||||||
|
google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
|
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||||
|
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||||
|
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
|
||||||
|
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||||
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
|
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||||
|
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||||
|
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
|
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||||
|
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||||
|
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||||
|
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||||
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
|
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/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/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||||
|
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||||
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||||
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
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=
|
||||||
|
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=
|
||||||
|
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck=
|
||||||
|
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=
|
||||||
@@ -13,18 +13,19 @@ import (
|
|||||||
"github.com/2dust/AndroidLibV2rayLite/VPN"
|
"github.com/2dust/AndroidLibV2rayLite/VPN"
|
||||||
mobasset "golang.org/x/mobile/asset"
|
mobasset "golang.org/x/mobile/asset"
|
||||||
|
|
||||||
v2core "v2ray.com/core"
|
v2core "github.com/xtls/xray-core/core"
|
||||||
v2filesystem "v2ray.com/core/common/platform/filesystem"
|
v2filesystem "github.com/xtls/xray-core/common/platform/filesystem"
|
||||||
v2stats "v2ray.com/core/features/stats"
|
v2stats "github.com/xtls/xray-core/features/stats"
|
||||||
v2serial "v2ray.com/core/infra/conf/serial"
|
v2serial "github.com/xtls/xray-core/infra/conf/serial"
|
||||||
v2internet "v2ray.com/core/transport/internet"
|
_ "github.com/xtls/xray-core/main/distro/all"
|
||||||
|
v2internet "github.com/xtls/xray-core/transport/internet"
|
||||||
|
|
||||||
v2applog "v2ray.com/core/app/log"
|
v2applog "github.com/xtls/xray-core/app/log"
|
||||||
v2commlog "v2ray.com/core/common/log"
|
v2commlog "github.com/xtls/xray-core/common/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
v2Assert = "v2ray.location.asset"
|
v2Assert = "xray.location.asset"
|
||||||
assetperfix = "/dev/libv2rayfs0/asset"
|
assetperfix = "/dev/libv2rayfs0/asset"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,58 +0,0 @@
|
|||||||
package libv2ray
|
|
||||||
|
|
||||||
import (
|
|
||||||
// The following are necessary as they register handlers in their init functions.
|
|
||||||
|
|
||||||
// Required features. Can't remove unless there is replacements.
|
|
||||||
_ "v2ray.com/core/app/dispatcher"
|
|
||||||
_ "v2ray.com/core/app/proxyman/inbound"
|
|
||||||
_ "v2ray.com/core/app/proxyman/outbound"
|
|
||||||
|
|
||||||
// Other optional features.
|
|
||||||
_ "v2ray.com/core/app/dns"
|
|
||||||
_ "v2ray.com/core/app/log"
|
|
||||||
_ "v2ray.com/core/app/policy"
|
|
||||||
_ "v2ray.com/core/app/router"
|
|
||||||
_ "v2ray.com/core/app/stats"
|
|
||||||
|
|
||||||
// Inbound and outbound proxies.
|
|
||||||
_ "v2ray.com/core/proxy/blackhole"
|
|
||||||
_ "v2ray.com/core/proxy/dns"
|
|
||||||
_ "v2ray.com/core/proxy/dokodemo"
|
|
||||||
_ "v2ray.com/core/proxy/freedom"
|
|
||||||
_ "v2ray.com/core/proxy/http"
|
|
||||||
_ "v2ray.com/core/proxy/mtproto"
|
|
||||||
_ "v2ray.com/core/proxy/shadowsocks"
|
|
||||||
_ "v2ray.com/core/proxy/socks"
|
|
||||||
_ "v2ray.com/core/proxy/trojan"
|
|
||||||
_ "v2ray.com/core/proxy/vless/inbound"
|
|
||||||
_ "v2ray.com/core/proxy/vless/outbound"
|
|
||||||
_ "v2ray.com/core/proxy/vmess/inbound"
|
|
||||||
_ "v2ray.com/core/proxy/vmess/outbound"
|
|
||||||
|
|
||||||
// Transport
|
|
||||||
_ "v2ray.com/core/transport/internet/http"
|
|
||||||
_ "v2ray.com/core/transport/internet/kcp"
|
|
||||||
_ "v2ray.com/core/transport/internet/quic"
|
|
||||||
_ "v2ray.com/core/transport/internet/tcp"
|
|
||||||
_ "v2ray.com/core/transport/internet/tls"
|
|
||||||
_ "v2ray.com/core/transport/internet/udp"
|
|
||||||
_ "v2ray.com/core/transport/internet/websocket"
|
|
||||||
|
|
||||||
// Transport headers
|
|
||||||
_ "v2ray.com/core/transport/internet/headers/http"
|
|
||||||
_ "v2ray.com/core/transport/internet/headers/noop"
|
|
||||||
_ "v2ray.com/core/transport/internet/headers/srtp"
|
|
||||||
_ "v2ray.com/core/transport/internet/headers/tls"
|
|
||||||
_ "v2ray.com/core/transport/internet/headers/utp"
|
|
||||||
_ "v2ray.com/core/transport/internet/headers/wechat"
|
|
||||||
_ "v2ray.com/core/transport/internet/headers/wireguard"
|
|
||||||
|
|
||||||
// JSON config support. Choose only one from the two below.
|
|
||||||
// The following line loads JSON from v2ctl
|
|
||||||
// _ "v2ray.com/core/main/json"
|
|
||||||
// The following line loads JSON internally
|
|
||||||
_ "v2ray.com/core/main/jsonem"
|
|
||||||
// Load config from file or http(s)
|
|
||||||
// _ "v2ray.com/core/main/confloader/external"
|
|
||||||
)
|
|
||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
v2commlog "v2ray.com/core/common/log"
|
v2commlog "github.com/xtls/xray-core/common/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
type consoleLogWriter struct {
|
type consoleLogWriter struct {
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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; }
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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(); }
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -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; }
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BIN
V2rayNG/app/src/main/jniLibs/arm64-v8a/libtun2socks.so
Normal file → Executable file
BIN
V2rayNG/app/src/main/jniLibs/arm64-v8a/libtun2socks.so
Normal file → Executable file
Binary file not shown.
BIN
V2rayNG/app/src/main/jniLibs/armeabi-v7a/libtun2socks.so
Normal file → Executable file
BIN
V2rayNG/app/src/main/jniLibs/armeabi-v7a/libtun2socks.so
Normal file → Executable file
Binary file not shown.
BIN
V2rayNG/app/src/main/jniLibs/x86/libtun2socks.so
Normal file → Executable file
BIN
V2rayNG/app/src/main/jniLibs/x86/libtun2socks.so
Normal file → Executable file
Binary file not shown.
BIN
V2rayNG/app/src/main/jniLibs/x86_64/libtun2socks.so
Normal file → Executable file
BIN
V2rayNG/app/src/main/jniLibs/x86_64/libtun2socks.so
Normal file → Executable file
Binary file not shown.
@@ -18,8 +18,11 @@ object AppConfig {
|
|||||||
const val PREF_SNIFFING_ENABLED = "pref_sniffing_enabled"
|
const val PREF_SNIFFING_ENABLED = "pref_sniffing_enabled"
|
||||||
const val PREF_PROXY_SHARING = "pref_proxy_sharing_enabled"
|
const val PREF_PROXY_SHARING = "pref_proxy_sharing_enabled"
|
||||||
const val PREF_LOCAL_DNS_ENABLED = "pref_local_dns_enabled"
|
const val PREF_LOCAL_DNS_ENABLED = "pref_local_dns_enabled"
|
||||||
|
const val PREF_FAKE_DNS_ENABLED = "pref_fake_dns_enabled"
|
||||||
|
const val PREF_VPN_DNS = "pref_vpn_dns"
|
||||||
const val PREF_REMOTE_DNS = "pref_remote_dns"
|
const val PREF_REMOTE_DNS = "pref_remote_dns"
|
||||||
const val PREF_DOMESTIC_DNS = "pref_domestic_dns"
|
const val PREF_DOMESTIC_DNS = "pref_domestic_dns"
|
||||||
|
const val PREF_LOCAL_DNS_PORT = "pref_local_dns_port"
|
||||||
const val PREF_FORWARD_IPV6 = "pref_forward_ipv6"
|
const val PREF_FORWARD_IPV6 = "pref_forward_ipv6"
|
||||||
const val PREF_ROUTING_DOMAIN_STRATEGY = "pref_routing_domain_strategy"
|
const val PREF_ROUTING_DOMAIN_STRATEGY = "pref_routing_domain_strategy"
|
||||||
const val PREF_ROUTING_MODE = "pref_routing_mode"
|
const val PREF_ROUTING_MODE = "pref_routing_mode"
|
||||||
|
|||||||
@@ -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,
|
||||||
val fakedns: Any? = null,
|
var fakedns: FakednsBean? = null,
|
||||||
val browserForwarder: Any? = null) {
|
val browserForwarder: Any? = null) {
|
||||||
companion object {
|
companion object {
|
||||||
const val DEFAULT_PORT = 443
|
const val DEFAULT_PORT = 443
|
||||||
@@ -57,7 +57,7 @@ data class V2rayConfig(
|
|||||||
val network: String? = null)
|
val network: String? = null)
|
||||||
|
|
||||||
data class SniffingBean(var enabled: Boolean,
|
data class SniffingBean(var enabled: Boolean,
|
||||||
val destOverride: List<String>,
|
val destOverride: ArrayList<String>,
|
||||||
val metadataOnly: Boolean? = null)
|
val metadataOnly: Boolean? = null)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,7 +78,7 @@ data class V2rayConfig(
|
|||||||
val address: String? = null,
|
val address: String? = null,
|
||||||
val port: Int? = null,
|
val port: Int? = null,
|
||||||
/*Freedom*/
|
/*Freedom*/
|
||||||
val domainStrategy: String? = null,
|
var domainStrategy: String? = null,
|
||||||
val redirect: String? = null,
|
val redirect: String? = null,
|
||||||
val userLevel: Int? = null,
|
val userLevel: Int? = null,
|
||||||
/*Loopback*/
|
/*Loopback*/
|
||||||
@@ -192,10 +192,10 @@ data class V2rayConfig(
|
|||||||
}
|
}
|
||||||
|
|
||||||
data class GrpcSettingsBean(var serviceName: String = "",
|
data class GrpcSettingsBean(var serviceName: String = "",
|
||||||
val multiMode: Boolean? = null)
|
var multiMode: Boolean? = null)
|
||||||
|
|
||||||
fun populateTransportSettings(transport: String, headerType: String?, host: String?, path: String?, seed: String?,
|
fun populateTransportSettings(transport: String, headerType: String?, host: String?, path: String?, seed: String?,
|
||||||
quicSecurity: String?, key: String?): String {
|
quicSecurity: String?, key: String?, mode: String?, serviceName: String?): String {
|
||||||
var sni = ""
|
var sni = ""
|
||||||
network = transport
|
network = transport
|
||||||
when (network) {
|
when (network) {
|
||||||
@@ -250,7 +250,8 @@ data class V2rayConfig(
|
|||||||
}
|
}
|
||||||
"grpc" -> {
|
"grpc" -> {
|
||||||
val grpcSetting = GrpcSettingsBean()
|
val grpcSetting = GrpcSettingsBean()
|
||||||
grpcSetting.serviceName = path ?: ""
|
grpcSetting.multiMode = mode == "multi"
|
||||||
|
grpcSetting.serviceName = serviceName ?: ""
|
||||||
sni = host ?: ""
|
sni = host ?: ""
|
||||||
grpcSettings = grpcSetting
|
grpcSettings = grpcSetting
|
||||||
}
|
}
|
||||||
@@ -357,7 +358,7 @@ data class V2rayConfig(
|
|||||||
}
|
}
|
||||||
"grpc" -> {
|
"grpc" -> {
|
||||||
val grpcSetting = streamSettings?.grpcSettings ?: return null
|
val grpcSetting = streamSettings?.grpcSettings ?: return null
|
||||||
listOf("",
|
listOf(if (grpcSetting.multiMode == true) "multi" else "gun",
|
||||||
"",
|
"",
|
||||||
grpcSetting.serviceName)
|
grpcSetting.serviceName)
|
||||||
}
|
}
|
||||||
@@ -368,7 +369,7 @@ 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, String>? = null,
|
||||||
val clientIp: String? = null,
|
val clientIp: String? = null,
|
||||||
val disableCache: Boolean? = null,
|
val disableCache: Boolean? = null,
|
||||||
@@ -376,9 +377,9 @@ data class V2rayConfig(
|
|||||||
val tag: String? = null
|
val tag: String? = null
|
||||||
) {
|
) {
|
||||||
data class ServersBean(var address: String = "",
|
data class ServersBean(var address: String = "",
|
||||||
var port: Int = 0,
|
var port: Int? = null,
|
||||||
var domains: List<String>?,
|
var domains: List<String>? = null,
|
||||||
var expectIPs: List<String>?,
|
var expectIPs: List<String>? = null,
|
||||||
val clientIp: String? = null)
|
val clientIp: String? = null)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -416,6 +417,9 @@ data class V2rayConfig(
|
|||||||
var bufferSize: Int? = null)
|
var bufferSize: Int? = null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class FakednsBean(var ipPool: String = "198.18.0.0/15",
|
||||||
|
var poolSize: Int = 10000) // roughly 10 times smaller than total ip pool
|
||||||
|
|
||||||
fun getProxyOutbound(): OutboundBean? {
|
fun getProxyOutbound(): OutboundBean? {
|
||||||
outbounds.forEach { outbound ->
|
outbounds.forEach { outbound ->
|
||||||
if (outbound.protocol.equals(EConfigType.VMESS.name, true) ||
|
if (outbound.protocol.equals(EConfigType.VMESS.name, true) ||
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import android.support.v4.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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -234,7 +240,7 @@ object V2RayServiceManager {
|
|||||||
PendingIntent.FLAG_UPDATE_CURRENT)
|
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,
|
||||||
|
|||||||
@@ -122,10 +122,12 @@ class V2RayVpnService : VpnService(), ServiceControl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(!enableLocalDns) {
|
if(!enableLocalDns) {
|
||||||
Utils.getRemoteDnsServers()
|
Utils.getVpnDnsServers()
|
||||||
.forEach {
|
.forEach {
|
||||||
builder.addDnsServer(it)
|
if (Utils.isPureIpAddress(it)) {
|
||||||
}
|
builder.addDnsServer(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.setSession(V2RayServiceManager.currentConfig?.remarks.orEmpty())
|
builder.setSession(V2RayServiceManager.currentConfig?.remarks.orEmpty())
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ 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.extension.toast
|
import com.v2ray.ang.extension.toast
|
||||||
import com.v2ray.ang.util.Utils
|
import com.v2ray.ang.util.Utils
|
||||||
@@ -48,7 +49,7 @@ 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))
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import android.widget.Toast
|
|||||||
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.dto.EConfigType
|
import com.v2ray.ang.dto.EConfigType
|
||||||
@@ -410,7 +411,7 @@ 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()
|
URL(url).readText()
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ 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 = 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)
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ 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.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
|
||||||
@@ -226,7 +227,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)
|
||||||
}
|
}
|
||||||
@@ -250,7 +251,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("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 +264,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)
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ 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.view.View
|
||||||
|
import android.widget.AdapterView
|
||||||
|
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
|
||||||
@@ -28,6 +31,7 @@ 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.et_request_host
|
||||||
import kotlinx.android.synthetic.main.activity_server_vmess.sp_allow_insecure
|
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
|
||||||
|
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_network
|
||||||
import kotlinx.android.synthetic.main.activity_server_vmess.sp_stream_security
|
import kotlinx.android.synthetic.main.activity_server_vmess.sp_stream_security
|
||||||
|
|
||||||
@@ -56,8 +60,14 @@ class ServerActivity : BaseActivity() {
|
|||||||
private val networks: Array<out String> by lazy {
|
private val networks: Array<out String> by lazy {
|
||||||
resources.getStringArray(R.array.networks)
|
resources.getStringArray(R.array.networks)
|
||||||
}
|
}
|
||||||
private val headertypes: Array<out String> by lazy {
|
private val tcpTypes: Array<out String> by lazy {
|
||||||
resources.getStringArray(R.array.headertypes)
|
resources.getStringArray(R.array.header_type_tcp)
|
||||||
|
}
|
||||||
|
private val kcpAndQuicTypes: Array<out String> by lazy {
|
||||||
|
resources.getStringArray(R.array.header_type_kcp_and_quic)
|
||||||
|
}
|
||||||
|
private val grpcModes: Array<out String> by lazy {
|
||||||
|
resources.getStringArray(R.array.mode_type_grpc)
|
||||||
}
|
}
|
||||||
private val streamSecuritys: Array<out String> by lazy {
|
private val streamSecuritys: Array<out String> by lazy {
|
||||||
resources.getStringArray(R.array.streamsecurityxs)
|
resources.getStringArray(R.array.streamsecurityxs)
|
||||||
@@ -79,6 +89,26 @@ class ServerActivity : BaseActivity() {
|
|||||||
// EConfigType.VLESS -> setContentView(R.layout.activity_server_vless)
|
// EConfigType.VLESS -> setContentView(R.layout.activity_server_vless)
|
||||||
// EConfigType.TROJAN -> setContentView(R.layout.activity_server_trojan)
|
// EConfigType.TROJAN -> setContentView(R.layout.activity_server_trojan)
|
||||||
}
|
}
|
||||||
|
sp_network?.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
||||||
|
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
|
||||||
|
val types = transportTypes(networks[position])
|
||||||
|
sp_header_type?.isEnabled = types.size > 1
|
||||||
|
val adapter = ArrayAdapter(this@ServerActivity, android.R.layout.simple_spinner_item, types)
|
||||||
|
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
|
||||||
|
sp_header_type?.adapter = adapter
|
||||||
|
sp_header_type_title?.text = if (networks[position] == "grpc")
|
||||||
|
getString(R.string.server_lab_mode_type) else
|
||||||
|
getString(R.string.server_lab_head_type)
|
||||||
|
config?.getProxyOutbound()?.getTransportSettingDetails()?.let { transportDetails ->
|
||||||
|
sp_header_type.setSelection(Utils.arrayFind(types, transportDetails[0]))
|
||||||
|
et_request_host.text = Utils.getEditable(transportDetails[1])
|
||||||
|
et_path.text = Utils.getEditable(transportDetails[2])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
override fun onNothingSelected(parent: AdapterView<*>?) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
if (config != null) {
|
if (config != null) {
|
||||||
bindingServer(config)
|
bindingServer(config)
|
||||||
} else {
|
} else {
|
||||||
@@ -129,11 +159,6 @@ class ServerActivity : BaseActivity() {
|
|||||||
if (network >= 0) {
|
if (network >= 0) {
|
||||||
sp_network?.setSelection(network)
|
sp_network?.setSelection(network)
|
||||||
}
|
}
|
||||||
outbound.getTransportSettingDetails()?.let { transportDetails ->
|
|
||||||
sp_header_type.setSelection(Utils.arrayFind(headertypes, transportDetails[0]))
|
|
||||||
et_request_host.text = Utils.getEditable(transportDetails[1])
|
|
||||||
et_path.text = Utils.getEditable(transportDetails[2])
|
|
||||||
}
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,16 +271,20 @@ class ServerActivity : BaseActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun saveStreamSettings(streamSetting: V2rayConfig.OutboundBean.StreamSettingsBean, config: ServerConfig) {
|
private fun saveStreamSettings(streamSetting: V2rayConfig.OutboundBean.StreamSettingsBean, config: ServerConfig) {
|
||||||
val requestHost = et_request_host?.text.toString().trim()
|
val network = if (sp_network != null) networks[sp_network.selectedItemPosition] else DEFAULT_NETWORK
|
||||||
val path = et_path?.text.toString().trim()
|
val type = if (sp_header_type != null) transportTypes(network)[sp_header_type.selectedItemPosition] else "";
|
||||||
|
val requestHost = if (et_request_host != null) et_request_host.text.toString().trim() else ""
|
||||||
|
val path = if (et_path != null) et_path.text.toString().trim() else ""
|
||||||
var sni = streamSetting.populateTransportSettings(
|
var sni = streamSetting.populateTransportSettings(
|
||||||
if (sp_network != null) networks[sp_network.selectedItemPosition] else DEFAULT_NETWORK,
|
transport = network,
|
||||||
if (sp_header_type != null) headertypes[sp_header_type.selectedItemPosition] else "",
|
headerType = type,
|
||||||
requestHost,
|
host = requestHost,
|
||||||
path,
|
path = path,
|
||||||
path,
|
seed = path,
|
||||||
requestHost,
|
quicSecurity = requestHost,
|
||||||
path
|
key = path,
|
||||||
|
mode = type,
|
||||||
|
serviceName = path
|
||||||
)
|
)
|
||||||
val allowInsecure = if (sp_allow_insecure == null || allowinsecures[sp_allow_insecure.selectedItemPosition].isBlank()) {
|
val allowInsecure = if (sp_allow_insecure == null || allowinsecures[sp_allow_insecure.selectedItemPosition].isBlank()) {
|
||||||
false//settingsStorage?.decodeBool(PREF_ALLOW_INSECURE) ?: false
|
false//settingsStorage?.decodeBool(PREF_ALLOW_INSECURE) ?: false
|
||||||
@@ -270,6 +299,18 @@ class ServerActivity : BaseActivity() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun transportTypes(network: String?): Array<out String> {
|
||||||
|
return if (network == "tcp") {
|
||||||
|
tcpTypes
|
||||||
|
} else if (network == "kcp" || network == "quic") {
|
||||||
|
kcpAndQuicTypes
|
||||||
|
} else if (network == "grpc") {
|
||||||
|
grpcModes
|
||||||
|
} else {
|
||||||
|
arrayOf("---")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* save server config
|
* save server config
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ 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 android.support.v7.preference.*
|
||||||
|
import android.text.TextUtils
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import com.v2ray.ang.R
|
import com.v2ray.ang.R
|
||||||
import com.v2ray.ang.AppConfig
|
import com.v2ray.ang.AppConfig
|
||||||
@@ -29,6 +30,10 @@ class SettingsActivity : BaseActivity() {
|
|||||||
|
|
||||||
class SettingsFragment : PreferenceFragmentCompat() {
|
class SettingsFragment : PreferenceFragmentCompat() {
|
||||||
private val perAppProxy by lazy { findPreference(AppConfig.PREF_PER_APP_PROXY) as CheckBoxPreference }
|
private val perAppProxy by lazy { findPreference(AppConfig.PREF_PER_APP_PROXY) as CheckBoxPreference }
|
||||||
|
private val localDns by lazy { findPreference(AppConfig.PREF_LOCAL_DNS_ENABLED) }
|
||||||
|
private val fakeDns by lazy { findPreference(AppConfig.PREF_FAKE_DNS_ENABLED) }
|
||||||
|
private val localDnsPort by lazy { findPreference(AppConfig.PREF_LOCAL_DNS_PORT) }
|
||||||
|
private val vpnDns by lazy { findPreference(AppConfig.PREF_VPN_DNS) }
|
||||||
private val sppedEnabled by lazy { findPreference(AppConfig.PREF_SPEED_ENABLED) as CheckBoxPreference }
|
private val sppedEnabled by lazy { findPreference(AppConfig.PREF_SPEED_ENABLED) as CheckBoxPreference }
|
||||||
private val sniffingEnabled by lazy { findPreference(AppConfig.PREF_SNIFFING_ENABLED) as CheckBoxPreference }
|
private val sniffingEnabled by lazy { findPreference(AppConfig.PREF_SNIFFING_ENABLED) as CheckBoxPreference }
|
||||||
private val proxySharing by lazy { findPreference(AppConfig.PREF_PROXY_SHARING) as CheckBoxPreference }
|
private val proxySharing by lazy { findPreference(AppConfig.PREF_PROXY_SHARING) as CheckBoxPreference }
|
||||||
@@ -141,9 +146,21 @@ class SettingsActivity : BaseActivity() {
|
|||||||
restartProxy()
|
restartProxy()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
localDns?.setOnPreferenceChangeListener{ _, any ->
|
||||||
|
updateLocalDns(any as Boolean)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
localDnsPort?.setOnPreferenceChangeListener { _, any ->
|
||||||
|
val nval = any as String
|
||||||
|
localDnsPort?.summary = if (TextUtils.isEmpty(nval)) "10807" else nval
|
||||||
|
true
|
||||||
|
}
|
||||||
|
vpnDns?.setOnPreferenceChangeListener { _, any ->
|
||||||
|
vpnDns?.summary = any as String
|
||||||
|
true
|
||||||
|
}
|
||||||
mode.setOnPreferenceChangeListener { _, newValue ->
|
mode.setOnPreferenceChangeListener { _, newValue ->
|
||||||
updatePerAppProxy(newValue.toString())
|
updateMode(newValue.toString())
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
mode.dialogLayoutResource = R.layout.preference_with_help_link
|
mode.dialogLayoutResource = R.layout.preference_with_help_link
|
||||||
@@ -188,32 +205,43 @@ class SettingsActivity : BaseActivity() {
|
|||||||
override fun onStart() {
|
override fun onStart() {
|
||||||
super.onStart()
|
super.onStart()
|
||||||
val defaultSharedPreferences = PreferenceManager.getDefaultSharedPreferences(activity)
|
val defaultSharedPreferences = PreferenceManager.getDefaultSharedPreferences(activity)
|
||||||
updatePerAppProxy(defaultSharedPreferences.getString(AppConfig.PREF_MODE, "VPN"))
|
updateMode(defaultSharedPreferences.getString(AppConfig.PREF_MODE, "VPN"))
|
||||||
remoteDns.summary = 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, "")
|
||||||
|
|
||||||
if (remoteDns.summary == "") {
|
if (TextUtils.isEmpty(remoteDnsString)) {
|
||||||
remoteDns.summary = AppConfig.DNS_AGENT
|
remoteDnsString = AppConfig.DNS_AGENT
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( domesticDns.summary == "") {
|
if ( domesticDns.summary == "") {
|
||||||
domesticDns.summary = AppConfig.DNS_DIRECT
|
domesticDns.summary = AppConfig.DNS_DIRECT
|
||||||
}
|
}
|
||||||
|
remoteDns.summary = remoteDnsString
|
||||||
|
vpnDns?.summary = defaultSharedPreferences.getString(AppConfig.PREF_VPN_DNS, remoteDnsString)
|
||||||
|
|
||||||
// socksPort.summary = defaultSharedPreferences.getString(PREF_SOCKS_PORT, "10808")
|
// socksPort.summary = defaultSharedPreferences.getString(PREF_SOCKS_PORT, "10808")
|
||||||
// lanconnPort.summary = defaultSharedPreferences.getString(PREF_HTTP_PORT, "")
|
// lanconnPort.summary = defaultSharedPreferences.getString(PREF_HTTP_PORT, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updatePerAppProxy(mode: String?) {
|
private fun updateMode(mode: String?) {
|
||||||
if (mode == "VPN") {
|
val defaultSharedPreferences = PreferenceManager.getDefaultSharedPreferences(activity)
|
||||||
perAppProxy.isEnabled = true
|
val vpn = mode == "VPN"
|
||||||
perAppProxy.isChecked = PreferenceManager.getDefaultSharedPreferences(activity)
|
perAppProxy.isEnabled = vpn
|
||||||
.getBoolean(AppConfig.PREF_PER_APP_PROXY, false)
|
perAppProxy.isChecked = PreferenceManager.getDefaultSharedPreferences(activity)
|
||||||
} else {
|
.getBoolean(AppConfig.PREF_PER_APP_PROXY, false)
|
||||||
perAppProxy.isEnabled = false
|
localDns?.isEnabled = vpn
|
||||||
perAppProxy.isChecked = false
|
fakeDns?.isEnabled = vpn
|
||||||
|
localDnsPort?.isEnabled = vpn
|
||||||
|
vpnDns?.isEnabled = vpn
|
||||||
|
if (vpn) {
|
||||||
|
updateLocalDns(defaultSharedPreferences.getBoolean(AppConfig.PREF_LOCAL_DNS_ENABLED, false))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun updateLocalDns(enabled: Boolean) {
|
||||||
|
fakeDns?.isEnabled = enabled
|
||||||
|
localDnsPort?.isEnabled = enabled
|
||||||
|
vpnDns?.isEnabled = !enabled
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onModeHelpClicked(view: View) {
|
fun onModeHelpClicked(view: View) {
|
||||||
|
|||||||
@@ -69,7 +69,6 @@ object AngConfigManager {
|
|||||||
settingsStorage?.encode(key, sharedPreferences.getString(key, null))
|
settingsStorage?.encode(key, sharedPreferences.getString(key, null))
|
||||||
}
|
}
|
||||||
listOf(AppConfig.PREF_SPEED_ENABLED,
|
listOf(AppConfig.PREF_SPEED_ENABLED,
|
||||||
AppConfig.PREF_SNIFFING_ENABLED,
|
|
||||||
AppConfig.PREF_PROXY_SHARING,
|
AppConfig.PREF_PROXY_SHARING,
|
||||||
AppConfig.PREF_LOCAL_DNS_ENABLED,
|
AppConfig.PREF_LOCAL_DNS_ENABLED,
|
||||||
// AppConfig.PREF_ALLOW_INSECURE,
|
// AppConfig.PREF_ALLOW_INSECURE,
|
||||||
@@ -78,6 +77,7 @@ object AngConfigManager {
|
|||||||
AppConfig.PREF_BYPASS_APPS,).forEach { key ->
|
AppConfig.PREF_BYPASS_APPS,).forEach { key ->
|
||||||
settingsStorage?.encode(key, sharedPreferences.getBoolean(key, false))
|
settingsStorage?.encode(key, sharedPreferences.getBoolean(key, false))
|
||||||
}
|
}
|
||||||
|
settingsStorage?.encode(AppConfig.PREF_SNIFFING_ENABLED, sharedPreferences.getBoolean(AppConfig.PREF_SNIFFING_ENABLED, true))
|
||||||
settingsStorage?.encode(AppConfig.PREF_PER_APP_PROXY_SET, sharedPreferences.getStringSet(AppConfig.PREF_PER_APP_PROXY_SET, setOf()))
|
settingsStorage?.encode(AppConfig.PREF_PER_APP_PROXY_SET, sharedPreferences.getStringSet(AppConfig.PREF_PER_APP_PROXY_SET, setOf()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,7 +131,8 @@ object AngConfigManager {
|
|||||||
}
|
}
|
||||||
config.outboundBean?.streamSettings?.let { streamSetting ->
|
config.outboundBean?.streamSettings?.let { streamSetting ->
|
||||||
val sni = streamSetting.populateTransportSettings(vmessBean.network, vmessBean.headerType,
|
val sni = streamSetting.populateTransportSettings(vmessBean.network, vmessBean.headerType,
|
||||||
vmessBean.requestHost, vmessBean.path, vmessBean.path, vmessBean.requestHost, vmessBean.path)
|
vmessBean.requestHost, vmessBean.path, vmessBean.path, vmessBean.requestHost, vmessBean.path,
|
||||||
|
vmessBean.headerType, vmessBean.path)
|
||||||
// val allowInsecure = if (vmessBean.allowInsecure.isBlank()) {
|
// val allowInsecure = if (vmessBean.allowInsecure.isBlank()) {
|
||||||
// settingsStorage?.decodeBool(AppConfig.PREF_ALLOW_INSECURE) ?: false
|
// settingsStorage?.decodeBool(AppConfig.PREF_ALLOW_INSECURE) ?: false
|
||||||
// } else {
|
// } else {
|
||||||
@@ -191,6 +192,7 @@ 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)
|
||||||
@@ -209,9 +211,9 @@ object AngConfigManager {
|
|||||||
vnext.users[0].alterId = Utils.parseInt(vmessQRCode.aid)
|
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.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)) {
|
||||||
@@ -312,7 +314,8 @@ object AngConfigManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val sni = streamSetting.populateTransportSettings(queryParam["type"] ?: "tcp", queryParam["headerType"],
|
val sni = streamSetting.populateTransportSettings(queryParam["type"] ?: "tcp", queryParam["headerType"],
|
||||||
queryParam["host"], queryParam["path"], queryParam["seed"], queryParam["quicSecurity"], queryParam["key"])
|
queryParam["host"], queryParam["path"], queryParam["seed"], queryParam["quicSecurity"], queryParam["key"],
|
||||||
|
queryParam["mode"], queryParam["serviceName"])
|
||||||
streamSetting.populateTlsSettings(queryParam["security"] ?: "", allowInsecure, queryParam["sni"] ?: sni)
|
streamSetting.populateTlsSettings(queryParam["security"] ?: "", allowInsecure, queryParam["sni"] ?: sni)
|
||||||
}
|
}
|
||||||
if (config == null){
|
if (config == null){
|
||||||
@@ -337,7 +340,7 @@ object AngConfigManager {
|
|||||||
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, alterId) =
|
||||||
Regex("(tcp|http|ws|kcp|quic)(\\+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})-([0-9]+)")
|
||||||
.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()
|
||||||
@@ -357,8 +360,8 @@ object AngConfigManager {
|
|||||||
|
|
||||||
val sni = streamSetting.populateTransportSettings(protocol, queryParam["type"],
|
val sni = streamSetting.populateTransportSettings(protocol, queryParam["type"],
|
||||||
queryParam["host"]?.split("|")?.get(0) ?: "",
|
queryParam["host"]?.split("|")?.get(0) ?: "",
|
||||||
queryParam["path"]?.takeIf { it.trim() != "/" } ?: "",
|
queryParam["path"]?.takeIf { it.trim() != "/" } ?: "", queryParam["seed"], queryParam["security"],
|
||||||
queryParam["seed"], queryParam["security"], queryParam["key"])
|
queryParam["key"], queryParam["mode"], queryParam["serviceName"])
|
||||||
streamSetting.populateTlsSettings(if (tls) TLS else "", allowInsecure, sni)
|
streamSetting.populateTlsSettings(if (tls) TLS else "", allowInsecure, sni)
|
||||||
true
|
true
|
||||||
}.getOrElse { false }
|
}.getOrElse { false }
|
||||||
@@ -501,6 +504,10 @@ object AngConfigManager {
|
|||||||
dicQuery["quicSecurity"] = Utils.urlEncode(transportDetails[1])
|
dicQuery["quicSecurity"] = Utils.urlEncode(transportDetails[1])
|
||||||
dicQuery["key"] = Utils.urlEncode(transportDetails[2])
|
dicQuery["key"] = Utils.urlEncode(transportDetails[2])
|
||||||
}
|
}
|
||||||
|
"grpc" -> {
|
||||||
|
dicQuery["mode"] = transportDetails[0]
|
||||||
|
dicQuery["serviceName"] = transportDetails[2]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val query = "?" + dicQuery.toList().joinToString(
|
val query = "?" + dicQuery.toList().joinToString(
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import android.content.ClipData
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.SystemClock
|
import android.os.SystemClock
|
||||||
import android.text.TextUtils
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.util.Patterns
|
import android.util.Patterns
|
||||||
import android.webkit.URLUtil
|
import android.webkit.URLUtil
|
||||||
@@ -103,9 +102,14 @@ object Utils {
|
|||||||
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 ""
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -123,41 +127,31 @@ object Utils {
|
|||||||
/**
|
/**
|
||||||
* get remote dns servers from preference
|
* get remote dns servers from preference
|
||||||
*/
|
*/
|
||||||
fun getRemoteDnsServers(): ArrayList<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 = ArrayList<String>()
|
val ret = remoteDns.split(",").filter { isPureIpAddress(it) || it.startsWith("https") }
|
||||||
if (!TextUtils.isEmpty(remoteDns)) {
|
if (ret.isEmpty()) {
|
||||||
remoteDns
|
return listOf(AppConfig.DNS_AGENT)
|
||||||
.split(",")
|
|
||||||
.forEach {
|
|
||||||
if (Utils.isPureIpAddress(it)) {
|
|
||||||
ret.add(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (ret.size == 0) {
|
|
||||||
ret.add(AppConfig.DNS_AGENT)
|
|
||||||
}
|
}
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getVpnDnsServers(): List<String> {
|
||||||
|
val vpnDns = settingsStorage?.decodeString(AppConfig.PREF_VPN_DNS)
|
||||||
|
?: settingsStorage?.decodeString(AppConfig.PREF_REMOTE_DNS)
|
||||||
|
?: AppConfig.DNS_AGENT
|
||||||
|
return vpnDns.split(",").filter { isPureIpAddress(it) }
|
||||||
|
// allow empty, in that case dns will use system default
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get remote dns servers from preference
|
* get remote dns servers from preference
|
||||||
*/
|
*/
|
||||||
fun getDomesticDnsServers(): ArrayList<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 = ArrayList<String>()
|
val ret = domesticDns.split(",").filter { isPureIpAddress(it) || it.startsWith("https") }
|
||||||
if (!TextUtils.isEmpty(domesticDns)) {
|
if (ret.isEmpty()) {
|
||||||
domesticDns
|
return listOf(AppConfig.DNS_DIRECT)
|
||||||
.split(",")
|
|
||||||
.forEach {
|
|
||||||
if (Utils.isPureIpAddress(it)) {
|
|
||||||
ret.add(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (ret.size == 0) {
|
|
||||||
ret.add(AppConfig.DNS_DIRECT)
|
|
||||||
}
|
}
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
@@ -391,7 +385,7 @@ object Utils {
|
|||||||
val command = "/system/bin/ping -c 3 $url"
|
val command = "/system/bin/ping -c 3 $url"
|
||||||
val process = Runtime.getRuntime().exec(command)
|
val process = Runtime.getRuntime().exec(command)
|
||||||
val allText = process.inputStream.bufferedReader().use { it.readText() }
|
val allText = process.inputStream.bufferedReader().use { it.readText() }
|
||||||
if (!TextUtils.isEmpty(allText)) {
|
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) {
|
||||||
|
|||||||
@@ -6,17 +6,13 @@ 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.HTTP
|
||||||
|
|
||||||
object V2rayConfigUtil {
|
object V2rayConfigUtil {
|
||||||
// private val requestObj: JsonObject by lazy {
|
|
||||||
// Gson().fromJson("""{"version":"1.1","method":"GET","path":["/"],"headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.75 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"}}""", JsonObject::class.java)
|
|
||||||
// }
|
|
||||||
// private val responseObj: JSONObject by lazy {
|
|
||||||
// JSONObject("""{"version":"1.1","status":"200","reason":"OK","headers":{"Content-Type":["application/octet-stream","video/mpeg"],"Transfer-Encoding":["chunked"],"Connection":["keep-alive"],"Pragma":"no-cache"}}""")
|
|
||||||
// }
|
|
||||||
|
|
||||||
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) }
|
||||||
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) }
|
||||||
|
|
||||||
@@ -35,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()
|
||||||
@@ -66,14 +62,18 @@ object V2rayConfigUtil {
|
|||||||
|
|
||||||
inbounds(v2rayConfig)
|
inbounds(v2rayConfig)
|
||||||
|
|
||||||
|
httpRequestObject(outbound)
|
||||||
|
|
||||||
v2rayConfig.outbounds[0] = outbound
|
v2rayConfig.outbounds[0] = outbound
|
||||||
|
|
||||||
routing(v2rayConfig)
|
routing(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
|
||||||
@@ -99,7 +99,17 @@ object V2rayConfigUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
v2rayConfig.inbounds[0].port = 10808
|
v2rayConfig.inbounds[0].port = 10808
|
||||||
v2rayConfig.inbounds[0].sniffing?.enabled = settingsStorage?.decodeBool(AppConfig.PREF_SNIFFING_ENABLED) ?: true
|
val fakedns = settingsStorage?.decodeBool(AppConfig.PREF_FAKE_DNS_ENABLED)
|
||||||
|
?: false
|
||||||
|
val sniffAllTlsAndHttp = settingsStorage?.decodeBool(AppConfig.PREF_SNIFFING_ENABLED)
|
||||||
|
?: true
|
||||||
|
v2rayConfig.inbounds[0].sniffing?.enabled = fakedns || sniffAllTlsAndHttp
|
||||||
|
if (!sniffAllTlsAndHttp) {
|
||||||
|
v2rayConfig.inbounds[0].sniffing?.destOverride?.clear()
|
||||||
|
}
|
||||||
|
if (fakedns) {
|
||||||
|
v2rayConfig.inbounds[0].sniffing?.destOverride?.add("fakedns")
|
||||||
|
}
|
||||||
|
|
||||||
//v2rayConfig.inbounds[1].port = httpPort
|
//v2rayConfig.inbounds[1].port = httpPort
|
||||||
|
|
||||||
@@ -116,16 +126,29 @@ object V2rayConfigUtil {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun fakedns(v2rayConfig: V2rayConfig) {
|
||||||
|
if (settingsStorage?.decodeBool(AppConfig.PREF_FAKE_DNS_ENABLED) == true) {
|
||||||
|
v2rayConfig.fakedns = V2rayConfig.FakednsBean()
|
||||||
|
v2rayConfig.outbounds.filter { it.protocol == "freedom" }.forEach {
|
||||||
|
it.settings?.domainStrategy = "UseIP"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* routing
|
* routing
|
||||||
*/
|
*/
|
||||||
private fun routing(v2rayConfig: V2rayConfig): Boolean {
|
private fun routing(v2rayConfig: V2rayConfig): Boolean {
|
||||||
try {
|
try {
|
||||||
routingUserRule(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_AGENT) ?: "", AppConfig.TAG_AGENT, v2rayConfig)
|
routingUserRule(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_AGENT)
|
||||||
routingUserRule(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_DIRECT) ?: "", AppConfig.TAG_DIRECT, v2rayConfig)
|
?: "", AppConfig.TAG_AGENT, v2rayConfig)
|
||||||
routingUserRule(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_BLOCKED) ?: "", AppConfig.TAG_BLOCKED, v2rayConfig)
|
routingUserRule(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_DIRECT)
|
||||||
|
?: "", AppConfig.TAG_DIRECT, v2rayConfig)
|
||||||
|
routingUserRule(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_BLOCKED)
|
||||||
|
?: "", AppConfig.TAG_BLOCKED, v2rayConfig)
|
||||||
|
|
||||||
v2rayConfig.routing.domainStrategy = settingsStorage?.decodeString(AppConfig.PREF_ROUTING_DOMAIN_STRATEGY) ?: "IPIfNonMatch"
|
v2rayConfig.routing.domainStrategy = settingsStorage?.decodeString(AppConfig.PREF_ROUTING_DOMAIN_STRATEGY)
|
||||||
|
?: "IPIfNonMatch"
|
||||||
val routingMode = settingsStorage?.decodeString(AppConfig.PREF_ROUTING_MODE) ?: "0"
|
val routingMode = settingsStorage?.decodeString(AppConfig.PREF_ROUTING_MODE) ?: "0"
|
||||||
|
|
||||||
// Hardcode googleapis.cn
|
// Hardcode googleapis.cn
|
||||||
@@ -239,47 +262,19 @@ object V2rayConfigUtil {
|
|||||||
*/
|
*/
|
||||||
private fun customLocalDns(v2rayConfig: V2rayConfig): Boolean {
|
private fun customLocalDns(v2rayConfig: V2rayConfig): Boolean {
|
||||||
try {
|
try {
|
||||||
val hosts = mutableMapOf<String, String>()
|
if (settingsStorage?.decodeBool(AppConfig.PREF_FAKE_DNS_ENABLED) == true) {
|
||||||
val servers = ArrayList<Any>()
|
val geositeCn = arrayListOf("geosite:cn")
|
||||||
val remoteDns = Utils.getRemoteDnsServers()
|
val proxyDomain = userRule2Domian(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_AGENT)
|
||||||
|
?: "")
|
||||||
remoteDns.forEach {
|
val directDomain = userRule2Domian(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_DIRECT)
|
||||||
servers.add(it)
|
?: "")
|
||||||
|
// fakedns with all domains to make it always top priority
|
||||||
|
v2rayConfig.dns.servers?.add(0,
|
||||||
|
V2rayConfig.DnsBean.ServersBean(address = "fakedns", domains = geositeCn.plus(proxyDomain).plus(directDomain)))
|
||||||
}
|
}
|
||||||
|
|
||||||
val domesticDns = Utils.getDomesticDnsServers()
|
|
||||||
val geositeCn = arrayListOf("geosite:cn")
|
|
||||||
val geoipCn = arrayListOf("geoip:cn")
|
|
||||||
|
|
||||||
val agDomain = userRule2Domian(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_AGENT) ?: "")
|
|
||||||
if (agDomain.size > 0) {
|
|
||||||
servers.add(V2rayConfig.DnsBean.ServersBean(remoteDns.first(), 53, agDomain, null))
|
|
||||||
}
|
|
||||||
|
|
||||||
val dirDomain = userRule2Domian(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_DIRECT) ?: "")
|
|
||||||
if (dirDomain.size > 0) {
|
|
||||||
servers.add(V2rayConfig.DnsBean.ServersBean(domesticDns.first(), 53, dirDomain, 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 (remoteDns.first().startsWith("https")) "1.1.1.1" else remoteDns.first(),
|
||||||
@@ -308,16 +303,75 @@ 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 (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 (!domesticDns.first().startsWith("https")) {
|
||||||
|
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.put("domain:googleapis.cn", "googleapis.com")
|
||||||
|
|
||||||
|
// DNS dns对象
|
||||||
|
v2rayConfig.dns = V2rayConfig.DnsBean(
|
||||||
|
servers = servers,
|
||||||
|
hosts = hosts)
|
||||||
|
|
||||||
|
// DNS routing
|
||||||
if (!remoteDns.first().startsWith("https")) {
|
if (!remoteDns.first().startsWith("https")) {
|
||||||
v2rayConfig.routing.rules.add(0, V2rayConfig.RoutingBean.RulesBean(
|
v2rayConfig.routing.rules.add(0, V2rayConfig.RoutingBean.RulesBean(
|
||||||
type = "field",
|
type = "field",
|
||||||
@@ -327,15 +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) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
return false
|
return false
|
||||||
@@ -343,25 +388,26 @@ object V2rayConfigUtil {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private fun httpRequestObject(outbound: V2rayConfig.OutboundBean): Boolean {
|
||||||
* Custom Remote Dns
|
|
||||||
*/
|
|
||||||
private fun customRemoteDns(v2rayConfig: V2rayConfig): Boolean {
|
|
||||||
try {
|
try {
|
||||||
val servers = ArrayList<Any>()
|
if (outbound.streamSettings?.network == DEFAULT_NETWORK
|
||||||
val hosts = mutableMapOf<String, String>()
|
&& outbound.streamSettings?.tcpSettings?.header?.type == HTTP) {
|
||||||
|
val path = outbound.streamSettings?.tcpSettings?.header?.request?.path
|
||||||
|
val Host = outbound.streamSettings?.tcpSettings?.header?.request?.headers?.Host
|
||||||
|
|
||||||
Utils.getRemoteDnsServers().forEach {
|
val requestString: String by lazy {
|
||||||
servers.add(it)
|
"""{"version":"1.1","method":"GET","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36","Mozilla/5.0 (iPhone; CPU iPhone OS 10_0_2 like Mac OS X) AppleWebKit/601.1 (KHTML, like Gecko) CriOS/53.0.2785.109 Mobile/14A456 Safari/601.1.46"],"Accept-Encoding":["gzip, deflate"],"Connection":["keep-alive"],"Pragma":"no-cache"}}"""
|
||||||
|
}
|
||||||
|
outbound.streamSettings?.tcpSettings?.header?.request = Gson().fromJson(requestString, V2rayConfig.OutboundBean.StreamSettingsBean.TcpSettingsBean.HeaderBean.RequestBean::class.java)
|
||||||
|
outbound.streamSettings?.tcpSettings?.header?.request?.path = path!!
|
||||||
|
outbound.streamSettings?.tcpSettings?.header?.request?.headers?.Host = Host!!
|
||||||
}
|
}
|
||||||
// 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
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,13 +21,15 @@ import com.v2ray.ang.util.*
|
|||||||
import com.v2ray.ang.util.MmkvManager.KEY_ANG_CONFIGS
|
import com.v2ray.ang.util.MmkvManager.KEY_ANG_CONFIGS
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
class MainViewModel(application: Application) : AndroidViewModel(application) {
|
class MainViewModel(application: Application) : AndroidViewModel(application) {
|
||||||
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) }
|
||||||
|
|
||||||
var serverList= MmkvManager.decodeServerList()
|
var serverList = MmkvManager.decodeServerList()
|
||||||
private set
|
private set
|
||||||
|
val serversCache = ConcurrentHashMap<String, ServerConfig>()
|
||||||
val isRunning by lazy { MutableLiveData<Boolean>() }
|
val isRunning by lazy { MutableLiveData<Boolean>() }
|
||||||
val updateListAction by lazy { MutableLiveData<Int>() }
|
val updateListAction by lazy { MutableLiveData<Int>() }
|
||||||
val updateTestResultAction by lazy { MutableLiveData<String>() }
|
val updateTestResultAction by lazy { MutableLiveData<String>() }
|
||||||
@@ -50,6 +52,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
|
|||||||
|
|
||||||
fun reloadServerList() {
|
fun reloadServerList() {
|
||||||
serverList = MmkvManager.decodeServerList()
|
serverList = MmkvManager.decodeServerList()
|
||||||
|
updateCache()
|
||||||
updateListAction.value = -1
|
updateListAction.value = -1
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,6 +68,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
|
|||||||
val key = MmkvManager.encodeServerConfig("", config)
|
val key = MmkvManager.encodeServerConfig("", config)
|
||||||
serverRawStorage?.encode(key, server)
|
serverRawStorage?.encode(key, server)
|
||||||
serverList.add(key)
|
serverList.add(key)
|
||||||
|
serversCache[key] = config
|
||||||
}
|
}
|
||||||
|
|
||||||
fun swapServer(fromPosition: Int, toPosition: Int) {
|
fun swapServer(fromPosition: Int, toPosition: Int) {
|
||||||
@@ -72,6 +76,17 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
|
|||||||
mainStorage?.encode(KEY_ANG_CONFIGS, Gson().toJson(serverList))
|
mainStorage?.encode(KEY_ANG_CONFIGS, Gson().toJson(serverList))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun updateCache() {
|
||||||
|
serversCache.clear()
|
||||||
|
GlobalScope.launch(Dispatchers.Default) {
|
||||||
|
serverList.forEach { guid ->
|
||||||
|
MmkvManager.decodeServerConfig(guid)?.let {
|
||||||
|
serversCache[guid] = it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun testAllTcping() {
|
fun testAllTcping() {
|
||||||
tcpingTestScope.coroutineContext[Job]?.cancelChildren()
|
tcpingTestScope.coroutineContext[Job]?.cancelChildren()
|
||||||
Utils.closeAllTcpSockets()
|
Utils.closeAllTcpSockets()
|
||||||
@@ -80,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) {
|
||||||
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) {
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ class SettingsViewModel(application: Application) : AndroidViewModel(application
|
|||||||
Log.d(AppConfig.ANG_PACKAGE, "Observe settings changed: $key")
|
Log.d(AppConfig.ANG_PACKAGE, "Observe settings changed: $key")
|
||||||
when(key) {
|
when(key) {
|
||||||
AppConfig.PREF_MODE,
|
AppConfig.PREF_MODE,
|
||||||
|
AppConfig.PREF_VPN_DNS,
|
||||||
AppConfig.PREF_REMOTE_DNS,
|
AppConfig.PREF_REMOTE_DNS,
|
||||||
AppConfig.PREF_DOMESTIC_DNS,
|
AppConfig.PREF_DOMESTIC_DNS,
|
||||||
AppConfig.PREF_ROUTING_DOMAIN_STRATEGY,
|
AppConfig.PREF_ROUTING_DOMAIN_STRATEGY,
|
||||||
@@ -37,14 +38,17 @@ class SettingsViewModel(application: Application) : AndroidViewModel(application
|
|||||||
settingsStorage?.encode(key, sharedPreferences.getString(key, ""))
|
settingsStorage?.encode(key, sharedPreferences.getString(key, ""))
|
||||||
}
|
}
|
||||||
AppConfig.PREF_SPEED_ENABLED,
|
AppConfig.PREF_SPEED_ENABLED,
|
||||||
AppConfig.PREF_SNIFFING_ENABLED,
|
|
||||||
AppConfig.PREF_PROXY_SHARING,
|
AppConfig.PREF_PROXY_SHARING,
|
||||||
AppConfig.PREF_LOCAL_DNS_ENABLED,
|
AppConfig.PREF_LOCAL_DNS_ENABLED,
|
||||||
|
AppConfig.PREF_FAKE_DNS_ENABLED,
|
||||||
AppConfig.PREF_FORWARD_IPV6,
|
AppConfig.PREF_FORWARD_IPV6,
|
||||||
AppConfig.PREF_PER_APP_PROXY,
|
AppConfig.PREF_PER_APP_PROXY,
|
||||||
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, -> {
|
||||||
|
settingsStorage?.encode(key, sharedPreferences.getBoolean(key, true))
|
||||||
|
}
|
||||||
AppConfig.PREF_PER_APP_PROXY_SET -> {
|
AppConfig.PREF_PER_APP_PROXY_SET -> {
|
||||||
settingsStorage?.encode(key, sharedPreferences.getStringSet(key, setOf()))
|
settingsStorage?.encode(key, sharedPreferences.getStringSet(key, setOf()))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -174,6 +174,7 @@
|
|||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
|
android:id="@+id/sp_header_type_title"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/server_lab_head_type" />
|
android:text="@string/server_lab_head_type" />
|
||||||
@@ -181,8 +182,7 @@
|
|||||||
<Spinner
|
<Spinner
|
||||||
android:id="@+id/sp_header_type"
|
android:id="@+id/sp_header_type"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="@dimen/edit_height"
|
android:layout_height="@dimen/edit_height" />
|
||||||
android:entries="@array/headertypes" />
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
|||||||
@@ -43,8 +43,9 @@
|
|||||||
<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>
|
||||||
<string name="server_lab_head_type">伪装类型(type)</string>
|
<string name="server_lab_head_type">伪装类型(type)</string>
|
||||||
|
<string name="server_lab_mode_type">gRPC 传输模式 (mode)</string>
|
||||||
<string name="server_lab_request_host">伪装域名(host)(host/ws host/h2 host)/QUIC 加密方式</string>
|
<string name="server_lab_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>
|
||||||
@@ -76,7 +77,7 @@
|
|||||||
<!-- 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_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">分应用代理仅支持 Android 5.0 Lollipop 及更高</string>
|
||||||
|
|
||||||
@@ -87,10 +88,13 @@
|
|||||||
<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由本地设备的v2Ray-core处理(测试)</string>
|
<string name="summary_pref_local_dns_enabled">DNS 请求导入 core 由 DNS 模块处理(推荐启用 如果需要路由绕过局域网及大陆地址)</string>
|
||||||
|
|
||||||
|
<string name="title_pref_fake_dns_enabled">启用虚拟DNS</string>
|
||||||
|
<string name="summary_pref_fake_dns_enabled">本地返回虚构解析结果 (减低延时 但个别应用可能无法使用)</string>
|
||||||
|
|
||||||
<string name="title_pref_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>
|
||||||
@@ -107,7 +111,9 @@
|
|||||||
<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_domestic_dns">境内DNS (可选,本地DNS模式下生效)</string>
|
<string name="title_pref_vpn_dns">VPN DNS (仅支持 IPv4/v6)</string>
|
||||||
|
|
||||||
|
<string name="title_pref_domestic_dns">境内DNS (可选)</string>
|
||||||
<string name="summary_pref_domestic_dns">DNS</string>
|
<string name="summary_pref_domestic_dns">DNS</string>
|
||||||
|
|
||||||
<string name="title_pref_socks_port">SOCKS5代理端口</string>
|
<string name="title_pref_socks_port">SOCKS5代理端口</string>
|
||||||
@@ -116,6 +122,9 @@
|
|||||||
<string name="title_pref_http_port">HTTP代理端口</string>
|
<string name="title_pref_http_port">HTTP代理端口</string>
|
||||||
<string name="summary_pref_http_port">HTTP代理端口</string>
|
<string name="summary_pref_http_port">HTTP代理端口</string>
|
||||||
|
|
||||||
|
<string name="title_pref_local_dns_port">本地DNS端口</string>
|
||||||
|
<string name="summary_pref_local_dns_port">本地DNS端口</string>
|
||||||
|
|
||||||
<string name="title_pref_feedback">反馈</string>
|
<string name="title_pref_feedback">反馈</string>
|
||||||
<string name="summary_pref_feedback">反馈改进或漏洞至 GitHub</string>
|
<string name="summary_pref_feedback">反馈改进或漏洞至 GitHub</string>
|
||||||
<string name="summary_pref_tg_group">加入Telegram Group</string>
|
<string name="summary_pref_tg_group">加入Telegram Group</string>
|
||||||
|
|||||||
@@ -43,8 +43,9 @@
|
|||||||
<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_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>
|
||||||
@@ -77,7 +78,7 @@
|
|||||||
<!-- 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_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">Proxy 個別應用程式模式只支援 Android 5.0 (Lollipop) 或更高</string>
|
||||||
|
|
||||||
@@ -88,10 +89,13 @@
|
|||||||
<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請求由本地的v2ray core處理(測試)</string>
|
<string name="summary_pref_local_dns_enabled">DNS 請求導入 core 由 DNS 模塊處理(推薦啟用 如果需要路由略過局域網及中國大陸)</string>
|
||||||
|
|
||||||
|
<string name="title_pref_fake_dns_enabled">啟用虛擬DNS</string>
|
||||||
|
<string name="summary_pref_fake_dns_enabled">本地返回虛構解析結果 (減低延時 但個別應用可能無法使用)</string>
|
||||||
|
|
||||||
<string name="title_pref_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>
|
||||||
@@ -108,7 +112,9 @@
|
|||||||
<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_domestic_dns">境内DNS (可選,僅本地DNS模式下生效)</string>
|
<string name="title_pref_vpn_dns">VPN DNS (僅支持 IPv4/v6)</string>
|
||||||
|
|
||||||
|
<string name="title_pref_domestic_dns">境内DNS (可選)</string>
|
||||||
<string name="summary_pref_domestic_dns">DNS</string>
|
<string name="summary_pref_domestic_dns">DNS</string>
|
||||||
|
|
||||||
<string name="title_pref_socks_port">SOCKS5代理連接埠</string>
|
<string name="title_pref_socks_port">SOCKS5代理連接埠</string>
|
||||||
@@ -117,6 +123,8 @@
|
|||||||
<string name="title_pref_http_port">HTTP代理連接埠</string>
|
<string name="title_pref_http_port">HTTP代理連接埠</string>
|
||||||
<string name="summary_pref_http_port">HTTP代理連接埠</string>
|
<string name="summary_pref_http_port">HTTP代理連接埠</string>
|
||||||
|
|
||||||
|
<string name="title_pref_local_dns_port">本地DNS端口</string>
|
||||||
|
<string name="summary_pref_local_dns_port">本地DNS端口</string>
|
||||||
|
|
||||||
<string name="title_pref_feedback">回饋</string>
|
<string name="title_pref_feedback">回饋</string>
|
||||||
<string name="summary_pref_feedback">回饋提升或前往 GitHub 回報 Bug</string>
|
<string name="summary_pref_feedback">回饋提升或前往 GitHub 回報 Bug</string>
|
||||||
|
|||||||
@@ -27,9 +27,13 @@
|
|||||||
<item>grpc</item>
|
<item>grpc</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
<string-array name="headertypes" translatable="false">
|
<string-array name="header_type_tcp" translatable="false">
|
||||||
<item>none</item>
|
<item>none</item>
|
||||||
<item>http</item>
|
<item>http</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
|
<string-array name="header_type_kcp_and_quic" translatable="false">
|
||||||
|
<item>none</item>
|
||||||
<item>srtp</item>
|
<item>srtp</item>
|
||||||
<item>utp</item>
|
<item>utp</item>
|
||||||
<item>wechat-video</item>
|
<item>wechat-video</item>
|
||||||
@@ -37,18 +41,10 @@
|
|||||||
<item>wireguard</item>
|
<item>wireguard</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
<string-array name="headertypetcps" translatable="false">
|
<string-array name="mode_type_grpc" translatable="false">
|
||||||
<item>none</item>
|
<item>gun</item>
|
||||||
<item>http</item>
|
<item>multi</item>
|
||||||
</string-array>
|
<!--Hide this option until core support it <item>guna</item>-->
|
||||||
|
|
||||||
<string-array name="headertypekcps" translatable="false">
|
|
||||||
<item>none</item>
|
|
||||||
<item>srtp</item>
|
|
||||||
<item>utp</item>
|
|
||||||
<item>wechat-video</item>
|
|
||||||
<item>dtls</item>
|
|
||||||
<item>wireguard</item>
|
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
<string-array name="streamsecuritys" translatable="false">
|
<string-array name="streamsecuritys" translatable="false">
|
||||||
@@ -136,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>
|
||||||
|
|||||||
@@ -40,11 +40,12 @@
|
|||||||
<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_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>
|
||||||
<string name="server_lab_head_type">head type</string>
|
<string name="server_lab_head_type">head type</string>
|
||||||
|
<string name="server_lab_mode_type">gRPC mode</string>
|
||||||
<string name="server_lab_request_host">request host(host/ws host/h2 host)/QUIC security</string>
|
<string name="server_lab_request_host">request host(host/ws host/h2 host)/QUIC security</string>
|
||||||
<string name="server_lab_path">path(ws path/h2 path)/QUIC key/kcp seed/grpc serviceName</string>
|
<string name="server_lab_path">path(ws path/h2 path)/QUIC key/kcp seed/gRPC serviceName</string>
|
||||||
<string name="server_lab_stream_security">tls</string>
|
<string name="server_lab_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">address</string>
|
<string name="server_lab_address3">address</string>
|
||||||
@@ -77,7 +78,7 @@
|
|||||||
<!-- Preferences -->
|
<!-- Preferences -->
|
||||||
<string name="title_settings">Settings</string>
|
<string name="title_settings">Settings</string>
|
||||||
<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_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">Per-app proxy mode only support Android 5.0 Lollipop or higher</string>
|
||||||
|
|
||||||
@@ -89,10 +90,14 @@
|
|||||||
usage.</string>
|
usage.</string>
|
||||||
|
|
||||||
<string name="title_pref_sniffing_enabled">Enable Sniffing</string>
|
<string name="title_pref_sniffing_enabled">Enable Sniffing</string>
|
||||||
<string name="summary_pref_sniffing_enabled">Sniffing</string>
|
<string name="summary_pref_sniffing_enabled">Try sniff domain from the packet (default on)</string>
|
||||||
|
|
||||||
<string name="title_pref_local_dns_enabled">Enable local DNS</string>
|
<string name="title_pref_local_dns_enabled">Enable local DNS</string>
|
||||||
<string name="summary_pref_local_dns_enabled">DNS processed by v2ray-core (BETA)</string>
|
<string name="summary_pref_local_dns_enabled">DNS processed by core‘s DNS module (Recommended, if need routing Bypassing LAN and
|
||||||
|
mainland address)</string>
|
||||||
|
|
||||||
|
<string name="title_pref_fake_dns_enabled">Enable fake DNS</string>
|
||||||
|
<string name="summary_pref_fake_dns_enabled">local DNS returns fake IP address (faster, but it may not work for some apps)</string>
|
||||||
|
|
||||||
<string name="title_pref_forward_ipv6">IPv6 Route</string>
|
<string name="title_pref_forward_ipv6">IPv6 Route</string>
|
||||||
<string name="summary_pref_forward_ipv6">Redirect IPv6 traffic to remote (BETA)</string>
|
<string name="summary_pref_forward_ipv6">Redirect IPv6 traffic to remote (BETA)</string>
|
||||||
@@ -109,7 +114,9 @@
|
|||||||
<string name="title_pref_remote_dns">Remote DNS (Optional)</string>
|
<string name="title_pref_remote_dns">Remote DNS (Optional)</string>
|
||||||
<string name="summary_pref_remote_dns">DNS</string>
|
<string name="summary_pref_remote_dns">DNS</string>
|
||||||
|
|
||||||
<string name="title_pref_domestic_dns">Domestic DNS (Optional, effects on LocalDNS mode)</string>
|
<string name="title_pref_vpn_dns">VPN DNS (only IPv4/v6)</string>
|
||||||
|
|
||||||
|
<string name="title_pref_domestic_dns">Domestic DNS (Optional)</string>
|
||||||
<string name="summary_pref_domestic_dns">DNS</string>
|
<string name="summary_pref_domestic_dns">DNS</string>
|
||||||
|
|
||||||
<string name="title_pref_socks_port">SOCKS5 proxy port</string>
|
<string name="title_pref_socks_port">SOCKS5 proxy port</string>
|
||||||
@@ -118,6 +125,9 @@
|
|||||||
<string name="title_pref_http_port">HTTP proxy port</string>
|
<string name="title_pref_http_port">HTTP proxy port</string>
|
||||||
<string name="summary_pref_http_port">HTTP proxy port</string>
|
<string name="summary_pref_http_port">HTTP proxy port</string>
|
||||||
|
|
||||||
|
<string name="title_pref_local_dns_port">Local DNS port</string>
|
||||||
|
<string name="summary_pref_local_dns_port">Local DNS port</string>
|
||||||
|
|
||||||
<string name="title_pref_feedback">Feedback</string>
|
<string name="title_pref_feedback">Feedback</string>
|
||||||
<string name="summary_pref_feedback">Feedback enhancements or bugs to GitHub</string>
|
<string name="summary_pref_feedback">Feedback enhancements or bugs to GitHub</string>
|
||||||
<string name="summary_pref_tg_group">Join Telegram Group</string>
|
<string name="summary_pref_tg_group">Join Telegram Group</string>
|
||||||
|
|||||||
@@ -1,6 +1,17 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<PreferenceCategory android:title="@string/title_settings">
|
<CheckBoxPreference
|
||||||
|
android:key="pref_speed_enabled"
|
||||||
|
android:summary="@string/summary_pref_speed_enabled"
|
||||||
|
android:title="@string/title_pref_speed_enabled" />
|
||||||
|
|
||||||
|
<CheckBoxPreference
|
||||||
|
android:defaultValue="true"
|
||||||
|
android:key="pref_sniffing_enabled"
|
||||||
|
android:summary="@string/summary_pref_sniffing_enabled"
|
||||||
|
android:title="@string/title_pref_sniffing_enabled" />
|
||||||
|
|
||||||
|
<PreferenceCategory android:title="@string/title_vpn_settings">
|
||||||
<CheckBoxPreference
|
<CheckBoxPreference
|
||||||
android:key="pref_per_app_proxy"
|
android:key="pref_per_app_proxy"
|
||||||
android:summary="@string/summary_pref_per_app_proxy"
|
android:summary="@string/summary_pref_per_app_proxy"
|
||||||
@@ -10,24 +21,26 @@
|
|||||||
<!--android:key="pref_mux_enabled"-->
|
<!--android:key="pref_mux_enabled"-->
|
||||||
<!--android:summary="@string/summary_pref_mux_enabled"-->
|
<!--android:summary="@string/summary_pref_mux_enabled"-->
|
||||||
<!--android:title="@string/title_pref_mux_enabled" />-->
|
<!--android:title="@string/title_pref_mux_enabled" />-->
|
||||||
|
<CheckBoxPreference
|
||||||
|
android:key="pref_local_dns_enabled"
|
||||||
|
android:summary="@string/summary_pref_local_dns_enabled"
|
||||||
|
android:title="@string/title_pref_local_dns_enabled" />
|
||||||
|
|
||||||
<CheckBoxPreference
|
<CheckBoxPreference
|
||||||
android:key="pref_speed_enabled"
|
android:key="pref_fake_dns_enabled"
|
||||||
android:summary="@string/summary_pref_speed_enabled"
|
android:summary="@string/summary_pref_fake_dns_enabled"
|
||||||
android:title="@string/title_pref_speed_enabled" />
|
android:title="@string/title_pref_fake_dns_enabled" />
|
||||||
|
|
||||||
<CheckBoxPreference
|
<EditTextPreference
|
||||||
android:defaultValue="true"
|
android:key="pref_local_dns_port"
|
||||||
android:key="pref_sniffing_enabled"
|
android:summary="10807"
|
||||||
android:summary="@string/summary_pref_sniffing_enabled"
|
android:inputType="number"
|
||||||
android:title="@string/title_pref_sniffing_enabled" />
|
android:title="@string/title_pref_local_dns_port" />
|
||||||
|
|
||||||
<CheckBoxPreference
|
<EditTextPreference
|
||||||
android:defaultValue="false"
|
android:key="pref_vpn_dns"
|
||||||
android:key="pref_proxy_sharing_enabled"
|
android:summary="@string/summary_pref_remote_dns"
|
||||||
android:onClick="proxySharingOnClick"
|
android:title="@string/title_pref_vpn_dns" />
|
||||||
android:summary="@string/summary_pref_proxy_sharing_enabled"
|
|
||||||
android:title="@string/title_pref_proxy_sharing_enabled" />
|
|
||||||
|
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
|
|
||||||
@@ -40,6 +53,11 @@
|
|||||||
android:summary="%s"
|
android:summary="%s"
|
||||||
android:title="@string/title_pref_routing_domain_strategy" />
|
android:title="@string/title_pref_routing_domain_strategy" />
|
||||||
|
|
||||||
|
<Preference
|
||||||
|
android:key="pref_routing_custom"
|
||||||
|
android:summary="@string/title_pref_routing_custom"
|
||||||
|
android:title="@string/title_pref_routing_custom" />
|
||||||
|
|
||||||
<ListPreference
|
<ListPreference
|
||||||
android:defaultValue="0"
|
android:defaultValue="0"
|
||||||
android:entries="@array/routing_mode"
|
android:entries="@array/routing_mode"
|
||||||
@@ -47,11 +65,6 @@
|
|||||||
android:key="pref_routing_mode"
|
android:key="pref_routing_mode"
|
||||||
android:summary="%s"
|
android:summary="%s"
|
||||||
android:title="@string/title_pref_routing_mode" />
|
android:title="@string/title_pref_routing_mode" />
|
||||||
|
|
||||||
<Preference
|
|
||||||
android:key="pref_routing_custom"
|
|
||||||
android:summary="@string/title_pref_routing_custom"
|
|
||||||
android:title="@string/title_pref_routing_custom" />
|
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
|
|
||||||
<PreferenceCategory android:title="@string/title_advanced">
|
<PreferenceCategory android:title="@string/title_advanced">
|
||||||
@@ -62,19 +75,11 @@
|
|||||||
android:title="@string/title_pref_forward_ipv6" />
|
android:title="@string/title_pref_forward_ipv6" />
|
||||||
|
|
||||||
<CheckBoxPreference
|
<CheckBoxPreference
|
||||||
android:key="pref_local_dns_enabled"
|
android:defaultValue="false"
|
||||||
android:summary="@string/summary_pref_local_dns_enabled"
|
android:key="pref_proxy_sharing_enabled"
|
||||||
android:title="@string/title_pref_local_dns_enabled" />
|
android:onClick="proxySharingOnClick"
|
||||||
|
android:summary="@string/summary_pref_proxy_sharing_enabled"
|
||||||
<EditTextPreference
|
android:title="@string/title_pref_proxy_sharing_enabled" />
|
||||||
android:key="pref_domestic_dns"
|
|
||||||
android:summary="@string/summary_pref_domestic_dns"
|
|
||||||
android:title="@string/title_pref_domestic_dns" />
|
|
||||||
|
|
||||||
<EditTextPreference
|
|
||||||
android:key="pref_remote_dns"
|
|
||||||
android:summary="@string/summary_pref_remote_dns"
|
|
||||||
android:title="@string/title_pref_remote_dns" />
|
|
||||||
|
|
||||||
<Preference
|
<Preference
|
||||||
android:key="pref_socks_port"
|
android:key="pref_socks_port"
|
||||||
@@ -86,6 +91,16 @@
|
|||||||
android:summary="10809"
|
android:summary="10809"
|
||||||
android:title="@string/title_pref_http_port" />
|
android:title="@string/title_pref_http_port" />
|
||||||
|
|
||||||
|
<EditTextPreference
|
||||||
|
android:key="pref_remote_dns"
|
||||||
|
android:summary="@string/summary_pref_remote_dns"
|
||||||
|
android:title="@string/title_pref_remote_dns" />
|
||||||
|
|
||||||
|
<EditTextPreference
|
||||||
|
android:key="pref_domestic_dns"
|
||||||
|
android:summary="@string/summary_pref_domestic_dns"
|
||||||
|
android:title="@string/title_pref_domestic_dns" />
|
||||||
|
|
||||||
<ListPreference
|
<ListPreference
|
||||||
android:defaultValue="VPN"
|
android:defaultValue="VPN"
|
||||||
android:entries="@array/mode_entries"
|
android:entries="@array/mode_entries"
|
||||||
|
|||||||
Reference in New Issue
Block a user