Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 12f8b1584c | |||
| 2b571e2a94 | |||
| ba30697309 | |||
| 879e25bb82 | |||
| 17c858d24c | |||
| 71a6531835 | |||
| 5d760382d7 | |||
| 4dda36673a | |||
| 5aca55cb8f | |||
| 82f14f0d07 | |||
| c0e968ac34 | |||
| 29d3fb710b |
@@ -1,5 +0,0 @@
|
|||||||
blank_issues_enabled: false
|
|
||||||
contact_links:
|
|
||||||
- name: V2Ray程序问题
|
|
||||||
url: https://github.com/v2fly/v2ray-core/
|
|
||||||
about: 如果您有V2Ray而非v2rayNG的问题,请至这个链接讨论。
|
|
||||||
@@ -1,8 +1,3 @@
|
|||||||
---
|
|
||||||
name: v2rayNG程序问题
|
|
||||||
about: 创建一个报告来帮助我们改进
|
|
||||||
---
|
|
||||||
|
|
||||||
在提出问题前请先自行排除服务器端问题,同时也请通过搜索确认是否有人提出过相同问题。
|
在提出问题前请先自行排除服务器端问题,同时也请通过搜索确认是否有人提出过相同问题。
|
||||||
|
|
||||||
|
|
||||||
@@ -19,8 +14,7 @@ about: 创建一个报告来帮助我们改进
|
|||||||
|
|
||||||
### 日志信息
|
### 日志信息
|
||||||
<details>
|
<details>
|
||||||
|
通过 `adb logcat -s com.v2ray.ang GoLog V2rayConfigUtilGoLog Main` 获取日志。请自行删减日志中可能出现的敏感信息。
|
||||||
通过`adb logcat -s com.v2ray.ang GoLog V2rayConfigUtilGoLog Main`获取日志。请自行删减日志中可能出现的敏感信息。
|
|
||||||
|
|
||||||
如果问题可重现,建议先执行`adb logcat -c`清空系统日志再执行上述命令,再操作重现问题。
|
如果问题可重现,建议先执行`adb logcat -c`清空系统日志再执行上述命令,再操作重现问题。
|
||||||
```
|
```
|
||||||
+6
-3
@@ -2,8 +2,11 @@ V2rayNG/app/src/main/res/layout/activity_inapp_buy.xml
|
|||||||
V2rayNG/app/src/main/assets/geoip.dat
|
V2rayNG/app/src/main/assets/geoip.dat
|
||||||
V2rayNG/app/src/main/assets/geosite.dat
|
V2rayNG/app/src/main/assets/geosite.dat
|
||||||
V2rayNG/app/src/main/java/com/v2ray/ang/InappBuyActivity.java
|
V2rayNG/app/src/main/java/com/v2ray/ang/InappBuyActivity.java
|
||||||
|
V2rayNG/gradle/wrapper/gradle-wrapper.properties
|
||||||
|
V2rayNG/gradle/wrapper/gradle-wrapper.properties
|
||||||
|
*.aar
|
||||||
*.dat
|
*.dat
|
||||||
*.jks
|
*.jks
|
||||||
V2rayNG/app/release/output.json
|
V2rayNG/gradle/wrapper/gradle-wrapper.properties
|
||||||
.idea/
|
V2rayNG/gradle/wrapper/gradle-wrapper.properties
|
||||||
.gradle/
|
V2rayNG/app/release/output.json
|
||||||
@@ -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,65 +0,0 @@
|
|||||||
package CoreI
|
|
||||||
|
|
||||||
import (
|
|
||||||
v2core "v2ray.com/core"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Status struct {
|
|
||||||
IsRunning bool
|
|
||||||
IsTRunning bool
|
|
||||||
PackageName string
|
|
||||||
PackageCodePath string
|
|
||||||
|
|
||||||
Vpoint v2core.Server
|
|
||||||
}
|
|
||||||
|
|
||||||
func CheckVersion() int {
|
|
||||||
return 22
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *Status) GetDataDir() string {
|
|
||||||
return v.PackageName
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *Status) GetApp(name string) string {
|
|
||||||
return v.PackageCodePath + name
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *Status) GetTun2socksArgs(localDNS bool, enableIPv6 bool) (ret []string) {
|
|
||||||
ret = []string{"--netif-ipaddr",
|
|
||||||
"26.26.26.2",
|
|
||||||
"--netif-netmask",
|
|
||||||
"255.255.255.252",
|
|
||||||
"--socks-server-addr",
|
|
||||||
"127.0.0.1:10808",
|
|
||||||
"--tunmtu",
|
|
||||||
"1500",
|
|
||||||
"--loglevel",
|
|
||||||
"notice",
|
|
||||||
"--enable-udprelay",
|
|
||||||
"--sock-path",
|
|
||||||
v.GetDataDir() + "sock_path",
|
|
||||||
}
|
|
||||||
|
|
||||||
if enableIPv6 {
|
|
||||||
ret = append(ret, "--netif-ip6addr", "da26:2626::2")
|
|
||||||
}
|
|
||||||
|
|
||||||
if localDNS {
|
|
||||||
ret = append(ret, "--dnsgw", "127.0.0.1:10807")
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *Status) GetVPNSetupArg(localDNS bool, enableIPv6 bool) (ret string) {
|
|
||||||
ret = "m,1500 a,26.26.26.1,30 r,0.0.0.0,0"
|
|
||||||
|
|
||||||
if enableIPv6 {
|
|
||||||
ret += " a,da26:2626::1,126 r,::,0"
|
|
||||||
}
|
|
||||||
if localDNS {
|
|
||||||
ret += " d,26.26.26.2"
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
pb:
|
|
||||||
go get -u github.com/golang/protobuf/protoc-gen-go
|
|
||||||
@echo "pb Start"
|
|
||||||
asset:
|
|
||||||
bash gen_assets.sh download
|
|
||||||
mkdir assets
|
|
||||||
cp -v data/*.dat assets/
|
|
||||||
# cd assets;curl https://raw.githubusercontent.com/2dust/AndroidLibV2rayLite/master/data/geosite.dat > geosite.dat
|
|
||||||
# cd assets;curl https://raw.githubusercontent.com/2dust/AndroidLibV2rayLite/master/data/geoip.dat > geoip.dat
|
|
||||||
|
|
||||||
fetchDep:
|
|
||||||
-go get github.com/2dust/AndroidLibV2rayLite
|
|
||||||
go get github.com/2dust/AndroidLibV2rayLite
|
|
||||||
|
|
||||||
ANDROID_HOME=$(HOME)/android-sdk-linux
|
|
||||||
export ANDROID_HOME
|
|
||||||
PATH:=$(PATH):$(GOPATH)/bin
|
|
||||||
export PATH
|
|
||||||
downloadGoMobile:
|
|
||||||
go get golang.org/x/mobile/cmd/...
|
|
||||||
sudo apt-get install -qq libstdc++6:i386 lib32z1 expect
|
|
||||||
cd ~ ;curl -L https://raw.githubusercontent.com/2dust/AndroidLibV2rayLite/master/ubuntu-cli-install-android-sdk.sh | sudo bash - > /dev/null
|
|
||||||
ls ~
|
|
||||||
ls ~/android-sdk-linux/
|
|
||||||
gomobile init ;gomobile bind -v -tags json github.com/2dust/AndroidLibV2rayLite
|
|
||||||
|
|
||||||
BuildMobile:
|
|
||||||
@echo Stub
|
|
||||||
|
|
||||||
all: asset pb fetchDep
|
|
||||||
@echo DONE
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
package Escort
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
|
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/2dust/AndroidLibV2rayLite/CoreI"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (v *Escorting) EscortRun(proc string, pt []string, additionalEnv string) {
|
|
||||||
log.Println(proc, pt)
|
|
||||||
count := 0
|
|
||||||
for count <= 42 {
|
|
||||||
cmd := exec.Command(proc, pt...)
|
|
||||||
cmd.Stdout = os.Stdout
|
|
||||||
cmd.Stderr = os.Stderr
|
|
||||||
|
|
||||||
if len(additionalEnv) > 0 {
|
|
||||||
//additionalEnv := "FOO=bar"
|
|
||||||
newEnv := append(os.Environ(), additionalEnv)
|
|
||||||
cmd.Env = newEnv
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := cmd.Start(); err != nil {
|
|
||||||
log.Println("EscortRun cmd.Start err", err)
|
|
||||||
goto CMDERROR
|
|
||||||
}
|
|
||||||
|
|
||||||
if v.escortProcess == nil {
|
|
||||||
log.Println("EscortRun v.escortProcess nil")
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
*v.escortProcess = append(*v.escortProcess, cmd.Process)
|
|
||||||
log.Println("EscortRun Waiting....")
|
|
||||||
|
|
||||||
if err := cmd.Wait(); err != nil {
|
|
||||||
log.Println("EscortRun cmd.Wait err:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
CMDERROR:
|
|
||||||
if v.Status.IsRunning {
|
|
||||||
log.Println("EscortRun Unexpected Exit, Restart now.")
|
|
||||||
count++
|
|
||||||
} else {
|
|
||||||
log.Println("EscortRun Exit")
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *Escorting) EscortingUp() {
|
|
||||||
if v.escortProcess != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
v.escortProcess = new([](*os.Process))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *Escorting) EscortingDown() {
|
|
||||||
if v.escortProcess == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Println("EscortingDown() Killing all escorted process ")
|
|
||||||
for _, pr := range *v.escortProcess {
|
|
||||||
pr.Kill()
|
|
||||||
if _, err := pr.Wait(); err != nil {
|
|
||||||
log.Println("EscortingDown pr.Wait err:", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
v.escortProcess = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type Escorting struct {
|
|
||||||
escortProcess *[](*os.Process)
|
|
||||||
Status *CoreI.Status
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
# AndroidLibV2rayLite
|
|
||||||
@@ -1,279 +0,0 @@
|
|||||||
package VPN
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"net"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
|
||||||
v2net "v2ray.com/core/common/net"
|
|
||||||
v2internet "v2ray.com/core/transport/internet"
|
|
||||||
)
|
|
||||||
|
|
||||||
type protectSet interface {
|
|
||||||
Protect(int) int
|
|
||||||
}
|
|
||||||
|
|
||||||
type resolved struct {
|
|
||||||
domain string
|
|
||||||
IPs []net.IP
|
|
||||||
Port int
|
|
||||||
ipIdx uint8
|
|
||||||
ipLock sync.Mutex
|
|
||||||
lastSwitched time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
// NextIP switch to another resolved result.
|
|
||||||
// there still be race-condition here if multiple err concurently occured
|
|
||||||
// may cause idx keep switching,
|
|
||||||
// but that's an outside error can hardly handled here
|
|
||||||
func (r *resolved) NextIP() {
|
|
||||||
r.ipLock.Lock()
|
|
||||||
defer r.ipLock.Unlock()
|
|
||||||
|
|
||||||
if len(r.IPs) > 1 {
|
|
||||||
|
|
||||||
// throttle, don't switch too quickly
|
|
||||||
now := time.Now()
|
|
||||||
if now.Sub(r.lastSwitched) < time.Second*5 {
|
|
||||||
log.Println("switch too quickly")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
r.lastSwitched = now
|
|
||||||
r.ipIdx++
|
|
||||||
|
|
||||||
} else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.ipIdx >= uint8(len(r.IPs)) {
|
|
||||||
r.ipIdx = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
cur := r.currentIP()
|
|
||||||
log.Printf("switched to next IP: %s", cur)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *resolved) currentIP() net.IP {
|
|
||||||
if len(r.IPs) > 0 {
|
|
||||||
return r.IPs[r.ipIdx]
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewPreotectedDialer ...
|
|
||||||
func NewPreotectedDialer(p protectSet) *ProtectedDialer {
|
|
||||||
d := &ProtectedDialer{
|
|
||||||
// prefer native lookup on Android
|
|
||||||
resolver: &net.Resolver{PreferGo: false},
|
|
||||||
protectSet: p,
|
|
||||||
}
|
|
||||||
return d
|
|
||||||
}
|
|
||||||
|
|
||||||
// ProtectedDialer ...
|
|
||||||
type ProtectedDialer struct {
|
|
||||||
currentServer string
|
|
||||||
resolveChan chan struct{}
|
|
||||||
|
|
||||||
vServer *resolved
|
|
||||||
resolver *net.Resolver
|
|
||||||
|
|
||||||
protectSet
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *ProtectedDialer) IsVServerReady() bool {
|
|
||||||
return (d.vServer != nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *ProtectedDialer) PrepareResolveChan() {
|
|
||||||
d.resolveChan = make(chan struct{})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *ProtectedDialer) ResolveChan() <-chan struct{} {
|
|
||||||
return d.resolveChan
|
|
||||||
}
|
|
||||||
|
|
||||||
// simplicated version of golang: internetAddrList in src/net/ipsock.go
|
|
||||||
func (d *ProtectedDialer) lookupAddr(addr string) (*resolved, error) {
|
|
||||||
|
|
||||||
var (
|
|
||||||
err error
|
|
||||||
host, port string
|
|
||||||
portnum int
|
|
||||||
)
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
if host, port, err = net.SplitHostPort(addr); err != nil {
|
|
||||||
log.Printf("PrepareDomain SplitHostPort Err: %v", err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if portnum, err = d.resolver.LookupPort(ctx, "tcp", port); err != nil {
|
|
||||||
log.Printf("PrepareDomain LookupPort Err: %v", err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
addrs, err := d.resolver.LookupIPAddr(ctx, host)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if len(addrs) == 0 {
|
|
||||||
return nil, fmt.Errorf("domain %s Failed to resolve", addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
IPs := make([]net.IP, len(addrs))
|
|
||||||
for i, ia := range addrs {
|
|
||||||
IPs[i] = ia.IP
|
|
||||||
}
|
|
||||||
|
|
||||||
rs := &resolved{
|
|
||||||
domain: host,
|
|
||||||
IPs: IPs,
|
|
||||||
Port: portnum,
|
|
||||||
}
|
|
||||||
|
|
||||||
return rs, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrepareDomain caches direct v2ray server host
|
|
||||||
func (d *ProtectedDialer) PrepareDomain(domainName string, closeCh <-chan struct{}) {
|
|
||||||
log.Printf("Preparing Domain: %s", domainName)
|
|
||||||
d.currentServer = domainName
|
|
||||||
|
|
||||||
defer close(d.resolveChan)
|
|
||||||
maxRetry := 10
|
|
||||||
for {
|
|
||||||
if maxRetry == 0 {
|
|
||||||
log.Println("PrepareDomain maxRetry reached. exiting.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
resolved, err := d.lookupAddr(domainName)
|
|
||||||
if err != nil {
|
|
||||||
maxRetry--
|
|
||||||
log.Printf("PrepareDomain err: %v\n", err)
|
|
||||||
select {
|
|
||||||
case <-closeCh:
|
|
||||||
log.Printf("PrepareDomain exit due to v2ray closed")
|
|
||||||
return
|
|
||||||
case <-time.After(time.Second * 2):
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
d.vServer = resolved
|
|
||||||
log.Printf("Prepare Result:\n Domain: %s\n Port: %d\n IPs: %v\n",
|
|
||||||
resolved.domain, resolved.Port, resolved.IPs)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *ProtectedDialer) getFd(network v2net.Network) (fd int, err error) {
|
|
||||||
switch network {
|
|
||||||
case v2net.Network_TCP:
|
|
||||||
fd, err = unix.Socket(unix.AF_INET6, unix.SOCK_STREAM, unix.IPPROTO_TCP)
|
|
||||||
case v2net.Network_UDP:
|
|
||||||
fd, err = unix.Socket(unix.AF_INET6, unix.SOCK_DGRAM, unix.IPPROTO_UDP)
|
|
||||||
default:
|
|
||||||
err = fmt.Errorf("unknow network")
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dial exported as the protected dial method
|
|
||||||
func (d *ProtectedDialer) Dial(ctx context.Context,
|
|
||||||
src v2net.Address, dest v2net.Destination, sockopt *v2internet.SocketConfig) (net.Conn, error) {
|
|
||||||
|
|
||||||
network := dest.Network.SystemString()
|
|
||||||
Address := dest.NetAddr()
|
|
||||||
|
|
||||||
// v2ray server address,
|
|
||||||
// try to connect fixed IP if multiple IP parsed from domain,
|
|
||||||
// and switch to next IP if error occurred
|
|
||||||
if strings.Compare(Address, d.currentServer) == 0 {
|
|
||||||
if d.vServer == nil {
|
|
||||||
log.Println("Dial pending prepare ...", Address)
|
|
||||||
<-d.resolveChan
|
|
||||||
|
|
||||||
// user may close connection during PrepareDomain,
|
|
||||||
// fast return release resources.
|
|
||||||
if d.vServer == nil {
|
|
||||||
return nil, fmt.Errorf("fail to prepare domain %s", d.currentServer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fd, err := d.getFd(dest.Network)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
curIP := d.vServer.currentIP()
|
|
||||||
conn, err := d.fdConn(ctx, curIP, d.vServer.Port, fd)
|
|
||||||
if err != nil {
|
|
||||||
d.vServer.NextIP()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
log.Printf("Using Prepared: %s", curIP)
|
|
||||||
return conn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// v2ray connecting to "domestic" servers, no caching results
|
|
||||||
log.Printf("Not Using Prepared: %s,%s", network, Address)
|
|
||||||
resolved, err := d.lookupAddr(Address)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
fd, err := d.getFd(dest.Network)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// use the first resolved address.
|
|
||||||
// the result IP may vary, eg: IPv6 addrs comes first if client has ipv6 address
|
|
||||||
return d.fdConn(ctx, resolved.IPs[0], resolved.Port, fd)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *ProtectedDialer) fdConn(ctx context.Context, ip net.IP, port int, fd int) (net.Conn, error) {
|
|
||||||
|
|
||||||
defer unix.Close(fd)
|
|
||||||
|
|
||||||
// call android VPN service to "protect" the fd connecting straight out
|
|
||||||
d.Protect(fd)
|
|
||||||
|
|
||||||
sa := &unix.SockaddrInet6{
|
|
||||||
Port: port,
|
|
||||||
}
|
|
||||||
copy(sa.Addr[:], ip)
|
|
||||||
|
|
||||||
if err := unix.Connect(fd, sa); err != nil {
|
|
||||||
log.Printf("fdConn unix.Connect err, Close Fd: %d Err: %v", fd, err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
file := os.NewFile(uintptr(fd), "Socket")
|
|
||||||
if file == nil {
|
|
||||||
// returned value will be nil if fd is not a valid file descriptor
|
|
||||||
return nil, errors.New("fdConn fd invalid")
|
|
||||||
}
|
|
||||||
|
|
||||||
defer file.Close()
|
|
||||||
//Closing conn does not affect file, and closing file does not affect conn.
|
|
||||||
conn, err := net.FileConn(file)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("fdConn FileConn Close Fd: %d Err: %v", fd, err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return conn, nil
|
|
||||||
}
|
|
||||||
@@ -1,151 +0,0 @@
|
|||||||
package VPN
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"sync"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
v2net "v2ray.com/core/common/net"
|
|
||||||
)
|
|
||||||
|
|
||||||
type fakeSupportSet struct{}
|
|
||||||
|
|
||||||
func (f fakeSupportSet) Protect(int) int {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestProtectedDialer_PrepareDomain(t *testing.T) {
|
|
||||||
type args struct {
|
|
||||||
domainName string
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
}{
|
|
||||||
// TODO: Add test cases.
|
|
||||||
{"", args{"baidu.com:80"}},
|
|
||||||
// {"", args{"cloudflare.com:443"}},
|
|
||||||
// {"", args{"apple.com:443"}},
|
|
||||||
// {"", args{"110.110.110.110:443"}},
|
|
||||||
// {"", args{"[2002:1234::1]:443"}},
|
|
||||||
}
|
|
||||||
d := NewPreotectedDialer(fakeSupportSet{})
|
|
||||||
for _, tt := range tests {
|
|
||||||
ch := make(chan struct{})
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
go d.PrepareDomain(tt.args.domainName, ch)
|
|
||||||
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
go d.vServer.NextIP()
|
|
||||||
t.Log(d.vServer.currentIP())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestProtectedDialer_Dial(t *testing.T) {
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
// TODO: Add test cases.
|
|
||||||
{"baidu.com:80", false},
|
|
||||||
{"cloudflare.com:80", false},
|
|
||||||
{"172.16.192.11:80", true},
|
|
||||||
// {"172.16.192.10:80", true},
|
|
||||||
// {"[2fff:4322::1]:443", true},
|
|
||||||
// {"[fc00::1]:443", true},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
ch := make(chan struct{})
|
|
||||||
|
|
||||||
d := NewPreotectedDialer(fakeSupportSet{})
|
|
||||||
d.currentServer = tt.name
|
|
||||||
|
|
||||||
go d.PrepareDomain(tt.name, ch)
|
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
|
|
||||||
dial := func() {
|
|
||||||
defer wg.Done()
|
|
||||||
dest, _ := v2net.ParseDestination("tcp:" + tt.name)
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
conn, err := d.Dial(ctx, nil, dest, nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Log(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_host, _, _ := net.SplitHostPort(tt.name)
|
|
||||||
fmt.Fprintf(conn, fmt.Sprintf("GET / HTTP/1.1\r\nHost: %s\r\n\r\n", _host))
|
|
||||||
status, err := bufio.NewReader(conn).ReadString('\n')
|
|
||||||
t.Logf("%#v, %#v\n", status, err)
|
|
||||||
conn.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
for n := 0; n < 3; n++ {
|
|
||||||
wg.Add(1)
|
|
||||||
go dial()
|
|
||||||
// time.Sleep(time.Millisecond * 10)
|
|
||||||
// d.pendingMap[tt.name] = make(chan struct{})
|
|
||||||
}
|
|
||||||
|
|
||||||
wg.Wait()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_resolved_NextIP(t *testing.T) {
|
|
||||||
type fields struct {
|
|
||||||
domain string
|
|
||||||
IPs []net.IP
|
|
||||||
Port int
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
fields fields
|
|
||||||
}{
|
|
||||||
// TODO: Add test cases.
|
|
||||||
{"test1",
|
|
||||||
fields{
|
|
||||||
domain: "www.baidu.com",
|
|
||||||
IPs: []net.IP{
|
|
||||||
net.ParseIP("1.2.3.4"),
|
|
||||||
net.ParseIP("4.3.2.1"),
|
|
||||||
net.ParseIP("1234::1"),
|
|
||||||
net.ParseIP("4321::1"),
|
|
||||||
},
|
|
||||||
}},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
r := &resolved{
|
|
||||||
domain: tt.fields.domain,
|
|
||||||
IPs: tt.fields.IPs,
|
|
||||||
Port: tt.fields.Port,
|
|
||||||
}
|
|
||||||
t.Logf("%v", r.IPs)
|
|
||||||
t.Logf("%v", r.currentIP())
|
|
||||||
r.NextIP()
|
|
||||||
t.Logf("%v", r.currentIP())
|
|
||||||
r.NextIP()
|
|
||||||
t.Logf("%v", r.currentIP())
|
|
||||||
r.NextIP()
|
|
||||||
t.Logf("%v", r.currentIP())
|
|
||||||
time.Sleep(3 * time.Second)
|
|
||||||
r.NextIP()
|
|
||||||
t.Logf("%v", r.currentIP())
|
|
||||||
time.Sleep(5 * time.Second)
|
|
||||||
r.NextIP()
|
|
||||||
t.Logf("%v", r.currentIP())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -o errexit
|
|
||||||
set -o pipefail
|
|
||||||
set -o nounset
|
|
||||||
|
|
||||||
# Set magic variables for current file & dir
|
|
||||||
__dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
||||||
__file="${__dir}/$(basename "${BASH_SOURCE[0]}")"
|
|
||||||
__base="$(basename ${__file} .sh)"
|
|
||||||
|
|
||||||
if [[ ! -d $NDK_HOME ]]; then
|
|
||||||
echo "Android NDK: NDK_HOME not found. please set env \$NDK_HOME"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
TMPDIR=$(mktemp -d)
|
|
||||||
clear_tmp () {
|
|
||||||
rm -rf $TMPDIR
|
|
||||||
}
|
|
||||||
|
|
||||||
trap 'echo -e "Aborted, error $? in command: $BASH_COMMAND"; trap ERR; clear_tmp; exit 1' ERR INT
|
|
||||||
install -m644 $__dir/tun2socks.mk $TMPDIR/
|
|
||||||
|
|
||||||
pushd $TMPDIR
|
|
||||||
git clone --depth=1 https://github.com/shadowsocks/badvpn.git
|
|
||||||
git clone --depth=1 https://github.com/shadowsocks/libancillary.git
|
|
||||||
$NDK_HOME/ndk-build \
|
|
||||||
NDK_PROJECT_PATH=. \
|
|
||||||
APP_BUILD_SCRIPT=./tun2socks.mk \
|
|
||||||
APP_ABI=all \
|
|
||||||
APP_PLATFORM=android-19 \
|
|
||||||
NDK_LIBS_OUT=$TMPDIR/libs \
|
|
||||||
NDK_OUT=$TMPDIR/tmp \
|
|
||||||
APP_SHORT_COMMANDS=false LOCAL_SHORT_COMMANDS=false -B -j4
|
|
||||||
|
|
||||||
install -v -m755 libs/armeabi-v7a/tun2socks $__dir/../V2rayNG/app/src/main/jniLibs/armeabi-v7a/libtun2socks.so
|
|
||||||
install -v -m755 libs/arm64-v8a/tun2socks $__dir/../V2rayNG/app/src/main/jniLibs/arm64-v8a/libtun2socks.so
|
|
||||||
install -v -m755 libs/x86/tun2socks $__dir/../V2rayNG/app/src/main/jniLibs/x86/libtun2socks.so
|
|
||||||
install -v -m755 libs/x86_64/tun2socks $__dir/../V2rayNG/app/src/main/jniLibs/x86_64/libtun2socks.so
|
|
||||||
popd
|
|
||||||
|
|
||||||
rm -rf $TMPDIR
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -o errexit
|
|
||||||
set -o pipefail
|
|
||||||
set -o nounset
|
|
||||||
|
|
||||||
# Set magic variables for current file & dir
|
|
||||||
__dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
||||||
__file="${__dir}/$(basename "${BASH_SOURCE[0]}")"
|
|
||||||
__base="$(basename ${__file} .sh)"
|
|
||||||
|
|
||||||
|
|
||||||
DATADIR=${__dir}/data
|
|
||||||
|
|
||||||
compile_dat () {
|
|
||||||
local TMPDIR=$(mktemp -d)
|
|
||||||
|
|
||||||
trap 'echo -e "Aborted, error $? in command: $BASH_COMMAND"; rm -rf $TMPDIR; trap ERR; exit 1' ERR
|
|
||||||
|
|
||||||
local GEOSITE=${GOPATH}/src/github.com/v2ray/domain-list-community
|
|
||||||
if [[ -d ${GEOSITE} ]]; then
|
|
||||||
cd ${GEOSITE} && git pull
|
|
||||||
else
|
|
||||||
mkdir -p ${GEOSITE}
|
|
||||||
cd ${GEOSITE} && git clone https://github.com/v2ray/domain-list-community.git .
|
|
||||||
fi
|
|
||||||
go run main.go
|
|
||||||
|
|
||||||
if [[ -e dlc.dat ]]; then
|
|
||||||
rm -f $DATADIR/geosite.dat
|
|
||||||
mv dlc.dat $DATADIR/geosite.dat
|
|
||||||
echo "----------> geosite.dat updated."
|
|
||||||
else
|
|
||||||
echo "----------> geosite.dat failed to update."
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
|
||||||
if [[ ! -x $GOPATH/bin/geoip ]]; then
|
|
||||||
go get -v -u github.com/v2ray/geoip
|
|
||||||
fi
|
|
||||||
|
|
||||||
cd $TMPDIR
|
|
||||||
curl -L -O http://geolite.maxmind.com/download/geoip/database/GeoLite2-Country-CSV.zip
|
|
||||||
unzip -q GeoLite2-Country-CSV.zip
|
|
||||||
mkdir geoip && find . -name '*.csv' -exec mv -t ./geoip {} +
|
|
||||||
$GOPATH/bin/geoip \
|
|
||||||
--country=./geoip/GeoLite2-Country-Locations-en.csv \
|
|
||||||
--ipv4=./geoip/GeoLite2-Country-Blocks-IPv4.csv \
|
|
||||||
--ipv6=./geoip/GeoLite2-Country-Blocks-IPv6.csv
|
|
||||||
|
|
||||||
if [[ -e geoip.dat ]]; then
|
|
||||||
rm -f $DATADIR/geoip.dat
|
|
||||||
mv ./geoip.dat $DATADIR/geoip.dat
|
|
||||||
echo "----------> geoip.dat updated."
|
|
||||||
else
|
|
||||||
echo "----------> geoip.dat failed to update."
|
|
||||||
fi
|
|
||||||
trap ERR
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
download_dat () {
|
|
||||||
wget -qO - https://api.github.com/repos/v2ray/geoip/releases/latest \
|
|
||||||
| grep browser_download_url | cut -d '"' -f 4 \
|
|
||||||
| wget -i - -O $DATADIR/geoip.dat
|
|
||||||
|
|
||||||
wget -qO - https://api.github.com/repos/v2ray/domain-list-community/releases/latest \
|
|
||||||
| grep browser_download_url | cut -d '"' -f 4 \
|
|
||||||
| wget -i - -O $DATADIR/geosite.dat
|
|
||||||
}
|
|
||||||
|
|
||||||
ACTION="${1:-}"
|
|
||||||
if [[ -z $ACTION ]]; then
|
|
||||||
ACTION=download
|
|
||||||
fi
|
|
||||||
|
|
||||||
case $ACTION in
|
|
||||||
"download") download_dat;;
|
|
||||||
"compile") compile_dat;;
|
|
||||||
esac
|
|
||||||
@@ -1,260 +0,0 @@
|
|||||||
package libv2ray
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/2dust/AndroidLibV2rayLite/CoreI"
|
|
||||||
"github.com/2dust/AndroidLibV2rayLite/Process/Escort"
|
|
||||||
"github.com/2dust/AndroidLibV2rayLite/VPN"
|
|
||||||
mobasset "golang.org/x/mobile/asset"
|
|
||||||
|
|
||||||
v2core "v2ray.com/core"
|
|
||||||
v2filesystem "v2ray.com/core/common/platform/filesystem"
|
|
||||||
v2stats "v2ray.com/core/features/stats"
|
|
||||||
v2serial "v2ray.com/core/infra/conf/serial"
|
|
||||||
_ "v2ray.com/core/main/distro/all"
|
|
||||||
v2internet "v2ray.com/core/transport/internet"
|
|
||||||
|
|
||||||
v2applog "v2ray.com/core/app/log"
|
|
||||||
v2commlog "v2ray.com/core/common/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
v2Assert = "v2ray.location.asset"
|
|
||||||
assetperfix = "/dev/libv2rayfs0/asset"
|
|
||||||
)
|
|
||||||
|
|
||||||
/*V2RayPoint V2Ray Point Server
|
|
||||||
This is territory of Go, so no getter and setters!
|
|
||||||
*/
|
|
||||||
type V2RayPoint struct {
|
|
||||||
SupportSet V2RayVPNServiceSupportsSet
|
|
||||||
statsManager v2stats.Manager
|
|
||||||
|
|
||||||
dialer *VPN.ProtectedDialer
|
|
||||||
status *CoreI.Status
|
|
||||||
escorter *Escort.Escorting
|
|
||||||
v2rayOP *sync.Mutex
|
|
||||||
closeChan chan struct{}
|
|
||||||
|
|
||||||
PackageName string
|
|
||||||
PackageCodePath string
|
|
||||||
DomainName string
|
|
||||||
ConfigureFileContent string
|
|
||||||
EnableLocalDNS bool
|
|
||||||
ForwardIpv6 bool
|
|
||||||
ProxyOnly bool
|
|
||||||
}
|
|
||||||
|
|
||||||
/*V2RayVPNServiceSupportsSet To support Android VPN mode*/
|
|
||||||
type V2RayVPNServiceSupportsSet interface {
|
|
||||||
Setup(Conf string) int
|
|
||||||
Prepare() int
|
|
||||||
Shutdown() int
|
|
||||||
Protect(int) int
|
|
||||||
OnEmitStatus(int, string) int
|
|
||||||
}
|
|
||||||
|
|
||||||
/*RunLoop Run V2Ray main loop
|
|
||||||
*/
|
|
||||||
func (v *V2RayPoint) RunLoop() (err error) {
|
|
||||||
v.v2rayOP.Lock()
|
|
||||||
defer v.v2rayOP.Unlock()
|
|
||||||
//Construct Context
|
|
||||||
v.status.PackageName = v.PackageName
|
|
||||||
v.status.PackageCodePath = v.PackageCodePath
|
|
||||||
|
|
||||||
if !v.status.IsRunning {
|
|
||||||
v.closeChan = make(chan struct{})
|
|
||||||
v.dialer.PrepareResolveChan()
|
|
||||||
go v.dialer.PrepareDomain(v.DomainName, v.closeChan)
|
|
||||||
go func() {
|
|
||||||
select {
|
|
||||||
// wait until resolved
|
|
||||||
case <-v.dialer.ResolveChan():
|
|
||||||
// shutdown VPNService if server name can not reolved
|
|
||||||
if !v.dialer.IsVServerReady() {
|
|
||||||
log.Println("vServer cannot resolved, shutdown")
|
|
||||||
v.StopLoop()
|
|
||||||
}
|
|
||||||
|
|
||||||
// stop waiting if manually closed
|
|
||||||
case <-v.closeChan:
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
err = v.pointloop()
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
/*StopLoop Stop V2Ray main loop
|
|
||||||
*/
|
|
||||||
func (v *V2RayPoint) StopLoop() (err error) {
|
|
||||||
v.v2rayOP.Lock()
|
|
||||||
defer v.v2rayOP.Unlock()
|
|
||||||
if v.status.IsRunning {
|
|
||||||
v.shutdownInit()
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
//Delegate Funcation
|
|
||||||
func (v *V2RayPoint) GetIsRunning() bool {
|
|
||||||
return v.status.IsRunning
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *V2RayPoint) GetIsTRunning() bool {
|
|
||||||
return v.status.IsTRunning
|
|
||||||
}
|
|
||||||
|
|
||||||
//Delegate Funcation
|
|
||||||
func (v V2RayPoint) QueryStats(tag string, direct string) int64 {
|
|
||||||
if v.statsManager == nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
counter := v.statsManager.GetCounter(fmt.Sprintf("outbound>>>%s>>>traffic>>>%s", tag, direct))
|
|
||||||
if counter == nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return counter.Set(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *V2RayPoint) shutdownInit() {
|
|
||||||
close(v.closeChan)
|
|
||||||
v.statsManager = nil
|
|
||||||
v.status.Vpoint.Close()
|
|
||||||
v.status.Vpoint = nil
|
|
||||||
v.status.IsRunning = false
|
|
||||||
|
|
||||||
v.escorter.EscortingDown()
|
|
||||||
|
|
||||||
v.SupportSet.Shutdown()
|
|
||||||
v.SupportSet.OnEmitStatus(0, "Closed")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *V2RayPoint) pointloop() error {
|
|
||||||
log.Println("loading v2ray config")
|
|
||||||
config, err := v2serial.LoadJSONConfig(strings.NewReader(v.ConfigureFileContent))
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Println("new v2ray core")
|
|
||||||
inst, err := v2core.New(config)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
v.status.Vpoint = inst
|
|
||||||
v.statsManager = inst.GetFeature(v2stats.ManagerType()).(v2stats.Manager)
|
|
||||||
|
|
||||||
log.Println("start v2ray core")
|
|
||||||
v.status.IsRunning = true
|
|
||||||
if err := v.status.Vpoint.Start(); err != nil {
|
|
||||||
v.status.IsRunning = false
|
|
||||||
log.Println(err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
v.SupportSet.Prepare()
|
|
||||||
v.SupportSet.Setup(v.status.GetVPNSetupArg(v.EnableLocalDNS, v.ForwardIpv6))
|
|
||||||
v.SupportSet.OnEmitStatus(0, "Running")
|
|
||||||
|
|
||||||
v.status.IsTRunning = false
|
|
||||||
if !v.ProxyOnly {
|
|
||||||
if err := v.runTun2socks(); err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
v.status.IsTRunning = true
|
|
||||||
|
|
||||||
log.Printf("EnableLocalDNS: %v\nForwardIpv6: %v\nDomainName: %s",
|
|
||||||
v.EnableLocalDNS,
|
|
||||||
v.ForwardIpv6,
|
|
||||||
v.DomainName)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func initV2Env() {
|
|
||||||
if os.Getenv(v2Assert) != "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
//Initialize asset API, Since Raymond Will not let notify the asset location inside Process,
|
|
||||||
//We need to set location outside V2Ray
|
|
||||||
os.Setenv(v2Assert, assetperfix)
|
|
||||||
//Now we handle read
|
|
||||||
v2filesystem.NewFileReader = func(path string) (io.ReadCloser, error) {
|
|
||||||
if strings.HasPrefix(path, assetperfix) {
|
|
||||||
p := path[len(assetperfix)+1:]
|
|
||||||
//is it overridden?
|
|
||||||
//by, ok := overridedAssets[p]
|
|
||||||
//if ok {
|
|
||||||
// return os.Open(by)
|
|
||||||
//}
|
|
||||||
return mobasset.Open(p)
|
|
||||||
}
|
|
||||||
return os.Open(path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Delegate Funcation
|
|
||||||
func TestConfig(ConfigureFileContent string) error {
|
|
||||||
initV2Env()
|
|
||||||
_, err := v2serial.LoadJSONConfig(strings.NewReader(ConfigureFileContent))
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
/*NewV2RayPoint new V2RayPoint*/
|
|
||||||
func NewV2RayPoint(s V2RayVPNServiceSupportsSet) *V2RayPoint {
|
|
||||||
initV2Env()
|
|
||||||
|
|
||||||
// inject our own log writer
|
|
||||||
v2applog.RegisterHandlerCreator(v2applog.LogType_Console,
|
|
||||||
func(lt v2applog.LogType,
|
|
||||||
options v2applog.HandlerCreatorOptions) (v2commlog.Handler, error) {
|
|
||||||
return v2commlog.NewLogger(createStdoutLogWriter()), nil
|
|
||||||
})
|
|
||||||
|
|
||||||
dialer := VPN.NewPreotectedDialer(s)
|
|
||||||
v2internet.UseAlternativeSystemDialer(dialer)
|
|
||||||
status := &CoreI.Status{}
|
|
||||||
return &V2RayPoint{
|
|
||||||
SupportSet: s,
|
|
||||||
v2rayOP: new(sync.Mutex),
|
|
||||||
status: status,
|
|
||||||
dialer: dialer,
|
|
||||||
escorter: &Escort.Escorting{Status: status},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v V2RayPoint) runTun2socks() error {
|
|
||||||
v.escorter.EscortingUp()
|
|
||||||
go v.escorter.EscortRun(
|
|
||||||
v.status.GetApp("libtun2socks.so"),
|
|
||||||
v.status.GetTun2socksArgs(v.EnableLocalDNS, v.ForwardIpv6), "")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
/*CheckVersion int
|
|
||||||
This func will return libv2ray binding version.
|
|
||||||
*/
|
|
||||||
func CheckVersion() int {
|
|
||||||
return CoreI.CheckVersion()
|
|
||||||
}
|
|
||||||
|
|
||||||
/*CheckVersionX string
|
|
||||||
This func will return libv2ray binding version and V2Ray version used.
|
|
||||||
*/
|
|
||||||
func CheckVersionX() string {
|
|
||||||
return fmt.Sprintf("Libv2rayLite V%d, Core V%s", CheckVersion(), v2core.Version())
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
package libv2ray
|
|
||||||
|
|
||||||
//go:generate make all
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
readme.txt
|
|
||||||
@@ -1,128 +0,0 @@
|
|||||||
# Copyright (C) 2009 The Android Open Source Project
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
#
|
|
||||||
#
|
|
||||||
LOCAL_PATH := $(call my-dir)
|
|
||||||
ROOT_PATH := $(LOCAL_PATH)
|
|
||||||
|
|
||||||
########################################################
|
|
||||||
## libancillary
|
|
||||||
########################################################
|
|
||||||
|
|
||||||
include $(CLEAR_VARS)
|
|
||||||
|
|
||||||
ANCILLARY_SOURCE := fd_recv.c fd_send.c
|
|
||||||
|
|
||||||
LOCAL_MODULE := libancillary
|
|
||||||
#LOCAL_CFLAGS += -I$(LOCAL_PATH)/libancillary
|
|
||||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)/libancillary
|
|
||||||
LOCAL_SRC_FILES := $(addprefix libancillary/, $(ANCILLARY_SOURCE))
|
|
||||||
|
|
||||||
include $(BUILD_STATIC_LIBRARY)
|
|
||||||
|
|
||||||
########################################################
|
|
||||||
## tun2socks
|
|
||||||
########################################################
|
|
||||||
|
|
||||||
include $(CLEAR_VARS)
|
|
||||||
|
|
||||||
LOCAL_CFLAGS := -std=gnu99
|
|
||||||
LOCAL_CFLAGS += -DBADVPN_THREADWORK_USE_PTHREAD -DBADVPN_LINUX -DBADVPN_BREACTOR_BADVPN -D_GNU_SOURCE
|
|
||||||
LOCAL_CFLAGS += -DBADVPN_USE_SIGNALFD -DBADVPN_USE_EPOLL
|
|
||||||
LOCAL_CFLAGS += -DBADVPN_LITTLE_ENDIAN -DBADVPN_THREAD_SAFE
|
|
||||||
LOCAL_CFLAGS += -DNDEBUG -DANDROID
|
|
||||||
LOCAL_CFLAGS += -I
|
|
||||||
|
|
||||||
LOCAL_STATIC_LIBRARIES := libancillary
|
|
||||||
|
|
||||||
LOCAL_C_INCLUDES := \
|
|
||||||
$(LOCAL_PATH)/badvpn/libancillary \
|
|
||||||
$(LOCAL_PATH)/badvpn/lwip/src/include/ipv4 \
|
|
||||||
$(LOCAL_PATH)/badvpn/lwip/src/include/ipv6 \
|
|
||||||
$(LOCAL_PATH)/badvpn/lwip/src/include \
|
|
||||||
$(LOCAL_PATH)/badvpn/lwip/custom \
|
|
||||||
$(LOCAL_PATH)/badvpn \
|
|
||||||
$(LOCAL_PATH)/libancillary
|
|
||||||
|
|
||||||
TUN2SOCKS_SOURCES := \
|
|
||||||
base/BLog_syslog.c \
|
|
||||||
system/BReactor_badvpn.c \
|
|
||||||
system/BSignal.c \
|
|
||||||
system/BConnection_common.c \
|
|
||||||
system/BConnection_unix.c \
|
|
||||||
system/BTime.c \
|
|
||||||
system/BUnixSignal.c \
|
|
||||||
system/BNetwork.c \
|
|
||||||
flow/StreamRecvInterface.c \
|
|
||||||
flow/PacketRecvInterface.c \
|
|
||||||
flow/PacketPassInterface.c \
|
|
||||||
flow/StreamPassInterface.c \
|
|
||||||
flow/SinglePacketBuffer.c \
|
|
||||||
flow/BufferWriter.c \
|
|
||||||
flow/PacketBuffer.c \
|
|
||||||
flow/PacketStreamSender.c \
|
|
||||||
flow/PacketPassConnector.c \
|
|
||||||
flow/PacketProtoFlow.c \
|
|
||||||
flow/PacketPassFairQueue.c \
|
|
||||||
flow/PacketProtoEncoder.c \
|
|
||||||
flow/PacketProtoDecoder.c \
|
|
||||||
socksclient/BSocksClient.c \
|
|
||||||
tuntap/BTap.c \
|
|
||||||
lwip/src/core/udp.c \
|
|
||||||
lwip/src/core/memp.c \
|
|
||||||
lwip/src/core/init.c \
|
|
||||||
lwip/src/core/pbuf.c \
|
|
||||||
lwip/src/core/tcp.c \
|
|
||||||
lwip/src/core/tcp_out.c \
|
|
||||||
lwip/src/core/netif.c \
|
|
||||||
lwip/src/core/def.c \
|
|
||||||
lwip/src/core/ip.c \
|
|
||||||
lwip/src/core/mem.c \
|
|
||||||
lwip/src/core/tcp_in.c \
|
|
||||||
lwip/src/core/stats.c \
|
|
||||||
lwip/src/core/inet_chksum.c \
|
|
||||||
lwip/src/core/timeouts.c \
|
|
||||||
lwip/src/core/ipv4/icmp.c \
|
|
||||||
lwip/src/core/ipv4/igmp.c \
|
|
||||||
lwip/src/core/ipv4/ip4_addr.c \
|
|
||||||
lwip/src/core/ipv4/ip4_frag.c \
|
|
||||||
lwip/src/core/ipv4/ip4.c \
|
|
||||||
lwip/src/core/ipv4/autoip.c \
|
|
||||||
lwip/src/core/ipv6/ethip6.c \
|
|
||||||
lwip/src/core/ipv6/inet6.c \
|
|
||||||
lwip/src/core/ipv6/ip6_addr.c \
|
|
||||||
lwip/src/core/ipv6/mld6.c \
|
|
||||||
lwip/src/core/ipv6/dhcp6.c \
|
|
||||||
lwip/src/core/ipv6/icmp6.c \
|
|
||||||
lwip/src/core/ipv6/ip6.c \
|
|
||||||
lwip/src/core/ipv6/ip6_frag.c \
|
|
||||||
lwip/src/core/ipv6/nd6.c \
|
|
||||||
lwip/custom/sys.c \
|
|
||||||
tun2socks/tun2socks.c \
|
|
||||||
base/DebugObject.c \
|
|
||||||
base/BLog.c \
|
|
||||||
base/BPending.c \
|
|
||||||
system/BDatagram_unix.c \
|
|
||||||
flowextra/PacketPassInactivityMonitor.c \
|
|
||||||
tun2socks/SocksUdpGwClient.c \
|
|
||||||
udpgw_client/UdpGwClient.c
|
|
||||||
|
|
||||||
LOCAL_MODULE := tun2socks
|
|
||||||
|
|
||||||
LOCAL_LDLIBS := -ldl -llog
|
|
||||||
|
|
||||||
LOCAL_SRC_FILES := $(addprefix badvpn/, $(TUN2SOCKS_SOURCES))
|
|
||||||
|
|
||||||
include $(BUILD_SYSTEM)/build-executable.mk
|
|
||||||
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Thanks to https://gist.github.com/wenzhixin/43cf3ce909c24948c6e7
|
|
||||||
# Execute this script in your home directory. Lines 17 and 21 will prompt you for a y/n
|
|
||||||
|
|
||||||
# Install Oracle JDK 8
|
|
||||||
apt-get update
|
|
||||||
apt-get install -y openjdk-8-jdk
|
|
||||||
apt-get install -y unzip make expect # NDK stuff
|
|
||||||
|
|
||||||
# Get SDK tools (link from https://developer.android.com/studio/index.html#downloads)
|
|
||||||
wget -q https://dl.google.com/android/repository/sdk-tools-linux-3859397.zip
|
|
||||||
mkdir android-sdk-linux
|
|
||||||
unzip sdk*.zip -d android-sdk-linux
|
|
||||||
|
|
||||||
# Get NDK (https://developer.android.com/ndk/downloads/index.html)
|
|
||||||
# wget -q https://dl.google.com/android/repository/android-ndk-r15c-linux-x86_64.zip
|
|
||||||
# unzip android-ndk*.zip >> /dev/null
|
|
||||||
|
|
||||||
ACCEPT_LICENSES_URL=https://gist.githubusercontent.com/xiaokangwang/1489fd223d26581bfec92adb3cb0088e/raw/328eb6925099df5aae3e76790f8232f0fc378f8b/accept-licenses
|
|
||||||
|
|
||||||
ACCEPT_LICENSES_ITEM="android-sdk-license-bcbbd656|intel-android-sysimage-license-1ea702d1|android-sdk-license-2742d1c5"
|
|
||||||
|
|
||||||
# Let it update itself and install some stuff
|
|
||||||
cd android-sdk-linux/tools
|
|
||||||
|
|
||||||
curl -L -o accept-licenses $ACCEPT_LICENSES_URL
|
|
||||||
|
|
||||||
chmod +x accept-licenses
|
|
||||||
|
|
||||||
./accept-licenses "./android update sdk --use-sdk-wrapper --all --no-ui" $ACCEPT_LICENSES_ITEM >/dev/null
|
|
||||||
|
|
||||||
# Download every build-tools version that has ever existed
|
|
||||||
# This will save you time! Thank me later for this
|
|
||||||
|
|
||||||
#./accept-licenses "./android update sdk --use-sdk-wrapper --all --no-ui --filter 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27" $ACCEPT_LICENSES_ITEM
|
|
||||||
|
|
||||||
PACKAGE_PARSE_URL=https://gist.githubusercontent.com/xiaokangwang/06268fb23034ed94bc301880e862da09/raw/afd95cbbe2f8c1d9e7b0277b7c5ef39af756a6ee/parse.awk
|
|
||||||
|
|
||||||
reduceout=https://gist.githubusercontent.com/xiaokangwang/4684bdb5c3415b943f52aa4803386480/raw/b46dab1cc60f02c0d87f88f01e27157034218faa/out.awk
|
|
||||||
|
|
||||||
cd bin
|
|
||||||
|
|
||||||
curl -L -o parse.awk $PACKAGE_PARSE_URL
|
|
||||||
|
|
||||||
curl -L -o reduce.awk $reduceout
|
|
||||||
|
|
||||||
sudo apt-get install gawk
|
|
||||||
|
|
||||||
./sdkmanager --verbose --list |awk -f parse.awk > ~/package_to_install
|
|
||||||
|
|
||||||
readarray -t filenames < $HOME/package_to_install
|
|
||||||
|
|
||||||
cat $HOME/package_to_install
|
|
||||||
|
|
||||||
yes|./sdkmanager --verbose "${filenames[@]}" |awk -f reduce.awk
|
|
||||||
|
|
||||||
# If you need additional packages for your app, check available packages with:
|
|
||||||
# ./android list sdk --all
|
|
||||||
|
|
||||||
# install certain packages with:
|
|
||||||
# ./android update sdk --no-ui --all --filter 1,2,3,<...>,N
|
|
||||||
# where N is the number of the package in the list (see previous command)
|
|
||||||
|
|
||||||
./sdkmanager "ndk-bundle"
|
|
||||||
|
|
||||||
# Add the directory containing executables in PATH so that they can be found
|
|
||||||
echo 'export ANDROID_HOME=$HOME/android-sdk-linux' >> ~/.bashrc
|
|
||||||
echo 'export PATH=$PATH:$ANDROID_HOME/tools:$ANDROID_HOME/platform-tools' >> ~/.bashrc
|
|
||||||
# echo 'export NDK_HOME=$HOME/android-ndk-r15c' >> ~/.bashrc
|
|
||||||
# echo 'export ANDROID_NDK_HOME=$NDK_HOME' >> ~/.bashrc
|
|
||||||
|
|
||||||
|
|
||||||
source ~/.bashrc
|
|
||||||
|
|
||||||
# Make sure you can execute 32 bit executables if this is 64 bit machine, otherwise skip this
|
|
||||||
dpkg --add-architecture i386
|
|
||||||
apt-get update
|
|
||||||
apt-get install -y libc6:i386 libstdc++6:i386 zlib1g:i386
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
package libv2ray
|
|
||||||
|
|
||||||
// This struct creates our own log writer without datatime stamp
|
|
||||||
// As Android adds time stamps on each line
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
v2commlog "v2ray.com/core/common/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
type consoleLogWriter struct {
|
|
||||||
logger *log.Logger
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *consoleLogWriter) Write(s string) error {
|
|
||||||
w.logger.Print(s)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *consoleLogWriter) Close() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// This logger won't print data/time stamps
|
|
||||||
func createStdoutLogWriter() v2commlog.WriterCreator {
|
|
||||||
return func() v2commlog.Writer {
|
|
||||||
return &consoleLogWriter{
|
|
||||||
logger: log.New(os.Stdout, "", 0)}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,34 +1,6 @@
|
|||||||
# v2rayNG
|
# v2rayNG
|
||||||
|
|
||||||
A V2Ray client for Android
|
|
||||||
|
|
||||||
[](https://developer.android.com/about/versions/jelly-bean#android-4.2)
|
|
||||||
[](https://kotlinlang.org)
|
|
||||||
[](https://github.com/2dust/v2rayNG/commits/master)
|
|
||||||
[](https://www.codefactor.io/repository/github/2dust/v2rayng)
|
|
||||||
[](https://github.com/2dust/v2rayNG/releases)
|
|
||||||
|
|
||||||
<a href="https://play.google.com/store/apps/details?id=com.v2ray.ang">
|
<a href="https://play.google.com/store/apps/details?id=com.v2ray.ang">
|
||||||
<img alt="Get it on Google Play" src="https://play.google.com/intl/en_us/badges/images/generic/en_badge_web_generic.png" width="165" height="64" />
|
<img alt="Get it on Google Play" src="https://play.google.com/intl/en_us/badges/images/generic/en_badge_web_generic.png" width="165" height="64" />
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
### Usage
|
|
||||||
|
|
||||||
#### Geoip and Geosite
|
|
||||||
v2rayNG release already embedded domain file `geoip.dat` and `geosite.dat`. However it is (probably) not the latest and not the most complete list.
|
|
||||||
For power user, the embedded files can be easily replaced with the following steps:
|
|
||||||
1. Launch v2rayNG (v1.4.9+)
|
|
||||||
2. Find existing geoip.dat and geosite.dat in `Android/data/com.v2ray.ang/files/assets` (path may differ on some Android device)
|
|
||||||
3. Replace them with the latest [domain list](https://github.com/v2fly/domain-list-community) and [ip list](https://github.com/v2fly/geoip)
|
|
||||||
4. Enhanced version can be found in this [repo](https://github.com/Loyalsoldier/v2ray-rules-dat) (recommend to use `geosite:geolocation-!cn` for proxy dns and routing)
|
|
||||||
5. It is also possible to use third party dat file in the same folder, like [h2y](https://guide.v2fly.org/routing/sitedata.html#%E5%A4%96%E7%BD%AE%E7%9A%84%E5%9F%9F%E5%90%8D%E6%96%87%E4%BB%B6)
|
|
||||||
|
|
||||||
#### See more in our [wiki](https://github.com/2dust/v2rayNG/wiki)
|
|
||||||
|
|
||||||
### Development guide
|
|
||||||
|
|
||||||
Android project under V2rayNG folder can be compiled directly in Android Studio, or using Gradle wrapper. But the v2ray core inside the aar is (probably) outdated.
|
|
||||||
The aar can be compiled from the Golang project under AndroidLibV2rayLite folder. For a quick start, read guide for [Go Mobile](https://github.com/golang/go/wiki/Mobile)
|
|
||||||
and [Makefiles for Go Developers](https://tutorialedge.net/golang/makefiles-for-go-developers/)
|
|
||||||
|
|
||||||
v2rayNG can run on Android Emulators, with minimum Android 5.0
|
|
||||||
|
|||||||
+31
-20
@@ -3,8 +3,8 @@ apply plugin: 'kotlin-android'
|
|||||||
apply plugin: 'kotlin-android-extensions'
|
apply plugin: 'kotlin-android-extensions'
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion Integer.parseInt("$compileSdkVer")
|
compileSdkVersion 28
|
||||||
buildToolsVersion buildToolsVer
|
buildToolsVersion '28.0.3'
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
targetCompatibility = "8"
|
targetCompatibility = "8"
|
||||||
@@ -20,17 +20,34 @@ android {
|
|||||||
versionName "1.0.2"
|
versionName "1.0.2"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
signingConfigs {
|
||||||
|
release {
|
||||||
|
storeFile file("../key.jks")
|
||||||
|
keyAlias 'ang'
|
||||||
|
keyPassword '123456'
|
||||||
|
storePassword '123456'
|
||||||
|
}
|
||||||
|
debug {
|
||||||
|
storeFile file("../key.jks")
|
||||||
|
keyAlias 'ang'
|
||||||
|
keyPassword '123456'
|
||||||
|
storePassword '123456'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
minifyEnabled false
|
minifyEnabled false
|
||||||
zipAlignEnabled false
|
zipAlignEnabled false
|
||||||
shrinkResources false
|
shrinkResources false
|
||||||
|
signingConfig signingConfigs.release
|
||||||
// proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
// proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||||
}
|
}
|
||||||
debug {
|
debug {
|
||||||
minifyEnabled false
|
minifyEnabled false
|
||||||
zipAlignEnabled false
|
zipAlignEnabled false
|
||||||
shrinkResources false
|
shrinkResources false
|
||||||
|
signingConfig signingConfigs.release
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,10 +55,6 @@ android {
|
|||||||
main.java.srcDirs += 'src/main/kotlin'
|
main.java.srcDirs += 'src/main/kotlin'
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlinOptions {
|
|
||||||
jvmTarget = JavaVersion.VERSION_1_8
|
|
||||||
}
|
|
||||||
|
|
||||||
splits {
|
splits {
|
||||||
abi {
|
abi {
|
||||||
enable true
|
enable true
|
||||||
@@ -66,18 +79,11 @@ android {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||||
testImplementation 'junit:junit:4.13'
|
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
|
||||||
|
testImplementation 'junit:junit:4.12'
|
||||||
implementation project(':dpreference')
|
implementation project(':dpreference')
|
||||||
|
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
|
||||||
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion"
|
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion"
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9"
|
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9"
|
|
||||||
|
|
||||||
// Androidx ktx
|
|
||||||
implementation 'android.arch.lifecycle:extensions:1.1.1'
|
|
||||||
implementation 'android.arch.lifecycle:livedata:1.1.1'
|
|
||||||
|
|
||||||
// Android support library
|
// Android support library
|
||||||
implementation "com.android.support:support-v4:$supportLibVersion"
|
implementation "com.android.support:support-v4:$supportLibVersion"
|
||||||
implementation "com.android.support:appcompat-v7:$supportLibVersion"
|
implementation "com.android.support:appcompat-v7:$supportLibVersion"
|
||||||
@@ -85,17 +91,22 @@ dependencies {
|
|||||||
implementation "com.android.support:cardview-v7:$supportLibVersion"
|
implementation "com.android.support:cardview-v7:$supportLibVersion"
|
||||||
implementation "com.android.support:preference-v7:$supportLibVersion"
|
implementation "com.android.support:preference-v7:$supportLibVersion"
|
||||||
implementation "com.android.support:recyclerview-v7:$supportLibVersion"
|
implementation "com.android.support:recyclerview-v7:$supportLibVersion"
|
||||||
implementation "com.android.support:multidex:1.0.3"
|
|
||||||
implementation 'com.android.support.constraint:constraint-layout:2.0.1'
|
|
||||||
|
|
||||||
// DSL
|
// DSL
|
||||||
implementation 'com.google.code.gson:gson:2.8.6'
|
implementation "org.jetbrains.anko:anko-sdk15:$ankoVersion"
|
||||||
|
implementation "org.jetbrains.anko:anko-support-v4:$ankoVersion"
|
||||||
|
implementation "org.jetbrains.anko:anko-appcompat-v7:$ankoVersion"
|
||||||
|
implementation "org.jetbrains.anko:anko-design:$ankoVersion"
|
||||||
|
implementation 'com.google.code.gson:gson:2.8.5'
|
||||||
implementation 'io.reactivex:rxjava:1.3.4'
|
implementation 'io.reactivex:rxjava:1.3.4'
|
||||||
implementation 'io.reactivex:rxandroid:1.2.1'
|
implementation 'io.reactivex:rxandroid:1.2.1'
|
||||||
implementation 'com.tbruyelle.rxpermissions:rxpermissions:0.9.4@aar'
|
implementation 'com.tbruyelle.rxpermissions:rxpermissions:0.9.4@aar'
|
||||||
|
implementation 'com.dinuscxj:recycleritemdecoration:1.0.0'
|
||||||
|
implementation 'io.reactivex:rxkotlin:0.60.0'
|
||||||
implementation 'me.dm7.barcodescanner:core:1.9.8'
|
implementation 'me.dm7.barcodescanner:core:1.9.8'
|
||||||
implementation 'me.dm7.barcodescanner:zxing:1.9.8'
|
implementation 'me.dm7.barcodescanner:zxing:1.9.8'
|
||||||
implementation 'com.github.jorgecastilloprz:fabprogresscircle:1.01@aar'
|
implementation 'com.github.jorgecastilloprz:fabprogresscircle:1.01@aar'
|
||||||
|
implementation 'com.beust:klaxon:3.0.1'
|
||||||
|
implementation 'com.android.support:multidex:1.0.3'
|
||||||
|
|
||||||
implementation(name: 'libv2ray', ext: 'aar')
|
implementation(name: 'libv2ray', ext: 'aar')
|
||||||
//implementation(name: 'tun2socks', ext: 'aar')
|
//implementation(name: 'tun2socks', ext: 'aar')
|
||||||
@@ -115,4 +126,4 @@ repositories {
|
|||||||
flatDir {
|
flatDir {
|
||||||
dirs 'libs'
|
dirs 'libs'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1 +0,0 @@
|
|||||||
https://github.com/2dust/v2rayNG/tree/master/AndroidLibV2rayLite
|
|
||||||
Binary file not shown.
Vendored
+3
@@ -44,6 +44,9 @@
|
|||||||
static void throwUninitializedPropertyAccessException(java.lang.String);
|
static void throwUninitializedPropertyAccessException(java.lang.String);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
-dontwarn org.jetbrains.anko.internals.**
|
||||||
|
-keep class org.jetbrains.anko.internals.** { *;}
|
||||||
|
|
||||||
-dontwarn rx.internal.util.unsafe.**
|
-dontwarn rx.internal.util.unsafe.**
|
||||||
-keep class rx.internal.util.unsafe.** { *;}
|
-keep class rx.internal.util.unsafe.** { *;}
|
||||||
|
|
||||||
|
|||||||
@@ -1,23 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
package="com.v2ray.ang">
|
package="com.v2ray.ang">
|
||||||
|
|
||||||
<supports-screens
|
|
||||||
android:anyDensity="true"
|
|
||||||
android:smallScreens="true"
|
|
||||||
android:normalScreens="true"
|
|
||||||
android:largeScreens="true"
|
|
||||||
android:xlargeScreens="true"/>
|
|
||||||
|
|
||||||
<uses-feature android:name="android.hardware.camera" android:required="false"/>
|
|
||||||
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/>
|
|
||||||
|
|
||||||
|
|
||||||
<!-- https://developer.android.com/about/versions/11/privacy/package-visibility -->
|
|
||||||
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"
|
|
||||||
tools:ignore="QueryAllPackagesPermission" />
|
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||||
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
|
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
@@ -27,14 +11,12 @@
|
|||||||
<uses-permission android:name="com.android.vending.BILLING" />
|
<uses-permission android:name="com.android.vending.BILLING" />
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
<!-- <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> -->
|
<!-- <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> -->
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".AngApplication"
|
android:name=".AngApplication"
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:extractNativeLibs="true"
|
|
||||||
android:theme="@style/AppTheme">
|
android:theme="@style/AppTheme">
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.MainActivity"
|
android:name=".ui.MainActivity"
|
||||||
@@ -42,16 +24,20 @@
|
|||||||
android:launchMode="singleTask">
|
android:launchMode="singleTask">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.SEND" />
|
<action android:name="android.intent.action.SEND" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
|
||||||
<data android:mimeType="text/plain" />
|
<data android:mimeType="text/plain" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.service.quicksettings.action.QS_TILE_PREFERENCES" />
|
<action android:name="android.service.quicksettings.action.QS_TILE_PREFERENCES" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.app.shortcuts"
|
android:name="android.app.shortcuts"
|
||||||
android:resource="@xml/shortcuts" />
|
android:resource="@xml/shortcuts" />
|
||||||
@@ -71,7 +57,7 @@
|
|||||||
<activity android:name=".ui.SettingsActivity" />
|
<activity android:name=".ui.SettingsActivity" />
|
||||||
<activity android:name=".ui.PerAppProxyActivity" />
|
<activity android:name=".ui.PerAppProxyActivity" />
|
||||||
<activity android:name=".ui.ScannerActivity" />
|
<activity android:name=".ui.ScannerActivity" />
|
||||||
<!-- <activity android:name=".InappBuyActivity" />-->
|
<activity android:name=".InappBuyActivity" />
|
||||||
<activity android:name=".ui.LogcatActivity" />
|
<activity android:name=".ui.LogcatActivity" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.RoutingSettingsActivity"
|
android:name=".ui.RoutingSettingsActivity"
|
||||||
@@ -80,11 +66,7 @@
|
|||||||
|
|
||||||
<activity android:name=".ui.SubEditActivity" />
|
<activity android:name=".ui.SubEditActivity" />
|
||||||
<activity android:name=".ui.ScScannerActivity" />
|
<activity android:name=".ui.ScScannerActivity" />
|
||||||
<activity
|
<activity android:name=".ui.ScSwitchActivity" />
|
||||||
android:name=".ui.ScSwitchActivity"
|
|
||||||
android:excludeFromRecents="true"
|
|
||||||
android:process=":RunSoLibV2RayDaemon"
|
|
||||||
android:theme="@style/AppTheme.NoActionBar.Translucent" />
|
|
||||||
|
|
||||||
<service
|
<service
|
||||||
android:name=".service.V2RayVpnService"
|
android:name=".service.V2RayVpnService"
|
||||||
@@ -101,30 +83,22 @@
|
|||||||
android:value="true" />
|
android:value="true" />
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<service android:name=".service.V2RayProxyOnlyService"
|
<!--<receiver android:name=".receiver.WidgetProvider">-->
|
||||||
android:exported="false"
|
<!--<meta-data-->
|
||||||
android:label="@string/app_name"
|
<!--android:name="android.appwidget.provider"-->
|
||||||
android:process=":RunSoLibV2RayDaemon">
|
<!--android:resource="@xml/app_widget_provider" />-->
|
||||||
</service>
|
|
||||||
|
|
||||||
<receiver android:name=".receiver.WidgetProvider"
|
<!--<intent-filter>-->
|
||||||
android:process=":RunSoLibV2RayDaemon">
|
<!--<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />-->
|
||||||
<meta-data
|
<!--<action android:name="com.v2ray.ang.action.widget.click" />-->
|
||||||
android:name="android.appwidget.provider"
|
<!--</intent-filter>-->
|
||||||
android:resource="@xml/app_widget_provider" />
|
<!--</receiver>-->
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
|
||||||
<action android:name="com.v2ray.ang.action.widget.click" />
|
|
||||||
<action android:name="com.v2ray.ang.action.activity" />
|
|
||||||
</intent-filter>
|
|
||||||
</receiver>
|
|
||||||
|
|
||||||
<service
|
<service
|
||||||
android:name=".service.QSTileService"
|
android:name=".service.QSTileService"
|
||||||
android:icon="@drawable/ic_v"
|
android:icon="@drawable/ic_v"
|
||||||
android:label="@string/app_tile_name"
|
android:label="@string/app_tile_name"
|
||||||
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE"
|
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
|
||||||
android:process=":RunSoLibV2RayDaemon">
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.service.quicksettings.action.QS_TILE" />
|
<action android:name="android.service.quicksettings.action.QS_TILE" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
@@ -139,8 +113,7 @@
|
|||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<receiver android:name=".receiver.TaskerReceiver"
|
<receiver android:name=".receiver.TaskerReceiver">
|
||||||
android:process=":RunSoLibV2RayDaemon">
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="com.twofortyfouram.locale.intent.action.FIRE_SETTING" />
|
<action android:name="com.twofortyfouram.locale.intent.action.FIRE_SETTING" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
@@ -149,4 +122,4 @@
|
|||||||
|
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
@@ -1,241 +1,196 @@
|
|||||||
amanita_design.samorost3.gp
|
|
||||||
android
|
|
||||||
au.com.shiftyjelly.pocketcasts
|
|
||||||
bbc.mobile.news.ww
|
|
||||||
be.mygod.vpnhotspot
|
|
||||||
ch.protonmail.android
|
|
||||||
co.wanqu.android
|
|
||||||
com.alphainventor.filemanager
|
|
||||||
com.amazon.kindle
|
|
||||||
com.amazon.mshop.android.shopping
|
|
||||||
com.android.chrome
|
com.android.chrome
|
||||||
com.android.providers.downloads
|
|
||||||
com.android.providers.downloads.ui
|
|
||||||
com.android.providers.telephony
|
|
||||||
com.android.settings
|
|
||||||
com.android.vending
|
|
||||||
com.android6park.m6park
|
|
||||||
com.apkpure.aegon
|
|
||||||
com.apkupdater
|
|
||||||
com.app.pornhub
|
|
||||||
com.arthurivanets.owly
|
|
||||||
com.asahi.tida.tablet
|
|
||||||
com.authy.authy
|
|
||||||
com.avmovie
|
|
||||||
com.ballistiq.artstation
|
|
||||||
com.binance.dev
|
|
||||||
com.bitly.app
|
|
||||||
com.brave.browser
|
|
||||||
com.brave.browser_beta
|
|
||||||
com.breel.wallpapers18
|
|
||||||
com.bvanced.android.youtube
|
|
||||||
com.chrome.beta
|
|
||||||
com.chrome.canary
|
|
||||||
com.chrome.dev
|
|
||||||
com.cl.newt66y
|
|
||||||
com.cradle.iitc_mobile
|
|
||||||
com.cygames.shadowverse
|
|
||||||
com.devhd.feedly
|
|
||||||
com.devolver.reigns2
|
|
||||||
com.discord
|
|
||||||
com.downloader.video.tumblr
|
|
||||||
com.driverbrowser
|
|
||||||
com.dropbox.android
|
|
||||||
com.duolingo
|
|
||||||
com.duckduckgo.mobile.android
|
|
||||||
com.dv.adm
|
|
||||||
com.estrongs.android.pop
|
|
||||||
com.estrongs.android.pop.pro
|
|
||||||
com.evernote
|
|
||||||
com.facebook.katana
|
|
||||||
com.facebook.lite
|
|
||||||
com.facebook.mlite
|
|
||||||
com.facebook.orca
|
|
||||||
com.facebook.services
|
|
||||||
com.facebook.system
|
|
||||||
com.fastaccess.github
|
|
||||||
com.felixfilip.scpae
|
|
||||||
com.fireproofstudios.theroom4
|
|
||||||
com.firstrowria.pushnotificationtester
|
|
||||||
com.flyersoft.moonreaderp
|
|
||||||
com.fooview.android.fooview
|
|
||||||
com.fvd.eversync
|
|
||||||
com.gameloft.android.anmp.glofta8hm
|
|
||||||
com.gameloft.android.anmp.glofta9hm
|
|
||||||
com.gianlu.aria2app
|
|
||||||
com.github.yeriomin.yalpstore
|
|
||||||
com.google.android.apps.adm
|
|
||||||
com.google.android.apps.books
|
|
||||||
com.google.android.apps.docs
|
|
||||||
com.google.android.apps.docs.editors.sheets
|
|
||||||
com.google.android.apps.fitness
|
|
||||||
com.google.android.apps.googleassistant
|
|
||||||
com.google.android.apps.googlevoice
|
|
||||||
com.google.android.apps.hangoutsdialer
|
|
||||||
com.google.android.apps.inbox
|
|
||||||
com.google.android.apps.magazines
|
|
||||||
com.google.android.apps.maps
|
|
||||||
com.google.android.apps.nbu.files
|
|
||||||
com.google.android.apps.paidtasks
|
|
||||||
com.google.android.apps.pdfviewer
|
|
||||||
com.google.android.apps.photos
|
|
||||||
com.google.android.apps.plus
|
|
||||||
com.google.android.apps.translate
|
|
||||||
com.google.android.gm
|
|
||||||
com.google.android.gms
|
|
||||||
com.google.android.gms.setup
|
|
||||||
com.google.android.googlequicksearchbox
|
com.google.android.googlequicksearchbox
|
||||||
|
com.google.android.apps.photos
|
||||||
|
com.google.android.youtube
|
||||||
|
com.google.android.gm
|
||||||
|
com.google.android.apps.plus
|
||||||
|
com.android.vending
|
||||||
|
com.google.android.inputmethod.latin
|
||||||
|
com.google.android.apps.paidtasks
|
||||||
|
com.google.android.keep
|
||||||
|
com.google.android.gms.setup
|
||||||
|
com. google.android. apps.magazines
|
||||||
|
com.google.android.videos
|
||||||
|
com. google.android.gms
|
||||||
|
com.google.android.apps.books
|
||||||
|
com.google.android.music
|
||||||
|
com.google.android.play.games
|
||||||
com.google.android.gsf
|
com.google.android.gsf
|
||||||
com.google.android.gsf.login
|
com.google.android.gsf.login
|
||||||
|
com.app.pornhub
|
||||||
|
com.spotify.music
|
||||||
|
org.thunderdog.challegram
|
||||||
|
com.tumblr
|
||||||
|
com.twitter.android
|
||||||
|
com.xda.labs
|
||||||
|
com.kapp.youtube.final
|
||||||
com.google.android.ims
|
com.google.android.ims
|
||||||
com.google.android.inputmethod.latin
|
com.wire
|
||||||
|
mark.via.gp
|
||||||
|
com.downloader.video.tumblr
|
||||||
|
com.sololearn
|
||||||
|
com.cygames.shadowverse
|
||||||
|
com.felixfilip.scpae
|
||||||
|
amanita_design.samorost3.gp
|
||||||
|
com.devolver.reigns2
|
||||||
|
com.utopia.pxview
|
||||||
|
ch.protonmail.android
|
||||||
|
com.perol.asdpl.pixivez
|
||||||
|
com.pinterest
|
||||||
|
com.paypal.android.p2pmobile
|
||||||
|
com.arthurivanets.owly
|
||||||
|
com.rubenmayayo.reddit
|
||||||
|
com.rayark.cytus2
|
||||||
|
com.rayark.pluto
|
||||||
|
com.rayark.implosion
|
||||||
|
com.fireproofstudios.theroom4
|
||||||
|
com.netflix.mediaclient
|
||||||
|
com.instagram.android
|
||||||
|
com.google.android.apps.hangoutsdialer
|
||||||
|
com.google.android.talk
|
||||||
|
com.google.android.apps.plus
|
||||||
|
com.google.android.apps.pdfviewer
|
||||||
|
com.google.android.apps.magazines
|
||||||
|
com.google.android.apps.nbu.files
|
||||||
|
com.evernote
|
||||||
|
net.tsapps.appsales
|
||||||
|
com.google.android.apps.translate
|
||||||
|
com.google.ar.lens
|
||||||
|
com.google.android.apps.adm
|
||||||
|
com.google.android.apps.googleassistant
|
||||||
|
tw.com.gamer.android.activecenter
|
||||||
|
org.telegram.plus
|
||||||
|
com.brave.browser
|
||||||
|
com.breel.wallpapers18
|
||||||
|
com.teslacoilsw.launcher
|
||||||
|
com.lastpass.lpandroid
|
||||||
|
org.kustom.widget
|
||||||
|
com.fooview.android.fooview
|
||||||
|
com.google.android.apps.docs
|
||||||
|
com.google.android.apps.maps
|
||||||
|
com.facebook.services
|
||||||
|
com.facebook.system
|
||||||
|
com.facebook.katana
|
||||||
|
com.nianticlabs.ingress.prime.qa
|
||||||
|
com.vanced.android.youtube
|
||||||
|
com.nianticproject.ingress
|
||||||
|
com.quoord.tapatalkpro.activity
|
||||||
|
org.mozilla.firefox
|
||||||
|
com.reddit.frontpage
|
||||||
|
com.google.android.apps.fitness
|
||||||
|
au.com.shiftyjelly.pocketcasts
|
||||||
|
com.google.android.gms
|
||||||
|
com.android.providers.telephony
|
||||||
|
com.resilio.sync
|
||||||
|
com.google.android.apps.googlevoice
|
||||||
|
com.discord
|
||||||
|
com.cradle.iitc_mobile
|
||||||
|
be.mygod.vpnhotspot
|
||||||
|
com.alphainventor.filemanager
|
||||||
|
com.android.providers.downloads
|
||||||
|
com.apkpure.aegon
|
||||||
|
com.ballistiq.artstation
|
||||||
|
com.bitly.app
|
||||||
|
com.chrome.canary
|
||||||
|
com.chrome.dev
|
||||||
|
com.devhd.feedly
|
||||||
|
com.dropbox.android
|
||||||
|
com.estrongs.android.pop
|
||||||
|
com.estrongs.android.pop.pro
|
||||||
|
com.fastaccess.github
|
||||||
|
com.firstrowria.pushnotificationtester
|
||||||
|
com.fvd.eversync
|
||||||
|
com.gianlu.aria2app
|
||||||
|
com.github.yeriomin.yalpstore
|
||||||
|
com.google.android.apps.docs.editors.sheets
|
||||||
com.google.android.instantapps.supervisor
|
com.google.android.instantapps.supervisor
|
||||||
com.google.android.keep
|
|
||||||
com.google.android.music
|
|
||||||
com.google.android.ogyoutube
|
com.google.android.ogyoutube
|
||||||
com.google.android.partnersetup
|
com.google.android.partnersetup
|
||||||
com.google.android.play.games
|
|
||||||
com.google.android.street
|
|
||||||
com.google.android.syncadapters.calendar
|
com.google.android.syncadapters.calendar
|
||||||
com.google.android.syncadapters.contacts
|
com.google.android.syncadapters.contacts
|
||||||
com.google.android.talk
|
|
||||||
com.google.android.tts
|
com.google.android.tts
|
||||||
com.google.android.videos
|
|
||||||
com.google.android.youtube
|
|
||||||
com.google.ar.lens
|
|
||||||
com.hochan.coldsoup
|
com.hochan.coldsoup
|
||||||
com.ifttt.ifttt
|
com.ifttt.ifttt
|
||||||
com.imgur.mobile
|
com.imgur.mobile
|
||||||
com.innologica.inoreader
|
com.innologica.inoreader
|
||||||
com.instagram.android
|
|
||||||
com.instapaper.android
|
com.instapaper.android
|
||||||
com.jarvanh.vpntether
|
com.jarvanh.vpntether
|
||||||
com.kapp.youtube.final
|
|
||||||
com.klinker.android.twitter_l
|
|
||||||
com.lastpass.lpandroid
|
|
||||||
com.linecorp.linelite
|
|
||||||
com.lingodeer
|
|
||||||
com.mediapods.tumbpods
|
com.mediapods.tumbpods
|
||||||
com.mgoogle.android.gms
|
com.mgoogle.android.gms
|
||||||
com.microsoft.emmx
|
|
||||||
com.microsoft.office.powerpoint
|
com.microsoft.office.powerpoint
|
||||||
com.microsoft.skydrive
|
|
||||||
com.mixplorer
|
com.mixplorer
|
||||||
com.msd.consumerchinese
|
com.msd.consumerchinese
|
||||||
com.msd.professionalchinese
|
com.msd.professionalchinese
|
||||||
com.mss2011c.sharehelper
|
com.mss2011c.sharehelper
|
||||||
com.netflix.mediaclient
|
|
||||||
com.newin.nplayer.pro
|
com.newin.nplayer.pro
|
||||||
com.nianticlabs.ingress.prime.qa
|
|
||||||
com.nianticproject.ingress
|
|
||||||
com.ninefolders.hd3
|
|
||||||
com.ninegag.android.app
|
|
||||||
com.nintendo.zara
|
|
||||||
com.nytimes.cn
|
|
||||||
com.oasisfeng.island
|
com.oasisfeng.island
|
||||||
com.ocnt.liveapp.hw
|
|
||||||
com.orekie.search
|
com.orekie.search
|
||||||
com.patreon.android
|
|
||||||
com.paypal.android.p2pmobile
|
|
||||||
com.perol.asdpl.pixivez
|
|
||||||
com.pinterest
|
|
||||||
com.popularapp.periodcalendar
|
|
||||||
com.popularapp.videodownloaderforinstagram
|
com.popularapp.videodownloaderforinstagram
|
||||||
com.pushbullet.android
|
com.pushbullet.android
|
||||||
com.quoord.tapatalkpro.activity
|
|
||||||
com.quora.android
|
|
||||||
com.rayark.cytus2
|
|
||||||
com.rayark.implosion
|
|
||||||
com.rayark.pluto
|
|
||||||
com.reddit.frontpage
|
|
||||||
com.resilio.sync
|
|
||||||
com.rhmsoft.edit
|
com.rhmsoft.edit
|
||||||
com.rubenmayayo.reddit
|
|
||||||
com.sec.android.app.sbrowser
|
|
||||||
com.sec.android.app.sbrowser.beta
|
|
||||||
com.shanga.walli
|
|
||||||
com.simplehabit.simplehabitapp
|
|
||||||
com.slack
|
com.slack
|
||||||
com.snaptube.premium
|
|
||||||
com.sololearn
|
|
||||||
com.sonelli.juicessh
|
|
||||||
com.spotify.music
|
|
||||||
com.tencent.huatuo
|
com.tencent.huatuo
|
||||||
com.termux
|
com.termux
|
||||||
com.teslacoilsw.launcher
|
|
||||||
com.theinitium.news
|
|
||||||
com.thomsonreuters.reuters
|
|
||||||
com.thunkable.android.hritvik00.freenom
|
com.thunkable.android.hritvik00.freenom
|
||||||
com.topjohnwu.magisk
|
com.topjohnwu.magisk
|
||||||
com.tripadvisor.tripadvisor
|
|
||||||
com.tumblr
|
|
||||||
com.twitter.android
|
|
||||||
com.u91porn
|
com.u91porn
|
||||||
com.u9porn
|
com.u9porn
|
||||||
com.ubisoft.dance.justdance2015companion
|
|
||||||
com.utopia.pxview
|
|
||||||
com.valvesoftware.android.steam.communimunity
|
|
||||||
com.valvesoftware.android.steam.community
|
|
||||||
com.vanced.android.youtube
|
|
||||||
com.vimeo.android.videoapp
|
com.vimeo.android.videoapp
|
||||||
com.vivaldi.browser
|
|
||||||
com.vivaldi.browser.snapshot
|
|
||||||
com.vkontakte.android
|
|
||||||
com.whatsapp
|
|
||||||
com.wire
|
|
||||||
com.wuxiangai.refactor
|
com.wuxiangai.refactor
|
||||||
com.xda.labs
|
|
||||||
com.xvideos.app
|
|
||||||
com.yandex.browser
|
com.yandex.browser
|
||||||
com.yandex.browser.beta
|
|
||||||
com.yandex.browser.alpha
|
|
||||||
com.z28j.feel
|
com.z28j.feel
|
||||||
con.medium.reader
|
|
||||||
de.apkgrabber
|
|
||||||
de.robv.android.xposed.installer
|
de.robv.android.xposed.installer
|
||||||
dk.tacit.android.foldersync.full
|
dk.tacit.android.foldersync.full
|
||||||
es.rafalense.telegram.themes
|
es.rafalense.telegram.themes
|
||||||
es.rafalense.themes
|
es.rafalense.themes
|
||||||
flipboard.app
|
flipboard.app
|
||||||
fm.moon.app
|
|
||||||
fr.gouv.etalab.mastodon
|
|
||||||
github.tornaco.xposedmoduletest
|
github.tornaco.xposedmoduletest
|
||||||
idm.internet.download.manager
|
|
||||||
idm.internet.download.manager.plus
|
|
||||||
io.github.javiewer
|
|
||||||
io.github.skyhacker2.magnetsearch
|
|
||||||
io.va.exposed
|
io.va.exposed
|
||||||
it.mvilla.android.fenix2
|
|
||||||
jp.bokete.app.android
|
|
||||||
jp.naver.line.android
|
|
||||||
jp.pxv.android
|
jp.pxv.android
|
||||||
luo.speedometergpspro
|
|
||||||
mark.via.gp
|
|
||||||
me.tshine.easymark
|
me.tshine.easymark
|
||||||
net.teeha.android.url_shortener
|
net.teeha.android.url_shortener
|
||||||
net.tsapps.appsales
|
|
||||||
onion.fire
|
onion.fire
|
||||||
org.fdroid.fdroid
|
org.fdroid.fdroid
|
||||||
org.freedownloadmanager.fdm
|
|
||||||
org.kustom.widget
|
|
||||||
org.mozilla.fennec_aurora
|
org.mozilla.fennec_aurora
|
||||||
org.mozilla.fenix
|
|
||||||
org.mozilla.fenix.nightly
|
|
||||||
org.mozilla.firefox
|
|
||||||
org.mozilla.firefox_beta
|
|
||||||
org.mozilla.focus
|
|
||||||
org.schabi.newpipe
|
org.schabi.newpipe
|
||||||
org.telegram.messenger
|
org.telegram.messenger
|
||||||
org.telegram.multi
|
|
||||||
org.telegram.plus
|
|
||||||
org.thunderdog.challegram
|
|
||||||
org.torproject.android
|
org.torproject.android
|
||||||
org.torproject.torbrowser_alpha
|
|
||||||
org.wikipedia
|
|
||||||
org.xbmc.kodi
|
org.xbmc.kodi
|
||||||
pl.zdunex25.updater
|
pl.zdunex25.updater
|
||||||
tv.twitch.android.app
|
|
||||||
tw.com.gamer.android.activecenter
|
|
||||||
videodownloader.downloadvideo.downloader
|
videodownloader.downloadvideo.downloader
|
||||||
uk.co.bbc.learningenglish
|
com.quora.android
|
||||||
com.ted.android
|
com.lingodeer
|
||||||
|
org.wikipedia
|
||||||
|
com.ninegag.android.app
|
||||||
|
com.duolingo
|
||||||
|
com.patreon.android
|
||||||
|
com.valvesoftware.android.steam.communimunity
|
||||||
|
co.wanqu.android
|
||||||
|
jp.bokete.app.android
|
||||||
|
com.vkontakte.android
|
||||||
|
com.amazon.mshop.android.shopping
|
||||||
|
com.ubisoft.dance.justdance2015companion
|
||||||
|
com.gameloft.android.anmp.glofta8hm
|
||||||
|
com.gameloft.android.anmp.glofta9hm
|
||||||
|
com.binance.dev
|
||||||
|
com.asahi.tida.tablet
|
||||||
|
com.theinitium.news
|
||||||
|
com.driverbrowser
|
||||||
|
com.thomsonreuters.reuters
|
||||||
|
com.nytimes.cn
|
||||||
|
com.android.providers.downloads.ui
|
||||||
|
com.avmovie
|
||||||
|
bbc.mobile.news.ww
|
||||||
|
org.mozilla.focus
|
||||||
|
io.github.javiewer
|
||||||
|
com.sonelli.juicessh
|
||||||
|
con.medium.reader
|
||||||
|
com.microsoft.skydrive
|
||||||
|
com.valvesoftware.android.steam.community
|
||||||
|
com.nintendo.zara
|
||||||
|
org.torproject.torbrowser_alpha
|
||||||
|
tv.twitch.android.app
|
||||||
|
com.shanga.walli
|
||||||
|
com.whatsapp
|
||||||
|
com.wire
|
||||||
|
com.simplehabit.simplehabitapp
|
||||||
@@ -13,8 +13,8 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"system": {
|
"system": {
|
||||||
"statsOutboundUplink": true,
|
"statsInboundUplink": true,
|
||||||
"statsOutboundDownlink": true
|
"statsInboundDownlink": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"inbounds": [{
|
"inbounds": [{
|
||||||
|
|||||||
@@ -43,8 +43,6 @@ public interface ItemTouchHelperAdapter {
|
|||||||
boolean onItemMove(int fromPosition, int toPosition);
|
boolean onItemMove(int fromPosition, int toPosition);
|
||||||
|
|
||||||
|
|
||||||
void onItemMoveCompleted();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when an item has been dismissed by a swipe.<br/>
|
* Called when an item has been dismissed by a swipe.<br/>
|
||||||
* <br/>
|
* <br/>
|
||||||
|
|||||||
@@ -112,8 +112,6 @@ public class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback {
|
|||||||
public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
|
public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
|
||||||
super.clearView(recyclerView, viewHolder);
|
super.clearView(recyclerView, viewHolder);
|
||||||
|
|
||||||
mAdapter.onItemMoveCompleted();
|
|
||||||
|
|
||||||
viewHolder.itemView.setAlpha(ALPHA_FULL);
|
viewHolder.itemView.setAlpha(ALPHA_FULL);
|
||||||
|
|
||||||
if (viewHolder instanceof ItemTouchHelperViewHolder) {
|
if (viewHolder instanceof ItemTouchHelperViewHolder) {
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,16 +1,16 @@
|
|||||||
package com.v2ray.ang
|
package com.v2ray.ang
|
||||||
|
|
||||||
import android.support.multidex.MultiDexApplication
|
import android.app.Application
|
||||||
import android.support.v7.preference.PreferenceManager
|
//import com.squareup.leakcanary.LeakCanary
|
||||||
import com.v2ray.ang.util.AngConfigManager
|
import com.v2ray.ang.util.AngConfigManager
|
||||||
import me.dozen.dpreference.DPreference
|
import me.dozen.dpreference.DPreference
|
||||||
|
import org.jetbrains.anko.defaultSharedPreferences
|
||||||
|
|
||||||
class AngApplication : MultiDexApplication() {
|
class AngApplication : Application() {
|
||||||
companion object {
|
companion object {
|
||||||
const val PREF_LAST_VERSION = "pref_last_version"
|
const val PREF_LAST_VERSION = "pref_last_version"
|
||||||
}
|
}
|
||||||
|
|
||||||
var curIndex = -1 //Current proxy that is opened. (Used to implement restart feature)
|
|
||||||
var firstRun = false
|
var firstRun = false
|
||||||
private set
|
private set
|
||||||
|
|
||||||
@@ -21,7 +21,6 @@ class AngApplication : MultiDexApplication() {
|
|||||||
|
|
||||||
// LeakCanary.install(this)
|
// LeakCanary.install(this)
|
||||||
|
|
||||||
val defaultSharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
|
|
||||||
firstRun = defaultSharedPreferences.getInt(PREF_LAST_VERSION, 0) != BuildConfig.VERSION_CODE
|
firstRun = defaultSharedPreferences.getInt(PREF_LAST_VERSION, 0) != BuildConfig.VERSION_CODE
|
||||||
if (firstRun)
|
if (firstRun)
|
||||||
defaultSharedPreferences.edit().putInt(PREF_LAST_VERSION, BuildConfig.VERSION_CODE).apply()
|
defaultSharedPreferences.edit().putInt(PREF_LAST_VERSION, BuildConfig.VERSION_CODE).apply()
|
||||||
|
|||||||
@@ -11,10 +11,7 @@ object AppConfig {
|
|||||||
const val PREF_CURR_CONFIG_GUID = "pref_v2ray_config_guid"
|
const val PREF_CURR_CONFIG_GUID = "pref_v2ray_config_guid"
|
||||||
const val PREF_CURR_CONFIG_NAME = "pref_v2ray_config_name"
|
const val PREF_CURR_CONFIG_NAME = "pref_v2ray_config_name"
|
||||||
const val PREF_CURR_CONFIG_DOMAIN = "pref_v2ray_config_domain"
|
const val PREF_CURR_CONFIG_DOMAIN = "pref_v2ray_config_domain"
|
||||||
const val PREF_CURR_CONFIG_OUTBOUND_TAGS = "pref_v2ray_config_outbound_tags"
|
|
||||||
const val PREF_INAPP_BUY_IS_PREMIUM = "pref_inapp_buy_is_premium"
|
const val PREF_INAPP_BUY_IS_PREMIUM = "pref_inapp_buy_is_premium"
|
||||||
const val PREF_MODE = "pref_mode"
|
|
||||||
|
|
||||||
const val VMESS_PROTOCOL: String = "vmess://"
|
const val VMESS_PROTOCOL: String = "vmess://"
|
||||||
const val SS_PROTOCOL: String = "ss://"
|
const val SS_PROTOCOL: String = "ss://"
|
||||||
const val SOCKS_PROTOCOL: String = "socks://"
|
const val SOCKS_PROTOCOL: String = "socks://"
|
||||||
@@ -38,7 +35,6 @@ object AppConfig {
|
|||||||
const val androidpackagenamelistUrl = "https://raw.githubusercontent.com/2dust/androidpackagenamelist/master/proxy.txt"
|
const val androidpackagenamelistUrl = "https://raw.githubusercontent.com/2dust/androidpackagenamelist/master/proxy.txt"
|
||||||
const val v2rayCustomRoutingListUrl = "https://raw.githubusercontent.com/2dust/v2rayCustomRoutingList/master/"
|
const val v2rayCustomRoutingListUrl = "https://raw.githubusercontent.com/2dust/v2rayCustomRoutingList/master/"
|
||||||
const val v2rayNGIssues = "https://github.com/2dust/v2rayNG/issues"
|
const val v2rayNGIssues = "https://github.com/2dust/v2rayNG/issues"
|
||||||
const val v2rayNGWikiMode = "https://github.com/2dust/v2rayNG/wiki/Mode"
|
|
||||||
const val promotionUrl = "https://1.2345345.xyz/ads.html"
|
const val promotionUrl = "https://1.2345345.xyz/ads.html"
|
||||||
|
|
||||||
const val DNS_AGENT = "1.1.1.1"
|
const val DNS_AGENT = "1.1.1.1"
|
||||||
@@ -54,4 +50,12 @@ object AppConfig {
|
|||||||
const val MSG_STATE_STOP = 4
|
const val MSG_STATE_STOP = 4
|
||||||
const val MSG_STATE_STOP_SUCCESS = 41
|
const val MSG_STATE_STOP_SUCCESS = 41
|
||||||
const val MSG_STATE_RESTART = 5
|
const val MSG_STATE_RESTART = 5
|
||||||
}
|
|
||||||
|
object EConfigType {
|
||||||
|
val Vmess = 1
|
||||||
|
val Custom = 2
|
||||||
|
val Shadowsocks = 3
|
||||||
|
val Socks = 4
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
package com.v2ray.ang.dto
|
|
||||||
|
|
||||||
enum class EConfigType(val value: Int) {
|
|
||||||
VMESS(1),
|
|
||||||
CUSTOM(2),
|
|
||||||
SHADOWSOCKS(3),
|
|
||||||
SOCKS(4);
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun fromInt(value: Int) = values().firstOrNull { it.value == value }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -64,21 +64,22 @@ data class V2rayConfig(
|
|||||||
|
|
||||||
data class StreamSettingsBean(var network: String,
|
data class StreamSettingsBean(var network: String,
|
||||||
var security: String,
|
var security: String,
|
||||||
var tcpSettings: TcpSettingsBean?,
|
var tcpSettings: TcpsettingsBean?,
|
||||||
var kcpSettings: KcpSettingsBean?,
|
var kcpsettings: KcpsettingsBean?,
|
||||||
var wsSettings: WsSettingsBean?,
|
var wssettings: WssettingsBean?,
|
||||||
var httpSettings: HttpSettingsBean?,
|
var httpsettings: HttpsettingsBean?,
|
||||||
var tlsSettings: TlsSettingsBean?,
|
var tlssettings: TlssettingsBean?,
|
||||||
var quicSettings: QuicSettingBean?
|
var quicsettings: QuicsettingBean?
|
||||||
) {
|
) {
|
||||||
|
|
||||||
data class TcpSettingsBean(var header: HeaderBean = HeaderBean()) {
|
data class TcpsettingsBean(var connectionReuse: Boolean = true,
|
||||||
|
var header: HeaderBean = HeaderBean()) {
|
||||||
data class HeaderBean(var type: String = "none",
|
data class HeaderBean(var type: String = "none",
|
||||||
var request: Any? = null,
|
var request: Any? = null,
|
||||||
var response: Any? = null)
|
var response: Any? = null)
|
||||||
}
|
}
|
||||||
|
|
||||||
data class KcpSettingsBean(var mtu: Int = 1350,
|
data class KcpsettingsBean(var mtu: Int = 1350,
|
||||||
var tti: Int = 20,
|
var tti: Int = 20,
|
||||||
var uplinkCapacity: Int = 12,
|
var uplinkCapacity: Int = 12,
|
||||||
var downlinkCapacity: Int = 100,
|
var downlinkCapacity: Int = 100,
|
||||||
@@ -89,43 +90,25 @@ data class V2rayConfig(
|
|||||||
data class HeaderBean(var type: String = "none")
|
data class HeaderBean(var type: String = "none")
|
||||||
}
|
}
|
||||||
|
|
||||||
data class WsSettingsBean(var path: String = "",
|
data class WssettingsBean(var connectionReuse: Boolean = true,
|
||||||
|
var path: String = "",
|
||||||
var headers: HeadersBean = HeadersBean()) {
|
var headers: HeadersBean = HeadersBean()) {
|
||||||
data class HeadersBean(var Host: String = "")
|
data class HeadersBean(var Host: String = "")
|
||||||
}
|
}
|
||||||
|
|
||||||
data class HttpSettingsBean(var host: List<String> = ArrayList(),
|
data class HttpsettingsBean(var host: List<String> = ArrayList<String>(), var path: String = "")
|
||||||
var path: String = "")
|
|
||||||
|
|
||||||
data class TlsSettingsBean(var allowInsecure: Boolean = true,
|
data class TlssettingsBean(var allowInsecure: Boolean = true,
|
||||||
var serverName: String = "")
|
var serverName: String = "")
|
||||||
|
|
||||||
data class QuicSettingBean(var security: String = "none",
|
data class QuicsettingBean(var security: String = "none",
|
||||||
var key: String = "",
|
var key: String = "",
|
||||||
var header: HeaderBean = HeaderBean()) {
|
var header: HeaderBean = HeaderBean()) {
|
||||||
data class HeaderBean(var type: String = "none")
|
data class HeaderBean(var type: String = "none")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class MuxBean(var enabled: Boolean)
|
data class MuxBean(var enabled: Boolean)
|
||||||
|
|
||||||
fun getServerAddress(): String? {
|
|
||||||
if (protocol.equals(EConfigType.VMESS.name.toLowerCase())) {
|
|
||||||
return settings?.vnext?.get(0)?.address
|
|
||||||
} else if (protocol.equals(EConfigType.SHADOWSOCKS.name.toLowerCase()) || protocol.equals(EConfigType.SOCKS.name.toLowerCase())) {
|
|
||||||
return settings?.servers?.get(0)?.address
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getServerPort(): Int? {
|
|
||||||
if (protocol.equals(EConfigType.VMESS.name.toLowerCase())) {
|
|
||||||
return settings?.vnext?.get(0)?.port
|
|
||||||
} else if (protocol.equals(EConfigType.SHADOWSOCKS.name.toLowerCase()) || protocol.equals(EConfigType.SOCKS.name.toLowerCase())) {
|
|
||||||
return settings?.servers?.get(0)?.port
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//data class DnsBean(var servers: List<String>)
|
//data class DnsBean(var servers: List<String>)
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package com.v2ray.ang.extension
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.widget.Toast
|
|
||||||
import com.v2ray.ang.AngApplication
|
import com.v2ray.ang.AngApplication
|
||||||
import me.dozen.dpreference.DPreference
|
import me.dozen.dpreference.DPreference
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
@@ -15,24 +14,9 @@ import java.net.URLConnection
|
|||||||
val Context.v2RayApplication: AngApplication
|
val Context.v2RayApplication: AngApplication
|
||||||
get() = applicationContext as AngApplication
|
get() = applicationContext as AngApplication
|
||||||
|
|
||||||
// Usage note: DPreference use Android ContentProvider to redirect multi process access to main process.
|
|
||||||
// Currently, RunSoLibV2RayDaemon process will run proxy core, keep minimum configuration and long running
|
|
||||||
// in the background, support toggle on/off. That means it should NOT use DPreference after the initial
|
|
||||||
// creation and setup of the service
|
|
||||||
val Context.defaultDPreference: DPreference
|
val Context.defaultDPreference: DPreference
|
||||||
get() = v2RayApplication.defaultDPreference
|
get() = v2RayApplication.defaultDPreference
|
||||||
|
|
||||||
inline fun Context.toast(message: Int): Toast = Toast
|
|
||||||
.makeText(this, message, Toast.LENGTH_SHORT)
|
|
||||||
.apply {
|
|
||||||
show()
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fun Context.toast(message: CharSequence): Toast = Toast
|
|
||||||
.makeText(this, message, Toast.LENGTH_SHORT)
|
|
||||||
.apply {
|
|
||||||
show()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun JSONObject.putOpt(pair: Pair<String, Any>) = putOpt(pair.first, pair.second)!!
|
fun JSONObject.putOpt(pair: Pair<String, Any>) = putOpt(pair.first, pair.second)!!
|
||||||
fun JSONObject.putOpt(pairs: Map<String, Any>) = pairs.forEach { putOpt(it.key to it.value) }
|
fun JSONObject.putOpt(pairs: Map<String, Any>) = pairs.forEach { putOpt(it.key to it.value) }
|
||||||
@@ -43,37 +27,34 @@ const val divisor = 1024F
|
|||||||
fun Long.toSpeedString() = toTrafficString() + "/s"
|
fun Long.toSpeedString() = toTrafficString() + "/s"
|
||||||
|
|
||||||
fun Long.toTrafficString(): String {
|
fun Long.toTrafficString(): String {
|
||||||
if (this == 0L)
|
|
||||||
return "\t\t\t0\t B"
|
|
||||||
|
|
||||||
if (this < threshold)
|
if (this < threshold)
|
||||||
return "${this.toFloat().toShortString()}\t B"
|
return "$this B"
|
||||||
|
|
||||||
val kib = this / divisor
|
val kib = this / divisor
|
||||||
if (kib < threshold)
|
if (kib < threshold)
|
||||||
return "${kib.toShortString()}\t KB"
|
return "${kib.toShortString()} KB"
|
||||||
|
|
||||||
val mib = kib / divisor
|
val mib = kib / divisor
|
||||||
if (mib < threshold)
|
if (mib < threshold)
|
||||||
return "${mib.toShortString()}\t MB"
|
return "${mib.toShortString()} MB"
|
||||||
|
|
||||||
val gib = mib / divisor
|
val gib = mib / divisor
|
||||||
if (gib < threshold)
|
if (gib < threshold)
|
||||||
return "${gib.toShortString()}\t GB"
|
return "${gib.toShortString()} GB"
|
||||||
|
|
||||||
val tib = gib / divisor
|
val tib = gib / divisor
|
||||||
if (tib < threshold)
|
if (tib < threshold)
|
||||||
return "${tib.toShortString()}\t TB"
|
return "${tib.toShortString()} TB"
|
||||||
|
|
||||||
val pib = tib / divisor
|
val pib = tib / divisor
|
||||||
if (pib < threshold)
|
if (pib < threshold)
|
||||||
return "${pib.toShortString()}\t PB"
|
return "${pib.toShortString()} PB"
|
||||||
|
|
||||||
return "∞"
|
return "∞"
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Float.toShortString(): String {
|
private fun Float.toShortString(): String {
|
||||||
val s = "%.2f".format(this)
|
val s = toString()
|
||||||
if (s.length <= 4)
|
if (s.length <= 4)
|
||||||
return s
|
return s
|
||||||
return s.substring(0, 4).removeSuffix(".")
|
return s.substring(0, 4).removeSuffix(".")
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ class TaskerReceiver : BroadcastReceiver() {
|
|||||||
return
|
return
|
||||||
} else if (switch) {
|
} else if (switch) {
|
||||||
if (guid == AppConfig.TASKER_DEFAULT_GUID) {
|
if (guid == AppConfig.TASKER_DEFAULT_GUID) {
|
||||||
Utils.startVServiceFromToggle(context)
|
Utils.startVService(context)
|
||||||
} else {
|
} else {
|
||||||
Utils.startVService(context, guid)
|
Utils.startVService(context, guid)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,14 +3,14 @@ package com.v2ray.ang.receiver
|
|||||||
import android.app.PendingIntent
|
import android.app.PendingIntent
|
||||||
import android.appwidget.AppWidgetManager
|
import android.appwidget.AppWidgetManager
|
||||||
import android.appwidget.AppWidgetProvider
|
import android.appwidget.AppWidgetProvider
|
||||||
import android.content.ComponentName
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.os.Bundle
|
||||||
import android.widget.RemoteViews
|
import android.widget.RemoteViews
|
||||||
import com.v2ray.ang.R
|
import com.v2ray.ang.R
|
||||||
import com.v2ray.ang.AppConfig
|
import com.v2ray.ang.AppConfig
|
||||||
import com.v2ray.ang.service.V2RayServiceManager
|
|
||||||
import com.v2ray.ang.util.Utils
|
import com.v2ray.ang.util.Utils
|
||||||
|
import org.jetbrains.anko.toast
|
||||||
|
|
||||||
class WidgetProvider : AppWidgetProvider() {
|
class WidgetProvider : AppWidgetProvider() {
|
||||||
/**
|
/**
|
||||||
@@ -18,20 +18,11 @@ class WidgetProvider : AppWidgetProvider() {
|
|||||||
*/
|
*/
|
||||||
override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
|
override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
|
||||||
super.onUpdate(context, appWidgetManager, appWidgetIds)
|
super.onUpdate(context, appWidgetManager, appWidgetIds)
|
||||||
updateWidgetBackground(context, appWidgetManager, appWidgetIds, V2RayServiceManager.v2rayPoint.isRunning)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun updateWidgetBackground(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray, isRunning: Boolean) {
|
|
||||||
val remoteViews = RemoteViews(context.packageName, R.layout.widget_switch)
|
val remoteViews = RemoteViews(context.packageName, R.layout.widget_switch)
|
||||||
val intent = Intent(context, WidgetProvider::class.java)
|
val intent = Intent(AppConfig.BROADCAST_ACTION_WIDGET_CLICK)
|
||||||
intent.action = AppConfig.BROADCAST_ACTION_WIDGET_CLICK
|
|
||||||
val pendingIntent = PendingIntent.getBroadcast(context, R.id.layout_switch, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
val pendingIntent = PendingIntent.getBroadcast(context, R.id.layout_switch, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||||
remoteViews.setOnClickPendingIntent(R.id.layout_switch, pendingIntent)
|
remoteViews.setOnClickPendingIntent(R.id.layout_switch, pendingIntent)
|
||||||
if (isRunning) {
|
|
||||||
remoteViews.setInt(R.id.layout_switch, "setBackgroundResource", R.drawable.ic_rounded_corner_theme)
|
|
||||||
} else {
|
|
||||||
remoteViews.setInt(R.id.layout_switch, "setBackgroundResource", R.drawable.ic_rounded_corner_grey)
|
|
||||||
}
|
|
||||||
|
|
||||||
for (appWidgetId in appWidgetIds) {
|
for (appWidgetId in appWidgetIds) {
|
||||||
appWidgetManager.updateAppWidget(appWidgetId, remoteViews)
|
appWidgetManager.updateAppWidget(appWidgetId, remoteViews)
|
||||||
@@ -39,28 +30,21 @@ class WidgetProvider : AppWidgetProvider() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 接收窗口小部件发送的广播
|
* 接收窗口小部件点击时发送的广播
|
||||||
*/
|
*/
|
||||||
override fun onReceive(context: Context, intent: Intent) {
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
super.onReceive(context, intent)
|
super.onReceive(context, intent)
|
||||||
if (AppConfig.BROADCAST_ACTION_WIDGET_CLICK == intent.action) {
|
if (AppConfig.BROADCAST_ACTION_WIDGET_CLICK == intent.action) {
|
||||||
if (V2RayServiceManager.v2rayPoint.isRunning) {
|
|
||||||
|
val isRunning = Utils.isServiceRun(context, "com.v2ray.ang.service.V2RayVpnService")
|
||||||
|
if (isRunning) {
|
||||||
|
// context.toast(R.string.toast_services_stop)
|
||||||
Utils.stopVService(context)
|
Utils.stopVService(context)
|
||||||
} else {
|
} else {
|
||||||
Utils.startVServiceFromToggle(context)
|
// context.toast(R.string.toast_services_start)
|
||||||
}
|
Utils.startVService(context)
|
||||||
} else if (AppConfig.BROADCAST_ACTION_ACTIVITY == intent.action) {
|
|
||||||
val manager = AppWidgetManager.getInstance(context)
|
|
||||||
when (intent.getIntExtra("key", 0)) {
|
|
||||||
AppConfig.MSG_STATE_RUNNING, AppConfig.MSG_STATE_START_SUCCESS -> {
|
|
||||||
updateWidgetBackground(context, manager, manager.getAppWidgetIds(ComponentName(context, WidgetProvider::class.java)),
|
|
||||||
true)
|
|
||||||
}
|
|
||||||
AppConfig.MSG_STATE_NOT_RUNNING, AppConfig.MSG_STATE_START_FAILURE, AppConfig.MSG_STATE_STOP_SUCCESS -> {
|
|
||||||
updateWidgetBackground(context, manager, manager.getAppWidgetIds(ComponentName(context, WidgetProvider::class.java)),
|
|
||||||
false)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,15 +6,19 @@ import android.content.Context
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.IntentFilter
|
import android.content.IntentFilter
|
||||||
import android.graphics.drawable.Icon
|
import android.graphics.drawable.Icon
|
||||||
|
import android.net.VpnService
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.service.quicksettings.Tile
|
import android.service.quicksettings.Tile
|
||||||
import android.service.quicksettings.TileService
|
import android.service.quicksettings.TileService
|
||||||
import com.v2ray.ang.AppConfig
|
import com.v2ray.ang.AppConfig
|
||||||
import com.v2ray.ang.R
|
import com.v2ray.ang.R
|
||||||
|
import com.v2ray.ang.extension.defaultDPreference
|
||||||
import com.v2ray.ang.util.MessageUtil
|
import com.v2ray.ang.util.MessageUtil
|
||||||
import com.v2ray.ang.util.Utils
|
import com.v2ray.ang.util.Utils
|
||||||
|
import org.jetbrains.anko.toast
|
||||||
import java.lang.ref.SoftReference
|
import java.lang.ref.SoftReference
|
||||||
|
|
||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.N)
|
@TargetApi(Build.VERSION_CODES.N)
|
||||||
class QSTileService : TileService() {
|
class QSTileService : TileService() {
|
||||||
|
|
||||||
@@ -25,10 +29,11 @@ class QSTileService : TileService() {
|
|||||||
qsTile?.icon = Icon.createWithResource(applicationContext, R.drawable.ic_v_idle)
|
qsTile?.icon = Icon.createWithResource(applicationContext, R.drawable.ic_v_idle)
|
||||||
} else if (state == Tile.STATE_ACTIVE) {
|
} else if (state == Tile.STATE_ACTIVE) {
|
||||||
qsTile?.state = Tile.STATE_ACTIVE
|
qsTile?.state = Tile.STATE_ACTIVE
|
||||||
qsTile?.label = V2RayServiceManager.currentConfigName
|
qsTile?.label = defaultDPreference.getPrefString(AppConfig.PREF_CURR_CONFIG_NAME, "NG")
|
||||||
qsTile?.icon = Icon.createWithResource(applicationContext, R.drawable.ic_v)
|
qsTile?.icon = Icon.createWithResource(applicationContext, R.drawable.ic_v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
qsTile?.updateTile()
|
qsTile?.updateTile()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,7 +56,11 @@ class QSTileService : TileService() {
|
|||||||
super.onClick()
|
super.onClick()
|
||||||
when (qsTile.state) {
|
when (qsTile.state) {
|
||||||
Tile.STATE_INACTIVE -> {
|
Tile.STATE_INACTIVE -> {
|
||||||
Utils.startVServiceFromToggle(this)
|
val intent = VpnService.prepare(this)
|
||||||
|
if (intent == null)
|
||||||
|
if (!Utils.startVService(this)) {
|
||||||
|
toast(R.string.app_tile_first_use)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Tile.STATE_ACTIVE -> {
|
Tile.STATE_ACTIVE -> {
|
||||||
Utils.stopVService(this)
|
Utils.stopVService(this)
|
||||||
@@ -84,4 +93,4 @@ class QSTileService : TileService() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
package com.v2ray.ang.service
|
|
||||||
|
|
||||||
import android.app.Service
|
|
||||||
|
|
||||||
interface ServiceControl {
|
|
||||||
fun getService(): Service
|
|
||||||
|
|
||||||
fun startService(parameters: String)
|
|
||||||
|
|
||||||
fun stopService()
|
|
||||||
|
|
||||||
fun vpnProtect(socket: Int): Boolean
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
package com.v2ray.ang.service
|
|
||||||
|
|
||||||
import android.app.Service
|
|
||||||
import android.content.Intent
|
|
||||||
import android.os.IBinder
|
|
||||||
import java.lang.ref.SoftReference
|
|
||||||
|
|
||||||
class V2RayProxyOnlyService : Service(), ServiceControl {
|
|
||||||
override fun onCreate() {
|
|
||||||
super.onCreate()
|
|
||||||
V2RayServiceManager.serviceControl = SoftReference(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
|
||||||
V2RayServiceManager.startV2rayPoint()
|
|
||||||
return START_STICKY
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroy() {
|
|
||||||
super.onDestroy()
|
|
||||||
V2RayServiceManager.stopV2rayPoint()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getService(): Service {
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun startService(parameters: String) {
|
|
||||||
// do nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun stopService() {
|
|
||||||
stopSelf()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun vpnProtect(socket: Int): Boolean {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onBind(intent: Intent?): IBinder? {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,364 +0,0 @@
|
|||||||
package com.v2ray.ang.service
|
|
||||||
|
|
||||||
import android.app.Notification
|
|
||||||
import android.app.NotificationChannel
|
|
||||||
import android.app.NotificationManager
|
|
||||||
import android.app.PendingIntent
|
|
||||||
import android.content.BroadcastReceiver
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
|
||||||
import android.content.IntentFilter
|
|
||||||
import android.graphics.Color
|
|
||||||
import android.os.Build
|
|
||||||
import android.support.annotation.RequiresApi
|
|
||||||
import android.support.v4.app.NotificationCompat
|
|
||||||
import android.util.Log
|
|
||||||
import com.v2ray.ang.AppConfig
|
|
||||||
import com.v2ray.ang.AppConfig.TAG_DIRECT
|
|
||||||
import com.v2ray.ang.R
|
|
||||||
import com.v2ray.ang.extension.defaultDPreference
|
|
||||||
import com.v2ray.ang.extension.toSpeedString
|
|
||||||
import com.v2ray.ang.extension.toast
|
|
||||||
import com.v2ray.ang.extension.v2RayApplication
|
|
||||||
import com.v2ray.ang.ui.MainActivity
|
|
||||||
import com.v2ray.ang.ui.SettingsActivity
|
|
||||||
import com.v2ray.ang.util.MessageUtil
|
|
||||||
import com.v2ray.ang.util.Utils
|
|
||||||
import go.Seq
|
|
||||||
import libv2ray.Libv2ray
|
|
||||||
import libv2ray.V2RayPoint
|
|
||||||
import libv2ray.V2RayVPNServiceSupportsSet
|
|
||||||
import rx.Observable
|
|
||||||
import rx.Subscription
|
|
||||||
import java.lang.ref.SoftReference
|
|
||||||
import kotlin.math.min
|
|
||||||
|
|
||||||
object V2RayServiceManager {
|
|
||||||
private const val NOTIFICATION_ID = 1
|
|
||||||
private const val NOTIFICATION_PENDING_INTENT_CONTENT = 0
|
|
||||||
private const val NOTIFICATION_PENDING_INTENT_STOP_V2RAY = 1
|
|
||||||
private const val NOTIFICATION_ICON_THRESHOLD = 3000
|
|
||||||
|
|
||||||
val v2rayPoint: V2RayPoint = Libv2ray.newV2RayPoint(V2RayCallback())
|
|
||||||
private val mMsgReceive = ReceiveMessageHandler()
|
|
||||||
|
|
||||||
var serviceControl: SoftReference<ServiceControl>? = null
|
|
||||||
set(value) {
|
|
||||||
field = value
|
|
||||||
val context = value?.get()?.getService()?.applicationContext
|
|
||||||
context?.let {
|
|
||||||
v2rayPoint.packageName = Utils.packagePath(context)
|
|
||||||
v2rayPoint.packageCodePath = context.applicationInfo.nativeLibraryDir + "/"
|
|
||||||
Seq.setContext(context)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var currentConfigName = "NG"
|
|
||||||
|
|
||||||
private var lastQueryTime = 0L
|
|
||||||
private var mBuilder: NotificationCompat.Builder? = null
|
|
||||||
private var mSubscription: Subscription? = null
|
|
||||||
private var mNotificationManager: NotificationManager? = null
|
|
||||||
|
|
||||||
fun startV2Ray(context: Context) {
|
|
||||||
if (context.v2RayApplication.defaultDPreference.getPrefBoolean(SettingsActivity.PREF_PROXY_SHARING, false)) {
|
|
||||||
context.toast(R.string.toast_warning_pref_proxysharing_short)
|
|
||||||
}else{
|
|
||||||
context.toast(R.string.toast_services_start)
|
|
||||||
}
|
|
||||||
val intent = if (context.v2RayApplication.defaultDPreference.getPrefString(AppConfig.PREF_MODE, "VPN") == "VPN") {
|
|
||||||
Intent(context.applicationContext, V2RayVpnService::class.java)
|
|
||||||
} else {
|
|
||||||
Intent(context.applicationContext, V2RayProxyOnlyService::class.java)
|
|
||||||
}
|
|
||||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) {
|
|
||||||
context.startForegroundService(intent)
|
|
||||||
} else {
|
|
||||||
context.startService(intent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class V2RayCallback : V2RayVPNServiceSupportsSet {
|
|
||||||
override fun shutdown(): Long {
|
|
||||||
val serviceControl = serviceControl?.get() ?: return -1
|
|
||||||
// called by go
|
|
||||||
// shutdown the whole vpn service
|
|
||||||
return try {
|
|
||||||
serviceControl.stopService()
|
|
||||||
0
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.d(serviceControl.getService().packageName, e.toString())
|
|
||||||
-1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun prepare(): Long {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun protect(l: Long): Long {
|
|
||||||
val serviceControl = serviceControl?.get() ?: return 0
|
|
||||||
return if (serviceControl.vpnProtect(l.toInt())) 0 else 1
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onEmitStatus(l: Long, s: String?): Long {
|
|
||||||
//Logger.d(s)
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setup(s: String): Long {
|
|
||||||
val serviceControl = serviceControl?.get() ?: return -1
|
|
||||||
//Logger.d(s)
|
|
||||||
return try {
|
|
||||||
serviceControl.startService(s)
|
|
||||||
lastQueryTime = System.currentTimeMillis()
|
|
||||||
startSpeedNotification()
|
|
||||||
0
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.d(serviceControl.getService().packageName, e.toString())
|
|
||||||
-1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
fun startV2rayPoint() {
|
|
||||||
val service = serviceControl?.get()?.getService() ?: return
|
|
||||||
if (!v2rayPoint.isRunning) {
|
|
||||||
|
|
||||||
try {
|
|
||||||
val mFilter = IntentFilter(AppConfig.BROADCAST_ACTION_SERVICE)
|
|
||||||
mFilter.addAction(Intent.ACTION_SCREEN_ON)
|
|
||||||
mFilter.addAction(Intent.ACTION_SCREEN_OFF)
|
|
||||||
mFilter.addAction(Intent.ACTION_USER_PRESENT)
|
|
||||||
service.registerReceiver(mMsgReceive, mFilter)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.d(service.packageName, e.toString())
|
|
||||||
}
|
|
||||||
|
|
||||||
v2rayPoint.configureFileContent = service.defaultDPreference.getPrefString(AppConfig.PREF_CURR_CONFIG, "")
|
|
||||||
v2rayPoint.enableLocalDNS = service.defaultDPreference.getPrefBoolean(SettingsActivity.PREF_LOCAL_DNS_ENABLED, false)
|
|
||||||
v2rayPoint.forwardIpv6 = service.defaultDPreference.getPrefBoolean(SettingsActivity.PREF_FORWARD_IPV6, false)
|
|
||||||
v2rayPoint.domainName = service.defaultDPreference.getPrefString(AppConfig.PREF_CURR_CONFIG_DOMAIN, "")
|
|
||||||
v2rayPoint.proxyOnly = service.defaultDPreference.getPrefString(AppConfig.PREF_MODE, "VPN") != "VPN"
|
|
||||||
currentConfigName = service.defaultDPreference.getPrefString(AppConfig.PREF_CURR_CONFIG_NAME, "NG")
|
|
||||||
|
|
||||||
try {
|
|
||||||
v2rayPoint.runLoop()
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.d(service.packageName, e.toString())
|
|
||||||
}
|
|
||||||
|
|
||||||
if (v2rayPoint.isRunning) {
|
|
||||||
MessageUtil.sendMsg2UI(service, AppConfig.MSG_STATE_START_SUCCESS, "")
|
|
||||||
showNotification()
|
|
||||||
} else {
|
|
||||||
MessageUtil.sendMsg2UI(service, AppConfig.MSG_STATE_START_FAILURE, "")
|
|
||||||
cancelNotification()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun stopV2rayPoint() {
|
|
||||||
val service = serviceControl?.get()?.getService() ?: return
|
|
||||||
|
|
||||||
if (v2rayPoint.isRunning) {
|
|
||||||
try {
|
|
||||||
v2rayPoint.stopLoop()
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.d(service.packageName, e.toString())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MessageUtil.sendMsg2UI(service, AppConfig.MSG_STATE_STOP_SUCCESS, "")
|
|
||||||
cancelNotification()
|
|
||||||
|
|
||||||
try {
|
|
||||||
service.unregisterReceiver(mMsgReceive)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.d(service.packageName, e.toString())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class ReceiveMessageHandler : BroadcastReceiver() {
|
|
||||||
override fun onReceive(ctx: Context?, intent: Intent?) {
|
|
||||||
val serviceControl = serviceControl?.get() ?: return
|
|
||||||
when (intent?.getIntExtra("key", 0)) {
|
|
||||||
AppConfig.MSG_REGISTER_CLIENT -> {
|
|
||||||
//Logger.e("ReceiveMessageHandler", intent?.getIntExtra("key", 0).toString())
|
|
||||||
if (v2rayPoint.isRunning) {
|
|
||||||
MessageUtil.sendMsg2UI(serviceControl.getService(), AppConfig.MSG_STATE_RUNNING, "")
|
|
||||||
} else {
|
|
||||||
MessageUtil.sendMsg2UI(serviceControl.getService(), AppConfig.MSG_STATE_NOT_RUNNING, "")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
AppConfig.MSG_UNREGISTER_CLIENT -> {
|
|
||||||
// nothing to do
|
|
||||||
}
|
|
||||||
AppConfig.MSG_STATE_START -> {
|
|
||||||
// nothing to do
|
|
||||||
}
|
|
||||||
AppConfig.MSG_STATE_STOP -> {
|
|
||||||
serviceControl.stopService()
|
|
||||||
}
|
|
||||||
AppConfig.MSG_STATE_RESTART -> {
|
|
||||||
startV2rayPoint()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
when (intent?.action) {
|
|
||||||
Intent.ACTION_SCREEN_OFF -> {
|
|
||||||
Log.d(AppConfig.ANG_PACKAGE, "SCREEN_OFF, stop querying stats")
|
|
||||||
stopSpeedNotification()
|
|
||||||
}
|
|
||||||
Intent.ACTION_SCREEN_ON -> {
|
|
||||||
Log.d(AppConfig.ANG_PACKAGE, "SCREEN_ON, start querying stats")
|
|
||||||
startSpeedNotification()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun showNotification() {
|
|
||||||
val service = serviceControl?.get()?.getService() ?: return
|
|
||||||
val startMainIntent = Intent(service, MainActivity::class.java)
|
|
||||||
val contentPendingIntent = PendingIntent.getActivity(service,
|
|
||||||
NOTIFICATION_PENDING_INTENT_CONTENT, startMainIntent,
|
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT)
|
|
||||||
|
|
||||||
val stopV2RayIntent = Intent(AppConfig.BROADCAST_ACTION_SERVICE)
|
|
||||||
stopV2RayIntent.`package` = AppConfig.ANG_PACKAGE
|
|
||||||
stopV2RayIntent.putExtra("key", AppConfig.MSG_STATE_STOP)
|
|
||||||
|
|
||||||
val stopV2RayPendingIntent = PendingIntent.getBroadcast(service,
|
|
||||||
NOTIFICATION_PENDING_INTENT_STOP_V2RAY, stopV2RayIntent,
|
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT)
|
|
||||||
|
|
||||||
val channelId =
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
||||||
createNotificationChannel()
|
|
||||||
} else {
|
|
||||||
// If earlier version channel ID is not used
|
|
||||||
// https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context)
|
|
||||||
""
|
|
||||||
}
|
|
||||||
|
|
||||||
mBuilder = NotificationCompat.Builder(service, channelId)
|
|
||||||
.setSmallIcon(R.drawable.ic_v)
|
|
||||||
.setContentTitle(currentConfigName)
|
|
||||||
.setPriority(NotificationCompat.PRIORITY_MIN)
|
|
||||||
.setOngoing(true)
|
|
||||||
.setShowWhen(false)
|
|
||||||
.setOnlyAlertOnce(true)
|
|
||||||
.setContentIntent(contentPendingIntent)
|
|
||||||
.addAction(R.drawable.ic_close_grey_800_24dp,
|
|
||||||
service.getString(R.string.notification_action_stop_v2ray),
|
|
||||||
stopV2RayPendingIntent)
|
|
||||||
//.build()
|
|
||||||
|
|
||||||
//mBuilder?.setDefaults(NotificationCompat.FLAG_ONLY_ALERT_ONCE) //取消震动,铃声其他都不好使
|
|
||||||
|
|
||||||
service.startForeground(NOTIFICATION_ID, mBuilder?.build())
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequiresApi(Build.VERSION_CODES.O)
|
|
||||||
private fun createNotificationChannel(): String {
|
|
||||||
val channelId = "RAY_NG_M_CH_ID"
|
|
||||||
val channelName = "V2rayNG Background Service"
|
|
||||||
val chan = NotificationChannel(channelId,
|
|
||||||
channelName, NotificationManager.IMPORTANCE_HIGH)
|
|
||||||
chan.lightColor = Color.DKGRAY
|
|
||||||
chan.importance = NotificationManager.IMPORTANCE_NONE
|
|
||||||
chan.lockscreenVisibility = Notification.VISIBILITY_PRIVATE
|
|
||||||
getNotificationManager()?.createNotificationChannel(chan)
|
|
||||||
return channelId
|
|
||||||
}
|
|
||||||
|
|
||||||
fun cancelNotification() {
|
|
||||||
val service = serviceControl?.get()?.getService() ?: return
|
|
||||||
service.stopForeground(true)
|
|
||||||
mBuilder = null
|
|
||||||
mSubscription?.unsubscribe()
|
|
||||||
mSubscription = null
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun updateNotification(contentText: String, proxyTraffic: Long, directTraffic: Long) {
|
|
||||||
if (mBuilder != null) {
|
|
||||||
if (proxyTraffic < NOTIFICATION_ICON_THRESHOLD && directTraffic < NOTIFICATION_ICON_THRESHOLD) {
|
|
||||||
mBuilder?.setSmallIcon(R.drawable.ic_v)
|
|
||||||
} else if (proxyTraffic > directTraffic) {
|
|
||||||
mBuilder?.setSmallIcon(R.drawable.ic_stat_proxy)
|
|
||||||
} else {
|
|
||||||
mBuilder?.setSmallIcon(R.drawable.ic_stat_direct)
|
|
||||||
}
|
|
||||||
mBuilder?.setStyle(NotificationCompat.BigTextStyle().bigText(contentText))
|
|
||||||
mBuilder?.setContentText(contentText) // Emui4.1 need content text even if style is set as BigTextStyle
|
|
||||||
getNotificationManager()?.notify(NOTIFICATION_ID, mBuilder?.build())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getNotificationManager(): NotificationManager? {
|
|
||||||
if (mNotificationManager == null) {
|
|
||||||
val service = serviceControl?.get()?.getService() ?: return null
|
|
||||||
mNotificationManager = service.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
|
||||||
}
|
|
||||||
return mNotificationManager
|
|
||||||
}
|
|
||||||
|
|
||||||
fun startSpeedNotification() {
|
|
||||||
val service = serviceControl?.get()?.getService() ?: return
|
|
||||||
if (mSubscription == null &&
|
|
||||||
v2rayPoint.isRunning &&
|
|
||||||
service.defaultDPreference.getPrefBoolean(SettingsActivity.PREF_SPEED_ENABLED, false)) {
|
|
||||||
var lastZeroSpeed = false
|
|
||||||
val outboundTags = service.defaultDPreference.getPrefStringOrderedSet(AppConfig.PREF_CURR_CONFIG_OUTBOUND_TAGS, LinkedHashSet())
|
|
||||||
outboundTags.remove(TAG_DIRECT)
|
|
||||||
|
|
||||||
mSubscription = Observable.interval(3, java.util.concurrent.TimeUnit.SECONDS)
|
|
||||||
.subscribe {
|
|
||||||
val queryTime = System.currentTimeMillis()
|
|
||||||
val sinceLastQueryInSeconds = (queryTime - lastQueryTime) / 1000.0
|
|
||||||
var proxyTotal = 0L
|
|
||||||
val text = StringBuilder()
|
|
||||||
outboundTags.forEach {
|
|
||||||
val up = v2rayPoint.queryStats(it, "uplink")
|
|
||||||
val down = v2rayPoint.queryStats(it, "downlink")
|
|
||||||
if (up + down > 0) {
|
|
||||||
appendSpeedString(text, it, up / sinceLastQueryInSeconds, down / sinceLastQueryInSeconds)
|
|
||||||
proxyTotal += up + down
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val directUplink = v2rayPoint.queryStats(TAG_DIRECT, "uplink")
|
|
||||||
val directDownlink = v2rayPoint.queryStats(TAG_DIRECT, "downlink")
|
|
||||||
val zeroSpeed = (proxyTotal == 0L && directUplink == 0L && directDownlink == 0L)
|
|
||||||
if (!zeroSpeed || !lastZeroSpeed) {
|
|
||||||
if (proxyTotal == 0L) {
|
|
||||||
appendSpeedString(text, outboundTags.firstOrNull(), 0.0, 0.0)
|
|
||||||
}
|
|
||||||
appendSpeedString(text, TAG_DIRECT, directUplink / sinceLastQueryInSeconds,
|
|
||||||
directDownlink / sinceLastQueryInSeconds)
|
|
||||||
updateNotification(text.toString(), proxyTotal, directDownlink + directUplink)
|
|
||||||
}
|
|
||||||
lastZeroSpeed = zeroSpeed
|
|
||||||
lastQueryTime = queryTime
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun appendSpeedString(text: StringBuilder, name: String?, up: Double, down: Double) {
|
|
||||||
var n = name ?: "no tag"
|
|
||||||
n = n.substring(0, min(n.length, 6))
|
|
||||||
text.append(n)
|
|
||||||
for (i in n.length..6 step 2) {
|
|
||||||
text.append("\t")
|
|
||||||
}
|
|
||||||
text.append("• ${up.toLong().toSpeedString()}↑ ${down.toLong().toSpeedString()}↓\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
fun stopSpeedNotification() {
|
|
||||||
if (mSubscription != null) {
|
|
||||||
mSubscription?.unsubscribe() //stop queryStats
|
|
||||||
mSubscription = null
|
|
||||||
updateNotification(currentConfigName, 0, 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +1,69 @@
|
|||||||
package com.v2ray.ang.service
|
package com.v2ray.ang.service
|
||||||
|
|
||||||
import android.app.*
|
import android.app.Notification
|
||||||
|
import android.app.NotificationChannel
|
||||||
|
import android.app.NotificationManager
|
||||||
|
import android.app.PendingIntent
|
||||||
|
import android.content.BroadcastReceiver
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.content.IntentFilter
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
|
import android.graphics.Color
|
||||||
import android.net.*
|
import android.net.*
|
||||||
import android.os.Build
|
import android.net.VpnService
|
||||||
import android.os.ParcelFileDescriptor
|
import android.os.*
|
||||||
import android.os.StrictMode
|
|
||||||
import android.support.annotation.RequiresApi
|
import android.support.annotation.RequiresApi
|
||||||
import android.util.Log
|
import android.support.v4.app.NotificationCompat
|
||||||
|
import com.v2ray.ang.AppConfig
|
||||||
import com.v2ray.ang.R
|
import com.v2ray.ang.R
|
||||||
import com.v2ray.ang.extension.defaultDPreference
|
import com.v2ray.ang.extension.defaultDPreference
|
||||||
|
import com.v2ray.ang.extension.toSpeedString
|
||||||
|
import com.v2ray.ang.ui.MainActivity
|
||||||
import com.v2ray.ang.ui.PerAppProxyActivity
|
import com.v2ray.ang.ui.PerAppProxyActivity
|
||||||
import com.v2ray.ang.ui.SettingsActivity
|
import com.v2ray.ang.ui.SettingsActivity
|
||||||
|
import com.v2ray.ang.util.MessageUtil
|
||||||
import com.v2ray.ang.util.Utils
|
import com.v2ray.ang.util.Utils
|
||||||
import kotlinx.coroutines.Dispatchers
|
import libv2ray.Libv2ray
|
||||||
import kotlinx.coroutines.GlobalScope
|
import libv2ray.V2RayVPNServiceSupportsSet
|
||||||
import kotlinx.coroutines.launch
|
import rx.Observable
|
||||||
|
import rx.Subscription
|
||||||
|
import java.net.InetAddress
|
||||||
|
import java.io.IOException
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.io.FileDescriptor
|
||||||
|
import java.io.FileInputStream
|
||||||
import java.lang.ref.SoftReference
|
import java.lang.ref.SoftReference
|
||||||
|
import android.os.Build
|
||||||
|
import android.annotation.TargetApi
|
||||||
|
import android.util.Log
|
||||||
|
import org.jetbrains.anko.doAsync
|
||||||
|
|
||||||
class V2RayVpnService : VpnService(), ServiceControl {
|
class V2RayVpnService : VpnService() {
|
||||||
|
companion object {
|
||||||
|
const val NOTIFICATION_ID = 1
|
||||||
|
const val NOTIFICATION_PENDING_INTENT_CONTENT = 0
|
||||||
|
const val NOTIFICATION_PENDING_INTENT_STOP_V2RAY = 1
|
||||||
|
|
||||||
|
fun startV2Ray(context: Context) {
|
||||||
|
val intent = Intent(context.applicationContext, V2RayVpnService::class.java)
|
||||||
|
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) {
|
||||||
|
context.startForegroundService(intent)
|
||||||
|
} else {
|
||||||
|
context.startService(intent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val v2rayPoint = Libv2ray.newV2RayPoint(V2RayCallback())
|
||||||
|
private lateinit var configContent: String
|
||||||
private lateinit var mInterface: ParcelFileDescriptor
|
private lateinit var mInterface: ParcelFileDescriptor
|
||||||
|
val fd: Int get() = mInterface.fd
|
||||||
|
private var mBuilder: NotificationCompat.Builder? = null
|
||||||
|
private var mSubscription: Subscription? = null
|
||||||
|
private var mNotificationManager: NotificationManager? = null
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unfortunately registerDefaultNetworkCallback is going to return our VPN interface: https://android.googlesource.com/platform/frameworks/base/+/dda156ab0c5d66ad82bdcf76cda07cbc0a9c8a2e
|
* Unfortunately registerDefaultNetworkCallback is going to return our VPN interface: https://android.googlesource.com/platform/frameworks/base/+/dda156ab0c5d66ad82bdcf76cda07cbc0a9c8a2e
|
||||||
@@ -33,38 +74,35 @@ class V2RayVpnService : VpnService(), ServiceControl {
|
|||||||
*
|
*
|
||||||
* Source: https://android.googlesource.com/platform/frameworks/base/+/2df4c7d/services/core/java/com/android/server/ConnectivityService.java#887
|
* Source: https://android.googlesource.com/platform/frameworks/base/+/2df4c7d/services/core/java/com/android/server/ConnectivityService.java#887
|
||||||
*/
|
*/
|
||||||
@delegate:RequiresApi(Build.VERSION_CODES.P)
|
@TargetApi(28)
|
||||||
private val defaultNetworkRequest by lazy {
|
private val defaultNetworkRequest = NetworkRequest.Builder()
|
||||||
NetworkRequest.Builder()
|
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
|
||||||
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
|
.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
|
||||||
.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
|
.build()
|
||||||
.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
private val connectivity by lazy { getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager }
|
private val connectivity by lazy { getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager }
|
||||||
|
@TargetApi(28)
|
||||||
@delegate:RequiresApi(Build.VERSION_CODES.P)
|
private val defaultNetworkCallback = object : ConnectivityManager.NetworkCallback() {
|
||||||
private val defaultNetworkCallback by lazy {
|
override fun onAvailable(network: Network) {
|
||||||
object : ConnectivityManager.NetworkCallback() {
|
setUnderlyingNetworks(arrayOf(network))
|
||||||
override fun onAvailable(network: Network) {
|
}
|
||||||
setUnderlyingNetworks(arrayOf(network))
|
override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities?) {
|
||||||
}
|
// it's a good idea to refresh capabilities
|
||||||
override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
|
setUnderlyingNetworks(arrayOf(network))
|
||||||
// it's a good idea to refresh capabilities
|
}
|
||||||
setUnderlyingNetworks(arrayOf(network))
|
override fun onLost(network: Network) {
|
||||||
}
|
setUnderlyingNetworks(null)
|
||||||
override fun onLost(network: Network) {
|
|
||||||
setUnderlyingNetworks(null)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
private var listeningForDefaultNetwork = false
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
|
|
||||||
val policy = StrictMode.ThreadPolicy.Builder().permitAll().build()
|
val policy = StrictMode.ThreadPolicy.Builder().permitAll().build()
|
||||||
StrictMode.setThreadPolicy(policy)
|
StrictMode.setThreadPolicy(policy)
|
||||||
V2RayServiceManager.serviceControl = SoftReference(this)
|
v2rayPoint.packageName = Utils.packagePath(applicationContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onRevoke() {
|
override fun onRevoke() {
|
||||||
@@ -78,12 +116,12 @@ class V2RayVpnService : VpnService(), ServiceControl {
|
|||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
stopV2Ray()
|
cancelNotification()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setup(parameters: String) {
|
fun setup(parameters: String) {
|
||||||
|
|
||||||
val prepare = prepare(this)
|
val prepare = VpnService.prepare(this)
|
||||||
if (prepare != null) {
|
if (prepare != null) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -92,7 +130,6 @@ class V2RayVpnService : VpnService(), ServiceControl {
|
|||||||
// Configure a builder while parsing the parameters.
|
// Configure a builder while parsing the parameters.
|
||||||
val builder = Builder()
|
val builder = Builder()
|
||||||
val enableLocalDns = defaultDPreference.getPrefBoolean(SettingsActivity.PREF_LOCAL_DNS_ENABLED, false)
|
val enableLocalDns = defaultDPreference.getPrefBoolean(SettingsActivity.PREF_LOCAL_DNS_ENABLED, false)
|
||||||
val routingMode = defaultDPreference.getPrefString(SettingsActivity.PREF_ROUTING_MODE, "0")
|
|
||||||
|
|
||||||
parameters.split(" ")
|
parameters.split(" ")
|
||||||
.map { it.split(",") }
|
.map { it.split(",") }
|
||||||
@@ -101,20 +138,7 @@ class V2RayVpnService : VpnService(), ServiceControl {
|
|||||||
'm' -> builder.setMtu(java.lang.Short.parseShort(it[1]).toInt())
|
'm' -> builder.setMtu(java.lang.Short.parseShort(it[1]).toInt())
|
||||||
's' -> builder.addSearchDomain(it[1])
|
's' -> builder.addSearchDomain(it[1])
|
||||||
'a' -> builder.addAddress(it[1], Integer.parseInt(it[2]))
|
'a' -> builder.addAddress(it[1], Integer.parseInt(it[2]))
|
||||||
'r' -> {
|
'r' -> builder.addRoute(it[1], Integer.parseInt(it[2]))
|
||||||
if (routingMode == "1" || routingMode == "3") {
|
|
||||||
if (it[1] == "::") { //not very elegant, should move Vpn setting in Kotlin, simplify go code
|
|
||||||
builder.addRoute("2000::", 3)
|
|
||||||
} else {
|
|
||||||
resources.getStringArray(R.array.bypass_private_ip_address).forEach { cidr ->
|
|
||||||
val addr = cidr.split('/')
|
|
||||||
builder.addRoute(addr[0], addr[1].toInt())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
builder.addRoute(it[1], Integer.parseInt(it[2]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
'd' -> builder.addDnsServer(it[1])
|
'd' -> builder.addDnsServer(it[1])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -126,7 +150,7 @@ class V2RayVpnService : VpnService(), ServiceControl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.setSession(V2RayServiceManager.currentConfigName)
|
builder.setSession(defaultDPreference.getPrefString(AppConfig.PREF_CURR_CONFIG_NAME, ""))
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP &&
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP &&
|
||||||
defaultDPreference.getPrefBoolean(SettingsActivity.PREF_PER_APP_PROXY, false)) {
|
defaultDPreference.getPrefBoolean(SettingsActivity.PREF_PER_APP_PROXY, false)) {
|
||||||
@@ -148,42 +172,33 @@ class V2RayVpnService : VpnService(), ServiceControl {
|
|||||||
try {
|
try {
|
||||||
mInterface.close()
|
mInterface.close()
|
||||||
} catch (ignored: Exception) {
|
} catch (ignored: Exception) {
|
||||||
// ignored
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
|
||||||
try {
|
|
||||||
connectivity.requestNetwork(defaultNetworkRequest, defaultNetworkCallback)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
e.printStackTrace()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
if (Build.VERSION.SDK_INT >= 28) {
|
||||||
builder.setMetered(false)
|
connectivity.requestNetwork(defaultNetworkRequest, defaultNetworkCallback)
|
||||||
|
listeningForDefaultNetwork = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new interface using the builder and save the parameters.
|
// Create a new interface using the builder and save the parameters.
|
||||||
try {
|
mInterface = builder.establish()
|
||||||
mInterface = builder.establish()!!
|
|
||||||
} catch (e: Exception) {
|
|
||||||
// non-nullable lateinit var
|
|
||||||
e.printStackTrace()
|
|
||||||
stopV2Ray()
|
|
||||||
}
|
|
||||||
|
|
||||||
sendFd()
|
sendFd()
|
||||||
|
startSpeedNotification()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun sendFd() {
|
fun shutdown() {
|
||||||
|
stopV2Ray(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun sendFd() {
|
||||||
val fd = mInterface.fileDescriptor
|
val fd = mInterface.fileDescriptor
|
||||||
val path = File(Utils.packagePath(applicationContext), "sock_path").absolutePath
|
val path = File(Utils.packagePath(applicationContext), "sock_path").absolutePath
|
||||||
|
|
||||||
GlobalScope.launch(Dispatchers.IO) {
|
doAsync {
|
||||||
var tries = 0
|
var tries = 0
|
||||||
while (true) try {
|
while (true) try {
|
||||||
Thread.sleep(1000L shl tries)
|
Thread.sleep(50L shl tries)
|
||||||
Log.d(packageName, "sendFd tries: $tries")
|
Log.d(packageName, "sendFd tries: " + tries.toString())
|
||||||
LocalSocket().use { localSocket ->
|
LocalSocket().use { localSocket ->
|
||||||
localSocket.connect(LocalSocketAddress(path, LocalSocketAddress.Namespace.FILESYSTEM))
|
localSocket.connect(LocalSocketAddress(path, LocalSocketAddress.Namespace.FILESYSTEM))
|
||||||
localSocket.setFileDescriptorsForSend(arrayOf(fd))
|
localSocket.setFileDescriptorsForSend(arrayOf(fd))
|
||||||
@@ -199,57 +214,278 @@ class V2RayVpnService : VpnService(), ServiceControl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||||
V2RayServiceManager.startV2rayPoint()
|
startV2ray()
|
||||||
return START_STICKY
|
return START_STICKY
|
||||||
//return super.onStartCommand(intent, flags, startId)
|
//return super.onStartCommand(intent, flags, startId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun startV2ray() {
|
||||||
|
if (!v2rayPoint.isRunning) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
val mFilter = IntentFilter(AppConfig.BROADCAST_ACTION_SERVICE)
|
||||||
|
mFilter.addAction(Intent.ACTION_SCREEN_ON)
|
||||||
|
mFilter.addAction(Intent.ACTION_SCREEN_OFF)
|
||||||
|
mFilter.addAction(Intent.ACTION_USER_PRESENT)
|
||||||
|
registerReceiver(mMsgReceive, mFilter)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
}
|
||||||
|
|
||||||
|
configContent = defaultDPreference.getPrefString(AppConfig.PREF_CURR_CONFIG, "")
|
||||||
|
v2rayPoint.configureFileContent = configContent
|
||||||
|
v2rayPoint.enableLocalDNS = defaultDPreference.getPrefBoolean(SettingsActivity.PREF_LOCAL_DNS_ENABLED, false)
|
||||||
|
v2rayPoint.forwardIpv6 = defaultDPreference.getPrefBoolean(SettingsActivity.PREF_FORWARD_IPV6, false)
|
||||||
|
v2rayPoint.domainName = defaultDPreference.getPrefString(AppConfig.PREF_CURR_CONFIG_DOMAIN, "")
|
||||||
|
|
||||||
|
try {
|
||||||
|
v2rayPoint.runLoop()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.d(packageName, e.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
if (v2rayPoint.isRunning) {
|
||||||
|
MessageUtil.sendMsg2UI(this, AppConfig.MSG_STATE_START_SUCCESS, "")
|
||||||
|
showNotification()
|
||||||
|
} else {
|
||||||
|
MessageUtil.sendMsg2UI(this, AppConfig.MSG_STATE_START_FAILURE, "")
|
||||||
|
cancelNotification()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// showNotification()
|
||||||
|
}
|
||||||
|
|
||||||
private fun stopV2Ray(isForced: Boolean = true) {
|
private fun stopV2Ray(isForced: Boolean = true) {
|
||||||
// val configName = defaultDPreference.getPrefString(PREF_CURR_CONFIG_GUID, "")
|
// val configName = defaultDPreference.getPrefString(PREF_CURR_CONFIG_GUID, "")
|
||||||
// val emptyInfo = VpnNetworkInfo()
|
// val emptyInfo = VpnNetworkInfo()
|
||||||
// val info = loadVpnNetworkInfo(configName, emptyInfo)!! + (lastNetworkInfo ?: emptyInfo)
|
// val info = loadVpnNetworkInfo(configName, emptyInfo)!! + (lastNetworkInfo ?: emptyInfo)
|
||||||
// saveVpnNetworkInfo(configName, info)
|
// saveVpnNetworkInfo(configName, info)
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
if (Build.VERSION.SDK_INT >= 28) {
|
||||||
try {
|
if (listeningForDefaultNetwork) {
|
||||||
connectivity.unregisterNetworkCallback(defaultNetworkCallback)
|
connectivity.unregisterNetworkCallback(defaultNetworkCallback)
|
||||||
} catch (ignored: Exception) {
|
listeningForDefaultNetwork = false
|
||||||
// ignored
|
}
|
||||||
|
}
|
||||||
|
if (v2rayPoint.isRunning) {
|
||||||
|
try {
|
||||||
|
v2rayPoint.stopLoop()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.d(packageName, e.toString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
V2RayServiceManager.stopV2rayPoint()
|
MessageUtil.sendMsg2UI(this, AppConfig.MSG_STATE_STOP_SUCCESS, "")
|
||||||
|
cancelNotification()
|
||||||
|
|
||||||
if (isForced) {
|
if (isForced) {
|
||||||
//stopSelf has to be called ahead of mInterface.close(). otherwise v2ray core cannot be stooped
|
try {
|
||||||
//It's strage but true.
|
unregisterReceiver(mMsgReceive)
|
||||||
//This can be verified by putting stopself() behind and call stopLoop and startLoop
|
} catch (e: Exception) {
|
||||||
//in a row for several times. You will find that later created v2ray core report port in use
|
}
|
||||||
//which means the first v2ray core somehow failed to stop and release the port.
|
|
||||||
stopSelf()
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
mInterface.close()
|
mInterface.close()
|
||||||
} catch (ignored: Exception) {
|
} catch (ignored: Exception) {
|
||||||
// ignored
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stopSelf()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getService(): Service {
|
private fun showNotification() {
|
||||||
return this
|
val startMainIntent = Intent(applicationContext, MainActivity::class.java)
|
||||||
|
val contentPendingIntent = PendingIntent.getActivity(applicationContext,
|
||||||
|
NOTIFICATION_PENDING_INTENT_CONTENT, startMainIntent,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT)
|
||||||
|
|
||||||
|
val stopV2RayIntent = Intent(AppConfig.BROADCAST_ACTION_SERVICE)
|
||||||
|
stopV2RayIntent.`package` = AppConfig.ANG_PACKAGE
|
||||||
|
stopV2RayIntent.putExtra("key", AppConfig.MSG_STATE_STOP)
|
||||||
|
|
||||||
|
val stopV2RayPendingIntent = PendingIntent.getBroadcast(applicationContext,
|
||||||
|
NOTIFICATION_PENDING_INTENT_STOP_V2RAY, stopV2RayIntent,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT)
|
||||||
|
|
||||||
|
val channelId =
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
createNotificationChannel()
|
||||||
|
} else {
|
||||||
|
// If earlier version channel ID is not used
|
||||||
|
// https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context)
|
||||||
|
""
|
||||||
|
}
|
||||||
|
|
||||||
|
mBuilder = NotificationCompat.Builder(applicationContext, channelId)
|
||||||
|
.setSmallIcon(R.drawable.ic_v)
|
||||||
|
.setContentTitle(defaultDPreference.getPrefString(AppConfig.PREF_CURR_CONFIG_NAME, ""))
|
||||||
|
.setPriority(NotificationCompat.PRIORITY_MIN)
|
||||||
|
.setOngoing(true)
|
||||||
|
.setShowWhen(false)
|
||||||
|
.setOnlyAlertOnce(true)
|
||||||
|
.setContentIntent(contentPendingIntent)
|
||||||
|
.addAction(R.drawable.ic_close_grey_800_24dp,
|
||||||
|
getString(R.string.notification_action_stop_v2ray),
|
||||||
|
stopV2RayPendingIntent)
|
||||||
|
//.build()
|
||||||
|
|
||||||
|
//mBuilder?.setDefaults(NotificationCompat.FLAG_ONLY_ALERT_ONCE) //取消震动,铃声其他都不好使
|
||||||
|
|
||||||
|
startForeground(NOTIFICATION_ID, mBuilder?.build())
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun startService(parameters: String) {
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
setup(parameters)
|
private fun createNotificationChannel(): String {
|
||||||
|
val channelId = "RAY_NG_M_CH_ID"
|
||||||
|
val channelName = "V2rayNG Background Service"
|
||||||
|
val chan = NotificationChannel(channelId,
|
||||||
|
channelName, NotificationManager.IMPORTANCE_HIGH)
|
||||||
|
chan.lightColor = Color.DKGRAY
|
||||||
|
chan.importance = NotificationManager.IMPORTANCE_NONE
|
||||||
|
chan.lockscreenVisibility = Notification.VISIBILITY_PRIVATE
|
||||||
|
getNotificationManager().createNotificationChannel(chan)
|
||||||
|
return channelId
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun stopService() {
|
private fun cancelNotification() {
|
||||||
stopV2Ray(true)
|
stopForeground(true)
|
||||||
|
mBuilder = null
|
||||||
|
mSubscription?.unsubscribe()
|
||||||
|
mSubscription = null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun vpnProtect(socket: Int): Boolean {
|
private fun updateNotification(contentText: String) {
|
||||||
return protect(socket)
|
if (mBuilder != null) {
|
||||||
|
mBuilder?.setContentTitle(contentText)
|
||||||
|
getNotificationManager().notify(NOTIFICATION_ID, mBuilder?.build())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getNotificationManager(): NotificationManager {
|
||||||
|
if (mNotificationManager == null) {
|
||||||
|
mNotificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||||
|
}
|
||||||
|
return mNotificationManager!!
|
||||||
|
}
|
||||||
|
|
||||||
|
fun startSpeedNotification() {
|
||||||
|
if (mSubscription == null &&
|
||||||
|
v2rayPoint.isRunning &&
|
||||||
|
defaultDPreference.getPrefBoolean(SettingsActivity.PREF_SPEED_ENABLED, false)) {
|
||||||
|
val cf_name = defaultDPreference.getPrefString(AppConfig.PREF_CURR_CONFIG_NAME, "")
|
||||||
|
var last_zero_speed = false
|
||||||
|
|
||||||
|
mSubscription = Observable.interval(3, java.util.concurrent.TimeUnit.SECONDS)
|
||||||
|
.subscribe {
|
||||||
|
val uplink = v2rayPoint.queryStats("socks", "uplink")
|
||||||
|
val downlink = v2rayPoint.queryStats("socks", "downlink")
|
||||||
|
val zero_speed = (uplink == 0L && downlink == 0L)
|
||||||
|
if (!zero_speed || !last_zero_speed) {
|
||||||
|
updateNotification("${cf_name} • ${(uplink / 3).toSpeedString()}↑ ${(downlink / 3).toSpeedString()}↓")
|
||||||
|
}
|
||||||
|
last_zero_speed = zero_speed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun stopSpeedNotification() {
|
||||||
|
if (mSubscription != null) {
|
||||||
|
mSubscription?.unsubscribe() //stop queryStats
|
||||||
|
mSubscription = null
|
||||||
|
|
||||||
|
val cf_name = defaultDPreference.getPrefString(AppConfig.PREF_CURR_CONFIG_NAME, "")
|
||||||
|
updateNotification(cf_name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private inner class V2RayCallback : V2RayVPNServiceSupportsSet {
|
||||||
|
override fun shutdown(): Long {
|
||||||
|
// called by go
|
||||||
|
// shutdown the whole vpn service
|
||||||
|
try {
|
||||||
|
this@V2RayVpnService.shutdown()
|
||||||
|
return 0
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.d(packageName, e.toString())
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun prepare(): Long {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun protect(l: Long) = (if (this@V2RayVpnService.protect(l.toInt())) 0 else 1).toLong()
|
||||||
|
|
||||||
|
override fun onEmitStatus(l: Long, s: String?): Long {
|
||||||
|
//Logger.d(s)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setup(s: String): Long {
|
||||||
|
//Logger.d(s)
|
||||||
|
try {
|
||||||
|
this@V2RayVpnService.setup(s)
|
||||||
|
return 0
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.d(packageName, e.toString())
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun sendFd(): Long {
|
||||||
|
try {
|
||||||
|
this@V2RayVpnService.sendFd()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.d(packageName, e.toString())
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var mMsgReceive = ReceiveMessageHandler(this@V2RayVpnService)
|
||||||
|
|
||||||
|
private class ReceiveMessageHandler(vpnService: V2RayVpnService) : BroadcastReceiver() {
|
||||||
|
internal var mReference: SoftReference<V2RayVpnService> = SoftReference(vpnService)
|
||||||
|
|
||||||
|
override fun onReceive(ctx: Context?, intent: Intent?) {
|
||||||
|
val vpnService = mReference.get()
|
||||||
|
when (intent?.getIntExtra("key", 0)) {
|
||||||
|
AppConfig.MSG_REGISTER_CLIENT -> {
|
||||||
|
//Logger.e("ReceiveMessageHandler", intent?.getIntExtra("key", 0).toString())
|
||||||
|
|
||||||
|
val isRunning = vpnService?.v2rayPoint!!.isRunning
|
||||||
|
&& VpnService.prepare(vpnService) == null
|
||||||
|
if (isRunning) {
|
||||||
|
MessageUtil.sendMsg2UI(vpnService, AppConfig.MSG_STATE_RUNNING, "")
|
||||||
|
} else {
|
||||||
|
MessageUtil.sendMsg2UI(vpnService, AppConfig.MSG_STATE_NOT_RUNNING, "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AppConfig.MSG_UNREGISTER_CLIENT -> {
|
||||||
|
// vpnService?.mMsgSend = null
|
||||||
|
}
|
||||||
|
AppConfig.MSG_STATE_START -> {
|
||||||
|
//nothing to do
|
||||||
|
}
|
||||||
|
AppConfig.MSG_STATE_STOP -> {
|
||||||
|
vpnService?.stopV2Ray()
|
||||||
|
}
|
||||||
|
AppConfig.MSG_STATE_RESTART -> {
|
||||||
|
vpnService?.startV2ray()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
when (intent?.action) {
|
||||||
|
Intent.ACTION_SCREEN_OFF -> {
|
||||||
|
Log.d(AppConfig.ANG_PACKAGE, "SCREEN_OFF, stop querying stats")
|
||||||
|
vpnService?.stopSpeedNotification()
|
||||||
|
}
|
||||||
|
Intent.ACTION_SCREEN_ON -> {
|
||||||
|
Log.d(AppConfig.ANG_PACKAGE, "SCREEN_ON, start querying stats")
|
||||||
|
vpnService?.startSpeedNotification()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,11 @@ import android.support.v7.widget.Toolbar
|
|||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import com.v2ray.ang.InappBuyActivity
|
||||||
|
|
||||||
import com.v2ray.ang.R
|
import com.v2ray.ang.R
|
||||||
|
import org.jetbrains.anko.startActivity
|
||||||
|
|
||||||
|
|
||||||
abstract class BaseDrawerActivity : BaseActivity() {
|
abstract class BaseDrawerActivity : BaseActivity() {
|
||||||
companion object {
|
companion object {
|
||||||
@@ -51,14 +55,15 @@ abstract class BaseDrawerActivity : BaseActivity() {
|
|||||||
this@BaseDrawerActivity, R.anim.fade_in, R.anim.fade_out).toBundle()
|
this@BaseDrawerActivity, R.anim.fade_in, R.anim.fade_out).toBundle()
|
||||||
var activityClass: Class<*>? = null
|
var activityClass: Class<*>? = null
|
||||||
when (mItemToOpenWhenDrawerCloses) {
|
when (mItemToOpenWhenDrawerCloses) {
|
||||||
|
R.id.server_profile -> activityClass = MainActivity::class.java
|
||||||
R.id.sub_setting -> activityClass = SubSettingActivity::class.java
|
R.id.sub_setting -> activityClass = SubSettingActivity::class.java
|
||||||
R.id.settings -> activityClass = SettingsActivity::class.java
|
R.id.settings -> activityClass = SettingsActivity::class.java
|
||||||
R.id.logcat -> {
|
R.id.logcat -> {
|
||||||
startActivity(Intent(this@BaseDrawerActivity, LogcatActivity::class.java))
|
startActivity<LogcatActivity>()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
R.id.donate -> {
|
R.id.donate -> {
|
||||||
// startActivity<InappBuyActivity>()
|
startActivity<InappBuyActivity>()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -190,7 +195,9 @@ abstract class BaseDrawerActivity : BaseActivity() {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SubSettingActivity::class.java.isAssignableFrom(javaClass)) {
|
if (MainActivity::class.java.isAssignableFrom(javaClass)) {
|
||||||
|
navigationView.setCheckedItem(R.id.server_profile)
|
||||||
|
} else if (SubSettingActivity::class.java.isAssignableFrom(javaClass)) {
|
||||||
navigationView.setCheckedItem(R.id.sub_setting)
|
navigationView.setCheckedItem(R.id.sub_setting)
|
||||||
} else if (SettingsActivity::class.java.isAssignableFrom(javaClass)) {
|
} else if (SettingsActivity::class.java.isAssignableFrom(javaClass)) {
|
||||||
navigationView.setCheckedItem(R.id.settings)
|
navigationView.setCheckedItem(R.id.settings)
|
||||||
|
|||||||
@@ -1,19 +1,17 @@
|
|||||||
package com.v2ray.ang.ui
|
package com.v2ray.ang.ui
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Handler
|
|
||||||
import android.os.Looper
|
|
||||||
import android.text.method.ScrollingMovementMethod
|
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.R
|
import com.v2ray.ang.R
|
||||||
import com.v2ray.ang.extension.toast
|
|
||||||
import com.v2ray.ang.util.Utils
|
import com.v2ray.ang.util.Utils
|
||||||
import kotlinx.android.synthetic.main.activity_logcat.*
|
import kotlinx.android.synthetic.main.activity_logcat.*
|
||||||
import kotlinx.coroutines.Dispatchers
|
import org.jetbrains.anko.doAsync
|
||||||
import kotlinx.coroutines.GlobalScope
|
import org.jetbrains.anko.toast
|
||||||
import kotlinx.coroutines.launch
|
import org.jetbrains.anko.uiThread
|
||||||
|
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.util.LinkedHashSet
|
import java.util.LinkedHashSet
|
||||||
|
|
||||||
@@ -26,22 +24,15 @@ class LogcatActivity : BaseActivity() {
|
|||||||
title = getString(R.string.title_logcat)
|
title = getString(R.string.title_logcat)
|
||||||
|
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
logcat(false)
|
logcat()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun logcat(shouldFlushLog: Boolean) {
|
private fun logcat() {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
pb_waiting.visibility = View.VISIBLE
|
pb_waiting.visibility = View.VISIBLE
|
||||||
|
|
||||||
GlobalScope.launch(Dispatchers.Default) {
|
doAsync {
|
||||||
if (shouldFlushLog) {
|
|
||||||
val lst = LinkedHashSet<String>()
|
|
||||||
lst.add("logcat")
|
|
||||||
lst.add("-c")
|
|
||||||
val process = Runtime.getRuntime().exec(lst.toTypedArray())
|
|
||||||
process.waitFor()
|
|
||||||
}
|
|
||||||
val lst = LinkedHashSet<String>()
|
val lst = LinkedHashSet<String>()
|
||||||
lst.add("logcat")
|
lst.add("logcat")
|
||||||
lst.add("-d")
|
lst.add("-d")
|
||||||
@@ -54,15 +45,13 @@ class LogcatActivity : BaseActivity() {
|
|||||||
// InputStreamReader(process.inputStream))
|
// InputStreamReader(process.inputStream))
|
||||||
// val allText = bufferedReader.use(BufferedReader::readText)
|
// val allText = bufferedReader.use(BufferedReader::readText)
|
||||||
val allText = process.inputStream.bufferedReader().use { it.readText() }
|
val allText = process.inputStream.bufferedReader().use { it.readText() }
|
||||||
launch(Dispatchers.Main) {
|
uiThread {
|
||||||
tv_logcat.text = allText
|
tv_logcat.text = allText
|
||||||
tv_logcat.movementMethod = ScrollingMovementMethod()
|
tv_logcat.movementMethod = ScrollingMovementMethod()
|
||||||
pb_waiting.visibility = View.GONE
|
pb_waiting.visibility = View.GONE
|
||||||
Handler(Looper.getMainLooper()).post { sv_logcat.fullScroll(View.FOCUS_DOWN) }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
e.printStackTrace()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,10 +66,7 @@ class LogcatActivity : BaseActivity() {
|
|||||||
toast(R.string.toast_success)
|
toast(R.string.toast_success)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
R.id.delete -> {
|
|
||||||
logcat(true)
|
|
||||||
true
|
|
||||||
}
|
|
||||||
else -> super.onOptionsItemSelected(item)
|
else -> super.onOptionsItemSelected(item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,41 +1,38 @@
|
|||||||
package com.v2ray.ang.ui
|
package com.v2ray.ang.ui
|
||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.arch.lifecycle.ViewModelProviders
|
import android.content.*
|
||||||
import android.content.Intent
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.net.VpnService
|
import android.net.VpnService
|
||||||
import android.os.Bundle
|
|
||||||
import android.support.design.widget.NavigationView
|
|
||||||
import android.support.v4.view.GravityCompat
|
|
||||||
import android.support.v7.app.ActionBarDrawerToggle
|
|
||||||
import android.support.v7.widget.LinearLayoutManager
|
import android.support.v7.widget.LinearLayoutManager
|
||||||
import android.support.v7.widget.helper.ItemTouchHelper
|
|
||||||
import android.text.TextUtils
|
|
||||||
import android.util.Log
|
|
||||||
import android.view.KeyEvent
|
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import com.tbruyelle.rxpermissions.RxPermissions
|
import com.tbruyelle.rxpermissions.RxPermissions
|
||||||
import com.v2ray.ang.AppConfig
|
|
||||||
import com.v2ray.ang.BuildConfig
|
|
||||||
import com.v2ray.ang.R
|
import com.v2ray.ang.R
|
||||||
import com.v2ray.ang.extension.defaultDPreference
|
|
||||||
import com.v2ray.ang.extension.toast
|
|
||||||
import com.v2ray.ang.helper.SimpleItemTouchHelperCallback
|
|
||||||
import com.v2ray.ang.util.AngConfigManager
|
import com.v2ray.ang.util.AngConfigManager
|
||||||
import com.v2ray.ang.util.Utils
|
import com.v2ray.ang.util.Utils
|
||||||
import com.v2ray.ang.util.V2rayConfigUtil
|
|
||||||
import com.v2ray.ang.viewmodel.MainViewModel
|
|
||||||
import kotlinx.android.synthetic.main.activity_main.*
|
import kotlinx.android.synthetic.main.activity_main.*
|
||||||
import kotlinx.coroutines.Dispatchers
|
import android.os.Bundle
|
||||||
import kotlinx.coroutines.GlobalScope
|
import android.text.TextUtils
|
||||||
import kotlinx.coroutines.launch
|
import android.view.KeyEvent
|
||||||
import libv2ray.Libv2ray
|
import com.v2ray.ang.AppConfig
|
||||||
|
import com.v2ray.ang.util.MessageUtil
|
||||||
|
import com.v2ray.ang.util.V2rayConfigUtil
|
||||||
|
import org.jetbrains.anko.*
|
||||||
|
import java.lang.ref.SoftReference
|
||||||
|
import java.net.URL
|
||||||
|
import android.content.IntentFilter
|
||||||
|
import android.support.design.widget.NavigationView
|
||||||
|
import android.support.v4.view.GravityCompat
|
||||||
|
import android.support.v7.app.ActionBarDrawerToggle
|
||||||
|
import android.support.v7.widget.helper.ItemTouchHelper
|
||||||
|
import android.util.Log
|
||||||
|
import com.v2ray.ang.InappBuyActivity
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
import rx.android.schedulers.AndroidSchedulers
|
import rx.android.schedulers.AndroidSchedulers
|
||||||
import java.net.URL
|
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
import com.v2ray.ang.helper.SimpleItemTouchHelperCallback
|
||||||
|
import com.v2ray.ang.util.AngConfigManager.configs
|
||||||
|
|
||||||
class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedListener {
|
class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedListener {
|
||||||
companion object {
|
companion object {
|
||||||
@@ -45,9 +42,22 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
|||||||
private const val REQUEST_SCAN_URL = 3
|
private const val REQUEST_SCAN_URL = 3
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var isRunning = false
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
adapter.changeable = !value
|
||||||
|
if (value) {
|
||||||
|
fab.imageResource = R.drawable.ic_v
|
||||||
|
tv_test_state.text = getString(R.string.connection_connected)
|
||||||
|
} else {
|
||||||
|
fab.imageResource = R.drawable.ic_v_idle
|
||||||
|
tv_test_state.text = getString(R.string.connection_not_connected)
|
||||||
|
}
|
||||||
|
hideCircle()
|
||||||
|
}
|
||||||
|
|
||||||
private val adapter by lazy { MainRecyclerAdapter(this) }
|
private val adapter by lazy { MainRecyclerAdapter(this) }
|
||||||
private var mItemTouchHelper: ItemTouchHelper? = null
|
private var mItemTouchHelper: ItemTouchHelper? = null
|
||||||
private val mainViewModel: MainViewModel by lazy { ViewModelProviders.of(this).get(MainViewModel::class.java) }
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
@@ -56,23 +66,28 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
|||||||
setSupportActionBar(toolbar)
|
setSupportActionBar(toolbar)
|
||||||
|
|
||||||
fab.setOnClickListener {
|
fab.setOnClickListener {
|
||||||
if (mainViewModel.isRunning.value == true) {
|
if (isRunning) {
|
||||||
Utils.stopVService(this)
|
Utils.stopVService(this)
|
||||||
} else if (defaultDPreference.getPrefString(AppConfig.PREF_MODE, "VPN") == "VPN") {
|
} else {
|
||||||
val intent = VpnService.prepare(this)
|
val intent = VpnService.prepare(this)
|
||||||
if (intent == null) {
|
if (intent == null) {
|
||||||
startV2Ray()
|
startV2Ray()
|
||||||
} else {
|
} else {
|
||||||
startActivityForResult(intent, REQUEST_CODE_VPN_PREPARE)
|
startActivityForResult(intent, REQUEST_CODE_VPN_PREPARE)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
startV2Ray()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
layout_test.setOnClickListener {
|
layout_test.setOnClickListener {
|
||||||
if (mainViewModel.isRunning.value == true) {
|
if (isRunning) {
|
||||||
|
val socksPort = 10808//Utils.parseInt(defaultDPreference.getPrefString(SettingsActivity.PREF_SOCKS_PORT, "10808"))
|
||||||
|
|
||||||
tv_test_state.text = getString(R.string.connection_test_testing)
|
tv_test_state.text = getString(R.string.connection_test_testing)
|
||||||
mainViewModel.testCurrentServerRealPing()
|
doAsync {
|
||||||
|
val result = Utils.testConnection(this@MainActivity, socksPort)
|
||||||
|
uiThread {
|
||||||
|
tv_test_state.text = Utils.getEditable(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// tv_test_state.text = getString(R.string.connection_test_fail)
|
// tv_test_state.text = getString(R.string.connection_test_fail)
|
||||||
}
|
}
|
||||||
@@ -92,34 +107,6 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
|||||||
drawer_layout.addDrawerListener(toggle)
|
drawer_layout.addDrawerListener(toggle)
|
||||||
toggle.syncState()
|
toggle.syncState()
|
||||||
nav_view.setNavigationItemSelectedListener(this)
|
nav_view.setNavigationItemSelectedListener(this)
|
||||||
version.text = "v${BuildConfig.VERSION_NAME} (${Libv2ray.checkVersionX()})"
|
|
||||||
|
|
||||||
setupViewModelObserver()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setupViewModelObserver() {
|
|
||||||
mainViewModel.updateListAction.observe(this, {
|
|
||||||
val index = it ?: return@observe
|
|
||||||
if (index >= 0) {
|
|
||||||
adapter.updateSelectedItem(index)
|
|
||||||
} else {
|
|
||||||
adapter.updateConfigList()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
mainViewModel.updateTestResultAction.observe(this, { tv_test_state.text = it })
|
|
||||||
mainViewModel.isRunning.observe(this, {
|
|
||||||
val isRunning = it ?: return@observe
|
|
||||||
adapter.changeable = !isRunning
|
|
||||||
if (isRunning) {
|
|
||||||
fab.setImageResource(R.drawable.ic_v)
|
|
||||||
tv_test_state.text = getString(R.string.connection_connected)
|
|
||||||
} else {
|
|
||||||
fab.setImageResource(R.drawable.ic_v_idle)
|
|
||||||
tv_test_state.text = getString(R.string.connection_not_connected)
|
|
||||||
}
|
|
||||||
hideCircle()
|
|
||||||
})
|
|
||||||
mainViewModel.startListenBroadcast()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun startV2Ray() {
|
fun startV2Ray() {
|
||||||
@@ -128,11 +115,32 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
|||||||
}
|
}
|
||||||
showCircle()
|
showCircle()
|
||||||
// toast(R.string.toast_services_start)
|
// toast(R.string.toast_services_start)
|
||||||
if (!Utils.startVService(this, AngConfigManager.configs.index)) {
|
if (!Utils.startVService(this)) {
|
||||||
hideCircle()
|
hideCircle()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onStart() {
|
||||||
|
super.onStart()
|
||||||
|
isRunning = false
|
||||||
|
|
||||||
|
// val intent = Intent(this.applicationContext, V2RayVpnService::class.java)
|
||||||
|
// intent.`package` = AppConfig.ANG_PACKAGE
|
||||||
|
// bindService(intent, mConnection, BIND_AUTO_CREATE)
|
||||||
|
|
||||||
|
mMsgReceive = ReceiveMessageHandler(this@MainActivity)
|
||||||
|
registerReceiver(mMsgReceive, IntentFilter(AppConfig.BROADCAST_ACTION_ACTIVITY))
|
||||||
|
MessageUtil.sendMsg2Service(this, AppConfig.MSG_REGISTER_CLIENT, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStop() {
|
||||||
|
super.onStop()
|
||||||
|
if (mMsgReceive != null) {
|
||||||
|
unregisterReceiver(mMsgReceive)
|
||||||
|
mMsgReceive = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public override fun onResume() {
|
public override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
adapter.updateConfigList()
|
adapter.updateConfigList()
|
||||||
@@ -154,8 +162,8 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
|||||||
importBatchConfig(data?.getStringExtra("SCAN_RESULT"))
|
importBatchConfig(data?.getStringExtra("SCAN_RESULT"))
|
||||||
}
|
}
|
||||||
REQUEST_FILE_CHOOSER -> {
|
REQUEST_FILE_CHOOSER -> {
|
||||||
val uri = data?.data
|
if (resultCode == RESULT_OK) {
|
||||||
if (resultCode == RESULT_OK && uri != null) {
|
val uri = data!!.data
|
||||||
readContentFromUri(uri)
|
readContentFromUri(uri)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -171,9 +179,6 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getOptionIntent() = Intent().putExtra("position", -1)
|
|
||||||
.putExtra("isRunning", mainViewModel.isRunning.value == true)
|
|
||||||
|
|
||||||
override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
|
override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
|
||||||
R.id.import_qrcode -> {
|
R.id.import_qrcode -> {
|
||||||
importQRcode(REQUEST_SCAN)
|
importQRcode(REQUEST_SCAN)
|
||||||
@@ -184,17 +189,17 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
R.id.import_manually_vmess -> {
|
R.id.import_manually_vmess -> {
|
||||||
startActivity(getOptionIntent().setClass(this, ServerActivity::class.java))
|
startActivity<ServerActivity>("position" to -1, "isRunning" to isRunning)
|
||||||
adapter.updateConfigList()
|
adapter.updateConfigList()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
R.id.import_manually_ss -> {
|
R.id.import_manually_ss -> {
|
||||||
startActivity(getOptionIntent().setClass(this, Server3Activity::class.java))
|
startActivity<Server3Activity>("position" to -1, "isRunning" to isRunning)
|
||||||
adapter.updateConfigList()
|
adapter.updateConfigList()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
R.id.import_manually_socks -> {
|
R.id.import_manually_socks -> {
|
||||||
startActivity(getOptionIntent().setClass(this, Server4Activity::class.java))
|
startActivity<Server4Activity>("position" to -1, "isRunning" to isRunning)
|
||||||
adapter.updateConfigList()
|
adapter.updateConfigList()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
@@ -227,7 +232,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
|||||||
|
|
||||||
R.id.export_all -> {
|
R.id.export_all -> {
|
||||||
if (AngConfigManager.shareAll2Clipboard() == 0) {
|
if (AngConfigManager.shareAll2Clipboard() == 0) {
|
||||||
//remove toast, otherwise it will block previous warning message
|
toast(R.string.toast_success)
|
||||||
} else {
|
} else {
|
||||||
toast(R.string.toast_failure)
|
toast(R.string.toast_failure)
|
||||||
}
|
}
|
||||||
@@ -235,7 +240,20 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
|||||||
}
|
}
|
||||||
|
|
||||||
R.id.ping_all -> {
|
R.id.ping_all -> {
|
||||||
mainViewModel.testAllTcping()
|
for (k in 0 until configs.vmess.count()) {
|
||||||
|
configs.vmess[k].testResult = ""
|
||||||
|
adapter.updateConfigList()
|
||||||
|
}
|
||||||
|
for (k in 0 until configs.vmess.count()) {
|
||||||
|
if (configs.vmess[k].configType != AppConfig.EConfigType.Custom) {
|
||||||
|
doAsync {
|
||||||
|
configs.vmess[k].testResult = Utils.tcping(configs.vmess[k].address, configs.vmess[k].port)
|
||||||
|
uiThread {
|
||||||
|
adapter.updateSelectedItem(k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,7 +282,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
|||||||
.request(Manifest.permission.CAMERA)
|
.request(Manifest.permission.CAMERA)
|
||||||
.subscribe {
|
.subscribe {
|
||||||
if (it)
|
if (it)
|
||||||
startActivityForResult(Intent(this, ScannerActivity::class.java), requestCode)
|
startActivityForResult<ScannerActivity>(requestCode)
|
||||||
else
|
else
|
||||||
toast(R.string.toast_permission_denied)
|
toast(R.string.toast_permission_denied)
|
||||||
}
|
}
|
||||||
@@ -350,14 +368,9 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
|||||||
toast(R.string.toast_invalid_url)
|
toast(R.string.toast_invalid_url)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
GlobalScope.launch(Dispatchers.IO) {
|
doAsync {
|
||||||
val configText = try {
|
val configText = URL(url).readText()
|
||||||
URL(url).readText()
|
uiThread {
|
||||||
} catch (e: Exception) {
|
|
||||||
e.printStackTrace()
|
|
||||||
""
|
|
||||||
}
|
|
||||||
launch(Dispatchers.Main) {
|
|
||||||
importCustomizeConfig(configText)
|
importCustomizeConfig(configText)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -389,14 +402,9 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
Log.d("Main", url)
|
Log.d("Main", url)
|
||||||
GlobalScope.launch(Dispatchers.IO) {
|
doAsync {
|
||||||
val configText = try {
|
val configText = URL(url).readText()
|
||||||
URL(url).readText()
|
uiThread {
|
||||||
} catch (e: Exception) {
|
|
||||||
e.printStackTrace()
|
|
||||||
""
|
|
||||||
}
|
|
||||||
launch(Dispatchers.Main) {
|
|
||||||
importBatchConfig(Utils.decode(configText), id)
|
importBatchConfig(Utils.decode(configText), id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -434,10 +442,9 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
|||||||
.subscribe {
|
.subscribe {
|
||||||
if (it) {
|
if (it) {
|
||||||
try {
|
try {
|
||||||
contentResolver.openInputStream(uri).use {
|
val inputStream = contentResolver.openInputStream(uri)
|
||||||
val configText = it?.bufferedReader()?.readText()
|
val configText = inputStream.bufferedReader().readText()
|
||||||
importCustomizeConfig(configText)
|
importCustomizeConfig(configText)
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
@@ -475,6 +482,35 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
private
|
||||||
|
var mMsgReceive: BroadcastReceiver? = null
|
||||||
|
|
||||||
|
private class ReceiveMessageHandler(activity: MainActivity) : BroadcastReceiver() {
|
||||||
|
internal var mReference: SoftReference<MainActivity> = SoftReference(activity)
|
||||||
|
override fun onReceive(ctx: Context?, intent: Intent?) {
|
||||||
|
val activity = mReference.get()
|
||||||
|
when (intent?.getIntExtra("key", 0)) {
|
||||||
|
AppConfig.MSG_STATE_RUNNING -> {
|
||||||
|
activity?.isRunning = true
|
||||||
|
}
|
||||||
|
AppConfig.MSG_STATE_NOT_RUNNING -> {
|
||||||
|
activity?.isRunning = false
|
||||||
|
}
|
||||||
|
AppConfig.MSG_STATE_START_SUCCESS -> {
|
||||||
|
activity?.toast(R.string.toast_services_success)
|
||||||
|
activity?.isRunning = true
|
||||||
|
}
|
||||||
|
AppConfig.MSG_STATE_START_FAILURE -> {
|
||||||
|
activity?.toast(R.string.toast_services_failure)
|
||||||
|
activity?.isRunning = false
|
||||||
|
}
|
||||||
|
AppConfig.MSG_STATE_STOP_SUCCESS -> {
|
||||||
|
activity?.isRunning = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
|
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
|
||||||
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
||||||
moveTaskToBack(false)
|
moveTaskToBack(false)
|
||||||
@@ -513,11 +549,10 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
|||||||
when (item.itemId) {
|
when (item.itemId) {
|
||||||
//R.id.server_profile -> activityClass = MainActivity::class.java
|
//R.id.server_profile -> activityClass = MainActivity::class.java
|
||||||
R.id.sub_setting -> {
|
R.id.sub_setting -> {
|
||||||
startActivity(Intent(this, SubSettingActivity::class.java))
|
startActivity<SubSettingActivity>()
|
||||||
}
|
}
|
||||||
R.id.settings -> {
|
R.id.settings -> {
|
||||||
startActivity(Intent(this, SettingsActivity::class.java)
|
startActivity<SettingsActivity>("isRunning" to isRunning)
|
||||||
.putExtra("isRunning", mainViewModel.isRunning.value == true))
|
|
||||||
}
|
}
|
||||||
R.id.feedback -> {
|
R.id.feedback -> {
|
||||||
Utils.openUri(this, AppConfig.v2rayNGIssues)
|
Utils.openUri(this, AppConfig.v2rayNGIssues)
|
||||||
@@ -526,13 +561,13 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
|||||||
Utils.openUri(this, AppConfig.promotionUrl)
|
Utils.openUri(this, AppConfig.promotionUrl)
|
||||||
}
|
}
|
||||||
R.id.donate -> {
|
R.id.donate -> {
|
||||||
// startActivity<InappBuyActivity>()
|
startActivity<InappBuyActivity>()
|
||||||
}
|
}
|
||||||
R.id.logcat -> {
|
R.id.logcat -> {
|
||||||
startActivity(Intent(this, LogcatActivity::class.java))
|
startActivity<LogcatActivity>()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
drawer_layout.closeDrawer(GravityCompat.START)
|
drawer_layout.closeDrawer(GravityCompat.START)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,28 +1,24 @@
|
|||||||
package com.v2ray.ang.ui
|
package com.v2ray.ang.ui
|
||||||
|
|
||||||
import android.content.Intent
|
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.support.v7.app.AlertDialog
|
|
||||||
import android.support.v7.widget.RecyclerView
|
import android.support.v7.widget.RecyclerView
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import com.v2ray.ang.AppConfig
|
import com.v2ray.ang.AppConfig
|
||||||
import com.v2ray.ang.R
|
import com.v2ray.ang.R
|
||||||
import com.v2ray.ang.dto.AngConfig
|
import com.v2ray.ang.dto.AngConfig
|
||||||
import com.v2ray.ang.dto.EConfigType
|
|
||||||
import com.v2ray.ang.extension.toast
|
|
||||||
import com.v2ray.ang.helper.ItemTouchHelperAdapter
|
import com.v2ray.ang.helper.ItemTouchHelperAdapter
|
||||||
import com.v2ray.ang.helper.ItemTouchHelperViewHolder
|
import com.v2ray.ang.helper.ItemTouchHelperViewHolder
|
||||||
import com.v2ray.ang.util.AngConfigManager
|
import com.v2ray.ang.util.AngConfigManager
|
||||||
import com.v2ray.ang.util.Utils
|
import com.v2ray.ang.util.Utils
|
||||||
import com.v2ray.ang.util.V2rayConfigUtil
|
|
||||||
import kotlinx.android.synthetic.main.item_qrcode.view.*
|
import kotlinx.android.synthetic.main.item_qrcode.view.*
|
||||||
import kotlinx.android.synthetic.main.item_recycler_main.view.*
|
import kotlinx.android.synthetic.main.item_recycler_main.view.*
|
||||||
|
import org.jetbrains.anko.*
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
import rx.android.schedulers.AndroidSchedulers
|
import rx.android.schedulers.AndroidSchedulers
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
import com.v2ray.ang.extension.defaultDPreference
|
||||||
|
|
||||||
class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<MainRecyclerAdapter.BaseViewHolder>()
|
class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<MainRecyclerAdapter.BaseViewHolder>()
|
||||||
, ItemTouchHelperAdapter {
|
, ItemTouchHelperAdapter {
|
||||||
@@ -53,7 +49,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 configType = EConfigType.fromInt(configs.vmess[position].configType)
|
val configType = configs.vmess[position].configType
|
||||||
val remarks = configs.vmess[position].remarks
|
val remarks = configs.vmess[position].remarks
|
||||||
val subid = configs.vmess[position].subid
|
val subid = configs.vmess[position].subid
|
||||||
val address = configs.vmess[position].address
|
val address = configs.vmess[position].address
|
||||||
@@ -62,7 +58,7 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
|
|||||||
|
|
||||||
holder.name.text = remarks
|
holder.name.text = remarks
|
||||||
holder.radio.isChecked = (position == configs.index)
|
holder.radio.isChecked = (position == configs.index)
|
||||||
holder.itemView.setBackgroundColor(Color.TRANSPARENT)
|
holder.itemView.backgroundColor = Color.TRANSPARENT
|
||||||
holder.test_result.text = test_result
|
holder.test_result.text = test_result
|
||||||
|
|
||||||
if (TextUtils.isEmpty(subid)) {
|
if (TextUtils.isEmpty(subid)) {
|
||||||
@@ -71,33 +67,39 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
|
|||||||
holder.subid.text = "S"
|
holder.subid.text = "S"
|
||||||
}
|
}
|
||||||
|
|
||||||
var shareOptions = share_method.asList()
|
if (configType == AppConfig.EConfigType.Vmess) {
|
||||||
if (configType == EConfigType.CUSTOM) {
|
holder.type.text = "vmess"
|
||||||
holder.type.text = mActivity.getString(R.string.server_customize_config)
|
|
||||||
val serverOutbound = V2rayConfigUtil.getCustomConfigServerOutbound(mActivity.applicationContext, configs.vmess[position].guid)
|
|
||||||
if (serverOutbound == null) {
|
|
||||||
holder.statistics.text = ""
|
|
||||||
} else {
|
|
||||||
holder.statistics.text = "${serverOutbound.getServerAddress()} : ${serverOutbound.getServerPort()}"
|
|
||||||
}
|
|
||||||
shareOptions = shareOptions.takeLast(1)
|
|
||||||
} else {
|
|
||||||
holder.type.text = configType?.name?.toLowerCase()
|
|
||||||
holder.statistics.text = "$address : $port"
|
holder.statistics.text = "$address : $port"
|
||||||
|
holder.layout_share.visibility = View.VISIBLE
|
||||||
|
} else if (configType == AppConfig.EConfigType.Custom) {
|
||||||
|
holder.type.text = mActivity.getString(R.string.server_customize_config)
|
||||||
|
holder.statistics.text = ""//mActivity.getString(R.string.server_customize_config)
|
||||||
|
holder.layout_share.visibility = View.INVISIBLE
|
||||||
|
} else if (configType == AppConfig.EConfigType.Shadowsocks) {
|
||||||
|
holder.type.text = "shadowsocks"
|
||||||
|
holder.statistics.text = "$address : $port"
|
||||||
|
holder.layout_share.visibility = View.VISIBLE
|
||||||
|
} else if (configType == AppConfig.EConfigType.Socks) {
|
||||||
|
holder.type.text = "socks"
|
||||||
|
holder.statistics.text = "$address : $port"
|
||||||
|
holder.layout_share.visibility = View.VISIBLE
|
||||||
}
|
}
|
||||||
|
|
||||||
holder.layout_share.setOnClickListener {
|
holder.layout_share.setOnClickListener {
|
||||||
AlertDialog.Builder(mActivity).setItems(shareOptions.toTypedArray()) { _, i ->
|
mActivity.selector(null, share_method.asList()) { dialogInterface, i ->
|
||||||
try {
|
try {
|
||||||
when (i) {
|
when (i) {
|
||||||
0 -> {
|
0 -> {
|
||||||
if (configType == EConfigType.CUSTOM) {
|
val iv = mActivity.layoutInflater.inflate(R.layout.item_qrcode, null)
|
||||||
shareFullContent(position)
|
iv.iv_qcode.setImageBitmap(AngConfigManager.share2QRCode(position))
|
||||||
} else {
|
|
||||||
val iv = mActivity.layoutInflater.inflate(R.layout.item_qrcode, null)
|
mActivity.alert {
|
||||||
iv.iv_qcode.setImageBitmap(AngConfigManager.share2QRCode(position))
|
customView {
|
||||||
AlertDialog.Builder(mActivity).setView(iv).show()
|
linearLayout {
|
||||||
}
|
addView(iv)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.show()
|
||||||
}
|
}
|
||||||
1 -> {
|
1 -> {
|
||||||
if (AngConfigManager.share2Clipboard(position) == 0) {
|
if (AngConfigManager.share2Clipboard(position) == 0) {
|
||||||
@@ -106,26 +108,31 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
|
|||||||
mActivity.toast(R.string.toast_failure)
|
mActivity.toast(R.string.toast_failure)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
2 -> shareFullContent(position)
|
2 -> {
|
||||||
else -> mActivity.toast("else")
|
if (AngConfigManager.shareFullContent2Clipboard(position) == 0) {
|
||||||
|
mActivity.toast(R.string.toast_success)
|
||||||
|
} else {
|
||||||
|
mActivity.toast(R.string.toast_failure)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else ->
|
||||||
|
mActivity.toast("else")
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
}.show()
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
holder.layout_edit.setOnClickListener {
|
holder.layout_edit.setOnClickListener {
|
||||||
val intent = Intent().putExtra("position", position)
|
if (configType == AppConfig.EConfigType.Vmess) {
|
||||||
.putExtra("isRunning", !changeable)
|
mActivity.startActivity<ServerActivity>("position" to position, "isRunning" to !changeable)
|
||||||
if (configType == EConfigType.VMESS) {
|
} else if (configType == AppConfig.EConfigType.Custom) {
|
||||||
mActivity.startActivity(intent.setClass(mActivity, ServerActivity::class.java))
|
mActivity.startActivity<Server2Activity>("position" to position, "isRunning" to !changeable)
|
||||||
} else if (configType == EConfigType.CUSTOM) {
|
} else if (configType == AppConfig.EConfigType.Shadowsocks) {
|
||||||
mActivity.startActivity(intent.setClass(mActivity, Server2Activity::class.java))
|
mActivity.startActivity<Server3Activity>("position" to position, "isRunning" to !changeable)
|
||||||
} else if (configType == EConfigType.SHADOWSOCKS) {
|
} else if (configType == AppConfig.EConfigType.Socks) {
|
||||||
mActivity.startActivity(intent.setClass(mActivity, Server3Activity::class.java))
|
mActivity.startActivity<Server4Activity>("position" to position, "isRunning" to !changeable)
|
||||||
} else if (configType == EConfigType.SOCKS) {
|
|
||||||
mActivity.startActivity(intent.setClass(mActivity, Server4Activity::class.java))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
holder.layout_remove.setOnClickListener {
|
holder.layout_remove.setOnClickListener {
|
||||||
@@ -143,14 +150,16 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
|
|||||||
} else {
|
} else {
|
||||||
mActivity.showCircle()
|
mActivity.showCircle()
|
||||||
Utils.stopVService(mActivity)
|
Utils.stopVService(mActivity)
|
||||||
|
AngConfigManager.setActiveServer(position)
|
||||||
Observable.timer(500, TimeUnit.MILLISECONDS)
|
Observable.timer(500, TimeUnit.MILLISECONDS)
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe {
|
.subscribe {
|
||||||
mActivity.showCircle()
|
mActivity.showCircle()
|
||||||
if (!Utils.startVService(mActivity, position)) {
|
if (!Utils.startVService(mActivity)) {
|
||||||
mActivity.hideCircle()
|
mActivity.hideCircle()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
notifyDataSetChanged()
|
notifyDataSetChanged()
|
||||||
}
|
}
|
||||||
@@ -167,21 +176,13 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun shareFullContent(position: Int) {
|
|
||||||
if (AngConfigManager.shareFullContent2Clipboard(position) == 0) {
|
|
||||||
mActivity.toast(R.string.toast_success)
|
|
||||||
} else {
|
|
||||||
mActivity.toast(R.string.toast_failure)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder {
|
||||||
when (viewType) {
|
when (viewType) {
|
||||||
VIEW_TYPE_ITEM ->
|
VIEW_TYPE_ITEM ->
|
||||||
return MainViewHolder(LayoutInflater.from(parent.context)
|
return MainViewHolder(parent.context.layoutInflater
|
||||||
.inflate(R.layout.item_recycler_main, parent, false))
|
.inflate(R.layout.item_recycler_main, parent, false))
|
||||||
else ->
|
else ->
|
||||||
return FooterViewHolder(LayoutInflater.from(parent.context)
|
return FooterViewHolder(parent.context.layoutInflater
|
||||||
.inflate(R.layout.item_recycler_footer, parent, false))
|
.inflate(R.layout.item_recycler_footer, parent, false))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -264,8 +265,4 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
|
|||||||
updateSelectedItem(if (fromPosition < toPosition) fromPosition else toPosition)
|
updateSelectedItem(if (fromPosition < toPosition) fromPosition else toPosition)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onItemMoveCompleted() {
|
|
||||||
AngConfigManager.storeConfigFile()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ import android.animation.Animator
|
|||||||
import android.animation.AnimatorListenerAdapter
|
import android.animation.AnimatorListenerAdapter
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.support.v7.widget.DividerItemDecoration
|
|
||||||
import android.support.v7.widget.LinearLayoutManager
|
|
||||||
import android.support.v7.widget.RecyclerView
|
import android.support.v7.widget.RecyclerView
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
@@ -14,6 +12,7 @@ import android.view.MenuItem
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.animation.AccelerateInterpolator
|
import android.view.animation.AccelerateInterpolator
|
||||||
import android.view.animation.DecelerateInterpolator
|
import android.view.animation.DecelerateInterpolator
|
||||||
|
import com.dinuscxj.itemdecoration.LinearDividerItemDecoration
|
||||||
import com.v2ray.ang.R
|
import com.v2ray.ang.R
|
||||||
import com.v2ray.ang.extension.defaultDPreference
|
import com.v2ray.ang.extension.defaultDPreference
|
||||||
import com.v2ray.ang.util.AppManagerUtil
|
import com.v2ray.ang.util.AppManagerUtil
|
||||||
@@ -26,12 +25,11 @@ 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.dto.AppInfo
|
import com.v2ray.ang.dto.AppInfo
|
||||||
import com.v2ray.ang.extension.toast
|
|
||||||
import com.v2ray.ang.extension.v2RayApplication
|
import com.v2ray.ang.extension.v2RayApplication
|
||||||
import com.v2ray.ang.util.Utils
|
import com.v2ray.ang.util.Utils
|
||||||
import kotlinx.coroutines.Dispatchers
|
import org.jetbrains.anko.doAsync
|
||||||
import kotlinx.coroutines.GlobalScope
|
import org.jetbrains.anko.toast
|
||||||
import kotlinx.coroutines.launch
|
import org.jetbrains.anko.uiThread
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
|
|
||||||
class PerAppProxyActivity : BaseActivity() {
|
class PerAppProxyActivity : BaseActivity() {
|
||||||
@@ -49,7 +47,8 @@ class PerAppProxyActivity : BaseActivity() {
|
|||||||
|
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
|
|
||||||
val dividerItemDecoration = DividerItemDecoration(this, LinearLayoutManager.VERTICAL)
|
val dividerItemDecoration = LinearDividerItemDecoration(
|
||||||
|
this, LinearDividerItemDecoration.LINEAR_DIVIDER_VERTICAL)
|
||||||
recycler_view.addItemDecoration(dividerItemDecoration)
|
recycler_view.addItemDecoration(dividerItemDecoration)
|
||||||
|
|
||||||
val blacklist = defaultDPreference.getPrefStringSet(PREF_PER_APP_PROXY_SET, null)
|
val blacklist = defaultDPreference.getPrefStringSet(PREF_PER_APP_PROXY_SET, null)
|
||||||
@@ -221,14 +220,9 @@ class PerAppProxyActivity : BaseActivity() {
|
|||||||
private fun selectProxyApp() {
|
private fun selectProxyApp() {
|
||||||
toast(R.string.msg_downloading_content)
|
toast(R.string.msg_downloading_content)
|
||||||
val url = AppConfig.androidpackagenamelistUrl
|
val url = AppConfig.androidpackagenamelistUrl
|
||||||
GlobalScope.launch(Dispatchers.IO) {
|
doAsync {
|
||||||
val content = try {
|
val content = URL(url).readText()
|
||||||
URL(url).readText()
|
uiThread {
|
||||||
} catch (e: Exception) {
|
|
||||||
e.printStackTrace()
|
|
||||||
""
|
|
||||||
}
|
|
||||||
launch(Dispatchers.Main) {
|
|
||||||
Log.d("selectProxyApp", content)
|
Log.d("selectProxyApp", content)
|
||||||
selectProxyApp(content)
|
selectProxyApp(content)
|
||||||
toast(R.string.toast_success)
|
toast(R.string.toast_success)
|
||||||
@@ -282,4 +276,4 @@ class PerAppProxyActivity : BaseActivity() {
|
|||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,12 +2,14 @@ package com.v2ray.ang.ui
|
|||||||
|
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.support.v7.widget.RecyclerView
|
import android.support.v7.widget.RecyclerView
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import com.v2ray.ang.R
|
import com.v2ray.ang.R
|
||||||
import com.v2ray.ang.dto.AppInfo
|
import com.v2ray.ang.dto.AppInfo
|
||||||
import kotlinx.android.synthetic.main.item_recycler_bypass_list.view.*
|
import kotlinx.android.synthetic.main.item_recycler_bypass_list.view.*
|
||||||
|
import org.jetbrains.anko.image
|
||||||
|
import org.jetbrains.anko.layoutInflater
|
||||||
|
import org.jetbrains.anko.textColor
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class PerAppProxyAdapter(val activity: BaseActivity, val apps: List<AppInfo>, blacklist: MutableSet<String>?) :
|
class PerAppProxyAdapter(val activity: BaseActivity, val apps: List<AppInfo>, blacklist: MutableSet<String>?) :
|
||||||
@@ -43,7 +45,7 @@ class PerAppProxyAdapter(val activity: BaseActivity, val apps: List<AppInfo>, bl
|
|||||||
// VIEW_TYPE_ITEM -> AppViewHolder(ctx.layoutInflater
|
// VIEW_TYPE_ITEM -> AppViewHolder(ctx.layoutInflater
|
||||||
// .inflate(R.layout.item_recycler_bypass_list, parent, false))
|
// .inflate(R.layout.item_recycler_bypass_list, parent, false))
|
||||||
|
|
||||||
else -> AppViewHolder(LayoutInflater.from(ctx)
|
else -> AppViewHolder(ctx.layoutInflater
|
||||||
.inflate(R.layout.item_recycler_bypass_list, parent, false))
|
.inflate(R.layout.item_recycler_bypass_list, parent, false))
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -66,17 +68,17 @@ class PerAppProxyAdapter(val activity: BaseActivity, val apps: List<AppInfo>, bl
|
|||||||
fun bind(appInfo: AppInfo) {
|
fun bind(appInfo: AppInfo) {
|
||||||
this.appInfo = appInfo
|
this.appInfo = appInfo
|
||||||
|
|
||||||
icon.setImageDrawable(appInfo.appIcon)
|
icon.image = appInfo.appIcon
|
||||||
// name.text = appInfo.appName
|
// name.text = appInfo.appName
|
||||||
|
|
||||||
checkBox.isChecked = inBlacklist
|
checkBox.isChecked = inBlacklist
|
||||||
package_name.text = appInfo.packageName
|
package_name.text = appInfo.packageName
|
||||||
if (appInfo.isSystemApp) {
|
if (appInfo.isSystemApp) {
|
||||||
name.text = String.format("** %1s", appInfo.appName)
|
name.text = String.format("** %1s", appInfo.appName)
|
||||||
name.setTextColor(Color.RED)
|
name.textColor = Color.RED
|
||||||
} else {
|
} else {
|
||||||
name.text = appInfo.appName
|
name.text = appInfo.appName
|
||||||
name.setTextColor(Color.DKGRAY)
|
name.textColor = Color.DKGRAY
|
||||||
}
|
}
|
||||||
|
|
||||||
itemView.setOnClickListener(this)
|
itemView.setOnClickListener(this)
|
||||||
@@ -92,4 +94,4 @@ class PerAppProxyAdapter(val activity: BaseActivity, val apps: List<AppInfo>, bl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,24 +1,30 @@
|
|||||||
package com.v2ray.ang.ui
|
package com.v2ray.ang.ui
|
||||||
|
|
||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.app.Activity.RESULT_OK
|
import android.app.Activity.RESULT_OK
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.support.v4.app.Fragment
|
import android.support.v4.app.Fragment
|
||||||
|
import android.text.TextUtils
|
||||||
|
import android.util.Log
|
||||||
import android.view.*
|
import android.view.*
|
||||||
import com.v2ray.ang.R
|
import com.v2ray.ang.R
|
||||||
import com.v2ray.ang.extension.defaultDPreference
|
import com.v2ray.ang.extension.defaultDPreference
|
||||||
import com.v2ray.ang.util.Utils
|
import com.v2ray.ang.util.Utils
|
||||||
import kotlinx.android.synthetic.main.fragment_routing_settings.*
|
import kotlinx.android.synthetic.main.fragment_routing_settings.*
|
||||||
|
import org.jetbrains.anko.toast
|
||||||
import android.view.MenuInflater
|
import android.view.MenuInflater
|
||||||
import com.tbruyelle.rxpermissions.RxPermissions
|
import com.tbruyelle.rxpermissions.RxPermissions
|
||||||
import com.v2ray.ang.AppConfig
|
import com.v2ray.ang.AppConfig
|
||||||
import com.v2ray.ang.extension.toast
|
import org.jetbrains.anko.doAsync
|
||||||
import kotlinx.coroutines.Dispatchers
|
import org.jetbrains.anko.startActivityForResult
|
||||||
import kotlinx.coroutines.GlobalScope
|
import org.jetbrains.anko.support.v4.startActivityForResult
|
||||||
import kotlinx.coroutines.launch
|
import org.jetbrains.anko.support.v4.toast
|
||||||
|
import org.jetbrains.anko.uiThread
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
|
|
||||||
|
|
||||||
class RoutingSettingsFragment : Fragment() {
|
class RoutingSettingsFragment : Fragment() {
|
||||||
companion object {
|
companion object {
|
||||||
private const val routing_arg = "routing_arg"
|
private const val routing_arg = "routing_arg"
|
||||||
@@ -90,7 +96,7 @@ class RoutingSettingsFragment : Fragment() {
|
|||||||
.request(Manifest.permission.CAMERA)
|
.request(Manifest.permission.CAMERA)
|
||||||
.subscribe {
|
.subscribe {
|
||||||
if (it)
|
if (it)
|
||||||
startActivityForResult(Intent(activity, ScannerActivity::class.java), requestCode)
|
startActivityForResult<ScannerActivity>(requestCode)
|
||||||
else
|
else
|
||||||
activity?.toast(R.string.toast_permission_denied)
|
activity?.toast(R.string.toast_permission_denied)
|
||||||
}
|
}
|
||||||
@@ -112,17 +118,12 @@ class RoutingSettingsFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
activity?.toast(R.string.msg_downloading_content)
|
toast(R.string.msg_downloading_content)
|
||||||
GlobalScope.launch(Dispatchers.IO) {
|
doAsync {
|
||||||
val content = try {
|
val content = URL(url).readText()
|
||||||
URL(url).readText()
|
uiThread {
|
||||||
} catch (e: Exception) {
|
et_routing_content.text = Utils.getEditable(content!!)
|
||||||
e.printStackTrace()
|
toast(R.string.toast_success)
|
||||||
""
|
|
||||||
}
|
|
||||||
launch(Dispatchers.Main) {
|
|
||||||
et_routing_content.text = Utils.getEditable(content)
|
|
||||||
activity?.toast(R.string.toast_success)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import com.tbruyelle.rxpermissions.RxPermissions
|
|||||||
import com.v2ray.ang.R
|
import com.v2ray.ang.R
|
||||||
import com.v2ray.ang.util.AngConfigManager
|
import com.v2ray.ang.util.AngConfigManager
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import com.v2ray.ang.extension.toast
|
import org.jetbrains.anko.*
|
||||||
|
|
||||||
class ScScannerActivity : BaseActivity() {
|
class ScScannerActivity : BaseActivity() {
|
||||||
companion object {
|
companion object {
|
||||||
@@ -24,7 +24,7 @@ class ScScannerActivity : BaseActivity() {
|
|||||||
.request(Manifest.permission.CAMERA)
|
.request(Manifest.permission.CAMERA)
|
||||||
.subscribe {
|
.subscribe {
|
||||||
if (it)
|
if (it)
|
||||||
startActivityForResult(Intent(this, ScannerActivity::class.java), requestCode)
|
startActivityForResult<ScannerActivity>(requestCode)
|
||||||
else
|
else
|
||||||
toast(R.string.toast_permission_denied)
|
toast(R.string.toast_permission_denied)
|
||||||
}
|
}
|
||||||
@@ -43,10 +43,10 @@ class ScScannerActivity : BaseActivity() {
|
|||||||
} else {
|
} else {
|
||||||
toast(R.string.toast_failure)
|
toast(R.string.toast_failure)
|
||||||
}
|
}
|
||||||
startActivity(Intent(this, MainActivity::class.java))
|
startActivity<MainActivity>()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,22 +1,104 @@
|
|||||||
package com.v2ray.ang.ui
|
package com.v2ray.ang.ui
|
||||||
|
|
||||||
|
import android.content.*
|
||||||
|
import android.net.VpnService
|
||||||
import com.v2ray.ang.R
|
import com.v2ray.ang.R
|
||||||
import com.v2ray.ang.util.Utils
|
import com.v2ray.ang.util.Utils
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import com.v2ray.ang.service.V2RayServiceManager
|
import com.v2ray.ang.AppConfig
|
||||||
|
import com.v2ray.ang.util.MessageUtil
|
||||||
|
import java.lang.ref.SoftReference
|
||||||
|
import android.content.IntentFilter
|
||||||
|
import kotlinx.android.synthetic.main.activity_main.*
|
||||||
|
import rx.Observable
|
||||||
|
import rx.android.schedulers.AndroidSchedulers
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
class ScSwitchActivity : BaseActivity() {
|
class ScSwitchActivity : BaseActivity() {
|
||||||
|
companion object {
|
||||||
|
private const val REQUEST_CODE_VPN_PREPARE = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var isRunning = false
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
if (value) {
|
||||||
|
Utils.stopVService(this)
|
||||||
|
} else {
|
||||||
|
val intent = VpnService.prepare(this)
|
||||||
|
if (intent == null) {
|
||||||
|
Utils.startVService(this)
|
||||||
|
} else {
|
||||||
|
startActivityForResult(intent, REQUEST_CODE_VPN_PREPARE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finishActivity()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun finishActivity() {
|
||||||
|
try {
|
||||||
|
Observable.timer(5000, TimeUnit.MILLISECONDS)
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe {
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
moveTaskToBack(true)
|
moveTaskToBack(true)
|
||||||
|
|
||||||
setContentView(R.layout.activity_none)
|
setContentView(R.layout.activity_none)
|
||||||
|
|
||||||
if (V2RayServiceManager.v2rayPoint.isRunning) {
|
val isRunning = Utils.isServiceRun(this, "com.v2ray.ang.service.V2RayVpnService")
|
||||||
Utils.stopVService(this)
|
if (isRunning) {
|
||||||
|
//Utils.stopVService(this)
|
||||||
|
mMsgReceive = ReceiveMessageHandler(this@ScSwitchActivity)
|
||||||
|
registerReceiver(mMsgReceive, IntentFilter(AppConfig.BROADCAST_ACTION_ACTIVITY))
|
||||||
|
MessageUtil.sendMsg2Service(this, AppConfig.MSG_REGISTER_CLIENT, "")
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
Utils.startVServiceFromToggle(this)
|
Utils.startVService(this)
|
||||||
|
finishActivity()
|
||||||
}
|
}
|
||||||
finish()
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
override fun onStop() {
|
||||||
|
super.onStop()
|
||||||
|
if (mMsgReceive != null) {
|
||||||
|
unregisterReceiver(mMsgReceive)
|
||||||
|
mMsgReceive = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var mMsgReceive: BroadcastReceiver? = null
|
||||||
|
|
||||||
|
private class ReceiveMessageHandler(activity: ScSwitchActivity) : BroadcastReceiver() {
|
||||||
|
internal var mReference: SoftReference<ScSwitchActivity> = SoftReference(activity)
|
||||||
|
override fun onReceive(ctx: Context?, intent: Intent?) {
|
||||||
|
val activity = mReference.get()
|
||||||
|
when (intent?.getIntExtra("key", 0)) {
|
||||||
|
AppConfig.MSG_STATE_RUNNING -> {
|
||||||
|
activity?.isRunning = true
|
||||||
|
}
|
||||||
|
AppConfig.MSG_STATE_NOT_RUNNING -> {
|
||||||
|
activity?.isRunning = false
|
||||||
|
}
|
||||||
|
// AppConfig.MSG_STATE_START_SUCCESS -> {
|
||||||
|
// activity?.toast(R.string.toast_services_success)
|
||||||
|
// activity?.isRunning = true
|
||||||
|
// }
|
||||||
|
// AppConfig.MSG_STATE_START_FAILURE -> {
|
||||||
|
// activity?.toast(R.string.toast_services_failure)
|
||||||
|
// activity?.isRunning = false
|
||||||
|
// }
|
||||||
|
// AppConfig.MSG_STATE_STOP_SUCCESS -> {
|
||||||
|
// activity?.isRunning = false
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -7,13 +7,23 @@ import com.google.zxing.Result
|
|||||||
import me.dm7.barcodescanner.zxing.ZXingScannerView
|
import me.dm7.barcodescanner.zxing.ZXingScannerView
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.graphics.BitmapFactory
|
import android.graphics.BitmapFactory
|
||||||
|
import android.icu.util.TimeUnit
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import com.google.zxing.BarcodeFormat
|
import com.google.zxing.BarcodeFormat
|
||||||
import com.tbruyelle.rxpermissions.RxPermissions
|
import com.tbruyelle.rxpermissions.RxPermissions
|
||||||
import com.v2ray.ang.R
|
import com.v2ray.ang.R
|
||||||
import com.v2ray.ang.extension.toast
|
|
||||||
import com.v2ray.ang.util.QRCodeDecoder
|
import com.v2ray.ang.util.QRCodeDecoder
|
||||||
|
import org.jetbrains.anko.toast
|
||||||
|
import rx.Observable
|
||||||
|
import android.os.SystemClock
|
||||||
|
import kotlinx.android.synthetic.main.activity_main.*
|
||||||
|
import rx.Observer
|
||||||
|
import rx.android.schedulers.AndroidSchedulers
|
||||||
|
import javax.xml.datatype.DatatypeConstants.SECONDS
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ScannerActivity : BaseActivity(), ZXingScannerView.ResultHandler {
|
class ScannerActivity : BaseActivity(), ZXingScannerView.ResultHandler {
|
||||||
companion object {
|
companion object {
|
||||||
@@ -109,10 +119,10 @@ class ScannerActivity : BaseActivity(), ZXingScannerView.ResultHandler {
|
|||||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||||
super.onActivityResult(requestCode, resultCode, data)
|
super.onActivityResult(requestCode, resultCode, data)
|
||||||
when (requestCode) {
|
when (requestCode) {
|
||||||
REQUEST_FILE_CHOOSER -> {
|
REQUEST_FILE_CHOOSER ->
|
||||||
val uri = data?.data
|
if (resultCode == RESULT_OK) {
|
||||||
if (resultCode == RESULT_OK && uri != null) {
|
|
||||||
try {
|
try {
|
||||||
|
val uri = data!!.data
|
||||||
val bitmap = BitmapFactory.decodeStream(contentResolver.openInputStream(uri))
|
val bitmap = BitmapFactory.decodeStream(contentResolver.openInputStream(uri))
|
||||||
val text = QRCodeDecoder.syncDecodeQRCode(bitmap)
|
val text = QRCodeDecoder.syncDecodeQRCode(bitmap)
|
||||||
finished(text)
|
finished(text)
|
||||||
@@ -121,7 +131,6 @@ class ScannerActivity : BaseActivity(), ZXingScannerView.ResultHandler {
|
|||||||
toast(e.message.toString())
|
toast(e.message.toString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,18 @@
|
|||||||
package com.v2ray.ang.ui
|
package com.v2ray.ang.ui
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.support.v7.app.AlertDialog
|
|
||||||
import android.text.Editable
|
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
import android.util.Log
|
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import com.google.gson.Gson
|
|
||||||
import com.v2ray.ang.AppConfig
|
import com.v2ray.ang.AppConfig
|
||||||
import com.v2ray.ang.R
|
import com.v2ray.ang.R
|
||||||
import com.v2ray.ang.extension.defaultDPreference
|
import com.v2ray.ang.extension.defaultDPreference
|
||||||
import com.v2ray.ang.dto.AngConfig
|
import com.v2ray.ang.dto.AngConfig
|
||||||
import com.v2ray.ang.extension.toast
|
|
||||||
import com.v2ray.ang.util.AngConfigManager
|
import com.v2ray.ang.util.AngConfigManager
|
||||||
import com.v2ray.ang.util.Utils
|
import com.v2ray.ang.util.Utils
|
||||||
import kotlinx.android.synthetic.main.activity_server2.*
|
import kotlinx.android.synthetic.main.activity_server2.*
|
||||||
import java.lang.Exception
|
import org.jetbrains.anko.*
|
||||||
|
|
||||||
|
|
||||||
class Server2Activity : BaseActivity() {
|
class Server2Activity : BaseActivity() {
|
||||||
companion object {
|
companion object {
|
||||||
@@ -54,7 +50,7 @@ class Server2Activity : BaseActivity() {
|
|||||||
*/
|
*/
|
||||||
fun bindingServer(vmess: AngConfig.VmessBean): Boolean {
|
fun bindingServer(vmess: AngConfig.VmessBean): Boolean {
|
||||||
et_remarks.text = Utils.getEditable(vmess.remarks)
|
et_remarks.text = Utils.getEditable(vmess.remarks)
|
||||||
tv_content.text = Editable.Factory.getInstance().newEditable(defaultDPreference.getPrefString(AppConfig.ANG_CONFIG + edit_guid, ""))
|
tv_content.text = defaultDPreference.getPrefString(AppConfig.ANG_CONFIG + edit_guid, "")
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,45 +66,21 @@ class Server2Activity : BaseActivity() {
|
|||||||
* save server config
|
* save server config
|
||||||
*/
|
*/
|
||||||
fun saveServer(): Boolean {
|
fun saveServer(): Boolean {
|
||||||
var saveSuccess: Boolean
|
|
||||||
val vmess = configs.vmess[edit_index]
|
val vmess = configs.vmess[edit_index]
|
||||||
|
|
||||||
vmess.remarks = et_remarks.text.toString()
|
vmess.remarks = et_remarks.text.toString()
|
||||||
|
|
||||||
if (TextUtils.isEmpty(vmess.remarks)) {
|
if (TextUtils.isEmpty(vmess.remarks)) {
|
||||||
toast(R.string.server_lab_remarks)
|
toast(R.string.server_lab_remarks)
|
||||||
saveSuccess = false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (AngConfigManager.addCustomServer(vmess, edit_index) == 0) {
|
if (AngConfigManager.addCustomServer(vmess, edit_index) == 0) {
|
||||||
toast(R.string.toast_success)
|
toast(R.string.toast_success)
|
||||||
saveSuccess = true
|
|
||||||
} else {
|
|
||||||
toast(R.string.toast_failure)
|
|
||||||
saveSuccess = false
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
try {
|
|
||||||
Gson().fromJson<Object>(tv_content.text.toString(), Object::class.java)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
e.printStackTrace()
|
|
||||||
toast(R.string.toast_malformed_josn)
|
|
||||||
saveSuccess = false
|
|
||||||
}
|
|
||||||
|
|
||||||
if (saveSuccess) {
|
|
||||||
//update config
|
|
||||||
defaultDPreference.setPrefString(AppConfig.ANG_CONFIG + edit_guid, tv_content.text.toString())
|
|
||||||
if (edit_index == configs.index) {
|
|
||||||
if (!AngConfigManager.genStoreV2rayConfig(edit_index)) {
|
|
||||||
Log.d(AppConfig.ANG_PACKAGE, "update custom config $edit_index but generate full configuration failed!")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finish()
|
finish()
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
|
toast(R.string.toast_failure)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -118,16 +90,17 @@ class Server2Activity : BaseActivity() {
|
|||||||
*/
|
*/
|
||||||
fun deleteServer(): Boolean {
|
fun deleteServer(): Boolean {
|
||||||
if (edit_index >= 0) {
|
if (edit_index >= 0) {
|
||||||
AlertDialog.Builder(this).setMessage(R.string.del_config_comfirm)
|
alert(R.string.del_config_comfirm) {
|
||||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
positiveButton(android.R.string.ok) {
|
||||||
if (AngConfigManager.removeServer(edit_index) == 0) {
|
if (AngConfigManager.removeServer(edit_index) == 0) {
|
||||||
toast(R.string.toast_success)
|
toast(R.string.toast_success)
|
||||||
finish()
|
finish()
|
||||||
} else {
|
} else {
|
||||||
toast(R.string.toast_failure)
|
toast(R.string.toast_failure)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.show()
|
}
|
||||||
|
show()
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
@@ -163,4 +136,4 @@ class Server2Activity : BaseActivity() {
|
|||||||
}
|
}
|
||||||
else -> super.onOptionsItemSelected(item)
|
else -> super.onOptionsItemSelected(item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,16 +1,16 @@
|
|||||||
package com.v2ray.ang.ui
|
package com.v2ray.ang.ui
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.support.v7.app.AlertDialog
|
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import com.v2ray.ang.R
|
import com.v2ray.ang.R
|
||||||
import com.v2ray.ang.dto.AngConfig
|
import com.v2ray.ang.dto.AngConfig
|
||||||
import com.v2ray.ang.extension.toast
|
|
||||||
import com.v2ray.ang.util.AngConfigManager
|
import com.v2ray.ang.util.AngConfigManager
|
||||||
import com.v2ray.ang.util.Utils
|
import com.v2ray.ang.util.Utils
|
||||||
import kotlinx.android.synthetic.main.activity_server3.*
|
import kotlinx.android.synthetic.main.activity_server3.*
|
||||||
|
import org.jetbrains.anko.*
|
||||||
|
|
||||||
|
|
||||||
class Server3Activity : BaseActivity() {
|
class Server3Activity : BaseActivity() {
|
||||||
companion object {
|
companion object {
|
||||||
@@ -126,16 +126,17 @@ class Server3Activity : BaseActivity() {
|
|||||||
*/
|
*/
|
||||||
fun deleteServer(): Boolean {
|
fun deleteServer(): Boolean {
|
||||||
if (edit_index >= 0) {
|
if (edit_index >= 0) {
|
||||||
AlertDialog.Builder(this).setMessage(R.string.del_config_comfirm)
|
alert(R.string.del_config_comfirm) {
|
||||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
positiveButton(android.R.string.ok) {
|
||||||
if (AngConfigManager.removeServer(edit_index) == 0) {
|
if (AngConfigManager.removeServer(edit_index) == 0) {
|
||||||
toast(R.string.toast_success)
|
toast(R.string.toast_success)
|
||||||
finish()
|
finish()
|
||||||
} else {
|
} else {
|
||||||
toast(R.string.toast_failure)
|
toast(R.string.toast_failure)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.show()
|
}
|
||||||
|
show()
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
@@ -171,4 +172,4 @@ class Server3Activity : BaseActivity() {
|
|||||||
}
|
}
|
||||||
else -> super.onOptionsItemSelected(item)
|
else -> super.onOptionsItemSelected(item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,16 +1,16 @@
|
|||||||
package com.v2ray.ang.ui
|
package com.v2ray.ang.ui
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.support.v7.app.AlertDialog
|
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import com.v2ray.ang.R
|
import com.v2ray.ang.R
|
||||||
import com.v2ray.ang.dto.AngConfig
|
import com.v2ray.ang.dto.AngConfig
|
||||||
import com.v2ray.ang.extension.toast
|
|
||||||
import com.v2ray.ang.util.AngConfigManager
|
import com.v2ray.ang.util.AngConfigManager
|
||||||
import com.v2ray.ang.util.Utils
|
import com.v2ray.ang.util.Utils
|
||||||
import kotlinx.android.synthetic.main.activity_server4.*
|
import kotlinx.android.synthetic.main.activity_server4.*
|
||||||
|
import org.jetbrains.anko.*
|
||||||
|
|
||||||
|
|
||||||
class Server4Activity : BaseActivity() {
|
class Server4Activity : BaseActivity() {
|
||||||
companion object {
|
companion object {
|
||||||
@@ -110,16 +110,17 @@ class Server4Activity : BaseActivity() {
|
|||||||
*/
|
*/
|
||||||
fun deleteServer(): Boolean {
|
fun deleteServer(): Boolean {
|
||||||
if (edit_index >= 0) {
|
if (edit_index >= 0) {
|
||||||
AlertDialog.Builder(this).setMessage(R.string.del_config_comfirm)
|
alert(R.string.del_config_comfirm) {
|
||||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
positiveButton(android.R.string.ok) {
|
||||||
if (AngConfigManager.removeServer(edit_index) == 0) {
|
if (AngConfigManager.removeServer(edit_index) == 0) {
|
||||||
toast(R.string.toast_success)
|
toast(R.string.toast_success)
|
||||||
finish()
|
finish()
|
||||||
} else {
|
} else {
|
||||||
toast(R.string.toast_failure)
|
toast(R.string.toast_failure)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.show()
|
}
|
||||||
|
show()
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
@@ -155,4 +156,4 @@ class Server4Activity : BaseActivity() {
|
|||||||
}
|
}
|
||||||
else -> super.onOptionsItemSelected(item)
|
else -> super.onOptionsItemSelected(item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,16 +1,16 @@
|
|||||||
package com.v2ray.ang.ui
|
package com.v2ray.ang.ui
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.support.v7.app.AlertDialog
|
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import com.v2ray.ang.R
|
import com.v2ray.ang.R
|
||||||
import com.v2ray.ang.dto.AngConfig
|
import com.v2ray.ang.dto.AngConfig
|
||||||
import com.v2ray.ang.extension.toast
|
|
||||||
import com.v2ray.ang.util.AngConfigManager
|
import com.v2ray.ang.util.AngConfigManager
|
||||||
import com.v2ray.ang.util.Utils
|
import com.v2ray.ang.util.Utils
|
||||||
import kotlinx.android.synthetic.main.activity_server.*
|
import kotlinx.android.synthetic.main.activity_server.*
|
||||||
|
import org.jetbrains.anko.*
|
||||||
|
|
||||||
|
|
||||||
class ServerActivity : BaseActivity() {
|
class ServerActivity : BaseActivity() {
|
||||||
companion object {
|
companion object {
|
||||||
@@ -169,16 +169,17 @@ class ServerActivity : BaseActivity() {
|
|||||||
*/
|
*/
|
||||||
fun deleteServer(): Boolean {
|
fun deleteServer(): Boolean {
|
||||||
if (edit_index >= 0) {
|
if (edit_index >= 0) {
|
||||||
AlertDialog.Builder(this).setMessage(R.string.del_config_comfirm)
|
alert(R.string.del_config_comfirm) {
|
||||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
positiveButton(android.R.string.ok) {
|
||||||
if (AngConfigManager.removeServer(edit_index) == 0) {
|
if (AngConfigManager.removeServer(edit_index) == 0) {
|
||||||
toast(R.string.toast_success)
|
toast(R.string.toast_success)
|
||||||
finish()
|
finish()
|
||||||
} else {
|
} else {
|
||||||
toast(R.string.toast_failure)
|
toast(R.string.toast_failure)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.show()
|
}
|
||||||
|
show()
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
@@ -214,4 +215,4 @@ class ServerActivity : BaseActivity() {
|
|||||||
}
|
}
|
||||||
else -> super.onOptionsItemSelected(item)
|
else -> super.onOptionsItemSelected(item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,16 +1,25 @@
|
|||||||
package com.v2ray.ang.ui
|
package com.v2ray.ang.ui
|
||||||
|
|
||||||
import android.arch.lifecycle.ViewModelProviders
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.content.SharedPreferences
|
||||||
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.support.v7.preference.*
|
import android.preference.CheckBoxPreference
|
||||||
import android.view.View
|
import android.preference.EditTextPreference
|
||||||
|
import android.preference.Preference
|
||||||
|
import android.preference.PreferenceFragment
|
||||||
|
import com.v2ray.ang.BuildConfig
|
||||||
|
import com.v2ray.ang.InappBuyActivity
|
||||||
import com.v2ray.ang.R
|
import com.v2ray.ang.R
|
||||||
import com.v2ray.ang.AppConfig
|
import com.v2ray.ang.AppConfig
|
||||||
import com.v2ray.ang.extension.toast
|
import com.v2ray.ang.extension.defaultDPreference
|
||||||
import com.v2ray.ang.util.AngConfigManager
|
import com.v2ray.ang.extension.onClick
|
||||||
import com.v2ray.ang.util.Utils
|
import com.v2ray.ang.util.Utils
|
||||||
import com.v2ray.ang.viewmodel.SettingsViewModel
|
import org.jetbrains.anko.act
|
||||||
|
import org.jetbrains.anko.defaultSharedPreferences
|
||||||
|
import org.jetbrains.anko.startActivity
|
||||||
|
import org.jetbrains.anko.toast
|
||||||
|
import libv2ray.Libv2ray
|
||||||
|
|
||||||
class SettingsActivity : BaseActivity() {
|
class SettingsActivity : BaseActivity() {
|
||||||
companion object {
|
companion object {
|
||||||
@@ -20,7 +29,6 @@ class SettingsActivity : BaseActivity() {
|
|||||||
// const val PREF_MUX_ENAimport libv2ray.Libv2rayBLED = "pref_mux_enabled"
|
// const val PREF_MUX_ENAimport libv2ray.Libv2rayBLED = "pref_mux_enabled"
|
||||||
const val PREF_SPEED_ENABLED = "pref_speed_enabled"
|
const val PREF_SPEED_ENABLED = "pref_speed_enabled"
|
||||||
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_LOCAL_DNS_ENABLED = "pref_local_dns_enabled"
|
const val PREF_LOCAL_DNS_ENABLED = "pref_local_dns_enabled"
|
||||||
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"
|
||||||
@@ -35,12 +43,11 @@ class SettingsActivity : BaseActivity() {
|
|||||||
// const val PREF_LICENSES = "pref_licenses"
|
// const val PREF_LICENSES = "pref_licenses"
|
||||||
// const val PREF_FEEDBACK = "pref_feedback"
|
// const val PREF_FEEDBACK = "pref_feedback"
|
||||||
// const val PREF_TG_GROUP = "pref_tg_group"
|
// const val PREF_TG_GROUP = "pref_tg_group"
|
||||||
|
const val PREF_VERSION = "pref_version"
|
||||||
// const val PREF_AUTO_RESTART = "pref_auto_restart"
|
// const val PREF_AUTO_RESTART = "pref_auto_restart"
|
||||||
const val PREF_FORWARD_IPV6 = "pref_forward_ipv6"
|
const val PREF_FORWARD_IPV6 = "pref_forward_ipv6"
|
||||||
}
|
}
|
||||||
|
|
||||||
private val settingsViewModel by lazy { ViewModelProviders.of(this).get(SettingsViewModel::class.java) }
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.activity_settings)
|
setContentView(R.layout.activity_settings)
|
||||||
@@ -48,130 +55,34 @@ class SettingsActivity : BaseActivity() {
|
|||||||
title = getString(R.string.title_settings)
|
title = getString(R.string.title_settings)
|
||||||
|
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
|
|
||||||
settingsViewModel.startListenPreferenceChange()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class SettingsFragment : PreferenceFragmentCompat() {
|
class SettingsFragment : PreferenceFragment(), SharedPreferences.OnSharedPreferenceChangeListener {
|
||||||
private val perAppProxy by lazy { findPreference(PREF_PER_APP_PROXY) as CheckBoxPreference }
|
val perAppProxy by lazy { findPreference(PREF_PER_APP_PROXY) as CheckBoxPreference }
|
||||||
private val sppedEnabled by lazy { findPreference(PREF_SPEED_ENABLED) as CheckBoxPreference }
|
|
||||||
private val sniffingEnabled by lazy { findPreference(PREF_SNIFFING_ENABLED) as CheckBoxPreference }
|
|
||||||
private val proxySharing by lazy { findPreference(PREF_PROXY_SHARING) as CheckBoxPreference }
|
|
||||||
private val domainStrategy by lazy { findPreference(PREF_ROUTING_DOMAIN_STRATEGY) as ListPreference }
|
|
||||||
private val routingMode by lazy { findPreference(PREF_ROUTING_MODE) as ListPreference }
|
|
||||||
|
|
||||||
private val forwardIpv6 by lazy { findPreference(PREF_FORWARD_IPV6) as CheckBoxPreference }
|
|
||||||
private val enableLocalDns by lazy { findPreference(PREF_LOCAL_DNS_ENABLED) as CheckBoxPreference }
|
|
||||||
private val domesticDns by lazy { findPreference(PREF_DOMESTIC_DNS) as EditTextPreference }
|
|
||||||
private val remoteDns by lazy { findPreference(PREF_REMOTE_DNS) as EditTextPreference }
|
|
||||||
|
|
||||||
// val autoRestart by lazy { findPreference(PREF_AUTO_RESTART) as CheckBoxPreference }
|
// val autoRestart by lazy { findPreference(PREF_AUTO_RESTART) as CheckBoxPreference }
|
||||||
|
val remoteDns by lazy { findPreference(PREF_REMOTE_DNS) as EditTextPreference }
|
||||||
|
val domesticDns by lazy { findPreference(PREF_DOMESTIC_DNS) as EditTextPreference }
|
||||||
|
|
||||||
|
val enableLocalDns by lazy { findPreference(PREF_LOCAL_DNS_ENABLED) as CheckBoxPreference }
|
||||||
|
val forwardIpv6 by lazy { findPreference(PREF_FORWARD_IPV6) as CheckBoxPreference }
|
||||||
|
|
||||||
// val socksPort by lazy { findPreference(PREF_SOCKS_PORT) as EditTextPreference }
|
// val socksPort by lazy { findPreference(PREF_SOCKS_PORT) as EditTextPreference }
|
||||||
// val httpPort by lazy { findPreference(PREF_HTTP_PORT) as EditTextPreference }
|
// val httpPort by lazy { findPreference(PREF_HTTP_PORT) as EditTextPreference }
|
||||||
|
|
||||||
private val routingCustom: Preference by lazy { findPreference(PREF_ROUTING_CUSTOM) }
|
val routingCustom: Preference by lazy { findPreference(PREF_ROUTING_CUSTOM) }
|
||||||
// val donate: Preference by lazy { findPreference(PREF_DONATE) }
|
// val donate: Preference by lazy { findPreference(PREF_DONATE) }
|
||||||
// val licenses: Preference by lazy { findPreference(PREF_LICENSES) }
|
// val licenses: Preference by lazy { findPreference(PREF_LICENSES) }
|
||||||
// val feedback: Preference by lazy { findPreference(PREF_FEEDBACK) }
|
// val feedback: Preference by lazy { findPreference(PREF_FEEDBACK) }
|
||||||
// val tgGroup: Preference by lazy { findPreference(PREF_TG_GROUP) }
|
// val tgGroup: Preference by lazy { findPreference(PREF_TG_GROUP) }
|
||||||
|
val version: Preference by lazy { findPreference(PREF_VERSION) }
|
||||||
|
|
||||||
private val mode by lazy { findPreference(AppConfig.PREF_MODE) as ListPreference }
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
private fun restartProxy() {
|
|
||||||
Utils.stopVService(requireContext())
|
|
||||||
Utils.startVService(requireContext(), AngConfigManager.configs.index)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun isRunning(): Boolean {
|
|
||||||
return false //TODO no point of adding logic now since Settings will be changed soon
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreatePreferences(bundle: Bundle?, s: String?) {
|
|
||||||
addPreferencesFromResource(R.xml.pref_settings)
|
addPreferencesFromResource(R.xml.pref_settings)
|
||||||
|
|
||||||
perAppProxy.setOnPreferenceClickListener {
|
routingCustom.onClick {
|
||||||
if (isRunning()) {
|
startActivity<RoutingSettingsActivity>()
|
||||||
Utils.stopVService(requireContext())
|
|
||||||
}
|
|
||||||
startActivity(Intent(activity, PerAppProxyActivity::class.java))
|
|
||||||
perAppProxy.isChecked = true
|
|
||||||
true
|
|
||||||
}
|
}
|
||||||
sppedEnabled.setOnPreferenceClickListener {
|
|
||||||
if (isRunning())
|
|
||||||
restartProxy()
|
|
||||||
true
|
|
||||||
}
|
|
||||||
sniffingEnabled.setOnPreferenceClickListener {
|
|
||||||
if (isRunning())
|
|
||||||
restartProxy()
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
proxySharing.setOnPreferenceClickListener {
|
|
||||||
if (proxySharing.isChecked)
|
|
||||||
activity?.toast(R.string.toast_warning_pref_proxysharing)
|
|
||||||
if (isRunning())
|
|
||||||
restartProxy()
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
domainStrategy.setOnPreferenceChangeListener { _, _ ->
|
|
||||||
if (isRunning())
|
|
||||||
restartProxy()
|
|
||||||
true
|
|
||||||
}
|
|
||||||
routingMode.setOnPreferenceChangeListener { _, _ ->
|
|
||||||
if (isRunning())
|
|
||||||
restartProxy()
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
routingCustom.setOnPreferenceClickListener {
|
|
||||||
if (isRunning())
|
|
||||||
Utils.stopVService(requireContext())
|
|
||||||
startActivity(Intent(activity, RoutingSettingsActivity::class.java))
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
forwardIpv6.setOnPreferenceClickListener {
|
|
||||||
if (isRunning())
|
|
||||||
restartProxy()
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
enableLocalDns.setOnPreferenceClickListener {
|
|
||||||
if (isRunning())
|
|
||||||
restartProxy()
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
domesticDns.setOnPreferenceChangeListener { _, any ->
|
|
||||||
// domesticDns.summary = any as String
|
|
||||||
val nval = any as String
|
|
||||||
domesticDns.summary = if (nval == "") AppConfig.DNS_DIRECT else nval
|
|
||||||
if (isRunning())
|
|
||||||
restartProxy()
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
remoteDns.setOnPreferenceChangeListener { _, any ->
|
|
||||||
// remoteDns.summary = any as String
|
|
||||||
val nval = any as String
|
|
||||||
remoteDns.summary = if (nval == "") AppConfig.DNS_AGENT else nval
|
|
||||||
if (isRunning())
|
|
||||||
restartProxy()
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
mode.setOnPreferenceChangeListener { _, newValue ->
|
|
||||||
updatePerAppProxy(newValue.toString())
|
|
||||||
true
|
|
||||||
}
|
|
||||||
mode.dialogLayoutResource = R.layout.preference_with_help_link
|
|
||||||
|
|
||||||
// donate.onClick {
|
// donate.onClick {
|
||||||
// startActivity<InappBuyActivity>()
|
// startActivity<InappBuyActivity>()
|
||||||
@@ -199,7 +110,24 @@ class SettingsActivity : BaseActivity() {
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
perAppProxy.setOnPreferenceClickListener {
|
||||||
|
startActivity<PerAppProxyActivity>()
|
||||||
|
perAppProxy.isChecked = true
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
remoteDns.setOnPreferenceChangeListener { preference, any ->
|
||||||
|
// remoteDns.summary = any as String
|
||||||
|
val nval = any as String
|
||||||
|
remoteDns.summary = if (nval == "") AppConfig.DNS_AGENT else nval
|
||||||
|
true
|
||||||
|
}
|
||||||
|
domesticDns.setOnPreferenceChangeListener { preference, any ->
|
||||||
|
// domesticDns.summary = any as String
|
||||||
|
val nval = any as String
|
||||||
|
domesticDns.summary = if (nval == "") AppConfig.DNS_DIRECT else nval
|
||||||
|
true
|
||||||
|
}
|
||||||
// socksPort.setOnPreferenceChangeListener { preference, any ->
|
// socksPort.setOnPreferenceChangeListener { preference, any ->
|
||||||
// socksPort.summary = any as String
|
// socksPort.summary = any as String
|
||||||
// true
|
// true
|
||||||
@@ -208,12 +136,14 @@ class SettingsActivity : BaseActivity() {
|
|||||||
// httpPort.summary = any as String
|
// httpPort.summary = any as String
|
||||||
// true
|
// true
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
version.summary = "${BuildConfig.VERSION_NAME} (${Libv2ray.checkVersionX()})"
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStart() {
|
override fun onStart() {
|
||||||
super.onStart()
|
super.onStart()
|
||||||
val defaultSharedPreferences = PreferenceManager.getDefaultSharedPreferences(activity)
|
|
||||||
updatePerAppProxy(defaultSharedPreferences.getString(AppConfig.PREF_MODE, "VPN"))
|
perAppProxy.isChecked = defaultSharedPreferences.getBoolean(PREF_PER_APP_PROXY, false)
|
||||||
remoteDns.summary = defaultSharedPreferences.getString(PREF_REMOTE_DNS, "")
|
remoteDns.summary = defaultSharedPreferences.getString(PREF_REMOTE_DNS, "")
|
||||||
domesticDns.summary = defaultSharedPreferences.getString(PREF_DOMESTIC_DNS, "")
|
domesticDns.summary = defaultSharedPreferences.getString(PREF_DOMESTIC_DNS, "")
|
||||||
|
|
||||||
@@ -227,21 +157,24 @@ class SettingsActivity : BaseActivity() {
|
|||||||
|
|
||||||
// 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, "")
|
||||||
|
|
||||||
|
defaultSharedPreferences.registerOnSharedPreferenceChangeListener(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updatePerAppProxy(mode: String?) {
|
override fun onStop() {
|
||||||
if (mode == "VPN") {
|
super.onStop()
|
||||||
perAppProxy.isEnabled = true
|
defaultSharedPreferences.unregisterOnSharedPreferenceChangeListener(this)
|
||||||
perAppProxy.isChecked = PreferenceManager.getDefaultSharedPreferences(activity)
|
}
|
||||||
.getBoolean(PREF_PER_APP_PROXY, false)
|
|
||||||
} else {
|
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String?) {
|
||||||
perAppProxy.isEnabled = false
|
when (key) {
|
||||||
perAppProxy.isChecked = false
|
// PREF_AUTO_RESTART ->
|
||||||
|
// act.defaultDPreference.setPrefBoolean(key, sharedPreferences.getBoolean(key, false))
|
||||||
|
|
||||||
|
PREF_PER_APP_PROXY ->
|
||||||
|
act.defaultDPreference.setPrefBoolean(key, sharedPreferences.getBoolean(key, false))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onModeHelpClicked(view: View) {
|
}
|
||||||
Utils.openUri(this, AppConfig.v2rayNGWikiMode)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +1,16 @@
|
|||||||
package com.v2ray.ang.ui
|
package com.v2ray.ang.ui
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.support.v7.app.AlertDialog
|
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import com.v2ray.ang.R
|
import com.v2ray.ang.R
|
||||||
import com.v2ray.ang.dto.AngConfig
|
import com.v2ray.ang.dto.AngConfig
|
||||||
import com.v2ray.ang.extension.toast
|
|
||||||
import com.v2ray.ang.util.AngConfigManager
|
import com.v2ray.ang.util.AngConfigManager
|
||||||
import com.v2ray.ang.util.Utils
|
import com.v2ray.ang.util.Utils
|
||||||
import kotlinx.android.synthetic.main.activity_sub_edit.*
|
import kotlinx.android.synthetic.main.activity_sub_edit.*
|
||||||
|
import org.jetbrains.anko.*
|
||||||
|
|
||||||
|
|
||||||
class SubEditActivity : BaseActivity() {
|
class SubEditActivity : BaseActivity() {
|
||||||
|
|
||||||
@@ -95,16 +95,17 @@ class SubEditActivity : BaseActivity() {
|
|||||||
*/
|
*/
|
||||||
fun deleteServer(): Boolean {
|
fun deleteServer(): Boolean {
|
||||||
if (edit_index >= 0) {
|
if (edit_index >= 0) {
|
||||||
AlertDialog.Builder(this).setMessage(R.string.del_config_comfirm)
|
alert(R.string.del_config_comfirm) {
|
||||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
positiveButton(android.R.string.ok) {
|
||||||
if (AngConfigManager.removeSubItem(edit_index) == 0) {
|
if (AngConfigManager.removeSubItem(edit_index) == 0) {
|
||||||
toast(R.string.toast_success)
|
toast(R.string.toast_success)
|
||||||
finish()
|
finish()
|
||||||
} else {
|
} else {
|
||||||
toast(R.string.toast_failure)
|
toast(R.string.toast_failure)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.show()
|
}
|
||||||
|
show()
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
@@ -134,4 +135,4 @@ class SubEditActivity : BaseActivity() {
|
|||||||
}
|
}
|
||||||
else -> super.onOptionsItemSelected(item)
|
else -> super.onOptionsItemSelected(item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
package com.v2ray.ang.ui
|
package com.v2ray.ang.ui
|
||||||
|
|
||||||
import android.content.Intent
|
|
||||||
import android.support.v7.widget.LinearLayoutManager
|
import android.support.v7.widget.LinearLayoutManager
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import com.v2ray.ang.R
|
import com.v2ray.ang.R
|
||||||
import kotlinx.android.synthetic.main.activity_sub_setting.*
|
import kotlinx.android.synthetic.main.activity_sub_setting.*
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import org.jetbrains.anko.startActivity
|
||||||
|
|
||||||
class SubSettingActivity : BaseActivity() {
|
class SubSettingActivity : BaseActivity() {
|
||||||
|
|
||||||
@@ -40,9 +40,7 @@ class SubSettingActivity : BaseActivity() {
|
|||||||
|
|
||||||
override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
|
override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
|
||||||
R.id.add_config -> {
|
R.id.add_config -> {
|
||||||
startActivity(Intent(this, SubEditActivity::class.java)
|
startActivity<SubEditActivity>("position" to -1)
|
||||||
.putExtra("position", -1)
|
|
||||||
)
|
|
||||||
adapter.updateConfigList()
|
adapter.updateConfigList()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
@@ -50,4 +48,4 @@ class SubSettingActivity : BaseActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,14 @@
|
|||||||
package com.v2ray.ang.ui
|
package com.v2ray.ang.ui
|
||||||
|
|
||||||
import android.content.Intent
|
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.support.v7.widget.RecyclerView
|
import android.support.v7.widget.RecyclerView
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import com.v2ray.ang.R
|
import com.v2ray.ang.R
|
||||||
import com.v2ray.ang.dto.AngConfig
|
import com.v2ray.ang.dto.AngConfig
|
||||||
import com.v2ray.ang.util.AngConfigManager
|
import com.v2ray.ang.util.AngConfigManager
|
||||||
import kotlinx.android.synthetic.main.item_recycler_sub_setting.view.*
|
import kotlinx.android.synthetic.main.item_recycler_sub_setting.view.*
|
||||||
|
import org.jetbrains.anko.*
|
||||||
|
|
||||||
class SubSettingRecyclerAdapter(val activity: SubSettingActivity) : RecyclerView.Adapter<SubSettingRecyclerAdapter.BaseViewHolder>() {
|
class SubSettingRecyclerAdapter(val activity: SubSettingActivity) : RecyclerView.Adapter<SubSettingRecyclerAdapter.BaseViewHolder>() {
|
||||||
|
|
||||||
@@ -29,19 +28,17 @@ class SubSettingRecyclerAdapter(val activity: SubSettingActivity) : RecyclerView
|
|||||||
|
|
||||||
holder.name.text = remarks
|
holder.name.text = remarks
|
||||||
holder.url.text = url
|
holder.url.text = url
|
||||||
holder.itemView.setBackgroundColor(Color.TRANSPARENT)
|
holder.itemView.backgroundColor = Color.TRANSPARENT
|
||||||
|
|
||||||
holder.layout_edit.setOnClickListener {
|
holder.layout_edit.setOnClickListener {
|
||||||
mActivity.startActivity(Intent(mActivity, SubEditActivity::class.java)
|
mActivity.startActivity<SubEditActivity>("position" to position)
|
||||||
.putExtra("position", position)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder {
|
||||||
return MainViewHolder(LayoutInflater.from(parent.context)
|
return MainViewHolder(parent.context.layoutInflater
|
||||||
.inflate(R.layout.item_recycler_sub_setting, parent, false))
|
.inflate(R.layout.item_recycler_sub_setting, parent, false))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,11 +15,13 @@ import com.v2ray.ang.AppConfig.SS_PROTOCOL
|
|||||||
import com.v2ray.ang.AppConfig.VMESS_PROTOCOL
|
import com.v2ray.ang.AppConfig.VMESS_PROTOCOL
|
||||||
import com.v2ray.ang.R
|
import com.v2ray.ang.R
|
||||||
import com.v2ray.ang.dto.AngConfig
|
import com.v2ray.ang.dto.AngConfig
|
||||||
import com.v2ray.ang.dto.EConfigType
|
|
||||||
import com.v2ray.ang.dto.VmessQRCode
|
import com.v2ray.ang.dto.VmessQRCode
|
||||||
import java.net.URI
|
import com.v2ray.ang.extension.defaultDPreference
|
||||||
|
import org.jetbrains.anko.toast
|
||||||
import java.net.URLDecoder
|
import java.net.URLDecoder
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import java.net.*
|
||||||
|
import java.math.BigInteger
|
||||||
|
|
||||||
object AngConfigManager {
|
object AngConfigManager {
|
||||||
private lateinit var app: AngApplication
|
private lateinit var app: AngApplication
|
||||||
@@ -67,7 +69,7 @@ object AngConfigManager {
|
|||||||
fun addServer(vmess: AngConfig.VmessBean, index: Int): Int {
|
fun addServer(vmess: AngConfig.VmessBean, index: Int): Int {
|
||||||
try {
|
try {
|
||||||
vmess.configVersion = 2
|
vmess.configVersion = 2
|
||||||
vmess.configType = EConfigType.VMESS.value
|
vmess.configType = AppConfig.EConfigType.Vmess
|
||||||
|
|
||||||
if (index >= 0) {
|
if (index >= 0) {
|
||||||
//edit
|
//edit
|
||||||
@@ -102,7 +104,16 @@ object AngConfigManager {
|
|||||||
angConfig.vmess.removeAt(index)
|
angConfig.vmess.removeAt(index)
|
||||||
|
|
||||||
//移除的是活动的
|
//移除的是活动的
|
||||||
adjustIndexForRemovalAt(index)
|
if (angConfig.index == index) {
|
||||||
|
if (angConfig.vmess.count() > 0) {
|
||||||
|
angConfig.index = 0
|
||||||
|
} else {
|
||||||
|
angConfig.index = -1
|
||||||
|
}
|
||||||
|
} else if (index < angConfig.index)//移除活动之前的
|
||||||
|
{
|
||||||
|
angConfig.index--
|
||||||
|
}
|
||||||
|
|
||||||
storeConfigFile()
|
storeConfigFile()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
@@ -112,19 +123,6 @@ object AngConfigManager {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun adjustIndexForRemovalAt(index: Int) {
|
|
||||||
if (angConfig.index == index) {
|
|
||||||
if (angConfig.vmess.count() > 0) {
|
|
||||||
angConfig.index = 0
|
|
||||||
} else {
|
|
||||||
angConfig.index = -1
|
|
||||||
}
|
|
||||||
} else if (index < angConfig.index)//移除活动之前的
|
|
||||||
{
|
|
||||||
angConfig.index--
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun swapServer(fromPosition: Int, toPosition: Int): Int {
|
fun swapServer(fromPosition: Int, toPosition: Int): Int {
|
||||||
try {
|
try {
|
||||||
Collections.swap(angConfig.vmess, fromPosition, toPosition)
|
Collections.swap(angConfig.vmess, fromPosition, toPosition)
|
||||||
@@ -135,7 +133,7 @@ object AngConfigManager {
|
|||||||
} else if (index == toPosition) {
|
} else if (index == toPosition) {
|
||||||
angConfig.index = fromPosition
|
angConfig.index = fromPosition
|
||||||
}
|
}
|
||||||
//storeConfigFile()
|
storeConfigFile()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
return -1
|
return -1
|
||||||
@@ -149,19 +147,13 @@ object AngConfigManager {
|
|||||||
fun setActiveServer(index: Int): Int {
|
fun setActiveServer(index: Int): Int {
|
||||||
try {
|
try {
|
||||||
if (index < 0 || index > angConfig.vmess.count() - 1) {
|
if (index < 0 || index > angConfig.vmess.count() - 1) {
|
||||||
app.curIndex = -1
|
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
angConfig.index = index
|
angConfig.index = index
|
||||||
app.curIndex = index
|
|
||||||
storeConfigFile()
|
storeConfigFile()
|
||||||
if (!genStoreV2rayConfig(index)) {
|
|
||||||
Log.d(AppConfig.ANG_PACKAGE, "set active index $index but generate full configuration failed!")
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
app.curIndex = -1
|
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
@@ -214,14 +206,14 @@ object AngConfigManager {
|
|||||||
return app.defaultDPreference.getPrefString(AppConfig.PREF_CURR_CONFIG, "")
|
return app.defaultDPreference.getPrefString(AppConfig.PREF_CURR_CONFIG, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun currConfigType(): EConfigType? {
|
fun currConfigType(): Int {
|
||||||
if (angConfig.index < 0
|
if (angConfig.index < 0
|
||||||
|| angConfig.vmess.count() <= 0
|
|| angConfig.vmess.count() <= 0
|
||||||
|| angConfig.index > angConfig.vmess.count() - 1
|
|| angConfig.index > angConfig.vmess.count() - 1
|
||||||
) {
|
) {
|
||||||
return null
|
return -1
|
||||||
}
|
}
|
||||||
return EConfigType.fromInt(angConfig.vmess[angConfig.index].configType)
|
return angConfig.vmess[angConfig.index].configType
|
||||||
}
|
}
|
||||||
|
|
||||||
fun currConfigName(): String {
|
fun currConfigName(): String {
|
||||||
@@ -247,7 +239,7 @@ object AngConfigManager {
|
|||||||
/**
|
/**
|
||||||
* import config form qrcode or...
|
* import config form qrcode or...
|
||||||
*/
|
*/
|
||||||
fun importConfig(server: String?, subid: String, removedSelectedServer: AngConfig.VmessBean?): Int {
|
fun importConfig(server: String?, subid: String): Int {
|
||||||
try {
|
try {
|
||||||
if (server == null || TextUtils.isEmpty(server)) {
|
if (server == null || TextUtils.isEmpty(server)) {
|
||||||
return R.string.toast_none_data
|
return R.string.toast_none_data
|
||||||
@@ -258,11 +250,7 @@ object AngConfigManager {
|
|||||||
if (server.startsWith(VMESS_PROTOCOL)) {
|
if (server.startsWith(VMESS_PROTOCOL)) {
|
||||||
|
|
||||||
val indexSplit = server.indexOf("?")
|
val indexSplit = server.indexOf("?")
|
||||||
val newVmess = tryParseNewVmess(server)
|
if (indexSplit > 0) {
|
||||||
if (newVmess != null) {
|
|
||||||
vmess = newVmess
|
|
||||||
vmess.subid = subid
|
|
||||||
} else if (indexSplit > 0) {
|
|
||||||
vmess = ResolveVmess4Kitsunebi(server)
|
vmess = ResolveVmess4Kitsunebi(server)
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
@@ -281,7 +269,7 @@ object AngConfigManager {
|
|||||||
return R.string.toast_incorrect_protocol
|
return R.string.toast_incorrect_protocol
|
||||||
}
|
}
|
||||||
|
|
||||||
vmess.configType = EConfigType.VMESS.value
|
vmess.configType = AppConfig.EConfigType.Vmess
|
||||||
vmess.security = "auto"
|
vmess.security = "auto"
|
||||||
vmess.network = "tcp"
|
vmess.network = "tcp"
|
||||||
vmess.headerType = "none"
|
vmess.headerType = "none"
|
||||||
@@ -323,7 +311,7 @@ object AngConfigManager {
|
|||||||
result = Utils.decode(result)
|
result = Utils.decode(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
val legacyPattern = "^(.+?):(.*)@(.+?):(\\d+?)/?$".toRegex()
|
val legacyPattern = "^(.+?):(.*)@(.+?):(\\d+?)$".toRegex()
|
||||||
val match = legacyPattern.matchEntire(result)
|
val match = legacyPattern.matchEntire(result)
|
||||||
if (match == null) {
|
if (match == null) {
|
||||||
return R.string.toast_incorrect_protocol
|
return R.string.toast_incorrect_protocol
|
||||||
@@ -371,12 +359,6 @@ object AngConfigManager {
|
|||||||
} else {
|
} else {
|
||||||
return R.string.toast_incorrect_protocol
|
return R.string.toast_incorrect_protocol
|
||||||
}
|
}
|
||||||
if (removedSelectedServer != null &&
|
|
||||||
vmess.subid.equals(removedSelectedServer.subid) &&
|
|
||||||
vmess.address.equals(removedSelectedServer.address) &&
|
|
||||||
vmess.port.equals(removedSelectedServer.port)) {
|
|
||||||
setActiveServer(configs.vmess.count() - 1)
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
return -1
|
return -1
|
||||||
@@ -384,61 +366,6 @@ object AngConfigManager {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
fun tryParseNewVmess(uri: String): AngConfig.VmessBean? {
|
|
||||||
return runCatching {
|
|
||||||
val uri = URI(uri)
|
|
||||||
check(uri.scheme == "vmess")
|
|
||||||
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]+)")
|
|
||||||
.matchEntire(uri.userInfo)?.groupValues
|
|
||||||
?: error("parse user info fail.")
|
|
||||||
val tls = tlsStr.isNotBlank()
|
|
||||||
val queryParam = uri.rawQuery.split("&")
|
|
||||||
.map { it.split("=").let { (k, v) -> k to URLDecoder.decode(v, "utf-8")!! } }
|
|
||||||
.toMap()
|
|
||||||
val vmess = AngConfig.VmessBean()
|
|
||||||
vmess.address = uri.host
|
|
||||||
vmess.port = uri.port
|
|
||||||
vmess.id = uuid
|
|
||||||
vmess.alterId = alterId.toInt()
|
|
||||||
vmess.streamSecurity = if (tls) "tls" else ""
|
|
||||||
vmess.remarks = uri.fragment
|
|
||||||
vmess.security = "auto"
|
|
||||||
|
|
||||||
// TODO: allowInsecure not supported
|
|
||||||
|
|
||||||
when (protocol) {
|
|
||||||
"tcp" -> {
|
|
||||||
vmess.network = "tcp"
|
|
||||||
vmess.headerType = queryParam["type"] ?: "none"
|
|
||||||
vmess.requestHost = queryParam["host"] ?: ""
|
|
||||||
}
|
|
||||||
"http" -> {
|
|
||||||
vmess.network = "h2"
|
|
||||||
vmess.path = queryParam["path"]?.takeIf { it.trim() != "/" } ?: ""
|
|
||||||
vmess.requestHost = queryParam["host"]?.split("|")?.get(0) ?: ""
|
|
||||||
}
|
|
||||||
"ws" -> {
|
|
||||||
vmess.network = "ws"
|
|
||||||
vmess.path = queryParam["path"]?.takeIf { it.trim() != "/" } ?: ""
|
|
||||||
vmess.requestHost = queryParam["host"]?.split("|")?.get(0) ?: ""
|
|
||||||
}
|
|
||||||
"kcp" -> {
|
|
||||||
vmess.network = "kcp"
|
|
||||||
vmess.headerType = queryParam["type"] ?: "none"
|
|
||||||
vmess.path = queryParam["seed"] ?: ""
|
|
||||||
}
|
|
||||||
"quic" -> {
|
|
||||||
vmess.network = "quic"
|
|
||||||
vmess.requestHost = queryParam["security"] ?: "none"
|
|
||||||
vmess.headerType = queryParam["type"] ?: "none"
|
|
||||||
vmess.path = queryParam["key"] ?: ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
vmess
|
|
||||||
}.getOrNull()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun ResolveVmess4Kitsunebi(server: String): AngConfig.VmessBean {
|
private fun ResolveVmess4Kitsunebi(server: String): AngConfig.VmessBean {
|
||||||
|
|
||||||
val vmess = AngConfig.VmessBean()
|
val vmess = AngConfig.VmessBean()
|
||||||
@@ -484,7 +411,7 @@ object AngConfigManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val vmess = angConfig.vmess[index]
|
val vmess = angConfig.vmess[index]
|
||||||
if (angConfig.vmess[index].configType == EConfigType.VMESS.value) {
|
if (angConfig.vmess[index].configType == AppConfig.EConfigType.Vmess) {
|
||||||
|
|
||||||
val vmessQRCode = VmessQRCode()
|
val vmessQRCode = VmessQRCode()
|
||||||
vmessQRCode.v = vmess.configVersion.toString()
|
vmessQRCode.v = vmess.configVersion.toString()
|
||||||
@@ -502,7 +429,7 @@ object AngConfigManager {
|
|||||||
val conf = VMESS_PROTOCOL + Utils.encode(json)
|
val conf = VMESS_PROTOCOL + Utils.encode(json)
|
||||||
|
|
||||||
return conf
|
return conf
|
||||||
} else if (angConfig.vmess[index].configType == EConfigType.SHADOWSOCKS.value) {
|
} else if (angConfig.vmess[index].configType == AppConfig.EConfigType.Shadowsocks) {
|
||||||
val remark = "#" + Utils.urlEncode(vmess.remarks)
|
val remark = "#" + Utils.urlEncode(vmess.remarks)
|
||||||
val url = String.format("%s:%s@%s:%s",
|
val url = String.format("%s:%s@%s:%s",
|
||||||
vmess.security,
|
vmess.security,
|
||||||
@@ -510,7 +437,7 @@ object AngConfigManager {
|
|||||||
vmess.address,
|
vmess.address,
|
||||||
vmess.port)
|
vmess.port)
|
||||||
return SS_PROTOCOL + Utils.encode(url) + remark
|
return SS_PROTOCOL + Utils.encode(url) + remark
|
||||||
} else if (angConfig.vmess[index].configType == EConfigType.SOCKS.value) {
|
} else if (angConfig.vmess[index].configType == AppConfig.EConfigType.Socks) {
|
||||||
val remark = "#" + Utils.urlEncode(vmess.remarks)
|
val remark = "#" + Utils.urlEncode(vmess.remarks)
|
||||||
val url = String.format("%s:%s",
|
val url = String.format("%s:%s",
|
||||||
vmess.address,
|
vmess.address,
|
||||||
@@ -591,9 +518,9 @@ object AngConfigManager {
|
|||||||
*/
|
*/
|
||||||
fun shareFullContent2Clipboard(index: Int): Int {
|
fun shareFullContent2Clipboard(index: Int): Int {
|
||||||
try {
|
try {
|
||||||
val result = V2rayConfigUtil.getV2rayConfig(app, angConfig.vmess[index])
|
if (AngConfigManager.genStoreV2rayConfig(index)) {
|
||||||
if (result.status) {
|
val configContent = app.defaultDPreference.getPrefString(AppConfig.PREF_CURR_CONFIG, "")
|
||||||
Utils.setClipboard(app.applicationContext, result.content)
|
Utils.setClipboard(app.applicationContext, configContent)
|
||||||
} else {
|
} else {
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
@@ -619,7 +546,7 @@ object AngConfigManager {
|
|||||||
//add
|
//add
|
||||||
val vmess = AngConfig.VmessBean()
|
val vmess = AngConfig.VmessBean()
|
||||||
vmess.configVersion = 2
|
vmess.configVersion = 2
|
||||||
vmess.configType = EConfigType.CUSTOM.value
|
vmess.configType = AppConfig.EConfigType.Custom
|
||||||
vmess.guid = guid
|
vmess.guid = guid
|
||||||
vmess.remarks = vmess.guid
|
vmess.remarks = vmess.guid
|
||||||
|
|
||||||
@@ -722,7 +649,7 @@ object AngConfigManager {
|
|||||||
fun addCustomServer(vmess: AngConfig.VmessBean, index: Int): Int {
|
fun addCustomServer(vmess: AngConfig.VmessBean, index: Int): Int {
|
||||||
try {
|
try {
|
||||||
vmess.configVersion = 2
|
vmess.configVersion = 2
|
||||||
vmess.configType = EConfigType.CUSTOM.value
|
vmess.configType = AppConfig.EConfigType.Custom
|
||||||
|
|
||||||
if (index >= 0) {
|
if (index >= 0) {
|
||||||
//edit
|
//edit
|
||||||
@@ -747,7 +674,7 @@ object AngConfigManager {
|
|||||||
fun addShadowsocksServer(vmess: AngConfig.VmessBean, index: Int): Int {
|
fun addShadowsocksServer(vmess: AngConfig.VmessBean, index: Int): Int {
|
||||||
try {
|
try {
|
||||||
vmess.configVersion = 2
|
vmess.configVersion = 2
|
||||||
vmess.configType = EConfigType.SHADOWSOCKS.value
|
vmess.configType = AppConfig.EConfigType.Shadowsocks
|
||||||
|
|
||||||
if (index >= 0) {
|
if (index >= 0) {
|
||||||
//edit
|
//edit
|
||||||
@@ -772,7 +699,7 @@ object AngConfigManager {
|
|||||||
fun addSocksServer(vmess: AngConfig.VmessBean, index: Int): Int {
|
fun addSocksServer(vmess: AngConfig.VmessBean, index: Int): Int {
|
||||||
try {
|
try {
|
||||||
vmess.configVersion = 2
|
vmess.configVersion = 2
|
||||||
vmess.configType = EConfigType.SOCKS.value
|
vmess.configType = AppConfig.EConfigType.Socks
|
||||||
|
|
||||||
if (index >= 0) {
|
if (index >= 0) {
|
||||||
//edit
|
//edit
|
||||||
@@ -799,17 +726,6 @@ object AngConfigManager {
|
|||||||
if (servers == null) {
|
if (servers == null) {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
val removedSelectedServer =
|
|
||||||
if (!TextUtils.isEmpty(subid)) {
|
|
||||||
configs.vmess.getOrNull(configs.index)?.let {
|
|
||||||
if (it.subid == subid) {
|
|
||||||
return@let it
|
|
||||||
}
|
|
||||||
return@let null
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
removeServerViaSubid(subid)
|
removeServerViaSubid(subid)
|
||||||
|
|
||||||
// var servers = server
|
// var servers = server
|
||||||
@@ -820,7 +736,7 @@ object AngConfigManager {
|
|||||||
var count = 0
|
var count = 0
|
||||||
servers.lines()
|
servers.lines()
|
||||||
.forEach {
|
.forEach {
|
||||||
val resId = importConfig(it, subid, removedSelectedServer)
|
val resId = importConfig(it, subid)
|
||||||
if (resId == 0) {
|
if (resId == 0) {
|
||||||
count++
|
count++
|
||||||
}
|
}
|
||||||
@@ -860,7 +776,6 @@ object AngConfigManager {
|
|||||||
for (k in configs.vmess.count() - 1 downTo 0) {
|
for (k in configs.vmess.count() - 1 downTo 0) {
|
||||||
if (configs.vmess[k].subid.equals(subid)) {
|
if (configs.vmess[k].subid.equals(subid)) {
|
||||||
angConfig.vmess.removeAt(k)
|
angConfig.vmess.removeAt(k)
|
||||||
adjustIndexForRemovalAt(k)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -906,4 +821,4 @@ object AngConfigManager {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -16,7 +16,7 @@ object AppManagerUtil {
|
|||||||
val apps = ArrayList<AppInfo>()
|
val apps = ArrayList<AppInfo>()
|
||||||
|
|
||||||
for (pkg in packages) {
|
for (pkg in packages) {
|
||||||
if (!pkg.hasInternetPermission && pkg.packageName != "android") continue
|
if (!pkg.hasInternetPermission) continue
|
||||||
|
|
||||||
val applicationInfo = pkg.applicationInfo
|
val applicationInfo = pkg.applicationInfo
|
||||||
|
|
||||||
@@ -40,4 +40,4 @@ object AppManagerUtil {
|
|||||||
val permissions = requestedPermissions
|
val permissions = requestedPermissions
|
||||||
return permissions?.any { it == Manifest.permission.INTERNET } ?: false
|
return permissions?.any { it == Manifest.permission.INTERNET } ?: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -11,33 +11,42 @@ import com.google.zxing.qrcode.QRCodeWriter
|
|||||||
import com.google.zxing.EncodeHintType
|
import com.google.zxing.EncodeHintType
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.collections.HashMap
|
import kotlin.collections.HashMap
|
||||||
|
import android.app.ActivityManager
|
||||||
import android.content.ClipData
|
import android.content.ClipData
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.content.res.AssetManager
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.SystemClock
|
import android.os.SystemClock
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
|
import android.text.method.ScrollingMovementMethod
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.util.Patterns
|
import android.util.Patterns
|
||||||
|
import android.view.View
|
||||||
import android.webkit.URLUtil
|
import android.webkit.URLUtil
|
||||||
import com.v2ray.ang.AngApplication
|
import com.v2ray.ang.AngApplication
|
||||||
import com.v2ray.ang.AppConfig
|
import com.v2ray.ang.AppConfig
|
||||||
import com.v2ray.ang.R
|
import com.v2ray.ang.R
|
||||||
import com.v2ray.ang.extension.defaultDPreference
|
|
||||||
import com.v2ray.ang.extension.responseLength
|
import com.v2ray.ang.extension.responseLength
|
||||||
import com.v2ray.ang.extension.toast
|
import com.v2ray.ang.service.V2RayVpnService
|
||||||
import com.v2ray.ang.extension.v2RayApplication
|
|
||||||
import com.v2ray.ang.service.V2RayServiceManager
|
|
||||||
import com.v2ray.ang.ui.SettingsActivity
|
import com.v2ray.ang.ui.SettingsActivity
|
||||||
import kotlinx.coroutines.isActive
|
import kotlinx.android.synthetic.main.activity_logcat.*
|
||||||
import me.dozen.dpreference.DPreference
|
import me.dozen.dpreference.DPreference
|
||||||
|
import org.jetbrains.anko.toast
|
||||||
|
import org.jetbrains.anko.uiThread
|
||||||
|
import java.io.BufferedReader
|
||||||
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
import java.io.InputStreamReader
|
||||||
import java.net.*
|
import java.net.*
|
||||||
import kotlin.coroutines.coroutineContext
|
import java.util.regex.Matcher
|
||||||
|
import java.util.regex.Pattern
|
||||||
|
import java.math.BigInteger
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
import libv2ray.Libv2ray
|
||||||
|
|
||||||
|
|
||||||
object Utils {
|
object Utils {
|
||||||
|
|
||||||
val tcpTestingSockets = ArrayList<Socket?>()
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* convert string to editalbe for kotlin
|
* convert string to editalbe for kotlin
|
||||||
*
|
*
|
||||||
@@ -92,7 +101,7 @@ object Utils {
|
|||||||
try {
|
try {
|
||||||
val cmb = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
val cmb = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||||
val clipData = ClipData.newPlainText(null, content)
|
val clipData = ClipData.newPlainText(null, content)
|
||||||
cmb.setPrimaryClip(clipData)
|
cmb.primaryClip = clipData
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
@@ -270,14 +279,55 @@ object Utils {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
fun startVServiceFromToggle(context: Context): Boolean {
|
|
||||||
val result = context.defaultDPreference.getPrefString(AppConfig.PREF_CURR_CONFIG, "")
|
/**
|
||||||
if (result.isBlank()) {
|
* 判断服务是否后台运行
|
||||||
context.toast(R.string.app_tile_first_use)
|
|
||||||
|
* @param context
|
||||||
|
* * Context
|
||||||
|
* *
|
||||||
|
* @param className
|
||||||
|
* * 判断的服务名字
|
||||||
|
* *
|
||||||
|
* @return true 在运行 false 不在运行
|
||||||
|
*/
|
||||||
|
fun isServiceRun(context: Context, className: String): Boolean {
|
||||||
|
var isRun = false
|
||||||
|
val activityManager = context
|
||||||
|
.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
|
||||||
|
val serviceList = activityManager
|
||||||
|
.getRunningServices(999)
|
||||||
|
val size = serviceList.size
|
||||||
|
for (i in 0..size - 1) {
|
||||||
|
if (serviceList[i].service.className == className) {
|
||||||
|
isRun = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return isRun
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* startVService
|
||||||
|
*/
|
||||||
|
fun startVService(context: Context): Boolean {
|
||||||
|
context.toast(R.string.toast_services_start)
|
||||||
|
if (AngConfigManager.genStoreV2rayConfig(-1)) {
|
||||||
|
val configContent = AngConfigManager.currGeneratedV2rayConfig()
|
||||||
|
val configType = AngConfigManager.currConfigType()
|
||||||
|
if (configType == AppConfig.EConfigType.Custom) {
|
||||||
|
try {
|
||||||
|
Libv2ray.testConfig(configContent)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
context.toast(e.toString())
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
V2RayVpnService.startV2Ray(context)
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
V2RayServiceManager.startV2Ray(context)
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -285,7 +335,6 @@ object Utils {
|
|||||||
*/
|
*/
|
||||||
fun startVService(context: Context, guid: String): Boolean {
|
fun startVService(context: Context, guid: String): Boolean {
|
||||||
val index = AngConfigManager.getIndexViaGuid(guid)
|
val index = AngConfigManager.getIndexViaGuid(guid)
|
||||||
context.v2RayApplication.curIndex=index
|
|
||||||
return startVService(context, index)
|
return startVService(context, index)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -293,11 +342,8 @@ object Utils {
|
|||||||
* startVService
|
* startVService
|
||||||
*/
|
*/
|
||||||
fun startVService(context: Context, index: Int): Boolean {
|
fun startVService(context: Context, index: Int): Boolean {
|
||||||
if (AngConfigManager.setActiveServer(index) < 0) {
|
AngConfigManager.setActiveServer(index)
|
||||||
return false
|
return startVService(context)
|
||||||
}
|
|
||||||
V2RayServiceManager.startV2Ray(context)
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -396,6 +442,7 @@ object Utils {
|
|||||||
return path
|
return path
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* readTextFromAssets
|
* readTextFromAssets
|
||||||
*/
|
*/
|
||||||
@@ -430,13 +477,10 @@ object Utils {
|
|||||||
/**
|
/**
|
||||||
* tcping
|
* tcping
|
||||||
*/
|
*/
|
||||||
suspend fun tcping(url: String, port: Int): String {
|
fun tcping(url: String, port: Int): String {
|
||||||
var time = -1L
|
var time = -1L
|
||||||
for (k in 0 until 2) {
|
for (k in 0 until 2) {
|
||||||
val one = socketConnectTime(url, port)
|
val one = socketConnectTime(url, port)
|
||||||
if (!coroutineContext.isActive) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if (one != -1L )
|
if (one != -1L )
|
||||||
if(time == -1L || one < time) {
|
if(time == -1L || one < time) {
|
||||||
time = one
|
time = one
|
||||||
@@ -447,35 +491,19 @@ object Utils {
|
|||||||
|
|
||||||
fun socketConnectTime(url: String, port: Int): Long {
|
fun socketConnectTime(url: String, port: Int): Long {
|
||||||
try {
|
try {
|
||||||
val socket = Socket()
|
|
||||||
synchronized(this) {
|
|
||||||
tcpTestingSockets.add(socket)
|
|
||||||
}
|
|
||||||
val start = System.currentTimeMillis()
|
val start = System.currentTimeMillis()
|
||||||
socket.connect(InetSocketAddress(url, port))
|
val socket = Socket(url, port)
|
||||||
val time = System.currentTimeMillis() - start
|
val time = System.currentTimeMillis() - start
|
||||||
synchronized(this) {
|
|
||||||
tcpTestingSockets.remove(socket)
|
|
||||||
}
|
|
||||||
socket.close()
|
socket.close()
|
||||||
return time
|
return time
|
||||||
} catch (e: UnknownHostException) {
|
} catch (e: UnknownHostException) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
Log.d(AppConfig.ANG_PACKAGE, "socketConnectTime IOException: $e")
|
e.printStackTrace()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
fun closeAllTcpSockets() {
|
|
||||||
synchronized(this) {
|
|
||||||
tcpTestingSockets.forEach {
|
|
||||||
it?.close()
|
|
||||||
}
|
|
||||||
tcpTestingSockets.clear()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,21 +1,20 @@
|
|||||||
package com.v2ray.ang.util
|
package com.v2ray.ang.util
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.google.gson.*
|
import com.google.gson.Gson
|
||||||
|
import com.google.gson.GsonBuilder
|
||||||
|
import com.google.gson.JsonArray
|
||||||
import com.v2ray.ang.AngApplication
|
import com.v2ray.ang.AngApplication
|
||||||
import com.v2ray.ang.AppConfig
|
import com.v2ray.ang.AppConfig
|
||||||
import com.v2ray.ang.dto.AngConfig.VmessBean
|
import com.v2ray.ang.dto.AngConfig.VmessBean
|
||||||
import com.v2ray.ang.dto.V2rayConfig
|
import com.v2ray.ang.dto.V2rayConfig
|
||||||
|
import com.v2ray.ang.extension.defaultDPreference
|
||||||
import com.v2ray.ang.ui.SettingsActivity
|
import com.v2ray.ang.ui.SettingsActivity
|
||||||
import org.json.JSONException
|
import org.json.JSONException
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
import org.json.JSONArray
|
import org.json.JSONArray
|
||||||
import com.v2ray.ang.dto.EConfigType
|
import com.google.gson.JsonObject
|
||||||
import com.v2ray.ang.extension.defaultDPreference
|
|
||||||
import kotlin.collections.ArrayList
|
|
||||||
import kotlin.collections.LinkedHashSet
|
|
||||||
|
|
||||||
object V2rayConfigUtil {
|
object V2rayConfigUtil {
|
||||||
private val requestObj: JsonObject by lazy {
|
private val requestObj: JsonObject by lazy {
|
||||||
@@ -42,10 +41,19 @@ object V2rayConfigUtil {
|
|||||||
// return result
|
// return result
|
||||||
// }
|
// }
|
||||||
|
|
||||||
if (vmess.configType == EConfigType.CUSTOM.value) {
|
if (vmess.configType == AppConfig.EConfigType.Vmess) {
|
||||||
result = getV2rayConfigType2(app, vmess)
|
|
||||||
} else {
|
|
||||||
result = getV2rayConfigType1(app, vmess)
|
result = getV2rayConfigType1(app, vmess)
|
||||||
|
} else if (vmess.configType == AppConfig.EConfigType.Custom) {
|
||||||
|
result = getV2rayConfigType2(app, vmess)
|
||||||
|
} else if (vmess.configType == AppConfig.EConfigType.Shadowsocks) {
|
||||||
|
result = getV2rayConfigType1(app, vmess)
|
||||||
|
} else if (vmess.configType == AppConfig.EConfigType.Socks) {
|
||||||
|
result = getV2rayConfigType1(app, vmess)
|
||||||
|
}
|
||||||
|
|
||||||
|
val domainName = parseDomainName(result.content)
|
||||||
|
if (!TextUtils.isEmpty(domainName)) {
|
||||||
|
app.defaultDPreference.setPrefString(AppConfig.PREF_CURR_CONFIG_DOMAIN, domainName)
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.d("V2rayConfigUtilGoLog", result.content)
|
Log.d("V2rayConfigUtilGoLog", result.content)
|
||||||
@@ -56,27 +64,6 @@ object V2rayConfigUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getCustomConfigServerOutbound(content: Context, guid: String): V2rayConfig.OutboundBean? {
|
|
||||||
val jsonConfig = content.defaultDPreference.getPrefString(AppConfig.ANG_CONFIG + guid, "")
|
|
||||||
if (TextUtils.isEmpty(jsonConfig)) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
val v2rayConfig: V2rayConfig? = try {
|
|
||||||
Gson().fromJson(jsonConfig, V2rayConfig::class.java)
|
|
||||||
} catch (e: JsonSyntaxException) {
|
|
||||||
e.printStackTrace()
|
|
||||||
null
|
|
||||||
}
|
|
||||||
v2rayConfig?.outbounds?.forEach { outbound ->
|
|
||||||
if (outbound.protocol.equals(EConfigType.VMESS.name, true) ||
|
|
||||||
outbound.protocol.equals(EConfigType.SHADOWSOCKS.name, true) ||
|
|
||||||
outbound.protocol.equals(EConfigType.SOCKS.name, true)) {
|
|
||||||
return outbound
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 生成v2ray的客户端配置文件
|
* 生成v2ray的客户端配置文件
|
||||||
*/
|
*/
|
||||||
@@ -129,7 +116,6 @@ object V2rayConfigUtil {
|
|||||||
val jsonConfig = app.defaultDPreference.getPrefString(AppConfig.ANG_CONFIG + guid, "")
|
val jsonConfig = app.defaultDPreference.getPrefString(AppConfig.ANG_CONFIG + guid, "")
|
||||||
result.status = true
|
result.status = true
|
||||||
result.content = jsonConfig
|
result.content = jsonConfig
|
||||||
parseDomainNameAndTag(app, jsonConfig)
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
@@ -143,12 +129,6 @@ object V2rayConfigUtil {
|
|||||||
*/
|
*/
|
||||||
private fun inbounds(vmess: VmessBean, v2rayConfig: V2rayConfig, app: AngApplication): Boolean {
|
private fun inbounds(vmess: VmessBean, v2rayConfig: V2rayConfig, app: AngApplication): Boolean {
|
||||||
try {
|
try {
|
||||||
v2rayConfig.inbounds.forEach { curInbound ->
|
|
||||||
if (!app.defaultDPreference.getPrefBoolean(SettingsActivity.PREF_PROXY_SHARING, false)) {
|
|
||||||
//bind all inbounds to localhost if the user requests
|
|
||||||
curInbound.listen = "127.0.0.1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
v2rayConfig.inbounds[0].port = 10808
|
v2rayConfig.inbounds[0].port = 10808
|
||||||
// val socksPort = Utils.parseInt(app.defaultDPreference.getPrefString(SettingsActivity.PREF_SOCKS_PORT, "10808"))
|
// val socksPort = Utils.parseInt(app.defaultDPreference.getPrefString(SettingsActivity.PREF_SOCKS_PORT, "10808"))
|
||||||
// val lanconnPort = Utils.parseInt(app.defaultDPreference.getPrefString(SettingsActivity.PREF_HTTP_PORT, ""))
|
// val lanconnPort = Utils.parseInt(app.defaultDPreference.getPrefString(SettingsActivity.PREF_HTTP_PORT, ""))
|
||||||
@@ -178,12 +158,8 @@ object V2rayConfigUtil {
|
|||||||
try {
|
try {
|
||||||
val outbound = v2rayConfig.outbounds[0]
|
val outbound = v2rayConfig.outbounds[0]
|
||||||
|
|
||||||
val configType = EConfigType.fromInt(vmess.configType)
|
when (vmess.configType) {
|
||||||
if (configType != null) {
|
AppConfig.EConfigType.Vmess -> {
|
||||||
outbound.protocol = configType.name.toLowerCase()
|
|
||||||
}
|
|
||||||
when (configType) {
|
|
||||||
EConfigType.VMESS -> {
|
|
||||||
outbound.settings?.servers = null
|
outbound.settings?.servers = null
|
||||||
|
|
||||||
val vnext = v2rayConfig.outbounds[0].settings?.vnext?.get(0)
|
val vnext = v2rayConfig.outbounds[0].settings?.vnext?.get(0)
|
||||||
@@ -201,8 +177,10 @@ object V2rayConfigUtil {
|
|||||||
|
|
||||||
//远程服务器底层传输配置
|
//远程服务器底层传输配置
|
||||||
outbound.streamSettings = boundStreamSettings(vmess)
|
outbound.streamSettings = boundStreamSettings(vmess)
|
||||||
|
|
||||||
|
outbound.protocol = "vmess"
|
||||||
}
|
}
|
||||||
EConfigType.SHADOWSOCKS -> {
|
AppConfig.EConfigType.Shadowsocks -> {
|
||||||
outbound.settings?.vnext = null
|
outbound.settings?.vnext = null
|
||||||
|
|
||||||
val server = outbound.settings?.servers?.get(0)
|
val server = outbound.settings?.servers?.get(0)
|
||||||
@@ -215,8 +193,10 @@ object V2rayConfigUtil {
|
|||||||
|
|
||||||
//Mux
|
//Mux
|
||||||
outbound.mux?.enabled = false
|
outbound.mux?.enabled = false
|
||||||
|
|
||||||
|
outbound.protocol = "shadowsocks"
|
||||||
}
|
}
|
||||||
EConfigType.SOCKS -> {
|
AppConfig.EConfigType.Socks -> {
|
||||||
outbound.settings?.vnext = null
|
outbound.settings?.vnext = null
|
||||||
|
|
||||||
val server = outbound.settings?.servers?.get(0)
|
val server = outbound.settings?.servers?.get(0)
|
||||||
@@ -225,22 +205,21 @@ object V2rayConfigUtil {
|
|||||||
|
|
||||||
//Mux
|
//Mux
|
||||||
outbound.mux?.enabled = false
|
outbound.mux?.enabled = false
|
||||||
|
|
||||||
|
outbound.protocol = "socks"
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val serverDomain = if (Utils.isIpv6Address(vmess.address)) {
|
var serverDomain: String
|
||||||
String.format("[%s]:%s", vmess.address, vmess.port)
|
if(Utils.isIpv6Address(vmess.address)) {
|
||||||
|
serverDomain = String.format("[%s]:%s", vmess.address, vmess.port)
|
||||||
} else {
|
} else {
|
||||||
String.format("%s:%s", vmess.address, vmess.port)
|
serverDomain = String.format("%s:%s", vmess.address, vmess.port)
|
||||||
}
|
}
|
||||||
app.defaultDPreference.setPrefString(AppConfig.PREF_CURR_CONFIG_DOMAIN, serverDomain)
|
app.defaultDPreference.setPrefString(AppConfig.PREF_CURR_CONFIG_DOMAIN, serverDomain)
|
||||||
val tags = LinkedHashSet<String>()
|
|
||||||
v2rayConfig.outbounds.forEach {
|
|
||||||
if (!TextUtils.isEmpty(it.tag)) {
|
|
||||||
tags.add(it.tag)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
app.defaultDPreference.setPrefStringOrderedSet(AppConfig.PREF_CURR_CONFIG_OUTBOUND_TAGS, tags)
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
return false
|
return false
|
||||||
@@ -261,7 +240,7 @@ object V2rayConfigUtil {
|
|||||||
//streamSettings
|
//streamSettings
|
||||||
when (streamSettings.network) {
|
when (streamSettings.network) {
|
||||||
"kcp" -> {
|
"kcp" -> {
|
||||||
val kcpsettings = V2rayConfig.OutboundBean.StreamSettingsBean.KcpSettingsBean()
|
val kcpsettings = V2rayConfig.OutboundBean.StreamSettingsBean.KcpsettingsBean()
|
||||||
kcpsettings.mtu = 1350
|
kcpsettings.mtu = 1350
|
||||||
kcpsettings.tti = 50
|
kcpsettings.tti = 50
|
||||||
kcpsettings.uplinkCapacity = 12
|
kcpsettings.uplinkCapacity = 12
|
||||||
@@ -269,33 +248,34 @@ object V2rayConfigUtil {
|
|||||||
kcpsettings.congestion = false
|
kcpsettings.congestion = false
|
||||||
kcpsettings.readBufferSize = 1
|
kcpsettings.readBufferSize = 1
|
||||||
kcpsettings.writeBufferSize = 1
|
kcpsettings.writeBufferSize = 1
|
||||||
kcpsettings.header = V2rayConfig.OutboundBean.StreamSettingsBean.KcpSettingsBean.HeaderBean()
|
kcpsettings.header = V2rayConfig.OutboundBean.StreamSettingsBean.KcpsettingsBean.HeaderBean()
|
||||||
kcpsettings.header.type = vmess.headerType
|
kcpsettings.header.type = vmess.headerType
|
||||||
streamSettings.kcpSettings = kcpsettings
|
streamSettings.kcpsettings = kcpsettings
|
||||||
}
|
}
|
||||||
"ws" -> {
|
"ws" -> {
|
||||||
val wssettings = V2rayConfig.OutboundBean.StreamSettingsBean.WsSettingsBean()
|
val wssettings = V2rayConfig.OutboundBean.StreamSettingsBean.WssettingsBean()
|
||||||
|
wssettings.connectionReuse = true
|
||||||
val host = vmess.requestHost.trim()
|
val host = vmess.requestHost.trim()
|
||||||
val path = vmess.path.trim()
|
val path = vmess.path.trim()
|
||||||
|
|
||||||
if (!TextUtils.isEmpty(host)) {
|
if (!TextUtils.isEmpty(host)) {
|
||||||
wssettings.headers = V2rayConfig.OutboundBean.StreamSettingsBean.WsSettingsBean.HeadersBean()
|
wssettings.headers = V2rayConfig.OutboundBean.StreamSettingsBean.WssettingsBean.HeadersBean()
|
||||||
wssettings.headers.Host = host
|
wssettings.headers.Host = host
|
||||||
}
|
}
|
||||||
if (!TextUtils.isEmpty(path)) {
|
if (!TextUtils.isEmpty(path)) {
|
||||||
wssettings.path = path
|
wssettings.path = path
|
||||||
}
|
}
|
||||||
streamSettings.wsSettings = wssettings
|
streamSettings.wssettings = wssettings
|
||||||
|
|
||||||
val tlssettings = V2rayConfig.OutboundBean.StreamSettingsBean.TlsSettingsBean()
|
val tlssettings = V2rayConfig.OutboundBean.StreamSettingsBean.TlssettingsBean()
|
||||||
tlssettings.allowInsecure = true
|
tlssettings.allowInsecure = true
|
||||||
if (!TextUtils.isEmpty(host)) {
|
if (!TextUtils.isEmpty(host)) {
|
||||||
tlssettings.serverName = host
|
tlssettings.serverName = host
|
||||||
}
|
}
|
||||||
streamSettings.tlsSettings = tlssettings
|
streamSettings.tlssettings = tlssettings
|
||||||
}
|
}
|
||||||
"h2" -> {
|
"h2" -> {
|
||||||
val httpsettings = V2rayConfig.OutboundBean.StreamSettingsBean.HttpSettingsBean()
|
val httpsettings = V2rayConfig.OutboundBean.StreamSettingsBean.HttpsettingsBean()
|
||||||
val host = vmess.requestHost.trim()
|
val host = vmess.requestHost.trim()
|
||||||
val path = vmess.path.trim()
|
val path = vmess.path.trim()
|
||||||
|
|
||||||
@@ -303,30 +283,31 @@ object V2rayConfigUtil {
|
|||||||
httpsettings.host = host.split(",").map { it.trim() }
|
httpsettings.host = host.split(",").map { it.trim() }
|
||||||
}
|
}
|
||||||
httpsettings.path = path
|
httpsettings.path = path
|
||||||
streamSettings.httpSettings = httpsettings
|
streamSettings.httpsettings = httpsettings
|
||||||
|
|
||||||
val tlssettings = V2rayConfig.OutboundBean.StreamSettingsBean.TlsSettingsBean()
|
val tlssettings = V2rayConfig.OutboundBean.StreamSettingsBean.TlssettingsBean()
|
||||||
tlssettings.allowInsecure = true
|
tlssettings.allowInsecure = true
|
||||||
streamSettings.tlsSettings = tlssettings
|
streamSettings.tlssettings = tlssettings
|
||||||
}
|
}
|
||||||
"quic" -> {
|
"quic" -> {
|
||||||
val quicsettings = V2rayConfig.OutboundBean.StreamSettingsBean.QuicSettingBean()
|
val quicsettings = V2rayConfig.OutboundBean.StreamSettingsBean.QuicsettingBean()
|
||||||
val host = vmess.requestHost.trim()
|
val host = vmess.requestHost.trim()
|
||||||
val path = vmess.path.trim()
|
val path = vmess.path.trim()
|
||||||
|
|
||||||
quicsettings.security = host
|
quicsettings.security = host
|
||||||
quicsettings.key = path
|
quicsettings.key = path
|
||||||
|
|
||||||
quicsettings.header = V2rayConfig.OutboundBean.StreamSettingsBean.QuicSettingBean.HeaderBean()
|
quicsettings.header = V2rayConfig.OutboundBean.StreamSettingsBean.QuicsettingBean.HeaderBean()
|
||||||
quicsettings.header.type = vmess.headerType
|
quicsettings.header.type = vmess.headerType
|
||||||
|
|
||||||
streamSettings.quicSettings = quicsettings
|
streamSettings.quicsettings = quicsettings
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
//tcp带http伪装
|
//tcp带http伪装
|
||||||
if (vmess.headerType == "http") {
|
if (vmess.headerType == "http") {
|
||||||
val tcpSettings = V2rayConfig.OutboundBean.StreamSettingsBean.TcpSettingsBean()
|
val tcpSettings = V2rayConfig.OutboundBean.StreamSettingsBean.TcpsettingsBean()
|
||||||
tcpSettings.header = V2rayConfig.OutboundBean.StreamSettingsBean.TcpSettingsBean.HeaderBean()
|
tcpSettings.connectionReuse = true
|
||||||
|
tcpSettings.header = V2rayConfig.OutboundBean.StreamSettingsBean.TcpsettingsBean.HeaderBean()
|
||||||
tcpSettings.header.type = vmess.headerType
|
tcpSettings.header.type = vmess.headerType
|
||||||
|
|
||||||
// if (requestObj.has("headers")
|
// if (requestObj.has("headers")
|
||||||
@@ -566,7 +547,7 @@ object V2rayConfigUtil {
|
|||||||
mux = null))
|
mux = null))
|
||||||
}
|
}
|
||||||
|
|
||||||
// DNS routing
|
// DNS routing
|
||||||
v2rayConfig.routing.rules.add(0, V2rayConfig.RoutingBean.RulesBean(
|
v2rayConfig.routing.rules.add(0, V2rayConfig.RoutingBean.RulesBean(
|
||||||
type = "field",
|
type = "field",
|
||||||
outboundTag = AppConfig.TAG_DIRECT,
|
outboundTag = AppConfig.TAG_DIRECT,
|
||||||
@@ -632,58 +613,42 @@ object V2rayConfigUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun parseDomainNameAndTag(app: AngApplication, jsonConfig: String) {
|
private fun parseDomainName(jsonConfig: String): String {
|
||||||
try {
|
try {
|
||||||
val jObj = JSONObject(jsonConfig)
|
val jObj = JSONObject(jsonConfig)
|
||||||
var domainName = ""
|
var domainName: String
|
||||||
val tags = LinkedHashSet<String>()
|
|
||||||
if (jObj.has("outbound")) {
|
if (jObj.has("outbound")) {
|
||||||
val (domain, tag) = parseDomainNameAndTag(jObj.optJSONObject("outbound"))
|
domainName = parseDomainName(jObj.optJSONObject("outbound"))
|
||||||
domainName = domain
|
if (!TextUtils.isEmpty(domainName)) {
|
||||||
if (!TextUtils.isEmpty(tag)) {
|
return domainName
|
||||||
tags.add(tag)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (jObj.has("outbounds")) {
|
if (jObj.has("outbounds")) {
|
||||||
for (i in 0..(jObj.optJSONArray("outbounds").length() - 1)) {
|
for (i in 0..(jObj.optJSONArray("outbounds").length() - 1)) {
|
||||||
val (domain, tag) = parseDomainNameAndTag(jObj.optJSONArray("outbounds").getJSONObject(i))
|
domainName = parseDomainName(jObj.optJSONArray("outbounds").getJSONObject(i))
|
||||||
if (!TextUtils.isEmpty(domain) && TextUtils.isEmpty(domainName)) {
|
if (!TextUtils.isEmpty(domainName)) {
|
||||||
domainName = domain
|
return domainName
|
||||||
}
|
|
||||||
if (!TextUtils.isEmpty(tag)) {
|
|
||||||
tags.add(tag)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (jObj.has("outboundDetour")) {
|
if (jObj.has("outboundDetour")) {
|
||||||
for (i in 0..(jObj.optJSONArray("outboundDetour").length() - 1)) {
|
for (i in 0..(jObj.optJSONArray("outboundDetour").length() - 1)) {
|
||||||
val (domain, tag) = parseDomainNameAndTag(jObj.optJSONArray("outboundDetour").getJSONObject(i))
|
domainName = parseDomainName(jObj.optJSONArray("outboundDetour").getJSONObject(i))
|
||||||
if (!TextUtils.isEmpty(domain) && TextUtils.isEmpty(domainName)) {
|
if (!TextUtils.isEmpty(domainName)) {
|
||||||
domainName = domain
|
return domainName
|
||||||
}
|
|
||||||
if (!TextUtils.isEmpty(tag)) {
|
|
||||||
tags.add(tag)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!TextUtils.isEmpty(domainName)) {
|
|
||||||
app.defaultDPreference.setPrefString(AppConfig.PREF_CURR_CONFIG_DOMAIN, domainName)
|
|
||||||
}
|
|
||||||
app.defaultDPreference.setPrefStringOrderedSet(AppConfig.PREF_CURR_CONFIG_OUTBOUND_TAGS, tags)
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun parseDomainNameAndTag(outbound: JSONObject): Pair<String, String> {
|
private fun parseDomainName(outbound: JSONObject): String {
|
||||||
val tag = if (outbound.has("tag")) {
|
|
||||||
outbound.getString("tag")
|
|
||||||
} else {
|
|
||||||
""
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
if (outbound.has("settings")) {
|
if (outbound.has("settings")) {
|
||||||
val vnext: JSONArray?
|
var vnext: JSONArray?
|
||||||
if (outbound.optJSONObject("settings").has("vnext")) {
|
if (outbound.optJSONObject("settings").has("vnext")) {
|
||||||
// vmess
|
// vmess
|
||||||
vnext = outbound.optJSONObject("settings").optJSONArray("vnext")
|
vnext = outbound.optJSONObject("settings").optJSONArray("vnext")
|
||||||
@@ -691,22 +656,22 @@ object V2rayConfigUtil {
|
|||||||
// shadowsocks or socks
|
// shadowsocks or socks
|
||||||
vnext = outbound.optJSONObject("settings").optJSONArray("servers")
|
vnext = outbound.optJSONObject("settings").optJSONArray("servers")
|
||||||
} else {
|
} else {
|
||||||
return Pair("", tag)
|
return ""
|
||||||
}
|
}
|
||||||
for (i in 0..(vnext.length() - 1)) {
|
for (i in 0..(vnext.length() - 1)) {
|
||||||
val item = vnext.getJSONObject(i)
|
val item = vnext.getJSONObject(i)
|
||||||
val address = item.getString("address")
|
val address = item.getString("address")
|
||||||
val port = item.getString("port")
|
val port = item.getString("port")
|
||||||
if(Utils.isIpv6Address(address)) {
|
if(Utils.isIpv6Address(address)) {
|
||||||
return Pair(String.format("[%s]:%s", address, port), tag)
|
return String.format("[%s]:%s", address, port)
|
||||||
} else {
|
} else {
|
||||||
return Pair(String.format("%s:%s", address, port), tag)
|
return String.format("%s:%s", address, port)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
return Pair("", tag)
|
return ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
package com.v2ray.ang.viewmodel
|
|
||||||
|
|
||||||
import android.app.Application
|
|
||||||
import android.arch.lifecycle.AndroidViewModel
|
|
||||||
import android.arch.lifecycle.MutableLiveData
|
|
||||||
import android.content.BroadcastReceiver
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
|
||||||
import android.content.IntentFilter
|
|
||||||
import android.util.Log
|
|
||||||
import com.v2ray.ang.AngApplication
|
|
||||||
import com.v2ray.ang.AppConfig
|
|
||||||
import com.v2ray.ang.R
|
|
||||||
import com.v2ray.ang.dto.EConfigType
|
|
||||||
import com.v2ray.ang.extension.toast
|
|
||||||
import com.v2ray.ang.util.AngConfigManager
|
|
||||||
import com.v2ray.ang.util.MessageUtil
|
|
||||||
import com.v2ray.ang.util.Utils
|
|
||||||
import com.v2ray.ang.util.V2rayConfigUtil
|
|
||||||
import kotlinx.coroutines.*
|
|
||||||
|
|
||||||
class MainViewModel(application: Application) : AndroidViewModel(application) {
|
|
||||||
val isRunning by lazy { MutableLiveData<Boolean>() }
|
|
||||||
val updateListAction by lazy { MutableLiveData<Int>() }
|
|
||||||
val updateTestResultAction by lazy { MutableLiveData<String>() }
|
|
||||||
|
|
||||||
private val tcpingTestScope by lazy { CoroutineScope(Dispatchers.IO) }
|
|
||||||
|
|
||||||
fun startListenBroadcast() {
|
|
||||||
isRunning.value = false
|
|
||||||
getApplication<AngApplication>().registerReceiver(mMsgReceiver, IntentFilter(AppConfig.BROADCAST_ACTION_ACTIVITY))
|
|
||||||
MessageUtil.sendMsg2Service(getApplication(), AppConfig.MSG_REGISTER_CLIENT, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCleared() {
|
|
||||||
getApplication<AngApplication>().unregisterReceiver(mMsgReceiver)
|
|
||||||
Log.i(AppConfig.ANG_PACKAGE, "Main ViewModel is cleared")
|
|
||||||
super.onCleared()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun testAllTcping() {
|
|
||||||
tcpingTestScope.coroutineContext[Job]?.cancelChildren()
|
|
||||||
Utils.closeAllTcpSockets()
|
|
||||||
for (k in 0 until AngConfigManager.configs.vmess.count()) {
|
|
||||||
AngConfigManager.configs.vmess[k].testResult = ""
|
|
||||||
updateListAction.value = -1 // update all
|
|
||||||
}
|
|
||||||
for (k in 0 until AngConfigManager.configs.vmess.count()) {
|
|
||||||
var serverAddress = AngConfigManager.configs.vmess[k].address
|
|
||||||
var serverPort = AngConfigManager.configs.vmess[k].port
|
|
||||||
if (AngConfigManager.configs.vmess[k].configType == EConfigType.CUSTOM.value) {
|
|
||||||
val serverOutbound = V2rayConfigUtil.getCustomConfigServerOutbound(getApplication(),
|
|
||||||
AngConfigManager.configs.vmess[k].guid) ?: continue
|
|
||||||
serverAddress = serverOutbound.getServerAddress() ?: continue
|
|
||||||
serverPort = serverOutbound.getServerPort() ?: continue
|
|
||||||
}
|
|
||||||
tcpingTestScope.launch {
|
|
||||||
AngConfigManager.configs.vmess.getOrNull(k)?.let { // check null in case array is modified during testing
|
|
||||||
it.testResult = Utils.tcping(serverAddress, serverPort)
|
|
||||||
launch(Dispatchers.Main) {
|
|
||||||
updateListAction.value = k
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun testCurrentServerRealPing() {
|
|
||||||
val socksPort = 10808//Utils.parseInt(defaultDPreference.getPrefString(SettingsActivity.PREF_SOCKS_PORT, "10808"))
|
|
||||||
GlobalScope.launch(Dispatchers.IO) {
|
|
||||||
val result = Utils.testConnection(getApplication(), socksPort)
|
|
||||||
launch(Dispatchers.Main) {
|
|
||||||
updateTestResultAction.value = result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val mMsgReceiver = object : BroadcastReceiver() {
|
|
||||||
override fun onReceive(ctx: Context?, intent: Intent?) {
|
|
||||||
when (intent?.getIntExtra("key", 0)) {
|
|
||||||
AppConfig.MSG_STATE_RUNNING -> {
|
|
||||||
isRunning.value = true
|
|
||||||
}
|
|
||||||
AppConfig.MSG_STATE_NOT_RUNNING -> {
|
|
||||||
isRunning.value = false
|
|
||||||
}
|
|
||||||
AppConfig.MSG_STATE_START_SUCCESS -> {
|
|
||||||
getApplication<AngApplication>().toast(R.string.toast_services_success)
|
|
||||||
isRunning.value = true
|
|
||||||
}
|
|
||||||
AppConfig.MSG_STATE_START_FAILURE -> {
|
|
||||||
getApplication<AngApplication>().toast(R.string.toast_services_failure)
|
|
||||||
isRunning.value = false
|
|
||||||
}
|
|
||||||
AppConfig.MSG_STATE_STOP_SUCCESS -> {
|
|
||||||
isRunning.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
package com.v2ray.ang.viewmodel
|
|
||||||
|
|
||||||
import android.app.Application
|
|
||||||
import android.arch.lifecycle.AndroidViewModel
|
|
||||||
import android.content.SharedPreferences
|
|
||||||
import android.support.v7.preference.PreferenceManager
|
|
||||||
import android.util.Log
|
|
||||||
import com.v2ray.ang.AppConfig
|
|
||||||
import com.v2ray.ang.ui.SettingsActivity.Companion
|
|
||||||
import com.v2ray.ang.util.AngConfigManager
|
|
||||||
import kotlinx.coroutines.GlobalScope
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
|
|
||||||
class SettingsViewModel(application: Application) : AndroidViewModel(application), SharedPreferences.OnSharedPreferenceChangeListener {
|
|
||||||
fun startListenPreferenceChange() {
|
|
||||||
PreferenceManager.getDefaultSharedPreferences(getApplication()).registerOnSharedPreferenceChangeListener(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCleared() {
|
|
||||||
PreferenceManager.getDefaultSharedPreferences(getApplication()).unregisterOnSharedPreferenceChangeListener(this)
|
|
||||||
Log.i(AppConfig.ANG_PACKAGE, "Settings ViewModel is cleared")
|
|
||||||
super.onCleared()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String?) {
|
|
||||||
Log.d(AppConfig.ANG_PACKAGE, "Observe settings changed: $key")
|
|
||||||
when(key) {
|
|
||||||
Companion.PREF_SNIFFING_ENABLED,
|
|
||||||
Companion.PREF_PROXY_SHARING,
|
|
||||||
Companion.PREF_LOCAL_DNS_ENABLED,
|
|
||||||
Companion.PREF_REMOTE_DNS,
|
|
||||||
Companion.PREF_DOMESTIC_DNS,
|
|
||||||
Companion.PREF_ROUTING_DOMAIN_STRATEGY,
|
|
||||||
Companion.PREF_ROUTING_MODE,
|
|
||||||
AppConfig.PREF_V2RAY_ROUTING_AGENT,
|
|
||||||
AppConfig.PREF_V2RAY_ROUTING_BLOCKED,
|
|
||||||
AppConfig.PREF_V2RAY_ROUTING_DIRECT -> {
|
|
||||||
GlobalScope.launch {
|
|
||||||
if (!AngConfigManager.genStoreV2rayConfig(AngConfigManager.configs.index)) {
|
|
||||||
Log.d(AppConfig.ANG_PACKAGE, "$key changed but generate full configuration failed!")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<item android:state_pressed="true"
|
|
||||||
android:drawable="@color/secondary_text" />
|
|
||||||
<item android:state_focused="true"
|
|
||||||
android:drawable="@color/secondary_text" />
|
|
||||||
<item android:state_hovered="true"
|
|
||||||
android:drawable="@color/secondary_text" />
|
|
||||||
<item android:drawable="@color/colorPrimary_text" />
|
|
||||||
</selector>
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<solid android:color="@color/accent"/>
|
|
||||||
<corners android:radius="20dp"/>
|
|
||||||
<padding android:left="0dp" android:top="0dp" android:right="0dp" android:bottom="0dp" />
|
|
||||||
</shape>
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<solid android:color="#009963"/>
|
|
||||||
<corners android:radius="20dp"/>
|
|
||||||
<padding android:left="0dp" android:top="0dp" android:right="0dp" android:bottom="0dp" />
|
|
||||||
</shape>
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.9 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 3.1 KiB |
@@ -54,10 +54,8 @@
|
|||||||
android:id="@+id/layout_test"
|
android:id="@+id/layout_test"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="@dimen/connection_test_height"
|
android:layout_height="@dimen/connection_test_height"
|
||||||
android:background="@drawable/background_test_button"
|
android:background="@color/colorPrimary_text"
|
||||||
android:gravity="center|left"
|
android:gravity="center|left">
|
||||||
android:clickable="true"
|
|
||||||
android:focusable="true">
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/tv_test_state"
|
android:id="@+id/tv_test_state"
|
||||||
@@ -94,8 +92,6 @@
|
|||||||
android:layout_marginLeft="16dp"
|
android:layout_marginLeft="16dp"
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="16dp"
|
||||||
android:src="@drawable/ic_v_idle"
|
android:src="@drawable/ic_v_idle"
|
||||||
android:clickable="true"
|
|
||||||
android:focusable="true"
|
|
||||||
app:layout_anchorGravity="bottom|right|end" />
|
app:layout_anchorGravity="bottom|right|end" />
|
||||||
|
|
||||||
</com.github.jorgecastilloprz.FABProgressCircle>
|
</com.github.jorgecastilloprz.FABProgressCircle>
|
||||||
@@ -111,23 +107,7 @@
|
|||||||
app:headerLayout="@layout/nav_header"
|
app:headerLayout="@layout/nav_header"
|
||||||
app:itemIconTint="@color/colorPrimary_dark"
|
app:itemIconTint="@color/colorPrimary_dark"
|
||||||
app:itemTextColor="@color/colorPrimary"
|
app:itemTextColor="@color/colorPrimary"
|
||||||
app:menu="@menu/menu_drawer" >
|
app:menu="@menu/menu_drawer" />
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="bottom"
|
|
||||||
android:background="@color/white"
|
|
||||||
android:padding="14dp">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/version"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:gravity="center"
|
|
||||||
android:textColor="@color/accent" />
|
|
||||||
</LinearLayout>
|
|
||||||
</android.support.design.widget.NavigationView>
|
|
||||||
|
|
||||||
</android.support.v4.widget.DrawerLayout>
|
</android.support.v4.widget.DrawerLayout>
|
||||||
|
|
||||||
|
|||||||
@@ -68,15 +68,20 @@
|
|||||||
android:text="@string/server_lab_content"
|
android:text="@string/server_lab_content"
|
||||||
android:textAppearance="@style/TextAppearance.AppCompat.Subhead" />
|
android:textAppearance="@style/TextAppearance.AppCompat.Subhead" />
|
||||||
|
|
||||||
<EditText
|
<TextView
|
||||||
android:id="@+id/tv_content"
|
android:id="@+id/tv_content"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="@dimen/layout_margin_top_height"
|
android:layout_marginTop="@dimen/layout_margin_top_height"
|
||||||
android:text="" />
|
android:text="" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="@dimen/layout_margin_top_height"
|
||||||
|
android:layout_marginBottom="@dimen/layout_margin_top_height"
|
||||||
|
android:orientation="vertical" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
@@ -2,10 +2,7 @@
|
|||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical">
|
||||||
android:background="?android:attr/selectableItemBackground"
|
|
||||||
android:clickable="true"
|
|
||||||
android:focusable="true">
|
|
||||||
|
|
||||||
<android.support.v7.widget.AppCompatImageView
|
<android.support.v7.widget.AppCompatImageView
|
||||||
android:id="@+id/icon"
|
android:id="@+id/icon"
|
||||||
@@ -43,7 +40,6 @@
|
|||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:clickable="false"
|
android:clickable="false"
|
||||||
android:focusable="false"
|
|
||||||
android:paddingStart="2dp"
|
android:paddingStart="2dp"
|
||||||
android:paddingLeft="2dp"
|
android:paddingLeft="2dp"
|
||||||
android:paddingEnd="6dp"
|
android:paddingEnd="6dp"
|
||||||
|
|||||||
@@ -9,6 +9,10 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_margin="3dp"
|
android:layout_margin="3dp"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
android:foreground="?android:attr/selectableItemBackground"
|
||||||
|
android:orientation="horizontal"
|
||||||
card_view:cardCornerRadius="5dp">
|
card_view:cardCornerRadius="5dp">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
@@ -17,11 +21,7 @@
|
|||||||
android:layout_height="@dimen/server_height"
|
android:layout_height="@dimen/server_height"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal">
|
||||||
android:background="?android:attr/selectableItemBackground"
|
|
||||||
android:clickable="true"
|
|
||||||
android:focusable="true"
|
|
||||||
android:nextFocusRight="@+id/layout_share">
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
@@ -111,14 +111,10 @@
|
|||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/layout_share"
|
android:id="@+id/layout_share"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="@dimen/server_height"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:padding="@dimen/layout_margin_spacing"
|
android:paddingEnd="@dimen/layout_margin_right_height">
|
||||||
android:background="?android:attr/selectableItemBackground"
|
|
||||||
android:clickable="true"
|
|
||||||
android:focusable="true"
|
|
||||||
android:nextFocusLeft="@+id/info_container">
|
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
@@ -130,13 +126,10 @@
|
|||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/layout_edit"
|
android:id="@+id/layout_edit"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="@dimen/server_height"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:padding="@dimen/layout_margin_spacing"
|
android:paddingEnd="@dimen/layout_margin_right_height">
|
||||||
android:background="?android:attr/selectableItemBackground"
|
|
||||||
android:clickable="true"
|
|
||||||
android:focusable="true">
|
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:layout_width="@dimen/png_height"
|
android:layout_width="@dimen/png_height"
|
||||||
@@ -148,13 +141,10 @@
|
|||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/layout_remove"
|
android:id="@+id/layout_remove"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="@dimen/server_height"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:padding="@dimen/layout_margin_spacing"
|
android:paddingEnd="@dimen/layout_margin_right_height">
|
||||||
android:background="?android:attr/selectableItemBackground"
|
|
||||||
android:clickable="true"
|
|
||||||
android:focusable="true">
|
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:layout_width="@dimen/png_height"
|
android:layout_width="@dimen/png_height"
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
android:gravity="center_vertical">
|
android:gravity="center_vertical">
|
||||||
|
|
||||||
<android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
|
<android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
|
||||||
android:id="@+id/item_cardview"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_margin="3dp"
|
android:layout_margin="3dp"
|
||||||
@@ -14,8 +13,7 @@
|
|||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:foreground="?android:attr/selectableItemBackground"
|
android:foreground="?android:attr/selectableItemBackground"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
card_view:cardCornerRadius="5dp"
|
card_view:cardCornerRadius="5dp">
|
||||||
android:nextFocusRight="@+id/layout_edit">
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/info_container"
|
android:id="@+id/info_container"
|
||||||
@@ -64,14 +62,11 @@
|
|||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/layout_share"
|
android:id="@+id/layout_share"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="@dimen/server_height"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:padding="@dimen/layout_margin_spacing"
|
android:paddingEnd="@dimen/layout_margin_right_height"
|
||||||
android:visibility="invisible"
|
android:visibility="invisible">
|
||||||
android:background="?android:attr/selectableItemBackground"
|
|
||||||
android:clickable="true"
|
|
||||||
android:focusable="true">
|
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:layout_width="@dimen/png_height"
|
android:layout_width="@dimen/png_height"
|
||||||
@@ -83,14 +78,10 @@
|
|||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/layout_edit"
|
android:id="@+id/layout_edit"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="@dimen/server_height"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:padding="@dimen/layout_margin_spacing"
|
android:paddingEnd="@dimen/layout_margin_right_height">
|
||||||
android:background="?android:attr/selectableItemBackground"
|
|
||||||
android:clickable="true"
|
|
||||||
android:focusable="true"
|
|
||||||
android:nextFocusLeft="@+id/item_cardview">
|
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:layout_width="@dimen/png_height"
|
android:layout_width="@dimen/png_height"
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<Button xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
style="@style/Widget.AppCompat.Button.Borderless"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:onClick="onModeHelpClicked"
|
|
||||||
android:text="@string/title_mode_help"
|
|
||||||
android:textAlignment="textStart"
|
|
||||||
android:textStyle="italic" />
|
|
||||||
@@ -1,5 +1,12 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
android:id="@+id/layout_switch"
|
android:id="@+id/layout_switch"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@@ -7,9 +14,16 @@
|
|||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:layout_width="40dp"
|
android:layout_width="40dp"
|
||||||
android:layout_height="40dp"
|
android:layout_height="40dp"
|
||||||
android:padding="10dp"
|
android:src="@mipmap/ic_launcher" />
|
||||||
android:src="@drawable/ic_v" />
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:text="@string/app_widget_name"
|
||||||
|
android:textAppearance="@style/TextAppearance.AppCompat.Small" />
|
||||||
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
@@ -4,7 +4,12 @@
|
|||||||
tools:showIn="navigation_view">
|
tools:showIn="navigation_view">
|
||||||
|
|
||||||
<group
|
<group
|
||||||
android:id="@+id/group_main">
|
android:id="@+id/group_main"
|
||||||
|
android:checkableBehavior="single">
|
||||||
|
<item
|
||||||
|
android:id="@+id/server_profile"
|
||||||
|
android:icon="@drawable/ic_description_white_24dp"
|
||||||
|
android:title="@string/title_server" />
|
||||||
<item
|
<item
|
||||||
android:id="@+id/sub_setting"
|
android:id="@+id/sub_setting"
|
||||||
android:icon="@drawable/ic_subscriptions_white_24dp"
|
android:icon="@drawable/ic_subscriptions_white_24dp"
|
||||||
@@ -16,27 +21,25 @@
|
|||||||
|
|
||||||
</group>
|
</group>
|
||||||
|
|
||||||
<group android:id="@+id/group_id2">
|
<item android:title="@string/title_about">
|
||||||
<item
|
<menu>
|
||||||
|
<item
|
||||||
android:id="@+id/promotion"
|
android:id="@+id/promotion"
|
||||||
android:icon="@drawable/ic_whatshot_white_24dp"
|
android:icon="@drawable/ic_whatshot_white_24dp"
|
||||||
android:title="@string/title_pref_promotion" />
|
android:title="@string/title_pref_promotion" />
|
||||||
<item
|
<item
|
||||||
android:id="@+id/donate"
|
android:id="@+id/donate"
|
||||||
android:icon="@drawable/ic_attach_money_white_24dp"
|
android:icon="@drawable/ic_attach_money_white_24dp"
|
||||||
android:title="@string/title_pref_donate" />
|
android:title="@string/title_pref_donate" />
|
||||||
<item
|
<item
|
||||||
android:id="@+id/logcat"
|
android:id="@+id/logcat"
|
||||||
android:icon="@drawable/ic_logcat_white_24dp"
|
android:icon="@drawable/ic_logcat_white_24dp"
|
||||||
android:title="@string/title_logcat" />
|
android:title="@string/title_logcat" />
|
||||||
<item
|
<item
|
||||||
android:id="@+id/feedback"
|
android:id="@+id/feedback"
|
||||||
android:icon="@drawable/ic_feedback_white_24dp"
|
android:icon="@drawable/ic_feedback_white_24dp"
|
||||||
android:title="@string/title_pref_feedback" />
|
android:title="@string/title_pref_feedback" />
|
||||||
<!-- place holder for version text at the bottom -->
|
</menu>
|
||||||
<item
|
</item>
|
||||||
android:id="@+id/placeholder"
|
|
||||||
android:enabled="false"
|
|
||||||
android:title="" />
|
|
||||||
</group>
|
|
||||||
</menu>
|
</menu>
|
||||||
|
|||||||
@@ -6,9 +6,4 @@
|
|||||||
android:icon="@drawable/ic_copy_white"
|
android:icon="@drawable/ic_copy_white"
|
||||||
android:title="@string/logcat_copy"
|
android:title="@string/logcat_copy"
|
||||||
app:showAsAction="always" />
|
app:showAsAction="always" />
|
||||||
<item
|
|
||||||
android:id="@+id/delete"
|
|
||||||
android:icon="@drawable/ic_delete_white_24dp"
|
|
||||||
android:title="@string/logcat_delete"
|
|
||||||
app:showAsAction="ifRoom" />
|
|
||||||
</menu>
|
</menu>
|
||||||
@@ -17,6 +17,17 @@
|
|||||||
<copyright>Copyright(C) 2008-2011 The Android Open Source Project</copyright>
|
<copyright>Copyright(C) 2008-2011 The Android Open Source Project</copyright>
|
||||||
<license>Apache Software License 2.0</license>
|
<license>Apache Software License 2.0</license>
|
||||||
</notice>
|
</notice>
|
||||||
|
<notice>
|
||||||
|
<name>Apache Commons Validator</name>
|
||||||
|
<url>http://commons.apache.org/proper/commons-collections/</url>
|
||||||
|
<copyright>Copyright(C) 2001-2014 The Apache Software Foundation</copyright>
|
||||||
|
<license>Apache Software License 2.0</license>
|
||||||
|
</notice>
|
||||||
|
<notice>
|
||||||
|
<name>anko</name>
|
||||||
|
<url>https://github.com/Kotlin/anko</url>
|
||||||
|
<license>Apache Software License 2.0</license>
|
||||||
|
</notice>
|
||||||
<notice>
|
<notice>
|
||||||
<name>Google Gson</name>
|
<name>Google Gson</name>
|
||||||
<url>https://github.com/google/gson</url>
|
<url>https://github.com/google/gson</url>
|
||||||
@@ -41,9 +52,33 @@
|
|||||||
<copyright>Copyright 2015 Square, Inc.</copyright>
|
<copyright>Copyright 2015 Square, Inc.</copyright>
|
||||||
<license>Apache Software License 2.0</license>
|
<license>Apache Software License 2.0</license>
|
||||||
</notice>
|
</notice>
|
||||||
|
<notice>
|
||||||
|
<name>ReactiveNetwork</name>
|
||||||
|
<url>https://github.com/pwittchen/ReactiveNetwork</url>
|
||||||
|
<copyright>Copyright 2016 Piotr Wittchen</copyright>
|
||||||
|
<license>Apache Software License 2.0</license>
|
||||||
|
</notice>
|
||||||
|
<notice>
|
||||||
|
<name>RecyclerItemDecoration</name>
|
||||||
|
<url>https://github.com/dinuscxj/RecyclerItemDecoration</url>
|
||||||
|
<copyright>Copyright 2015-2019 dinus</copyright>
|
||||||
|
<license>Apache Software License 2.0</license>
|
||||||
|
</notice>
|
||||||
|
<notice>
|
||||||
|
<name>RxKotlin</name>
|
||||||
|
<url>https://github.com/ReactiveX/RxKotlin</url>
|
||||||
|
<copyright>Copyright 2012 Netflix, Inc.</copyright>
|
||||||
|
<license>Apache Software License 2.0</license>
|
||||||
|
</notice>
|
||||||
<notice>
|
<notice>
|
||||||
<name>RxPermissions</name>
|
<name>RxPermissions</name>
|
||||||
<url>https://github.com/tbruyelle/RxPermissions</url>
|
<url>https://github.com/tbruyelle/RxPermissions</url>
|
||||||
<license>Apache Software License 2.0</license>
|
<license>Apache Software License 2.0</license>
|
||||||
</notice>
|
</notice>
|
||||||
</notices>
|
<notice>
|
||||||
|
<name>RecyclerItemDecoration</name>
|
||||||
|
<url>https://github.com/dinuscxj/RecyclerItemDecoration</url>
|
||||||
|
<copyright>Copyright 2015-2019 dinus</copyright>
|
||||||
|
<license>Apache Software License 2.0</license>
|
||||||
|
</notice>
|
||||||
|
</notices>
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!-- https://stackoverflow.com/a/52960668/5495739 -->
|
|
||||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
|
||||||
<bool name="config_materialPreferenceIconSpaceReserved" tools:ignore="MissingDefaultResource,PrivateResource">false</bool>
|
|
||||||
<dimen name="preference_category_padding_start" tools:ignore="MissingDefaultResource,PrivateResource">0dp</dimen>
|
|
||||||
</resources>
|
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
<string name="app_name">v2rayNG</string>
|
<string name="app_name">v2rayNG</string>
|
||||||
<string name="app_widget_name">开关</string>
|
<string name="app_widget_name">开关</string>
|
||||||
<string name="app_tile_name">开关</string>
|
<string name="app_tile_name">开关</string>
|
||||||
<string name="app_tile_first_use">初次使用此功能请先用APP添加配置</string>
|
<string name="app_tile_first_use">初次使用此功能请先用APP激活VPN</string>
|
||||||
<string name="navigation_drawer_open">Open navigation drawer</string>
|
<string name="navigation_drawer_open">Open navigation drawer</string>
|
||||||
<string name="navigation_drawer_close">Close navigation drawer</string>
|
<string name="navigation_drawer_close">Close navigation drawer</string>
|
||||||
|
|
||||||
@@ -73,6 +73,7 @@
|
|||||||
|
|
||||||
<!-- Preferences -->
|
<!-- Preferences -->
|
||||||
<string name="title_settings">设置</string>
|
<string name="title_settings">设置</string>
|
||||||
|
<string name="title_about">关于</string>
|
||||||
<string name="title_advanced">进阶设置</string>
|
<string name="title_advanced">进阶设置</string>
|
||||||
|
|
||||||
<string name="title_pref_per_app_proxy">分应用代理</string>
|
<string name="title_pref_per_app_proxy">分应用代理</string>
|
||||||
@@ -82,7 +83,7 @@
|
|||||||
<string name="summary_pref_mux_enabled">开启可能会加速,关闭可能会减少断流</string>
|
<string name="summary_pref_mux_enabled">开启可能会加速,关闭可能会减少断流</string>
|
||||||
|
|
||||||
<string name="title_pref_speed_enabled">启用速度显示</string>
|
<string name="title_pref_speed_enabled">启用速度显示</string>
|
||||||
<string name="summary_pref_speed_enabled">在通知中显示当前速度\n小图标显示流量的路由情况</string>
|
<string name="summary_pref_speed_enabled">在通知中显示当前速度</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>
|
||||||
@@ -122,8 +123,7 @@
|
|||||||
<string name="title_pref_promotion">推广</string>
|
<string name="title_pref_promotion">推广</string>
|
||||||
<string name="summary_pref_promotion">一些推广,点击查看详情(捐赠可去除)</string>
|
<string name="summary_pref_promotion">一些推广,点击查看详情(捐赠可去除)</string>
|
||||||
|
|
||||||
<string name="title_mode">模式</string>
|
<string name="title_pref_version">版本</string>
|
||||||
<string name="title_mode_help">点此查看更多帮助</string>
|
|
||||||
|
|
||||||
<string name="donate_error_setup">初始化错误:</string>
|
<string name="donate_error_setup">初始化错误:</string>
|
||||||
<string name="donate_error_inventory">无法查询到项目</string>
|
<string name="donate_error_inventory">无法查询到项目</string>
|
||||||
@@ -135,8 +135,7 @@
|
|||||||
|
|
||||||
<string name="title_logcat">Logcat</string>
|
<string name="title_logcat">Logcat</string>
|
||||||
<string name="logcat_copy">复制</string>
|
<string name="logcat_copy">复制</string>
|
||||||
<string name="logcat_delete">删除</string>
|
<string name="title_export_all">导出全部配置至剪贴板</string>
|
||||||
<string name="title_export_all">导出全部(非自定义)配置至剪贴板</string>
|
|
||||||
<string name="title_sub_setting">订阅设置</string>
|
<string name="title_sub_setting">订阅设置</string>
|
||||||
<string name="sub_setting_remarks">备注</string>
|
<string name="sub_setting_remarks">备注</string>
|
||||||
<string name="sub_setting_url">地址(url)</string>
|
<string name="sub_setting_url">地址(url)</string>
|
||||||
@@ -181,15 +180,5 @@
|
|||||||
<item>绕过大陆地址</item>
|
<item>绕过大陆地址</item>
|
||||||
<item>绕过局域网及大陆地址</item>
|
<item>绕过局域网及大陆地址</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
<string name="title_pref_proxy_sharing_enabled">代理共享</string>
|
|
||||||
<string name="summary_pref_proxy_sharing_enabled">绑定代理入口ip到0.0.0.0</string>
|
|
||||||
<string name="toast_warning_pref_proxysharing">其他设备可以使用socks/http协议通过您的IP地址连接到代理\nHttp 代理: http://您的ip:10809\nSocks 代理: socks(4/5)://您的ip:10808\n仅在受信任的网络中启用以避免未经授权的连接</string>
|
|
||||||
<string name="toast_warning_pref_proxysharing_short">代理共享已启用,请确保处于受信网络</string>
|
|
||||||
<string name="toast_malformed_josn">配置格式错误</string>
|
|
||||||
|
|
||||||
<string-array name="mode_entries">
|
|
||||||
<item>VPN</item>
|
|
||||||
<item>仅代理</item>
|
|
||||||
</string-array>
|
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<string name="app_name">v2rayNG</string>
|
<string name="app_name">v2rayNG</string>
|
||||||
<string name="app_widget_name">切換</string>
|
<string name="app_widget_name">切換</string>
|
||||||
<string name="app_tile_name">切換</string>
|
<string name="app_tile_name">切換</string>
|
||||||
<string name="app_tile_first_use">首次使用此功能,請使用此應用程式新增組態</string>
|
<string name="app_tile_first_use">首次使用此功能,請使用此應用程式來啟用 VPN</string>
|
||||||
<string name="navigation_drawer_open">Open navigation drawer</string>
|
<string name="navigation_drawer_open">Open navigation drawer</string>
|
||||||
<string name="navigation_drawer_close">Close navigation drawer</string>
|
<string name="navigation_drawer_close">Close navigation drawer</string>
|
||||||
|
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
<string name="toast_services_failure">啟動服務失敗</string>
|
<string name="toast_services_failure">啟動服務失敗</string>
|
||||||
|
|
||||||
<!--ServerActivity-->
|
<!--ServerActivity-->
|
||||||
<string name="title_server">設定檔</string>
|
<string name="title_server">組態</string>
|
||||||
<string name="menu_item_add_config">新增組態</string>
|
<string name="menu_item_add_config">新增組態</string>
|
||||||
<string name="menu_item_save_config">儲存組態</string>
|
<string name="menu_item_save_config">儲存組態</string>
|
||||||
<string name="menu_item_del_config">刪除組態</string>
|
<string name="menu_item_del_config">刪除組態</string>
|
||||||
@@ -57,7 +57,7 @@
|
|||||||
<string name="title_file_chooser">選取一個設定檔</string>
|
<string name="title_file_chooser">選取一個設定檔</string>
|
||||||
<string name="toast_require_file_manager">請安裝檔案總管。</string>
|
<string name="toast_require_file_manager">請安裝檔案總管。</string>
|
||||||
<string name="server_customize_config">自訂組態</string>
|
<string name="server_customize_config">自訂組態</string>
|
||||||
<string name="toast_config_file_invalid">無效設定檔</string>
|
<string name="toast_config_file_invalid">無效組態</string>
|
||||||
<string name="server_lab_content">內容</string>
|
<string name="server_lab_content">內容</string>
|
||||||
<string name="toast_none_data_clipboard">剪貼簿內無資料</string>
|
<string name="toast_none_data_clipboard">剪貼簿內無資料</string>
|
||||||
<string name="toast_invalid_url">網址無效</string>
|
<string name="toast_invalid_url">網址無效</string>
|
||||||
@@ -74,6 +74,7 @@
|
|||||||
|
|
||||||
<!-- Preferences -->
|
<!-- Preferences -->
|
||||||
<string name="title_settings">設定</string>
|
<string name="title_settings">設定</string>
|
||||||
|
<string name="title_about">關於</string>
|
||||||
<string name="title_advanced">進階設定</string>
|
<string name="title_advanced">進階設定</string>
|
||||||
|
|
||||||
<string name="title_pref_per_app_proxy">Proxy 個別應用程式</string>
|
<string name="title_pref_per_app_proxy">Proxy 個別應用程式</string>
|
||||||
@@ -83,7 +84,7 @@
|
|||||||
<string name="summary_pref_mux_enabled">啟用或許會加快網路速度,切換或許會閃爍</string>
|
<string name="summary_pref_mux_enabled">啟用或許會加快網路速度,切換或許會閃爍</string>
|
||||||
|
|
||||||
<string name="title_pref_speed_enabled">啟用速度顯示</string>
|
<string name="title_pref_speed_enabled">啟用速度顯示</string>
|
||||||
<string name="summary_pref_speed_enabled">在通知中顯示當前速度\n小圖標顯示流量的路由情況</string>
|
<string name="summary_pref_speed_enabled">在通知中顯示當前速度</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>
|
||||||
@@ -103,17 +104,17 @@
|
|||||||
<string name="summary_pref_donate">向開發人員捐款</string>
|
<string name="summary_pref_donate">向開發人員捐款</string>
|
||||||
<string name="donate_detail">新增一些實驗性進階功能</string>
|
<string name="donate_detail">新增一些實驗性進階功能</string>
|
||||||
|
|
||||||
<string name="title_pref_remote_dns">遠端DNS (可選)</string>
|
<string name="title_pref_remote_dns">遠端DNS (可选)</string>
|
||||||
<string name="summary_pref_remote_dns">DNS</string>
|
<string name="summary_pref_remote_dns">DNS</string>
|
||||||
|
|
||||||
<string name="title_pref_domestic_dns">境内DNS (可選,僅本地DNS模式下生效)</string>
|
<string name="title_pref_domestic_dns">境内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>
|
||||||
<string name="summary_pref_socks_port">SOCKS5代理連接埠</string>
|
<string name="summary_pref_socks_port">SOCKS5代理端口</string>
|
||||||
|
|
||||||
<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_feedback">回饋</string>
|
<string name="title_pref_feedback">回饋</string>
|
||||||
@@ -124,8 +125,7 @@
|
|||||||
<string name="title_pref_promotion">推廣</string>
|
<string name="title_pref_promotion">推廣</string>
|
||||||
<string name="summary_pref_promotion">一些推廣,點擊查看詳情(捐款可去除)</string>
|
<string name="summary_pref_promotion">一些推廣,點擊查看詳情(捐款可去除)</string>
|
||||||
|
|
||||||
<string name="title_mode">模式</string>
|
<string name="title_pref_version">版本</string>
|
||||||
<string name="title_mode_help">點此查看更多幫助</string>
|
|
||||||
|
|
||||||
<string name="donate_error_setup">錯誤設定:</string>
|
<string name="donate_error_setup">錯誤設定:</string>
|
||||||
<string name="donate_error_inventory">Error querying inventory</string>
|
<string name="donate_error_inventory">Error querying inventory</string>
|
||||||
@@ -137,8 +137,7 @@
|
|||||||
|
|
||||||
<string name="title_logcat">Logcat</string>
|
<string name="title_logcat">Logcat</string>
|
||||||
<string name="logcat_copy">複製</string>
|
<string name="logcat_copy">複製</string>
|
||||||
<string name="logcat_delete">刪除</string>
|
<string name="title_export_all">匯出全部配置至剪貼簿</string>
|
||||||
<string name="title_export_all">匯出全部(非自訂)配置至剪貼簿</string>
|
|
||||||
<string name="title_sub_setting">訂閱設定</string>
|
<string name="title_sub_setting">訂閱設定</string>
|
||||||
<string name="sub_setting_remarks">備註</string>
|
<string name="sub_setting_remarks">備註</string>
|
||||||
<string name="sub_setting_url">位址(url)</string>
|
<string name="sub_setting_url">位址(url)</string>
|
||||||
@@ -154,7 +153,7 @@
|
|||||||
<string name="routing_settings_delete">清除</string>
|
<string name="routing_settings_delete">清除</string>
|
||||||
<string name="routing_settings_scan_replace">掃描並取代</string>
|
<string name="routing_settings_scan_replace">掃描並取代</string>
|
||||||
<string name="routing_settings_scan_append">掃描並附加</string>
|
<string name="routing_settings_scan_append">掃描並附加</string>
|
||||||
<string name="routing_settings_default_rules">設置預設路由規則</string>
|
<string name="routing_settings_default_rules">設置默認路由規則</string>
|
||||||
|
|
||||||
<string name="connection_test_pending">"檢查連線能力"</string>
|
<string name="connection_test_pending">"檢查連線能力"</string>
|
||||||
<string name="connection_test_testing">"測試中……"</string>
|
<string name="connection_test_testing">"測試中……"</string>
|
||||||
@@ -179,19 +178,8 @@
|
|||||||
|
|
||||||
<string-array name="routing_mode">
|
<string-array name="routing_mode">
|
||||||
<item>全球</item>
|
<item>全球</item>
|
||||||
<item>略過區域網路</item>
|
<item>略過局域網</item>
|
||||||
<item>略過中國大陸</item>
|
<item>略過中國大陸</item>
|
||||||
<item>略過區域網路及中國大陸</item>
|
<item>略過局域網及中國大陸</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
<string name="title_pref_proxy_sharing_enabled">代理共享</string>
|
|
||||||
<string name="summary_pref_proxy_sharing_enabled">綁定代理入口ip到0.0.0.0</string>
|
|
||||||
<string name="toast_warning_pref_proxysharing">其他設備可以使用socks/http協定通過您的IP地址連接到代理\nHttp 代理: http://您的ip:10809\nSocks 代理: socks(4/5)://您的ip:10808\n僅在受信任的網路中啟用以避免未經授權的連接</string>
|
|
||||||
<string name="toast_warning_pref_proxysharing_short">代理共享已啟用,請確保處於受信網路</string>
|
|
||||||
<string name="toast_malformed_josn">配置格式錯誤</string>
|
|
||||||
|
|
||||||
<string-array name="mode_entries">
|
|
||||||
<item>VPN</item>
|
|
||||||
<item>僅代理</item>
|
|
||||||
</string-array>
|
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -66,44 +66,4 @@
|
|||||||
<item>IPIfNonMatch</item>
|
<item>IPIfNonMatch</item>
|
||||||
<item>IPOnDemand</item>
|
<item>IPOnDemand</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
<string-array name="mode_value" translatable="false">
|
|
||||||
<item>VPN</item>
|
|
||||||
<item>Proxy only</item>
|
|
||||||
</string-array>
|
|
||||||
|
|
||||||
<!-- minimum list https://serverfault.com/a/304791 -->
|
|
||||||
<string-array name="bypass_private_ip_address" translatable="false">
|
|
||||||
<item>0.0.0.0/5</item>
|
|
||||||
<item>8.0.0.0/7</item>
|
|
||||||
<item>11.0.0.0/8</item>
|
|
||||||
<item>12.0.0.0/6</item>
|
|
||||||
<item>16.0.0.0/4</item>
|
|
||||||
<item>32.0.0.0/3</item>
|
|
||||||
<item>64.0.0.0/2</item>
|
|
||||||
<item>128.0.0.0/3</item>
|
|
||||||
<item>160.0.0.0/5</item>
|
|
||||||
<item>168.0.0.0/6</item>
|
|
||||||
<item>172.0.0.0/12</item>
|
|
||||||
<item>172.32.0.0/11</item>
|
|
||||||
<item>172.64.0.0/10</item>
|
|
||||||
<item>172.128.0.0/9</item>
|
|
||||||
<item>173.0.0.0/8</item>
|
|
||||||
<item>174.0.0.0/7</item>
|
|
||||||
<item>176.0.0.0/4</item>
|
|
||||||
<item>192.0.0.0/9</item>
|
|
||||||
<item>192.128.0.0/11</item>
|
|
||||||
<item>192.160.0.0/13</item>
|
|
||||||
<item>192.169.0.0/16</item>
|
|
||||||
<item>192.170.0.0/15</item>
|
|
||||||
<item>192.172.0.0/14</item>
|
|
||||||
<item>192.176.0.0/12</item>
|
|
||||||
<item>192.192.0.0/10</item>
|
|
||||||
<item>193.0.0.0/8</item>
|
|
||||||
<item>194.0.0.0/7</item>
|
|
||||||
<item>196.0.0.0/6</item>
|
|
||||||
<item>200.0.0.0/5</item>
|
|
||||||
<item>208.0.0.0/4</item>
|
|
||||||
<item>224.0.0.0/3</item>
|
|
||||||
</string-array>
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
<dimen name="bypass_list_header_height">50dp</dimen>
|
<dimen name="bypass_list_header_height">50dp</dimen>
|
||||||
<dimen name="layout_margin_top_height">16dp</dimen>
|
<dimen name="layout_margin_top_height">16dp</dimen>
|
||||||
<dimen name="layout_margin_right_height">16dp</dimen>
|
<dimen name="layout_margin_right_height">16dp</dimen>
|
||||||
<dimen name="layout_margin_spacing">8dp</dimen>
|
|
||||||
<dimen name="edit_height">50dp</dimen>
|
<dimen name="edit_height">50dp</dimen>
|
||||||
<dimen name="png_height">24dp</dimen>
|
<dimen name="png_height">24dp</dimen>
|
||||||
<dimen name="server_height">72dp</dimen>
|
<dimen name="server_height">72dp</dimen>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<string name="app_name">v2rayNG</string>
|
<string name="app_name">v2rayNG</string>
|
||||||
<string name="app_widget_name">Switch</string>
|
<string name="app_widget_name">Switch</string>
|
||||||
<string name="app_tile_name">Switch</string>
|
<string name="app_tile_name">Switch</string>
|
||||||
<string name="app_tile_first_use">First use of this feature, please use the app to add server</string>
|
<string name="app_tile_first_use">First use of this feature, please use the app to activate VPN</string>
|
||||||
<string name="navigation_drawer_open">Open navigation drawer</string>
|
<string name="navigation_drawer_open">Open navigation drawer</string>
|
||||||
<string name="navigation_drawer_close">Close navigation drawer</string>
|
<string name="navigation_drawer_close">Close navigation drawer</string>
|
||||||
|
|
||||||
@@ -41,7 +41,7 @@
|
|||||||
<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_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 securty</string>
|
||||||
<string name="server_lab_path">path(ws path/h2 path)/QUIC key</string>
|
<string name="server_lab_path">path(ws path/h2 path)/QUIC key</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>
|
||||||
@@ -74,6 +74,7 @@
|
|||||||
|
|
||||||
<!-- Preferences -->
|
<!-- Preferences -->
|
||||||
<string name="title_settings">Settings</string>
|
<string name="title_settings">Settings</string>
|
||||||
|
<string name="title_about">About</string>
|
||||||
<string name="title_advanced">Advanced Settings</string>
|
<string name="title_advanced">Advanced Settings</string>
|
||||||
|
|
||||||
<string name="title_pref_per_app_proxy">Per-app proxy</string>
|
<string name="title_pref_per_app_proxy">Per-app proxy</string>
|
||||||
@@ -83,8 +84,7 @@
|
|||||||
<string name="summary_pref_mux_enabled">Enable maybe speed up network and switch network maybe flash</string>
|
<string name="summary_pref_mux_enabled">Enable maybe speed up network and switch network maybe flash</string>
|
||||||
|
|
||||||
<string name="title_pref_speed_enabled">Enable speed display</string>
|
<string name="title_pref_speed_enabled">Enable speed display</string>
|
||||||
<string name="summary_pref_speed_enabled">Display current speed in the notification.\nNotification icon would change based on
|
<string name="summary_pref_speed_enabled">Display current speed in the notification</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">Sniffing</string>
|
||||||
@@ -124,8 +124,7 @@
|
|||||||
<string name="title_pref_promotion">Promotion</string>
|
<string name="title_pref_promotion">Promotion</string>
|
||||||
<string name="summary_pref_promotion">Promotion,click for details(Donation can be removed)</string>
|
<string name="summary_pref_promotion">Promotion,click for details(Donation can be removed)</string>
|
||||||
|
|
||||||
<string name="title_mode">Mode</string>
|
<string name="title_pref_version">Version</string>
|
||||||
<string name="title_mode_help">Click me for more help</string>
|
|
||||||
|
|
||||||
<string name="donate_error_setup">Error Setup:</string>
|
<string name="donate_error_setup">Error Setup:</string>
|
||||||
<string name="donate_error_inventory">Error querying inventory</string>
|
<string name="donate_error_inventory">Error querying inventory</string>
|
||||||
@@ -137,8 +136,7 @@
|
|||||||
|
|
||||||
<string name="title_logcat">Logcat</string>
|
<string name="title_logcat">Logcat</string>
|
||||||
<string name="logcat_copy">Copy</string>
|
<string name="logcat_copy">Copy</string>
|
||||||
<string name="logcat_delete">Delete</string>
|
<string name="title_export_all">Export all config to clipboard</string>
|
||||||
<string name="title_export_all">Export non-custom configs to clipboard</string>
|
|
||||||
<string name="title_sub_setting">Subscription setting</string>
|
<string name="title_sub_setting">Subscription setting</string>
|
||||||
<string name="sub_setting_remarks">remarks</string>
|
<string name="sub_setting_remarks">remarks</string>
|
||||||
<string name="sub_setting_url">url</string>
|
<string name="sub_setting_url">url</string>
|
||||||
@@ -183,15 +181,5 @@
|
|||||||
<item>Bypass mainland address</item>
|
<item>Bypass mainland address</item>
|
||||||
<item>Bypassing LAN and mainland address</item>
|
<item>Bypassing LAN and mainland address</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
<string name="title_pref_proxy_sharing_enabled">Proxy sharing</string>
|
|
||||||
<string name="summary_pref_proxy_sharing_enabled">Bind inbound to 0.0.0.0</string>
|
|
||||||
<string name="toast_warning_pref_proxysharing">Other devices can connect to proxy by your ip address through socks/http protocol\nHttp Proxy: http://yourIP:10809\nSocks Proxy: socks(4/5)://yourIP:10808\nOnly enable in trusted network to avoid unauthorized connection</string>
|
|
||||||
<string name="toast_warning_pref_proxysharing_short">Proxy sharing enabled\nMake sure you are in a trusted network</string>
|
|
||||||
<string name="toast_malformed_josn">Config malformed</string>
|
|
||||||
|
|
||||||
<string-array name="mode_entries">
|
|
||||||
<item>VPN</item>
|
|
||||||
<item>Proxy only</item>
|
|
||||||
</string-array>
|
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -13,12 +13,6 @@
|
|||||||
<item name="windowNoTitle">true</item>
|
<item name="windowNoTitle">true</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="AppTheme.NoActionBar.Translucent">
|
|
||||||
<item name="android:windowBackground">@android:color/transparent</item>
|
|
||||||
<item name="android:colorBackgroundCacheHint">@null</item>
|
|
||||||
<item name="android:windowIsTranslucent">true</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
|
<style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
|
||||||
|
|
||||||
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
|
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
|
||||||
|
|||||||
@@ -22,13 +22,6 @@
|
|||||||
android:summary="@string/summary_pref_sniffing_enabled"
|
android:summary="@string/summary_pref_sniffing_enabled"
|
||||||
android:title="@string/title_pref_sniffing_enabled" />
|
android:title="@string/title_pref_sniffing_enabled" />
|
||||||
|
|
||||||
<CheckBoxPreference
|
|
||||||
android:defaultValue="false"
|
|
||||||
android:key="pref_proxy_sharing_enabled"
|
|
||||||
android:onClick="proxySharingOnClick"
|
|
||||||
android:summary="@string/summary_pref_proxy_sharing_enabled"
|
|
||||||
android:title="@string/title_pref_proxy_sharing_enabled" />
|
|
||||||
|
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
|
|
||||||
<PreferenceCategory android:title="@string/title_pref_routing">
|
<PreferenceCategory android:title="@string/title_pref_routing">
|
||||||
@@ -85,13 +78,29 @@
|
|||||||
android:key="pref_http_port"
|
android:key="pref_http_port"
|
||||||
android:summary="10809"
|
android:summary="10809"
|
||||||
android:title="@string/title_pref_http_port" />
|
android:title="@string/title_pref_http_port" />
|
||||||
|
|
||||||
<ListPreference
|
|
||||||
android:defaultValue="VPN"
|
|
||||||
android:entries="@array/mode_entries"
|
|
||||||
android:entryValues="@array/mode_value"
|
|
||||||
android:key="pref_mode"
|
|
||||||
android:summary="%s"
|
|
||||||
android:title="@string/title_mode" />
|
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
</PreferenceScreen>
|
|
||||||
|
<PreferenceCategory android:title="@string/title_about">
|
||||||
|
<!--<Preference-->
|
||||||
|
<!--android:key="pref_donate"-->
|
||||||
|
<!--android:summary="@string/summary_pref_donate"-->
|
||||||
|
<!--android:title="@string/title_pref_donate" />-->
|
||||||
|
|
||||||
|
<!--<Preference-->
|
||||||
|
<!--android:key="pref_licenses"-->
|
||||||
|
<!--android:title="@string/notices_title" />-->
|
||||||
|
|
||||||
|
<!--<Preference-->
|
||||||
|
<!--android:key="pref_feedback"-->
|
||||||
|
<!--android:summary="@string/summary_pref_feedback"-->
|
||||||
|
<!--android:title="@string/title_pref_feedback" />-->
|
||||||
|
|
||||||
|
<!--<Preference-->
|
||||||
|
<!--android:key="pref_tg_group"-->
|
||||||
|
<!--android:title="@string/summary_pref_tg_group" />-->
|
||||||
|
|
||||||
|
<Preference
|
||||||
|
android:key="pref_version"
|
||||||
|
android:title="@string/title_pref_version" />
|
||||||
|
</PreferenceCategory>
|
||||||
|
</PreferenceScreen>
|
||||||
@@ -7,7 +7,7 @@ buildscript {
|
|||||||
maven { url 'https://maven.google.com' }
|
maven { url 'https://maven.google.com' }
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:4.1.0'
|
classpath 'com.android.tools.build:gradle:3.1.3'
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
|
||||||
|
|
||||||
// NOTE: Do not place your application dependencies here; they belong
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
apply plugin: 'com.android.library'
|
apply plugin: 'com.android.library'
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion Integer.parseInt("$compileSdkVer")
|
compileSdkVersion 28
|
||||||
buildToolsVersion buildToolsVer
|
buildToolsVersion '28.0.3'
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 17
|
minSdkVersion 17
|
||||||
@@ -19,7 +19,7 @@ android {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||||
testImplementation 'junit:junit:4.13'
|
testImplementation 'junit:junit:4.12'
|
||||||
implementation "com.android.support:support-annotations:$supportLibVersion"
|
implementation "com.android.support:support-annotations:$supportLibVersion"
|
||||||
implementation 'com.google.code.gson:gson:2.8.6'
|
implementation 'com.google.code.gson:gson:2.7'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,7 @@ package me.dozen.dpreference;
|
|||||||
|
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.text.TextUtils;
|
|
||||||
|
|
||||||
import java.util.LinkedHashSet;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -47,6 +45,10 @@ public class DPreference {
|
|||||||
PrefAccessor.setInt(mContext, mName, key, value);
|
PrefAccessor.setInt(mContext, mName, key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setPrefStringSet(final String key, final Set<String> value) {
|
||||||
|
PrefAccessor.setStringSet(mContext, mName, key, value);
|
||||||
|
}
|
||||||
|
|
||||||
public int getPrefInt(final String key, final int defaultValue) {
|
public int getPrefInt(final String key, final int defaultValue) {
|
||||||
return PrefAccessor.getInt(mContext, mName, key, defaultValue);
|
return PrefAccessor.getInt(mContext, mName, key, defaultValue);
|
||||||
}
|
}
|
||||||
@@ -59,26 +61,10 @@ public class DPreference {
|
|||||||
return PrefAccessor.getLong(mContext, mName, key, defaultValue);
|
return PrefAccessor.getLong(mContext, mName, key, defaultValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPrefStringSet(final String key, final Set<String> value) {
|
|
||||||
PrefAccessor.setStringSet(mContext, mName, key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Set<String> getPrefStringSet(final String key, final Set<String> defaultValue) {
|
public Set<String> getPrefStringSet(final String key, final Set<String> defaultValue) {
|
||||||
return PrefAccessor.getStringSet(mContext, mName, key, defaultValue);
|
return PrefAccessor.getStringSet(mContext, mName, key, defaultValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPrefStringOrderedSet(final String key, final LinkedHashSet<String> value) {
|
|
||||||
PrefAccessor.setString(mContext, mName, key, StringSetConverter.encode(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
public LinkedHashSet<String> getPrefStringOrderedSet(final String key, final LinkedHashSet<String> defaultValue) {
|
|
||||||
String value = PrefAccessor.getString(mContext, mName, key, "");
|
|
||||||
if (TextUtils.isEmpty(value)) {
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
return StringSetConverter.decode(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removePreference(final String key) {
|
public void removePreference(final String key) {
|
||||||
PrefAccessor.remove(mContext, mName, key);
|
PrefAccessor.remove(mContext, mName, key);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import com.google.gson.Gson;
|
|||||||
import com.google.gson.reflect.TypeToken;
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
import java.util.LinkedHashSet;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
public class StringSetConverter {
|
public class StringSetConverter {
|
||||||
@@ -14,8 +13,8 @@ public class StringSetConverter {
|
|||||||
return gson.toJson(src);
|
return gson.toJson(src);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LinkedHashSet<String> decode(String json) {
|
public static Set<String> decode(String json) {
|
||||||
Type setType = new TypeToken<LinkedHashSet<String>>() {
|
Type setType = new TypeToken<Set<String>>() {
|
||||||
}.getType();
|
}.getType();
|
||||||
return gson.fromJson(json, setType);
|
return gson.fromJson(json, setType);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,16 +6,17 @@
|
|||||||
# Specifies the JVM arguments used for the daemon process.
|
# Specifies the JVM arguments used for the daemon process.
|
||||||
# The setting is particularly useful for tweaking memory settings.
|
# The setting is particularly useful for tweaking memory settings.
|
||||||
# Default value: -Xmx1024m -XX:MaxPermSize=256m
|
# Default value: -Xmx1024m -XX:MaxPermSize=256m
|
||||||
org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
|
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
|
||||||
#
|
#
|
||||||
# When configured, Gradle will run in incubating parallel mode.
|
# When configured, Gradle will run in incubating parallel mode.
|
||||||
# This option should only be used with decoupled projects. More details, visit
|
# This option should only be used with decoupled projects. More details, visit
|
||||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||||
# org.gradle.parallel=true
|
# org.gradle.parallel=true
|
||||||
#Fri Jun 02 14:08:42 CST 2017
|
#Fri Jun 02 14:08:42 CST 2017
|
||||||
kotlinVersion=1.4.10
|
ankoVersion=0.10.8
|
||||||
|
kotlinVersion=1.3.40
|
||||||
supportLibVersion=28.0.0
|
supportLibVersion=28.0.0
|
||||||
buildToolsVer=30.0.2
|
buildToolsVer=28.0.3
|
||||||
compileSdkVer=30
|
compileSdkVer=28
|
||||||
kotlin.incremental=true
|
kotlin.incremental=true
|
||||||
targetSdkVer=30
|
targetSdkVer=28
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
#Tue Feb 25 12:40:41 CST 2020
|
|
||||||
distributionBase=GRADLE_USER_HOME
|
|
||||||
distributionPath=wrapper/dists
|
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
|
||||||
zipStorePath=wrapper/dists
|
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5.1-all.zip
|
|
||||||
Reference in New Issue
Block a user