Compare commits

..

24 Commits

Author SHA1 Message Date
2dust f25aaf43d7 Update V2RayVpnService.kt 2019-09-24 13:12:39 +08:00
2dust 3b365533fc Create libv2ray.aar 2019-09-02 08:48:17 +08:00
2dust a982327030 Update .gitignore 2019-09-02 08:48:11 +08:00
2dust 6ce12b2ea5 Update AndroidManifest.xml 2019-09-02 08:47:01 +08:00
2dust d7f3ed5182 Update BaseDrawerActivity.kt 2019-09-02 08:46:58 +08:00
2dust f519103b3e Update MainActivity.kt 2019-09-02 08:46:54 +08:00
2dust d7c40ee883 Update SettingsActivity.kt 2019-09-02 08:46:50 +08:00
2dust 809d50689b Update gradle.properties 2019-09-02 08:46:36 +08:00
2dust 042def7e83 Update V2RayVpnService.kt 2019-08-27 15:14:22 +08:00
2dust 8bb0d6b175 Create Readme.md 2019-08-22 11:26:07 +08:00
2dust 3ae76fab72 add lib 2019-08-18 18:15:37 +08:00
2dust 46a63e8704 Create readme.txt 2019-08-18 18:14:52 +08:00
2dust 12f8b1584c Create issue_template.md 2019-08-14 07:55:48 +08:00
2dust 2b571e2a94 Merge pull request #44 from cutelua/dev
improved notification
2019-07-30 14:47:38 +08:00
cutelua ba30697309 no need for USER_PRESENT 2019-07-11 09:19:30 +08:00
2dust 879e25bb82 sdk28 2019-07-10 14:24:09 +08:00
cutelua 17c858d24c only show speed if core isrunning 2019-07-09 15:58:36 +08:00
cutelua 71a6531835 stop speed query when screen off; resume when screen on 2019-07-09 14:32:47 +08:00
cutelua 5d760382d7 pass callback as V2RayPoint arg; stop on LowMemory 2019-07-09 13:06:28 +08:00
cutelua 4dda36673a allow go to shutdown VpnService 2019-07-09 11:43:31 +08:00
cutelua 5aca55cb8f only use title to show speed 2019-07-07 16:42:22 +08:00
cutelua 82f14f0d07 show total speed as notification title 2019-07-07 16:42:22 +08:00
2dust c0e968ac34 up ver 2019-07-01 13:22:48 +08:00
2dust 29d3fb710b ii 2019-05-15 10:17:59 +08:00
90 changed files with 1553 additions and 2197 deletions
-5
View File
@@ -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>
通过`adb logcat -s com.v2ray.ang GoLog V2rayConfigUtilGoLog Main`获取日志。请自行删减日志中可能出现的敏感信息。
通过 `adb logcat -s com.v2ray.ang GoLog V2rayConfigUtilGoLog Main` 获取日志。请自行删减日志中可能出现的敏感信息。
如果问题可重现,建议先执行`adb logcat -c`清空系统日志再执行上述命令,再操作重现问题。
```
+5 -3
View File
@@ -2,8 +2,10 @@ V2rayNG/app/src/main/res/layout/activity_inapp_buy.xml
V2rayNG/app/src/main/assets/geoip.dat
V2rayNG/app/src/main/assets/geosite.dat
V2rayNG/app/src/main/java/com/v2ray/ang/InappBuyActivity.java
V2rayNG/gradle/wrapper/gradle-wrapper.properties
V2rayNG/gradle/wrapper/gradle-wrapper.properties
*.dat
*.jks
V2rayNG/app/release/output.json
.idea/
.gradle/
V2rayNG/gradle/wrapper/gradle-wrapper.properties
V2rayNG/gradle/wrapper/gradle-wrapper.properties
V2rayNG/app/release/output.json
+4 -6
View File
@@ -5,16 +5,14 @@ import (
)
type Status struct {
IsRunning bool
IsTRunning bool
PackageName string
PackageCodePath string
IsRunning bool
PackageName string
Vpoint v2core.Server
}
func CheckVersion() int {
return 22
return 20
}
func (v *Status) GetDataDir() string {
@@ -22,7 +20,7 @@ func (v *Status) GetDataDir() string {
}
func (v *Status) GetApp(name string) string {
return v.PackageCodePath + name
return v.PackageName + name
}
func (v *Status) GetTun2socksArgs(localDNS bool, enableIPv6 bool) (ret []string) {
+4 -1
View File
@@ -8,6 +8,9 @@ asset:
# cd assets;curl https://raw.githubusercontent.com/2dust/AndroidLibV2rayLite/master/data/geosite.dat > geosite.dat
# cd assets;curl https://raw.githubusercontent.com/2dust/AndroidLibV2rayLite/master/data/geoip.dat > geoip.dat
shippedBinary:
cd shippedBinarys; $(MAKE) shippedBinary
fetchDep:
-go get github.com/2dust/AndroidLibV2rayLite
go get github.com/2dust/AndroidLibV2rayLite
@@ -27,5 +30,5 @@ downloadGoMobile:
BuildMobile:
@echo Stub
all: asset pb fetchDep
all: asset pb shippedBinary fetchDep
@echo DONE
+9 -1
View File
@@ -3,13 +3,14 @@ package Escort
import (
"os"
"os/exec"
"time"
"log"
"github.com/2dust/AndroidLibV2rayLite/CoreI"
)
func (v *Escorting) EscortRun(proc string, pt []string, additionalEnv string) {
func (v *Escorting) EscortRun(proc string, pt []string, additionalEnv string, sendFd func() int) {
log.Println(proc, pt)
count := 0
for count <= 42 {
@@ -36,6 +37,13 @@ func (v *Escorting) EscortRun(proc string, pt []string, additionalEnv string) {
*v.escortProcess = append(*v.escortProcess, cmd.Process)
log.Println("EscortRun Waiting....")
if count > 0 {
go func() {
time.Sleep(time.Second)
sendFd()
}()
}
if err := cmd.Wait(); err != nil {
log.Println("EscortRun cmd.Wait err:", err)
}
+9 -5
View File
@@ -34,10 +34,14 @@ $NDK_HOME/ndk-build \
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
install -v -m755 libs/armeabi-v7a/tun2socks $__dir/shippedBinarys/ArchDep/arm/
install -v -m755 libs/arm64-v8a/tun2socks $__dir/shippedBinarys/ArchDep/arm64/
install -v -m755 libs/x86/tun2socks $__dir/shippedBinarys/ArchDep/386/
install -v -m755 libs/x86_64/tun2socks $__dir/shippedBinarys/ArchDep/amd64/
popd
rm -rf $TMPDIR
pushd $__dir/shippedBinarys
make clean && make shippedBinary
popd
rm -rf $TMPDIR
+28 -32
View File
@@ -11,12 +11,14 @@ import (
"github.com/2dust/AndroidLibV2rayLite/CoreI"
"github.com/2dust/AndroidLibV2rayLite/Process/Escort"
"github.com/2dust/AndroidLibV2rayLite/VPN"
"github.com/2dust/AndroidLibV2rayLite/shippedBinarys"
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"
@@ -42,12 +44,10 @@ type V2RayPoint struct {
closeChan chan struct{}
PackageName string
PackageCodePath string
DomainName string
ConfigureFileContent string
EnableLocalDNS bool
ForwardIpv6 bool
ProxyOnly bool
}
/*V2RayVPNServiceSupportsSet To support Android VPN mode*/
@@ -57,6 +57,7 @@ type V2RayVPNServiceSupportsSet interface {
Shutdown() int
Protect(int) int
OnEmitStatus(int, string) int
SendFd() int
}
/*RunLoop Run V2Ray main loop
@@ -66,7 +67,6 @@ func (v *V2RayPoint) RunLoop() (err error) {
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{})
@@ -80,6 +80,7 @@ func (v *V2RayPoint) RunLoop() (err error) {
if !v.dialer.IsVServerReady() {
log.Println("vServer cannot resolved, shutdown")
v.StopLoop()
v.SupportSet.Shutdown()
}
// stop waiting if manually closed
@@ -98,7 +99,9 @@ func (v *V2RayPoint) StopLoop() (err error) {
v.v2rayOP.Lock()
defer v.v2rayOP.Unlock()
if v.status.IsRunning {
close(v.closeChan)
v.shutdownInit()
v.SupportSet.OnEmitStatus(0, "Closed")
}
return
}
@@ -108,16 +111,12 @@ 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))
counter := v.statsManager.GetCounter(fmt.Sprintf("inbound>>>%s>>>traffic>>>%s", tag, direct))
if counter == nil {
return 0
}
@@ -125,19 +124,24 @@ func (v V2RayPoint) QueryStats(tag string, direct string) int64 {
}
func (v *V2RayPoint) shutdownInit() {
close(v.closeChan)
v.statsManager = nil
v.status.IsRunning = false
v.status.Vpoint.Close()
v.status.Vpoint = nil
v.status.IsRunning = false
v.statsManager = nil
v.escorter.EscortingDown()
v.SupportSet.Shutdown()
v.SupportSet.OnEmitStatus(0, "Closed")
}
func (v *V2RayPoint) pointloop() error {
if err := v.runTun2socks(); err != nil {
log.Println(err)
return err
}
log.Printf("EnableLocalDNS: %v\nForwardIpv6: %v\nDomainName: %s",
v.EnableLocalDNS,
v.ForwardIpv6,
v.DomainName)
log.Println("loading v2ray config")
config, err := v2serial.LoadJSONConfig(strings.NewReader(v.ConfigureFileContent))
if err != nil {
@@ -165,21 +169,6 @@ func (v *V2RayPoint) pointloop() error {
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
}
@@ -236,10 +225,17 @@ func NewV2RayPoint(s V2RayVPNServiceSupportsSet) *V2RayPoint {
}
func (v V2RayPoint) runTun2socks() error {
shipb := shippedBinarys.FirstRun{Status: v.status}
if err := shipb.CheckAndExport(); err != nil {
log.Println(err)
return err
}
v.escorter.EscortingUp()
go v.escorter.EscortRun(
v.status.GetApp("libtun2socks.so"),
v.status.GetTun2socksArgs(v.EnableLocalDNS, v.ForwardIpv6), "")
v.status.GetApp("tun2socks"),
v.status.GetTun2socksArgs(v.EnableLocalDNS, v.ForwardIpv6), "",
v.SupportSet.SendFd)
return nil
}
-58
View File
@@ -1,58 +0,0 @@
package libv2ray
import (
// The following are necessary as they register handlers in their init functions.
// Required features. Can't remove unless there is replacements.
_ "v2ray.com/core/app/dispatcher"
_ "v2ray.com/core/app/proxyman/inbound"
_ "v2ray.com/core/app/proxyman/outbound"
// Other optional features.
_ "v2ray.com/core/app/dns"
_ "v2ray.com/core/app/log"
_ "v2ray.com/core/app/policy"
_ "v2ray.com/core/app/router"
_ "v2ray.com/core/app/stats"
// Inbound and outbound proxies.
_ "v2ray.com/core/proxy/blackhole"
_ "v2ray.com/core/proxy/dns"
_ "v2ray.com/core/proxy/dokodemo"
_ "v2ray.com/core/proxy/freedom"
_ "v2ray.com/core/proxy/http"
_ "v2ray.com/core/proxy/mtproto"
_ "v2ray.com/core/proxy/shadowsocks"
_ "v2ray.com/core/proxy/socks"
_ "v2ray.com/core/proxy/trojan"
_ "v2ray.com/core/proxy/vless/inbound"
_ "v2ray.com/core/proxy/vless/outbound"
_ "v2ray.com/core/proxy/vmess/inbound"
_ "v2ray.com/core/proxy/vmess/outbound"
// Transport
_ "v2ray.com/core/transport/internet/http"
_ "v2ray.com/core/transport/internet/kcp"
_ "v2ray.com/core/transport/internet/quic"
_ "v2ray.com/core/transport/internet/tcp"
_ "v2ray.com/core/transport/internet/tls"
_ "v2ray.com/core/transport/internet/udp"
_ "v2ray.com/core/transport/internet/websocket"
// Transport headers
_ "v2ray.com/core/transport/internet/headers/http"
_ "v2ray.com/core/transport/internet/headers/noop"
_ "v2ray.com/core/transport/internet/headers/srtp"
_ "v2ray.com/core/transport/internet/headers/tls"
_ "v2ray.com/core/transport/internet/headers/utp"
_ "v2ray.com/core/transport/internet/headers/wechat"
_ "v2ray.com/core/transport/internet/headers/wireguard"
// JSON config support. Choose only one from the two below.
// The following line loads JSON from v2ctl
// _ "v2ray.com/core/main/json"
// The following line loads JSON internally
_ "v2ray.com/core/main/jsonem"
// Load config from file or http(s)
// _ "v2ray.com/core/main/confloader/external"
)
@@ -0,0 +1,13 @@
Platdep=shippedBinary.386 shippedBinary.amd64 shippedBinary.arm64 shippedBinary.arm
shippedBinaryDep:
go get -u github.com/jteeuwen/go-bindata/...
shippedBinary.%:
go-bindata -nometadata -nomemcopy -pkg shippedBinarys -o ./binary_$*.go -tags $* ArchIndep/ ArchDep/$*/
shippedBinary:shippedBinaryDep $(Platdep)
@echo "Done"
clean:
-rm binary*
@@ -0,0 +1,69 @@
package shippedBinarys
import (
"log"
"os"
"strconv"
"github.com/2dust/AndroidLibV2rayLite/CoreI"
)
type FirstRun struct {
Status *CoreI.Status
}
func (v *FirstRun) checkIfRcExist() error {
datadir := v.Status.GetDataDir()
if _, err := os.Stat(datadir + strconv.Itoa(CoreI.CheckVersion())); !os.IsNotExist(err) {
log.Println("file exists")
return nil
}
IndepDir, err := AssetDir("ArchIndep")
log.Println(IndepDir)
if err != nil {
return err
}
for _, fn := range IndepDir {
log.Println(datadir+"ArchIndep/"+fn)
err := RestoreAsset(datadir, "ArchIndep/"+fn)
log.Println(err)
//GrantPremission
os.Chmod(datadir+"ArchIndep/"+fn, 0700)
log.Println(os.Remove(datadir + fn))
log.Println(os.Symlink(datadir+"ArchIndep/"+fn, datadir + fn))
}
DepDir, err := AssetDir("ArchDep")
log.Println(DepDir)
if err != nil {
return err
}
for _, fn := range DepDir {
DepDir2, err := AssetDir("ArchDep/" + fn)
log.Println("ArchDep/" + fn)
if err != nil {
return err
}
for _, FND := range DepDir2 {
log.Println(datadir+"ArchDep/"+fn+"/"+FND)
RestoreAsset(datadir, "ArchDep/"+fn+"/"+FND)
os.Chmod(datadir+"ArchDep/"+fn+"/"+FND, 0700)
log.Println(os.Remove(datadir + FND))
log.Println(os.Symlink(datadir+"ArchDep/"+fn+"/"+FND, datadir+FND))
}
}
s, _ := os.Create(datadir + strconv.Itoa(CoreI.CheckVersion()))
s.Close()
return nil
}
func (v *FirstRun) CheckAndExport() error {
return v.checkIfRcExist()
}
-28
View File
@@ -1,34 +1,6 @@
# v2rayNG
A V2Ray client for Android
[![API](https://img.shields.io/badge/API-17%2B-yellow.svg?style=flat)](https://developer.android.com/about/versions/jelly-bean#android-4.2)
[![Kotlin Version](https://img.shields.io/badge/Kotlin-1.4.10-blue.svg)](https://kotlinlang.org)
[![GitHub commit activity](https://img.shields.io/github/commit-activity/m/2dust/v2rayNG)](https://github.com/2dust/v2rayNG/commits/master)
[![CodeFactor](https://www.codefactor.io/repository/github/2dust/v2rayng/badge)](https://www.codefactor.io/repository/github/2dust/v2rayng)
[![GitHub Releases](https://img.shields.io/github/downloads/2dust/v2rayNG/latest/total?logo=github)](https://github.com/2dust/v2rayNG/releases)
<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" />
</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
View File
@@ -3,8 +3,8 @@ apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion Integer.parseInt("$compileSdkVer")
buildToolsVersion buildToolsVer
compileSdkVersion 28
buildToolsVersion '28.0.3'
compileOptions {
targetCompatibility = "8"
@@ -20,17 +20,34 @@ android {
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 {
release {
minifyEnabled false
zipAlignEnabled false
shrinkResources false
signingConfig signingConfigs.release
// proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
debug {
minifyEnabled false
zipAlignEnabled false
shrinkResources false
signingConfig signingConfigs.release
}
}
@@ -38,10 +55,6 @@ android {
main.java.srcDirs += 'src/main/kotlin'
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8
}
splits {
abi {
enable true
@@ -66,18 +79,11 @@ android {
dependencies {
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 "org.jetbrains.kotlin:kotlin-stdlib:$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
implementation "com.android.support:support-v4:$supportLibVersion"
implementation "com.android.support:appcompat-v7:$supportLibVersion"
@@ -85,17 +91,22 @@ dependencies {
implementation "com.android.support:cardview-v7:$supportLibVersion"
implementation "com.android.support:preference-v7:$supportLibVersion"
implementation "com.android.support:recyclerview-v7:$supportLibVersion"
implementation "com.android.support:multidex:1.0.3"
implementation 'com.android.support.constraint:constraint-layout:2.0.1'
// DSL
implementation 'com.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:rxandroid:1.2.1'
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:zxing:1.9.8'
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: 'tun2socks', ext: 'aar')
@@ -115,4 +126,4 @@ repositories {
flatDir {
dirs 'libs'
}
}
}
Binary file not shown.
+3
View File
@@ -44,6 +44,9 @@
static void throwUninitializedPropertyAccessException(java.lang.String);
}
-dontwarn org.jetbrains.anko.internals.**
-keep class org.jetbrains.anko.internals.** { *;}
-dontwarn rx.internal.util.unsafe.**
-keep class rx.internal.util.unsafe.** { *;}
+20 -47
View File
@@ -1,23 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
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.CHANGE_NETWORK_STATE"/>
<uses-permission android:name="android.permission.INTERNET" />
@@ -27,14 +11,12 @@
<uses-permission android:name="com.android.vending.BILLING" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<!-- <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> -->
<application
android:name=".AngApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:extractNativeLibs="true"
android:theme="@style/AppTheme">
<activity
android:name=".ui.MainActivity"
@@ -42,16 +24,20 @@
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
<intent-filter>
<action android:name="android.service.quicksettings.action.QS_TILE_PREFERENCES" />
</intent-filter>
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/shortcuts" />
@@ -80,11 +66,7 @@
<activity android:name=".ui.SubEditActivity" />
<activity android:name=".ui.ScScannerActivity" />
<activity
android:name=".ui.ScSwitchActivity"
android:excludeFromRecents="true"
android:process=":RunSoLibV2RayDaemon"
android:theme="@style/AppTheme.NoActionBar.Translucent" />
<activity android:name=".ui.ScSwitchActivity" />
<service
android:name=".service.V2RayVpnService"
@@ -101,30 +83,22 @@
android:value="true" />
</service>
<service android:name=".service.V2RayProxyOnlyService"
android:exported="false"
android:label="@string/app_name"
android:process=":RunSoLibV2RayDaemon">
</service>
<!--<receiver android:name=".receiver.WidgetProvider">-->
<!--<meta-data-->
<!--android:name="android.appwidget.provider"-->
<!--android:resource="@xml/app_widget_provider" />-->
<receiver android:name=".receiver.WidgetProvider"
android:process=":RunSoLibV2RayDaemon">
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/app_widget_provider" />
<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>
<!--<intent-filter>-->
<!--<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />-->
<!--<action android:name="com.v2ray.ang.action.widget.click" />-->
<!--</intent-filter>-->
<!--</receiver>-->
<service
android:name=".service.QSTileService"
android:icon="@drawable/ic_v"
android:label="@string/app_tile_name"
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE"
android:process=":RunSoLibV2RayDaemon">
android:name=".service.QSTileService"
android:icon="@drawable/ic_v"
android:label="@string/app_tile_name"
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
<intent-filter>
<action android:name="android.service.quicksettings.action.QS_TILE" />
</intent-filter>
@@ -139,8 +113,7 @@
</intent-filter>
</activity>
<receiver android:name=".receiver.TaskerReceiver"
android:process=":RunSoLibV2RayDaemon">
<receiver android:name=".receiver.TaskerReceiver">
<intent-filter>
<action android:name="com.twofortyfouram.locale.intent.action.FIRE_SETTING" />
</intent-filter>
@@ -149,4 +122,4 @@
</application>
</manifest>
</manifest>
+136 -181
View File
@@ -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.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.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.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.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.keep
com.google.android.music
com.google.android.ogyoutube
com.google.android.partnersetup
com.google.android.play.games
com.google.android.street
com.google.android.syncadapters.calendar
com.google.android.syncadapters.contacts
com.google.android.talk
com.google.android.tts
com.google.android.videos
com.google.android.youtube
com.google.ar.lens
com.hochan.coldsoup
com.ifttt.ifttt
com.imgur.mobile
com.innologica.inoreader
com.instagram.android
com.instapaper.android
com.jarvanh.vpntether
com.kapp.youtube.final
com.klinker.android.twitter_l
com.lastpass.lpandroid
com.linecorp.linelite
com.lingodeer
com.mediapods.tumbpods
com.mgoogle.android.gms
com.microsoft.emmx
com.microsoft.office.powerpoint
com.microsoft.skydrive
com.mixplorer
com.msd.consumerchinese
com.msd.professionalchinese
com.mss2011c.sharehelper
com.netflix.mediaclient
com.newin.nplayer.pro
com.nianticlabs.ingress.prime.qa
com.nianticproject.ingress
com.ninefolders.hd3
com.ninegag.android.app
com.nintendo.zara
com.nytimes.cn
com.oasisfeng.island
com.ocnt.liveapp.hw
com.orekie.search
com.patreon.android
com.paypal.android.p2pmobile
com.perol.asdpl.pixivez
com.pinterest
com.popularapp.periodcalendar
com.popularapp.videodownloaderforinstagram
com.pushbullet.android
com.quoord.tapatalkpro.activity
com.quora.android
com.rayark.cytus2
com.rayark.implosion
com.rayark.pluto
com.reddit.frontpage
com.resilio.sync
com.rhmsoft.edit
com.rubenmayayo.reddit
com.sec.android.app.sbrowser
com.sec.android.app.sbrowser.beta
com.shanga.walli
com.simplehabit.simplehabitapp
com.slack
com.snaptube.premium
com.sololearn
com.sonelli.juicessh
com.spotify.music
com.tencent.huatuo
com.termux
com.teslacoilsw.launcher
com.theinitium.news
com.thomsonreuters.reuters
com.thunkable.android.hritvik00.freenom
com.topjohnwu.magisk
com.tripadvisor.tripadvisor
com.tumblr
com.twitter.android
com.u91porn
com.u9porn
com.ubisoft.dance.justdance2015companion
com.utopia.pxview
com.valvesoftware.android.steam.communimunity
com.valvesoftware.android.steam.community
com.vanced.android.youtube
com.vimeo.android.videoapp
com.vivaldi.browser
com.vivaldi.browser.snapshot
com.vkontakte.android
com.whatsapp
com.wire
com.wuxiangai.refactor
com.xda.labs
com.xvideos.app
com.yandex.browser
com.yandex.browser.beta
com.yandex.browser.alpha
com.z28j.feel
con.medium.reader
de.apkgrabber
de.robv.android.xposed.installer
dk.tacit.android.foldersync.full
es.rafalense.telegram.themes
es.rafalense.themes
flipboard.app
fm.moon.app
fr.gouv.etalab.mastodon
github.tornaco.xposedmoduletest
idm.internet.download.manager
idm.internet.download.manager.plus
io.github.javiewer
io.github.skyhacker2.magnetsearch
io.va.exposed
it.mvilla.android.fenix2
jp.bokete.app.android
jp.naver.line.android
jp.pxv.android
luo.speedometergpspro
mark.via.gp
me.tshine.easymark
net.teeha.android.url_shortener
net.tsapps.appsales
onion.fire
org.fdroid.fdroid
org.freedownloadmanager.fdm
org.kustom.widget
org.mozilla.fennec_aurora
org.mozilla.fenix
org.mozilla.fenix.nightly
org.mozilla.firefox
org.mozilla.firefox_beta
org.mozilla.focus
org.schabi.newpipe
org.telegram.messenger
org.telegram.multi
org.telegram.plus
org.thunderdog.challegram
org.torproject.android
org.torproject.torbrowser_alpha
org.wikipedia
org.xbmc.kodi
pl.zdunex25.updater
tv.twitch.android.app
tw.com.gamer.android.activecenter
videodownloader.downloadvideo.downloader
uk.co.bbc.learningenglish
com.ted.android
com.quora.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": {
"statsOutboundUplink": true,
"statsOutboundDownlink": true
"statsInboundUplink": true,
"statsInboundDownlink": true
}
},
"inbounds": [{
@@ -43,8 +43,6 @@ public interface ItemTouchHelperAdapter {
boolean onItemMove(int fromPosition, int toPosition);
void onItemMoveCompleted();
/**
* Called when an item has been dismissed by a swipe.<br/>
* <br/>
@@ -112,8 +112,6 @@ public class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback {
public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
super.clearView(recyclerView, viewHolder);
mAdapter.onItemMoveCompleted();
viewHolder.itemView.setAlpha(ALPHA_FULL);
if (viewHolder instanceof ItemTouchHelperViewHolder) {
@@ -1,16 +1,16 @@
package com.v2ray.ang
import android.support.multidex.MultiDexApplication
import android.support.v7.preference.PreferenceManager
import android.app.Application
//import com.squareup.leakcanary.LeakCanary
import com.v2ray.ang.util.AngConfigManager
import me.dozen.dpreference.DPreference
import org.jetbrains.anko.defaultSharedPreferences
class AngApplication : MultiDexApplication() {
class AngApplication : Application() {
companion object {
const val PREF_LAST_VERSION = "pref_last_version"
}
var curIndex = -1 //Current proxy that is opened. (Used to implement restart feature)
var firstRun = false
private set
@@ -21,7 +21,6 @@ class AngApplication : MultiDexApplication() {
// LeakCanary.install(this)
val defaultSharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
firstRun = defaultSharedPreferences.getInt(PREF_LAST_VERSION, 0) != BuildConfig.VERSION_CODE
if (firstRun)
defaultSharedPreferences.edit().putInt(PREF_LAST_VERSION, BuildConfig.VERSION_CODE).apply()
@@ -11,10 +11,7 @@ object AppConfig {
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_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_MODE = "pref_mode"
const val VMESS_PROTOCOL: String = "vmess://"
const val SS_PROTOCOL: String = "ss://"
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 v2rayCustomRoutingListUrl = "https://raw.githubusercontent.com/2dust/v2rayCustomRoutingList/master/"
const val v2rayNGIssues = "https://github.com/2dust/v2rayNG/issues"
const val v2rayNGWikiMode = "https://github.com/2dust/v2rayNG/wiki/Mode"
const val promotionUrl = "https://1.2345345.xyz/ads.html"
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_SUCCESS = 41
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,
var security: String,
var tcpSettings: TcpSettingsBean?,
var kcpSettings: KcpSettingsBean?,
var wsSettings: WsSettingsBean?,
var httpSettings: HttpSettingsBean?,
var tlsSettings: TlsSettingsBean?,
var quicSettings: QuicSettingBean?
var tcpSettings: TcpsettingsBean?,
var kcpsettings: KcpsettingsBean?,
var wssettings: WssettingsBean?,
var httpsettings: HttpsettingsBean?,
var tlssettings: TlssettingsBean?,
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",
var request: 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 uplinkCapacity: Int = 12,
var downlinkCapacity: Int = 100,
@@ -89,43 +90,25 @@ data class V2rayConfig(
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()) {
data class HeadersBean(var Host: String = "")
}
data class HttpSettingsBean(var host: List<String> = ArrayList(),
var path: String = "")
data class HttpsettingsBean(var host: List<String> = ArrayList<String>(), var path: String = "")
data class TlsSettingsBean(var allowInsecure: Boolean = true,
data class TlssettingsBean(var allowInsecure: Boolean = true,
var serverName: String = "")
data class QuicSettingBean(var security: String = "none",
var key: String = "",
var header: HeaderBean = HeaderBean()) {
data class QuicsettingBean(var security: String = "none",
var key: String = "",
var header: HeaderBean = HeaderBean()) {
data class HeaderBean(var type: String = "none")
}
}
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>)
@@ -2,7 +2,6 @@ package com.v2ray.ang.extension
import android.content.Context
import android.os.Build
import android.widget.Toast
import com.v2ray.ang.AngApplication
import me.dozen.dpreference.DPreference
import org.json.JSONObject
@@ -15,24 +14,9 @@ import java.net.URLConnection
val Context.v2RayApplication: 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
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(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.toTrafficString(): String {
if (this == 0L)
return "\t\t\t0\t B"
if (this < threshold)
return "${this.toFloat().toShortString()}\t B"
return "$this B"
val kib = this / divisor
if (kib < threshold)
return "${kib.toShortString()}\t KB"
return "${kib.toShortString()} KB"
val mib = kib / divisor
if (mib < threshold)
return "${mib.toShortString()}\t MB"
return "${mib.toShortString()} MB"
val gib = mib / divisor
if (gib < threshold)
return "${gib.toShortString()}\t GB"
return "${gib.toShortString()} GB"
val tib = gib / divisor
if (tib < threshold)
return "${tib.toShortString()}\t TB"
return "${tib.toShortString()} TB"
val pib = tib / divisor
if (pib < threshold)
return "${pib.toShortString()}\t PB"
return "${pib.toShortString()} PB"
return ""
}
private fun Float.toShortString(): String {
val s = "%.2f".format(this)
val s = toString()
if (s.length <= 4)
return s
return s.substring(0, 4).removeSuffix(".")
@@ -21,7 +21,7 @@ class TaskerReceiver : BroadcastReceiver() {
return
} else if (switch) {
if (guid == AppConfig.TASKER_DEFAULT_GUID) {
Utils.startVServiceFromToggle(context)
Utils.startVService(context)
} else {
Utils.startVService(context, guid)
}
@@ -3,14 +3,14 @@ package com.v2ray.ang.receiver
import android.app.PendingIntent
import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProvider
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.widget.RemoteViews
import com.v2ray.ang.R
import com.v2ray.ang.AppConfig
import com.v2ray.ang.service.V2RayServiceManager
import com.v2ray.ang.util.Utils
import org.jetbrains.anko.toast
class WidgetProvider : AppWidgetProvider() {
/**
@@ -18,20 +18,11 @@ class WidgetProvider : AppWidgetProvider() {
*/
override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
super.onUpdate(context, appWidgetManager, appWidgetIds)
updateWidgetBackground(context, appWidgetManager, appWidgetIds, V2RayServiceManager.v2rayPoint.isRunning)
}
private fun updateWidgetBackground(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray, isRunning: Boolean) {
val remoteViews = RemoteViews(context.packageName, R.layout.widget_switch)
val intent = Intent(context, WidgetProvider::class.java)
intent.action = AppConfig.BROADCAST_ACTION_WIDGET_CLICK
val intent = Intent(AppConfig.BROADCAST_ACTION_WIDGET_CLICK)
val pendingIntent = PendingIntent.getBroadcast(context, R.id.layout_switch, intent, PendingIntent.FLAG_UPDATE_CURRENT)
remoteViews.setOnClickPendingIntent(R.id.layout_switch, pendingIntent)
if (isRunning) {
remoteViews.setInt(R.id.layout_switch, "setBackgroundResource", R.drawable.ic_rounded_corner_theme)
} else {
remoteViews.setInt(R.id.layout_switch, "setBackgroundResource", R.drawable.ic_rounded_corner_grey)
}
for (appWidgetId in appWidgetIds) {
appWidgetManager.updateAppWidget(appWidgetId, remoteViews)
@@ -39,28 +30,21 @@ class WidgetProvider : AppWidgetProvider() {
}
/**
* 接收窗口小部件发送的广播
* 接收窗口小部件点击时发送的广播
*/
override fun onReceive(context: Context, intent: Intent) {
super.onReceive(context, intent)
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)
} else {
Utils.startVServiceFromToggle(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)
}
// context.toast(R.string.toast_services_start)
Utils.startVService(context)
}
}
}
}
@@ -6,15 +6,19 @@ import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.graphics.drawable.Icon
import android.net.VpnService
import android.os.Build
import android.service.quicksettings.Tile
import android.service.quicksettings.TileService
import com.v2ray.ang.AppConfig
import com.v2ray.ang.R
import com.v2ray.ang.extension.defaultDPreference
import com.v2ray.ang.util.MessageUtil
import com.v2ray.ang.util.Utils
import org.jetbrains.anko.toast
import java.lang.ref.SoftReference
@TargetApi(Build.VERSION_CODES.N)
class QSTileService : TileService() {
@@ -25,10 +29,11 @@ class QSTileService : TileService() {
qsTile?.icon = Icon.createWithResource(applicationContext, R.drawable.ic_v_idle)
} else if (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?.updateTile()
}
@@ -51,7 +56,11 @@ class QSTileService : TileService() {
super.onClick()
when (qsTile.state) {
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 -> {
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
import android.app.*
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.PackageManager
import android.graphics.Color
import android.net.*
import android.os.Build
import android.os.ParcelFileDescriptor
import android.os.StrictMode
import android.net.VpnService
import android.os.*
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.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.SettingsActivity
import com.v2ray.ang.util.MessageUtil
import com.v2ray.ang.util.Utils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import libv2ray.Libv2ray
import libv2ray.V2RayVPNServiceSupportsSet
import rx.Observable
import rx.Subscription
import java.net.InetAddress
import java.io.IOException
import java.io.File
import java.io.FileDescriptor
import java.io.FileInputStream
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
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
@@ -33,38 +74,36 @@ class V2RayVpnService : VpnService(), ServiceControl {
*
* Source: https://android.googlesource.com/platform/frameworks/base/+/2df4c7d/services/core/java/com/android/server/ConnectivityService.java#887
*/
@delegate:RequiresApi(Build.VERSION_CODES.P)
private val defaultNetworkRequest by lazy {
NetworkRequest.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
.build()
}
@TargetApi(28)
private val defaultNetworkRequest = NetworkRequest.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
.build()
private val connectivity by lazy { getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager }
@delegate:RequiresApi(Build.VERSION_CODES.P)
private val defaultNetworkCallback by lazy {
object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
setUnderlyingNetworks(arrayOf(network))
}
override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
// it's a good idea to refresh capabilities
setUnderlyingNetworks(arrayOf(network))
}
override fun onLost(network: Network) {
setUnderlyingNetworks(null)
}
@TargetApi(28)
private val defaultNetworkCallback = object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
setUnderlyingNetworks(arrayOf(network))
}
override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities?) {
// it's a good idea to refresh capabilities
setUnderlyingNetworks(arrayOf(network))
}
override fun onLost(network: Network) {
setUnderlyingNetworks(null)
}
}
private var listeningForDefaultNetwork = false
override fun onCreate() {
super.onCreate()
val policy = StrictMode.ThreadPolicy.Builder().permitAll().build()
StrictMode.setThreadPolicy(policy)
V2RayServiceManager.serviceControl = SoftReference(this)
v2rayPoint.packageName = Utils.packagePath(applicationContext)
Seq.setContext(applicationContext)
}
override fun onRevoke() {
@@ -78,12 +117,12 @@ class V2RayVpnService : VpnService(), ServiceControl {
override fun 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) {
return
}
@@ -92,7 +131,6 @@ class V2RayVpnService : VpnService(), ServiceControl {
// Configure a builder while parsing the parameters.
val builder = Builder()
val enableLocalDns = defaultDPreference.getPrefBoolean(SettingsActivity.PREF_LOCAL_DNS_ENABLED, false)
val routingMode = defaultDPreference.getPrefString(SettingsActivity.PREF_ROUTING_MODE, "0")
parameters.split(" ")
.map { it.split(",") }
@@ -101,20 +139,7 @@ class V2RayVpnService : VpnService(), ServiceControl {
'm' -> builder.setMtu(java.lang.Short.parseShort(it[1]).toInt())
's' -> builder.addSearchDomain(it[1])
'a' -> builder.addAddress(it[1], Integer.parseInt(it[2]))
'r' -> {
if (routingMode == "1" || routingMode == "3") {
if (it[1] == "::") { //not very elegant, should move Vpn setting in Kotlin, simplify go code
builder.addRoute("2000::", 3)
} else {
resources.getStringArray(R.array.bypass_private_ip_address).forEach { cidr ->
val addr = cidr.split('/')
builder.addRoute(addr[0], addr[1].toInt())
}
}
} else {
builder.addRoute(it[1], Integer.parseInt(it[2]))
}
}
'r' -> builder.addRoute(it[1], Integer.parseInt(it[2]))
'd' -> builder.addDnsServer(it[1])
}
}
@@ -126,7 +151,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 &&
defaultDPreference.getPrefBoolean(SettingsActivity.PREF_PER_APP_PROXY, false)) {
@@ -148,42 +173,33 @@ class V2RayVpnService : VpnService(), ServiceControl {
try {
mInterface.close()
} catch (ignored: Exception) {
// ignored
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
try {
connectivity.requestNetwork(defaultNetworkRequest, defaultNetworkCallback)
} catch (e: Exception) {
e.printStackTrace()
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
builder.setMetered(false)
if (Build.VERSION.SDK_INT >= 28) {
connectivity.requestNetwork(defaultNetworkRequest, defaultNetworkCallback)
listeningForDefaultNetwork = true
}
// Create a new interface using the builder and save the parameters.
try {
mInterface = builder.establish()!!
} catch (e: Exception) {
// non-nullable lateinit var
e.printStackTrace()
stopV2Ray()
}
mInterface = builder.establish()
sendFd()
startSpeedNotification()
}
private fun sendFd() {
fun shutdown() {
stopV2Ray(true)
}
fun sendFd() {
val fd = mInterface.fileDescriptor
val path = File(Utils.packagePath(applicationContext), "sock_path").absolutePath
GlobalScope.launch(Dispatchers.IO) {
doAsync {
var tries = 0
while (true) try {
Thread.sleep(1000L shl tries)
Log.d(packageName, "sendFd tries: $tries")
Thread.sleep(50L shl tries)
Log.d(packageName, "sendFd tries: " + tries.toString())
LocalSocket().use { localSocket ->
localSocket.connect(LocalSocketAddress(path, LocalSocketAddress.Namespace.FILESYSTEM))
localSocket.setFileDescriptorsForSend(arrayOf(fd))
@@ -199,57 +215,278 @@ class V2RayVpnService : VpnService(), ServiceControl {
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
V2RayServiceManager.startV2rayPoint()
startV2ray()
return START_STICKY
//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) {
// val configName = defaultDPreference.getPrefString(PREF_CURR_CONFIG_GUID, "")
// val emptyInfo = VpnNetworkInfo()
// val info = loadVpnNetworkInfo(configName, emptyInfo)!! + (lastNetworkInfo ?: emptyInfo)
// saveVpnNetworkInfo(configName, info)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
try {
if (Build.VERSION.SDK_INT >= 28) {
if (listeningForDefaultNetwork) {
connectivity.unregisterNetworkCallback(defaultNetworkCallback)
} catch (ignored: Exception) {
// ignored
listeningForDefaultNetwork = false
}
}
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) {
//stopSelf has to be called ahead of mInterface.close(). otherwise v2ray core cannot be stooped
//It's strage but true.
//This can be verified by putting stopself() behind and call stopLoop and startLoop
//in a row for several times. You will find that later created v2ray core report port in use
//which means the first v2ray core somehow failed to stop and release the port.
stopSelf()
try {
unregisterReceiver(mMsgReceive)
} catch (e: Exception) {
}
try {
mInterface.close()
} catch (ignored: Exception) {
// ignored
}
stopSelf()
}
}
override fun getService(): Service {
return this
private fun showNotification() {
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) {
setup(parameters)
@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
}
override fun stopService() {
stopV2Ray(true)
private fun cancelNotification() {
stopForeground(true)
mBuilder = null
mSubscription?.unsubscribe()
mSubscription = null
}
override fun vpnProtect(socket: Int): Boolean {
return protect(socket)
private fun updateNotification(contentText: String) {
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.view.MenuItem
import android.view.View
//import com.v2ray.ang.InappBuyActivity
import com.v2ray.ang.R
import org.jetbrains.anko.startActivity
abstract class BaseDrawerActivity : BaseActivity() {
companion object {
@@ -51,10 +55,11 @@ abstract class BaseDrawerActivity : BaseActivity() {
this@BaseDrawerActivity, R.anim.fade_in, R.anim.fade_out).toBundle()
var activityClass: Class<*>? = null
when (mItemToOpenWhenDrawerCloses) {
R.id.server_profile -> activityClass = MainActivity::class.java
R.id.sub_setting -> activityClass = SubSettingActivity::class.java
R.id.settings -> activityClass = SettingsActivity::class.java
R.id.logcat -> {
startActivity(Intent(this@BaseDrawerActivity, LogcatActivity::class.java))
startActivity<LogcatActivity>()
return
}
R.id.donate -> {
@@ -190,7 +195,9 @@ abstract class BaseDrawerActivity : BaseActivity() {
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)
} else if (SettingsActivity::class.java.isAssignableFrom(javaClass)) {
navigationView.setCheckedItem(R.id.settings)
@@ -1,19 +1,17 @@
package com.v2ray.ang.ui
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.text.method.ScrollingMovementMethod
import android.view.Menu
import android.view.MenuItem
import android.view.View
import com.v2ray.ang.R
import com.v2ray.ang.extension.toast
import com.v2ray.ang.util.Utils
import kotlinx.android.synthetic.main.activity_logcat.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import org.jetbrains.anko.doAsync
import org.jetbrains.anko.toast
import org.jetbrains.anko.uiThread
import java.io.IOException
import java.util.LinkedHashSet
@@ -26,22 +24,15 @@ class LogcatActivity : BaseActivity() {
title = getString(R.string.title_logcat)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
logcat(false)
logcat()
}
private fun logcat(shouldFlushLog: Boolean) {
private fun logcat() {
try {
pb_waiting.visibility = View.VISIBLE
GlobalScope.launch(Dispatchers.Default) {
if (shouldFlushLog) {
val lst = LinkedHashSet<String>()
lst.add("logcat")
lst.add("-c")
val process = Runtime.getRuntime().exec(lst.toTypedArray())
process.waitFor()
}
doAsync {
val lst = LinkedHashSet<String>()
lst.add("logcat")
lst.add("-d")
@@ -54,15 +45,13 @@ class LogcatActivity : BaseActivity() {
// InputStreamReader(process.inputStream))
// val allText = bufferedReader.use(BufferedReader::readText)
val allText = process.inputStream.bufferedReader().use { it.readText() }
launch(Dispatchers.Main) {
uiThread {
tv_logcat.text = allText
tv_logcat.movementMethod = ScrollingMovementMethod()
pb_waiting.visibility = View.GONE
Handler(Looper.getMainLooper()).post { sv_logcat.fullScroll(View.FOCUS_DOWN) }
}
}
} catch (e: IOException) {
e.printStackTrace()
}
}
@@ -77,10 +66,7 @@ class LogcatActivity : BaseActivity() {
toast(R.string.toast_success)
true
}
R.id.delete -> {
logcat(true)
true
}
else -> super.onOptionsItemSelected(item)
}
}
@@ -1,41 +1,38 @@
package com.v2ray.ang.ui
import android.Manifest
import android.arch.lifecycle.ViewModelProviders
import android.content.Intent
import android.content.*
import android.net.Uri
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.helper.ItemTouchHelper
import android.text.TextUtils
import android.util.Log
import android.view.KeyEvent
import android.view.Menu
import android.view.MenuItem
import com.tbruyelle.rxpermissions.RxPermissions
import com.v2ray.ang.AppConfig
import com.v2ray.ang.BuildConfig
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.Utils
import com.v2ray.ang.util.V2rayConfigUtil
import com.v2ray.ang.viewmodel.MainViewModel
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import libv2ray.Libv2ray
import android.os.Bundle
import android.text.TextUtils
import android.view.KeyEvent
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.android.schedulers.AndroidSchedulers
import java.net.URL
import java.util.concurrent.TimeUnit
import com.v2ray.ang.helper.SimpleItemTouchHelperCallback
import com.v2ray.ang.util.AngConfigManager.configs
class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedListener {
companion object {
@@ -45,9 +42,22 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
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 var mItemTouchHelper: ItemTouchHelper? = null
private val mainViewModel: MainViewModel by lazy { ViewModelProviders.of(this).get(MainViewModel::class.java) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -56,23 +66,28 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
setSupportActionBar(toolbar)
fab.setOnClickListener {
if (mainViewModel.isRunning.value == true) {
if (isRunning) {
Utils.stopVService(this)
} else if (defaultDPreference.getPrefString(AppConfig.PREF_MODE, "VPN") == "VPN") {
} else {
val intent = VpnService.prepare(this)
if (intent == null) {
startV2Ray()
} else {
startActivityForResult(intent, REQUEST_CODE_VPN_PREPARE)
}
} else {
startV2Ray()
}
}
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)
mainViewModel.testCurrentServerRealPing()
doAsync {
val result = Utils.testConnection(this@MainActivity, socksPort)
uiThread {
tv_test_state.text = Utils.getEditable(result)
}
}
} else {
// tv_test_state.text = getString(R.string.connection_test_fail)
}
@@ -92,34 +107,6 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
drawer_layout.addDrawerListener(toggle)
toggle.syncState()
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() {
@@ -128,11 +115,32 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
}
showCircle()
// toast(R.string.toast_services_start)
if (!Utils.startVService(this, AngConfigManager.configs.index)) {
if (!Utils.startVService(this)) {
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() {
super.onResume()
adapter.updateConfigList()
@@ -154,8 +162,8 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
importBatchConfig(data?.getStringExtra("SCAN_RESULT"))
}
REQUEST_FILE_CHOOSER -> {
val uri = data?.data
if (resultCode == RESULT_OK && uri != null) {
if (resultCode == RESULT_OK) {
val uri = data!!.data
readContentFromUri(uri)
}
}
@@ -171,9 +179,6 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
return true
}
private fun getOptionIntent() = Intent().putExtra("position", -1)
.putExtra("isRunning", mainViewModel.isRunning.value == true)
override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
R.id.import_qrcode -> {
importQRcode(REQUEST_SCAN)
@@ -184,17 +189,17 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
true
}
R.id.import_manually_vmess -> {
startActivity(getOptionIntent().setClass(this, ServerActivity::class.java))
startActivity<ServerActivity>("position" to -1, "isRunning" to isRunning)
adapter.updateConfigList()
true
}
R.id.import_manually_ss -> {
startActivity(getOptionIntent().setClass(this, Server3Activity::class.java))
startActivity<Server3Activity>("position" to -1, "isRunning" to isRunning)
adapter.updateConfigList()
true
}
R.id.import_manually_socks -> {
startActivity(getOptionIntent().setClass(this, Server4Activity::class.java))
startActivity<Server4Activity>("position" to -1, "isRunning" to isRunning)
adapter.updateConfigList()
true
}
@@ -227,7 +232,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
R.id.export_all -> {
if (AngConfigManager.shareAll2Clipboard() == 0) {
//remove toast, otherwise it will block previous warning message
toast(R.string.toast_success)
} else {
toast(R.string.toast_failure)
}
@@ -235,7 +240,20 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
}
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
}
@@ -264,7 +282,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
.request(Manifest.permission.CAMERA)
.subscribe {
if (it)
startActivityForResult(Intent(this, ScannerActivity::class.java), requestCode)
startActivityForResult<ScannerActivity>(requestCode)
else
toast(R.string.toast_permission_denied)
}
@@ -350,14 +368,9 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
toast(R.string.toast_invalid_url)
return false
}
GlobalScope.launch(Dispatchers.IO) {
val configText = try {
URL(url).readText()
} catch (e: Exception) {
e.printStackTrace()
""
}
launch(Dispatchers.Main) {
doAsync {
val configText = URL(url).readText()
uiThread {
importCustomizeConfig(configText)
}
}
@@ -389,14 +402,9 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
continue
}
Log.d("Main", url)
GlobalScope.launch(Dispatchers.IO) {
val configText = try {
URL(url).readText()
} catch (e: Exception) {
e.printStackTrace()
""
}
launch(Dispatchers.Main) {
doAsync {
val configText = URL(url).readText()
uiThread {
importBatchConfig(Utils.decode(configText), id)
}
}
@@ -434,10 +442,9 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
.subscribe {
if (it) {
try {
contentResolver.openInputStream(uri).use {
val configText = it?.bufferedReader()?.readText()
importCustomizeConfig(configText)
}
val inputStream = contentResolver.openInputStream(uri)
val configText = inputStream.bufferedReader().readText()
importCustomizeConfig(configText)
} catch (e: Exception) {
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 {
if (keyCode == KeyEvent.KEYCODE_BACK) {
moveTaskToBack(false)
@@ -513,11 +549,10 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
when (item.itemId) {
//R.id.server_profile -> activityClass = MainActivity::class.java
R.id.sub_setting -> {
startActivity(Intent(this, SubSettingActivity::class.java))
startActivity<SubSettingActivity>()
}
R.id.settings -> {
startActivity(Intent(this, SettingsActivity::class.java)
.putExtra("isRunning", mainViewModel.isRunning.value == true))
startActivity<SettingsActivity>("isRunning" to isRunning)
}
R.id.feedback -> {
Utils.openUri(this, AppConfig.v2rayNGIssues)
@@ -529,10 +564,10 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
// startActivity<InappBuyActivity>()
}
R.id.logcat -> {
startActivity(Intent(this, LogcatActivity::class.java))
startActivity<LogcatActivity>()
}
}
drawer_layout.closeDrawer(GravityCompat.START)
return true
}
}
}
@@ -1,28 +1,24 @@
package com.v2ray.ang.ui
import android.content.Intent
import android.graphics.Color
import android.support.v7.app.AlertDialog
import android.support.v7.widget.RecyclerView
import android.text.TextUtils
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.v2ray.ang.AppConfig
import com.v2ray.ang.R
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.ItemTouchHelperViewHolder
import com.v2ray.ang.util.AngConfigManager
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_recycler_main.view.*
import org.jetbrains.anko.*
import rx.Observable
import rx.android.schedulers.AndroidSchedulers
import java.util.concurrent.TimeUnit
import com.v2ray.ang.extension.defaultDPreference
class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<MainRecyclerAdapter.BaseViewHolder>()
, ItemTouchHelperAdapter {
@@ -53,7 +49,7 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
override fun onBindViewHolder(holder: BaseViewHolder, position: Int) {
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 subid = configs.vmess[position].subid
val address = configs.vmess[position].address
@@ -62,7 +58,7 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
holder.name.text = remarks
holder.radio.isChecked = (position == configs.index)
holder.itemView.setBackgroundColor(Color.TRANSPARENT)
holder.itemView.backgroundColor = Color.TRANSPARENT
holder.test_result.text = test_result
if (TextUtils.isEmpty(subid)) {
@@ -71,33 +67,39 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
holder.subid.text = "S"
}
var shareOptions = share_method.asList()
if (configType == EConfigType.CUSTOM) {
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()
if (configType == AppConfig.EConfigType.Vmess) {
holder.type.text = "vmess"
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 {
AlertDialog.Builder(mActivity).setItems(shareOptions.toTypedArray()) { _, i ->
mActivity.selector(null, share_method.asList()) { dialogInterface, i ->
try {
when (i) {
0 -> {
if (configType == EConfigType.CUSTOM) {
shareFullContent(position)
} else {
val iv = mActivity.layoutInflater.inflate(R.layout.item_qrcode, null)
iv.iv_qcode.setImageBitmap(AngConfigManager.share2QRCode(position))
AlertDialog.Builder(mActivity).setView(iv).show()
}
val iv = mActivity.layoutInflater.inflate(R.layout.item_qrcode, null)
iv.iv_qcode.setImageBitmap(AngConfigManager.share2QRCode(position))
mActivity.alert {
customView {
linearLayout {
addView(iv)
}
}
}.show()
}
1 -> {
if (AngConfigManager.share2Clipboard(position) == 0) {
@@ -106,26 +108,31 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
mActivity.toast(R.string.toast_failure)
}
}
2 -> shareFullContent(position)
else -> mActivity.toast("else")
2 -> {
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) {
e.printStackTrace()
}
}.show()
}
}
holder.layout_edit.setOnClickListener {
val intent = Intent().putExtra("position", position)
.putExtra("isRunning", !changeable)
if (configType == EConfigType.VMESS) {
mActivity.startActivity(intent.setClass(mActivity, ServerActivity::class.java))
} else if (configType == EConfigType.CUSTOM) {
mActivity.startActivity(intent.setClass(mActivity, Server2Activity::class.java))
} else if (configType == EConfigType.SHADOWSOCKS) {
mActivity.startActivity(intent.setClass(mActivity, Server3Activity::class.java))
} else if (configType == EConfigType.SOCKS) {
mActivity.startActivity(intent.setClass(mActivity, Server4Activity::class.java))
if (configType == AppConfig.EConfigType.Vmess) {
mActivity.startActivity<ServerActivity>("position" to position, "isRunning" to !changeable)
} else if (configType == AppConfig.EConfigType.Custom) {
mActivity.startActivity<Server2Activity>("position" to position, "isRunning" to !changeable)
} else if (configType == AppConfig.EConfigType.Shadowsocks) {
mActivity.startActivity<Server3Activity>("position" to position, "isRunning" to !changeable)
} else if (configType == AppConfig.EConfigType.Socks) {
mActivity.startActivity<Server4Activity>("position" to position, "isRunning" to !changeable)
}
}
holder.layout_remove.setOnClickListener {
@@ -143,14 +150,16 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
} else {
mActivity.showCircle()
Utils.stopVService(mActivity)
AngConfigManager.setActiveServer(position)
Observable.timer(500, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
mActivity.showCircle()
if (!Utils.startVService(mActivity, position)) {
if (!Utils.startVService(mActivity)) {
mActivity.hideCircle()
}
}
}
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 {
when (viewType) {
VIEW_TYPE_ITEM ->
return MainViewHolder(LayoutInflater.from(parent.context)
return MainViewHolder(parent.context.layoutInflater
.inflate(R.layout.item_recycler_main, parent, false))
else ->
return FooterViewHolder(LayoutInflater.from(parent.context)
return FooterViewHolder(parent.context.layoutInflater
.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)
return true
}
override fun onItemMoveCompleted() {
AngConfigManager.storeConfigFile()
}
}
@@ -4,8 +4,6 @@ import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.content.Context
import android.os.Bundle
import android.support.v7.widget.DividerItemDecoration
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
import android.text.TextUtils
import android.util.Log
@@ -14,6 +12,7 @@ import android.view.MenuItem
import android.view.View
import android.view.animation.AccelerateInterpolator
import android.view.animation.DecelerateInterpolator
import com.dinuscxj.itemdecoration.LinearDividerItemDecoration
import com.v2ray.ang.R
import com.v2ray.ang.extension.defaultDPreference
import com.v2ray.ang.util.AppManagerUtil
@@ -26,12 +25,11 @@ import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager
import com.v2ray.ang.AppConfig
import com.v2ray.ang.dto.AppInfo
import com.v2ray.ang.extension.toast
import com.v2ray.ang.extension.v2RayApplication
import com.v2ray.ang.util.Utils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import org.jetbrains.anko.doAsync
import org.jetbrains.anko.toast
import org.jetbrains.anko.uiThread
import java.net.URL
class PerAppProxyActivity : BaseActivity() {
@@ -49,7 +47,8 @@ class PerAppProxyActivity : BaseActivity() {
supportActionBar?.setDisplayHomeAsUpEnabled(true)
val dividerItemDecoration = DividerItemDecoration(this, LinearLayoutManager.VERTICAL)
val dividerItemDecoration = LinearDividerItemDecoration(
this, LinearDividerItemDecoration.LINEAR_DIVIDER_VERTICAL)
recycler_view.addItemDecoration(dividerItemDecoration)
val blacklist = defaultDPreference.getPrefStringSet(PREF_PER_APP_PROXY_SET, null)
@@ -221,14 +220,9 @@ class PerAppProxyActivity : BaseActivity() {
private fun selectProxyApp() {
toast(R.string.msg_downloading_content)
val url = AppConfig.androidpackagenamelistUrl
GlobalScope.launch(Dispatchers.IO) {
val content = try {
URL(url).readText()
} catch (e: Exception) {
e.printStackTrace()
""
}
launch(Dispatchers.Main) {
doAsync {
val content = URL(url).readText()
uiThread {
Log.d("selectProxyApp", content)
selectProxyApp(content)
toast(R.string.toast_success)
@@ -282,4 +276,4 @@ class PerAppProxyActivity : BaseActivity() {
}
return true
}
}
}
@@ -2,12 +2,14 @@ package com.v2ray.ang.ui
import android.graphics.Color
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.v2ray.ang.R
import com.v2ray.ang.dto.AppInfo
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.*
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
// .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))
}
@@ -66,17 +68,17 @@ class PerAppProxyAdapter(val activity: BaseActivity, val apps: List<AppInfo>, bl
fun bind(appInfo: AppInfo) {
this.appInfo = appInfo
icon.setImageDrawable(appInfo.appIcon)
icon.image = appInfo.appIcon
// name.text = appInfo.appName
checkBox.isChecked = inBlacklist
package_name.text = appInfo.packageName
if (appInfo.isSystemApp) {
name.text = String.format("** %1s", appInfo.appName)
name.setTextColor(Color.RED)
name.textColor = Color.RED
} else {
name.text = appInfo.appName
name.setTextColor(Color.DKGRAY)
name.textColor = Color.DKGRAY
}
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
import android.Manifest
import android.app.Activity.RESULT_OK
import android.content.Intent
import android.os.Bundle
import android.support.v4.app.Fragment
import android.text.TextUtils
import android.util.Log
import android.view.*
import com.v2ray.ang.R
import com.v2ray.ang.extension.defaultDPreference
import com.v2ray.ang.util.Utils
import kotlinx.android.synthetic.main.fragment_routing_settings.*
import org.jetbrains.anko.toast
import android.view.MenuInflater
import com.tbruyelle.rxpermissions.RxPermissions
import com.v2ray.ang.AppConfig
import com.v2ray.ang.extension.toast
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import org.jetbrains.anko.doAsync
import org.jetbrains.anko.startActivityForResult
import org.jetbrains.anko.support.v4.startActivityForResult
import org.jetbrains.anko.support.v4.toast
import org.jetbrains.anko.uiThread
import java.net.URL
class RoutingSettingsFragment : Fragment() {
companion object {
private const val routing_arg = "routing_arg"
@@ -90,7 +96,7 @@ class RoutingSettingsFragment : Fragment() {
.request(Manifest.permission.CAMERA)
.subscribe {
if (it)
startActivityForResult(Intent(activity, ScannerActivity::class.java), requestCode)
startActivityForResult<ScannerActivity>(requestCode)
else
activity?.toast(R.string.toast_permission_denied)
}
@@ -112,17 +118,12 @@ class RoutingSettingsFragment : Fragment() {
}
}
activity?.toast(R.string.msg_downloading_content)
GlobalScope.launch(Dispatchers.IO) {
val content = try {
URL(url).readText()
} catch (e: Exception) {
e.printStackTrace()
""
}
launch(Dispatchers.Main) {
et_routing_content.text = Utils.getEditable(content)
activity?.toast(R.string.toast_success)
toast(R.string.msg_downloading_content)
doAsync {
val content = URL(url).readText()
uiThread {
et_routing_content.text = Utils.getEditable(content!!)
toast(R.string.toast_success)
}
}
return true
@@ -6,7 +6,7 @@ import com.tbruyelle.rxpermissions.RxPermissions
import com.v2ray.ang.R
import com.v2ray.ang.util.AngConfigManager
import android.os.Bundle
import com.v2ray.ang.extension.toast
import org.jetbrains.anko.*
class ScScannerActivity : BaseActivity() {
companion object {
@@ -24,7 +24,7 @@ class ScScannerActivity : BaseActivity() {
.request(Manifest.permission.CAMERA)
.subscribe {
if (it)
startActivityForResult(Intent(this, ScannerActivity::class.java), requestCode)
startActivityForResult<ScannerActivity>(requestCode)
else
toast(R.string.toast_permission_denied)
}
@@ -43,10 +43,10 @@ class ScScannerActivity : BaseActivity() {
} else {
toast(R.string.toast_failure)
}
startActivity(Intent(this, MainActivity::class.java))
startActivity<MainActivity>()
}
}
finish()
}
}
}
@@ -1,22 +1,104 @@
package com.v2ray.ang.ui
import android.content.*
import android.net.VpnService
import com.v2ray.ang.R
import com.v2ray.ang.util.Utils
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() {
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?) {
super.onCreate(savedInstanceState)
moveTaskToBack(true)
setContentView(R.layout.activity_none)
if (V2RayServiceManager.v2rayPoint.isRunning) {
Utils.stopVService(this)
val isRunning = Utils.isServiceRun(this, "com.v2ray.ang.service.V2RayVpnService")
if (isRunning) {
//Utils.stopVService(this)
mMsgReceive = ReceiveMessageHandler(this@ScSwitchActivity)
registerReceiver(mMsgReceive, IntentFilter(AppConfig.BROADCAST_ACTION_ACTIVITY))
MessageUtil.sendMsg2Service(this, AppConfig.MSG_REGISTER_CLIENT, "")
} 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 android.content.Intent
import android.graphics.BitmapFactory
import android.icu.util.TimeUnit
import android.view.Menu
import android.view.MenuItem
import com.google.zxing.BarcodeFormat
import com.tbruyelle.rxpermissions.RxPermissions
import com.v2ray.ang.R
import com.v2ray.ang.extension.toast
import com.v2ray.ang.util.QRCodeDecoder
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 {
companion object {
@@ -109,10 +119,10 @@ class ScannerActivity : BaseActivity(), ZXingScannerView.ResultHandler {
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when (requestCode) {
REQUEST_FILE_CHOOSER -> {
val uri = data?.data
if (resultCode == RESULT_OK && uri != null) {
REQUEST_FILE_CHOOSER ->
if (resultCode == RESULT_OK) {
try {
val uri = data!!.data
val bitmap = BitmapFactory.decodeStream(contentResolver.openInputStream(uri))
val text = QRCodeDecoder.syncDecodeQRCode(bitmap)
finished(text)
@@ -121,7 +131,6 @@ class ScannerActivity : BaseActivity(), ZXingScannerView.ResultHandler {
toast(e.message.toString())
}
}
}
}
}
}
@@ -1,22 +1,18 @@
package com.v2ray.ang.ui
import android.os.Bundle
import android.support.v7.app.AlertDialog
import android.text.Editable
import android.text.TextUtils
import android.util.Log
import android.view.Menu
import android.view.MenuItem
import com.google.gson.Gson
import com.v2ray.ang.AppConfig
import com.v2ray.ang.R
import com.v2ray.ang.extension.defaultDPreference
import com.v2ray.ang.dto.AngConfig
import com.v2ray.ang.extension.toast
import com.v2ray.ang.util.AngConfigManager
import com.v2ray.ang.util.Utils
import kotlinx.android.synthetic.main.activity_server2.*
import java.lang.Exception
import org.jetbrains.anko.*
class Server2Activity : BaseActivity() {
companion object {
@@ -54,7 +50,7 @@ class Server2Activity : BaseActivity() {
*/
fun bindingServer(vmess: AngConfig.VmessBean): Boolean {
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
}
@@ -70,45 +66,21 @@ class Server2Activity : BaseActivity() {
* save server config
*/
fun saveServer(): Boolean {
var saveSuccess: Boolean
val vmess = configs.vmess[edit_index]
vmess.remarks = et_remarks.text.toString()
if (TextUtils.isEmpty(vmess.remarks)) {
toast(R.string.server_lab_remarks)
saveSuccess = false
return false
}
if (AngConfigManager.addCustomServer(vmess, edit_index) == 0) {
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()
return true
} else {
toast(R.string.toast_failure)
return false
}
}
@@ -118,16 +90,17 @@ class Server2Activity : BaseActivity() {
*/
fun deleteServer(): Boolean {
if (edit_index >= 0) {
AlertDialog.Builder(this).setMessage(R.string.del_config_comfirm)
.setPositiveButton(android.R.string.ok) { _, _ ->
if (AngConfigManager.removeServer(edit_index) == 0) {
toast(R.string.toast_success)
finish()
} else {
toast(R.string.toast_failure)
}
alert(R.string.del_config_comfirm) {
positiveButton(android.R.string.ok) {
if (AngConfigManager.removeServer(edit_index) == 0) {
toast(R.string.toast_success)
finish()
} else {
toast(R.string.toast_failure)
}
.show()
}
show()
}
} else {
}
return true
@@ -163,4 +136,4 @@ class Server2Activity : BaseActivity() {
}
else -> super.onOptionsItemSelected(item)
}
}
}
@@ -1,16 +1,16 @@
package com.v2ray.ang.ui
import android.os.Bundle
import android.support.v7.app.AlertDialog
import android.text.TextUtils
import android.view.Menu
import android.view.MenuItem
import com.v2ray.ang.R
import com.v2ray.ang.dto.AngConfig
import com.v2ray.ang.extension.toast
import com.v2ray.ang.util.AngConfigManager
import com.v2ray.ang.util.Utils
import kotlinx.android.synthetic.main.activity_server3.*
import org.jetbrains.anko.*
class Server3Activity : BaseActivity() {
companion object {
@@ -126,16 +126,17 @@ class Server3Activity : BaseActivity() {
*/
fun deleteServer(): Boolean {
if (edit_index >= 0) {
AlertDialog.Builder(this).setMessage(R.string.del_config_comfirm)
.setPositiveButton(android.R.string.ok) { _, _ ->
if (AngConfigManager.removeServer(edit_index) == 0) {
toast(R.string.toast_success)
finish()
} else {
toast(R.string.toast_failure)
}
alert(R.string.del_config_comfirm) {
positiveButton(android.R.string.ok) {
if (AngConfigManager.removeServer(edit_index) == 0) {
toast(R.string.toast_success)
finish()
} else {
toast(R.string.toast_failure)
}
.show()
}
show()
}
} else {
}
return true
@@ -171,4 +172,4 @@ class Server3Activity : BaseActivity() {
}
else -> super.onOptionsItemSelected(item)
}
}
}
@@ -1,16 +1,16 @@
package com.v2ray.ang.ui
import android.os.Bundle
import android.support.v7.app.AlertDialog
import android.text.TextUtils
import android.view.Menu
import android.view.MenuItem
import com.v2ray.ang.R
import com.v2ray.ang.dto.AngConfig
import com.v2ray.ang.extension.toast
import com.v2ray.ang.util.AngConfigManager
import com.v2ray.ang.util.Utils
import kotlinx.android.synthetic.main.activity_server4.*
import org.jetbrains.anko.*
class Server4Activity : BaseActivity() {
companion object {
@@ -110,16 +110,17 @@ class Server4Activity : BaseActivity() {
*/
fun deleteServer(): Boolean {
if (edit_index >= 0) {
AlertDialog.Builder(this).setMessage(R.string.del_config_comfirm)
.setPositiveButton(android.R.string.ok) { _, _ ->
if (AngConfigManager.removeServer(edit_index) == 0) {
toast(R.string.toast_success)
finish()
} else {
toast(R.string.toast_failure)
}
alert(R.string.del_config_comfirm) {
positiveButton(android.R.string.ok) {
if (AngConfigManager.removeServer(edit_index) == 0) {
toast(R.string.toast_success)
finish()
} else {
toast(R.string.toast_failure)
}
.show()
}
show()
}
} else {
}
return true
@@ -155,4 +156,4 @@ class Server4Activity : BaseActivity() {
}
else -> super.onOptionsItemSelected(item)
}
}
}
@@ -1,16 +1,16 @@
package com.v2ray.ang.ui
import android.os.Bundle
import android.support.v7.app.AlertDialog
import android.text.TextUtils
import android.view.Menu
import android.view.MenuItem
import com.v2ray.ang.R
import com.v2ray.ang.dto.AngConfig
import com.v2ray.ang.extension.toast
import com.v2ray.ang.util.AngConfigManager
import com.v2ray.ang.util.Utils
import kotlinx.android.synthetic.main.activity_server.*
import org.jetbrains.anko.*
class ServerActivity : BaseActivity() {
companion object {
@@ -169,16 +169,17 @@ class ServerActivity : BaseActivity() {
*/
fun deleteServer(): Boolean {
if (edit_index >= 0) {
AlertDialog.Builder(this).setMessage(R.string.del_config_comfirm)
.setPositiveButton(android.R.string.ok) { _, _ ->
if (AngConfigManager.removeServer(edit_index) == 0) {
toast(R.string.toast_success)
finish()
} else {
toast(R.string.toast_failure)
}
alert(R.string.del_config_comfirm) {
positiveButton(android.R.string.ok) {
if (AngConfigManager.removeServer(edit_index) == 0) {
toast(R.string.toast_success)
finish()
} else {
toast(R.string.toast_failure)
}
.show()
}
show()
}
} else {
}
return true
@@ -214,4 +215,4 @@ class ServerActivity : BaseActivity() {
}
else -> super.onOptionsItemSelected(item)
}
}
}
@@ -1,16 +1,25 @@
package com.v2ray.ang.ui
import android.arch.lifecycle.ViewModelProviders
import android.content.Intent
import android.content.SharedPreferences
import android.net.Uri
import android.os.Bundle
import android.support.v7.preference.*
import android.view.View
import android.preference.CheckBoxPreference
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.AppConfig
import com.v2ray.ang.extension.toast
import com.v2ray.ang.util.AngConfigManager
import com.v2ray.ang.extension.defaultDPreference
import com.v2ray.ang.extension.onClick
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() {
companion object {
@@ -20,7 +29,6 @@ class SettingsActivity : BaseActivity() {
// const val PREF_MUX_ENAimport libv2ray.Libv2rayBLED = "pref_mux_enabled"
const val PREF_SPEED_ENABLED = "pref_speed_enabled"
const val PREF_SNIFFING_ENABLED = "pref_sniffing_enabled"
const val PREF_PROXY_SHARING = "pref_proxy_sharing_enabled"
const val PREF_LOCAL_DNS_ENABLED = "pref_local_dns_enabled"
const val PREF_REMOTE_DNS = "pref_remote_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_FEEDBACK = "pref_feedback"
// 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_FORWARD_IPV6 = "pref_forward_ipv6"
}
private val settingsViewModel by lazy { ViewModelProviders.of(this).get(SettingsViewModel::class.java) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_settings)
@@ -48,130 +55,34 @@ class SettingsActivity : BaseActivity() {
title = getString(R.string.title_settings)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
settingsViewModel.startListenPreferenceChange()
}
class SettingsFragment : PreferenceFragmentCompat() {
private 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 }
class SettingsFragment : PreferenceFragment(), SharedPreferences.OnSharedPreferenceChangeListener {
val perAppProxy by lazy { findPreference(PREF_PER_APP_PROXY) 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 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 licenses: Preference by lazy { findPreference(PREF_LICENSES) }
// val feedback: Preference by lazy { findPreference(PREF_FEEDBACK) }
// 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 }
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?) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
addPreferencesFromResource(R.xml.pref_settings)
perAppProxy.setOnPreferenceClickListener {
if (isRunning()) {
Utils.stopVService(requireContext())
}
startActivity(Intent(activity, PerAppProxyActivity::class.java))
perAppProxy.isChecked = true
true
routingCustom.onClick {
startActivity<RoutingSettingsActivity>()
}
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 {
// 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.summary = any as String
// true
@@ -208,12 +136,14 @@ class SettingsActivity : BaseActivity() {
// httpPort.summary = any as String
// true
// }
version.summary = "${BuildConfig.VERSION_NAME} (${Libv2ray.checkVersionX()})"
}
override fun 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, "")
domesticDns.summary = defaultSharedPreferences.getString(PREF_DOMESTIC_DNS, "")
@@ -227,21 +157,24 @@ class SettingsActivity : BaseActivity() {
// socksPort.summary = defaultSharedPreferences.getString(PREF_SOCKS_PORT, "10808")
// lanconnPort.summary = defaultSharedPreferences.getString(PREF_HTTP_PORT, "")
defaultSharedPreferences.registerOnSharedPreferenceChangeListener(this)
}
private fun updatePerAppProxy(mode: String?) {
if (mode == "VPN") {
perAppProxy.isEnabled = true
perAppProxy.isChecked = PreferenceManager.getDefaultSharedPreferences(activity)
.getBoolean(PREF_PER_APP_PROXY, false)
} else {
perAppProxy.isEnabled = false
perAppProxy.isChecked = false
override fun onStop() {
super.onStop()
defaultSharedPreferences.unregisterOnSharedPreferenceChangeListener(this)
}
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String?) {
when (key) {
// 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
import android.os.Bundle
import android.support.v7.app.AlertDialog
import android.text.TextUtils
import android.view.Menu
import android.view.MenuItem
import com.v2ray.ang.R
import com.v2ray.ang.dto.AngConfig
import com.v2ray.ang.extension.toast
import com.v2ray.ang.util.AngConfigManager
import com.v2ray.ang.util.Utils
import kotlinx.android.synthetic.main.activity_sub_edit.*
import org.jetbrains.anko.*
class SubEditActivity : BaseActivity() {
@@ -95,16 +95,17 @@ class SubEditActivity : BaseActivity() {
*/
fun deleteServer(): Boolean {
if (edit_index >= 0) {
AlertDialog.Builder(this).setMessage(R.string.del_config_comfirm)
.setPositiveButton(android.R.string.ok) { _, _ ->
if (AngConfigManager.removeSubItem(edit_index) == 0) {
toast(R.string.toast_success)
finish()
} else {
toast(R.string.toast_failure)
}
alert(R.string.del_config_comfirm) {
positiveButton(android.R.string.ok) {
if (AngConfigManager.removeSubItem(edit_index) == 0) {
toast(R.string.toast_success)
finish()
} else {
toast(R.string.toast_failure)
}
.show()
}
show()
}
} else {
}
return true
@@ -134,4 +135,4 @@ class SubEditActivity : BaseActivity() {
}
else -> super.onOptionsItemSelected(item)
}
}
}
@@ -1,12 +1,12 @@
package com.v2ray.ang.ui
import android.content.Intent
import android.support.v7.widget.LinearLayoutManager
import android.view.Menu
import android.view.MenuItem
import com.v2ray.ang.R
import kotlinx.android.synthetic.main.activity_sub_setting.*
import android.os.Bundle
import org.jetbrains.anko.startActivity
class SubSettingActivity : BaseActivity() {
@@ -40,9 +40,7 @@ class SubSettingActivity : BaseActivity() {
override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
R.id.add_config -> {
startActivity(Intent(this, SubEditActivity::class.java)
.putExtra("position", -1)
)
startActivity<SubEditActivity>("position" to -1)
adapter.updateConfigList()
true
}
@@ -50,4 +48,4 @@ class SubSettingActivity : BaseActivity() {
}
}
}
@@ -1,15 +1,14 @@
package com.v2ray.ang.ui
import android.content.Intent
import android.graphics.Color
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.v2ray.ang.R
import com.v2ray.ang.dto.AngConfig
import com.v2ray.ang.util.AngConfigManager
import kotlinx.android.synthetic.main.item_recycler_sub_setting.view.*
import org.jetbrains.anko.*
class SubSettingRecyclerAdapter(val activity: SubSettingActivity) : RecyclerView.Adapter<SubSettingRecyclerAdapter.BaseViewHolder>() {
@@ -29,19 +28,17 @@ class SubSettingRecyclerAdapter(val activity: SubSettingActivity) : RecyclerView
holder.name.text = remarks
holder.url.text = url
holder.itemView.setBackgroundColor(Color.TRANSPARENT)
holder.itemView.backgroundColor = Color.TRANSPARENT
holder.layout_edit.setOnClickListener {
mActivity.startActivity(Intent(mActivity, SubEditActivity::class.java)
.putExtra("position", position)
)
mActivity.startActivity<SubEditActivity>("position" to position)
}
} else {
}
}
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))
}
@@ -15,11 +15,13 @@ import com.v2ray.ang.AppConfig.SS_PROTOCOL
import com.v2ray.ang.AppConfig.VMESS_PROTOCOL
import com.v2ray.ang.R
import com.v2ray.ang.dto.AngConfig
import com.v2ray.ang.dto.EConfigType
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.util.*
import java.net.*
import java.math.BigInteger
object AngConfigManager {
private lateinit var app: AngApplication
@@ -67,7 +69,7 @@ object AngConfigManager {
fun addServer(vmess: AngConfig.VmessBean, index: Int): Int {
try {
vmess.configVersion = 2
vmess.configType = EConfigType.VMESS.value
vmess.configType = AppConfig.EConfigType.Vmess
if (index >= 0) {
//edit
@@ -102,7 +104,16 @@ object AngConfigManager {
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()
} catch (e: Exception) {
@@ -112,19 +123,6 @@ object AngConfigManager {
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 {
try {
Collections.swap(angConfig.vmess, fromPosition, toPosition)
@@ -135,7 +133,7 @@ object AngConfigManager {
} else if (index == toPosition) {
angConfig.index = fromPosition
}
//storeConfigFile()
storeConfigFile()
} catch (e: Exception) {
e.printStackTrace()
return -1
@@ -149,19 +147,13 @@ object AngConfigManager {
fun setActiveServer(index: Int): Int {
try {
if (index < 0 || index > angConfig.vmess.count() - 1) {
app.curIndex = -1
return -1
}
angConfig.index = index
app.curIndex = index
storeConfigFile()
if (!genStoreV2rayConfig(index)) {
Log.d(AppConfig.ANG_PACKAGE, "set active index $index but generate full configuration failed!")
return -1
}
} catch (e: Exception) {
e.printStackTrace()
app.curIndex = -1
return -1
}
return 0
@@ -214,14 +206,14 @@ object AngConfigManager {
return app.defaultDPreference.getPrefString(AppConfig.PREF_CURR_CONFIG, "")
}
fun currConfigType(): EConfigType? {
fun currConfigType(): Int {
if (angConfig.index < 0
|| angConfig.vmess.count() <= 0
|| 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 {
@@ -247,7 +239,7 @@ object AngConfigManager {
/**
* import config form qrcode or...
*/
fun importConfig(server: String?, subid: String, removedSelectedServer: AngConfig.VmessBean?): Int {
fun importConfig(server: String?, subid: String): Int {
try {
if (server == null || TextUtils.isEmpty(server)) {
return R.string.toast_none_data
@@ -258,11 +250,7 @@ object AngConfigManager {
if (server.startsWith(VMESS_PROTOCOL)) {
val indexSplit = server.indexOf("?")
val newVmess = tryParseNewVmess(server)
if (newVmess != null) {
vmess = newVmess
vmess.subid = subid
} else if (indexSplit > 0) {
if (indexSplit > 0) {
vmess = ResolveVmess4Kitsunebi(server)
} else {
@@ -281,7 +269,7 @@ object AngConfigManager {
return R.string.toast_incorrect_protocol
}
vmess.configType = EConfigType.VMESS.value
vmess.configType = AppConfig.EConfigType.Vmess
vmess.security = "auto"
vmess.network = "tcp"
vmess.headerType = "none"
@@ -323,7 +311,7 @@ object AngConfigManager {
result = Utils.decode(result)
}
val legacyPattern = "^(.+?):(.*)@(.+?):(\\d+?)/?$".toRegex()
val legacyPattern = "^(.+?):(.*)@(.+?):(\\d+?)$".toRegex()
val match = legacyPattern.matchEntire(result)
if (match == null) {
return R.string.toast_incorrect_protocol
@@ -371,12 +359,6 @@ object AngConfigManager {
} else {
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) {
e.printStackTrace()
return -1
@@ -384,61 +366,6 @@ object AngConfigManager {
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 {
val vmess = AngConfig.VmessBean()
@@ -484,7 +411,7 @@ object AngConfigManager {
}
val vmess = angConfig.vmess[index]
if (angConfig.vmess[index].configType == EConfigType.VMESS.value) {
if (angConfig.vmess[index].configType == AppConfig.EConfigType.Vmess) {
val vmessQRCode = VmessQRCode()
vmessQRCode.v = vmess.configVersion.toString()
@@ -502,7 +429,7 @@ object AngConfigManager {
val conf = VMESS_PROTOCOL + Utils.encode(json)
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 url = String.format("%s:%s@%s:%s",
vmess.security,
@@ -510,7 +437,7 @@ object AngConfigManager {
vmess.address,
vmess.port)
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 url = String.format("%s:%s",
vmess.address,
@@ -591,9 +518,9 @@ object AngConfigManager {
*/
fun shareFullContent2Clipboard(index: Int): Int {
try {
val result = V2rayConfigUtil.getV2rayConfig(app, angConfig.vmess[index])
if (result.status) {
Utils.setClipboard(app.applicationContext, result.content)
if (AngConfigManager.genStoreV2rayConfig(index)) {
val configContent = app.defaultDPreference.getPrefString(AppConfig.PREF_CURR_CONFIG, "")
Utils.setClipboard(app.applicationContext, configContent)
} else {
return -1
}
@@ -619,7 +546,7 @@ object AngConfigManager {
//add
val vmess = AngConfig.VmessBean()
vmess.configVersion = 2
vmess.configType = EConfigType.CUSTOM.value
vmess.configType = AppConfig.EConfigType.Custom
vmess.guid = guid
vmess.remarks = vmess.guid
@@ -722,7 +649,7 @@ object AngConfigManager {
fun addCustomServer(vmess: AngConfig.VmessBean, index: Int): Int {
try {
vmess.configVersion = 2
vmess.configType = EConfigType.CUSTOM.value
vmess.configType = AppConfig.EConfigType.Custom
if (index >= 0) {
//edit
@@ -747,7 +674,7 @@ object AngConfigManager {
fun addShadowsocksServer(vmess: AngConfig.VmessBean, index: Int): Int {
try {
vmess.configVersion = 2
vmess.configType = EConfigType.SHADOWSOCKS.value
vmess.configType = AppConfig.EConfigType.Shadowsocks
if (index >= 0) {
//edit
@@ -772,7 +699,7 @@ object AngConfigManager {
fun addSocksServer(vmess: AngConfig.VmessBean, index: Int): Int {
try {
vmess.configVersion = 2
vmess.configType = EConfigType.SOCKS.value
vmess.configType = AppConfig.EConfigType.Socks
if (index >= 0) {
//edit
@@ -799,17 +726,6 @@ object AngConfigManager {
if (servers == null) {
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)
// var servers = server
@@ -820,7 +736,7 @@ object AngConfigManager {
var count = 0
servers.lines()
.forEach {
val resId = importConfig(it, subid, removedSelectedServer)
val resId = importConfig(it, subid)
if (resId == 0) {
count++
}
@@ -860,7 +776,6 @@ object AngConfigManager {
for (k in configs.vmess.count() - 1 downTo 0) {
if (configs.vmess[k].subid.equals(subid)) {
angConfig.vmess.removeAt(k)
adjustIndexForRemovalAt(k)
}
}
@@ -906,4 +821,4 @@ object AngConfigManager {
return 0
}
}
}
@@ -16,7 +16,7 @@ object AppManagerUtil {
val apps = ArrayList<AppInfo>()
for (pkg in packages) {
if (!pkg.hasInternetPermission && pkg.packageName != "android") continue
if (!pkg.hasInternetPermission) continue
val applicationInfo = pkg.applicationInfo
@@ -40,4 +40,4 @@ object AppManagerUtil {
val permissions = requestedPermissions
return permissions?.any { it == Manifest.permission.INTERNET } ?: false
}
}
}
@@ -11,33 +11,42 @@ import com.google.zxing.qrcode.QRCodeWriter
import com.google.zxing.EncodeHintType
import java.util.*
import kotlin.collections.HashMap
import android.app.ActivityManager
import android.content.ClipData
import android.content.Intent
import android.content.res.AssetManager
import android.net.Uri
import android.os.SystemClock
import android.text.TextUtils
import android.text.method.ScrollingMovementMethod
import android.util.Log
import android.util.Patterns
import android.view.View
import android.webkit.URLUtil
import com.v2ray.ang.AngApplication
import com.v2ray.ang.AppConfig
import com.v2ray.ang.R
import com.v2ray.ang.extension.defaultDPreference
import com.v2ray.ang.extension.responseLength
import com.v2ray.ang.extension.toast
import com.v2ray.ang.extension.v2RayApplication
import com.v2ray.ang.service.V2RayServiceManager
import com.v2ray.ang.service.V2RayVpnService
import com.v2ray.ang.ui.SettingsActivity
import kotlinx.coroutines.isActive
import kotlinx.android.synthetic.main.activity_logcat.*
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.InputStreamReader
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 {
val tcpTestingSockets = ArrayList<Socket?>()
/**
* convert string to editalbe for kotlin
*
@@ -92,7 +101,7 @@ object Utils {
try {
val cmb = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val clipData = ClipData.newPlainText(null, content)
cmb.setPrimaryClip(clipData)
cmb.primaryClip = clipData
} catch (e: Exception) {
e.printStackTrace()
}
@@ -270,14 +279,55 @@ object Utils {
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
}
V2RayServiceManager.startV2Ray(context)
return true
}
/**
@@ -285,7 +335,6 @@ object Utils {
*/
fun startVService(context: Context, guid: String): Boolean {
val index = AngConfigManager.getIndexViaGuid(guid)
context.v2RayApplication.curIndex=index
return startVService(context, index)
}
@@ -293,11 +342,8 @@ object Utils {
* startVService
*/
fun startVService(context: Context, index: Int): Boolean {
if (AngConfigManager.setActiveServer(index) < 0) {
return false
}
V2RayServiceManager.startV2Ray(context)
return true
AngConfigManager.setActiveServer(index)
return startVService(context)
}
/**
@@ -396,6 +442,7 @@ object Utils {
return path
}
/**
* readTextFromAssets
*/
@@ -430,13 +477,10 @@ object Utils {
/**
* tcping
*/
suspend fun tcping(url: String, port: Int): String {
fun tcping(url: String, port: Int): String {
var time = -1L
for (k in 0 until 2) {
val one = socketConnectTime(url, port)
if (!coroutineContext.isActive) {
break
}
if (one != -1L )
if(time == -1L || one < time) {
time = one
@@ -447,35 +491,19 @@ object Utils {
fun socketConnectTime(url: String, port: Int): Long {
try {
val socket = Socket()
synchronized(this) {
tcpTestingSockets.add(socket)
}
val start = System.currentTimeMillis()
socket.connect(InetSocketAddress(url, port))
val socket = Socket(url, port)
val time = System.currentTimeMillis() - start
synchronized(this) {
tcpTestingSockets.remove(socket)
}
socket.close()
return time
} catch (e: UnknownHostException) {
e.printStackTrace()
} catch (e: IOException) {
Log.d(AppConfig.ANG_PACKAGE, "socketConnectTime IOException: $e")
e.printStackTrace()
} catch (e: Exception) {
e.printStackTrace()
}
return -1
}
fun closeAllTcpSockets() {
synchronized(this) {
tcpTestingSockets.forEach {
it?.close()
}
tcpTestingSockets.clear()
}
}
}
@@ -1,21 +1,20 @@
package com.v2ray.ang.util
import android.content.Context
import android.text.TextUtils
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.AppConfig
import com.v2ray.ang.dto.AngConfig.VmessBean
import com.v2ray.ang.dto.V2rayConfig
import com.v2ray.ang.extension.defaultDPreference
import com.v2ray.ang.ui.SettingsActivity
import org.json.JSONException
import org.json.JSONObject
import org.json.JSONArray
import com.v2ray.ang.dto.EConfigType
import com.v2ray.ang.extension.defaultDPreference
import kotlin.collections.ArrayList
import kotlin.collections.LinkedHashSet
import com.google.gson.JsonObject
object V2rayConfigUtil {
private val requestObj: JsonObject by lazy {
@@ -42,10 +41,19 @@ object V2rayConfigUtil {
// return result
// }
if (vmess.configType == EConfigType.CUSTOM.value) {
result = getV2rayConfigType2(app, vmess)
} else {
if (vmess.configType == AppConfig.EConfigType.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)
@@ -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的客户端配置文件
*/
@@ -129,7 +116,6 @@ object V2rayConfigUtil {
val jsonConfig = app.defaultDPreference.getPrefString(AppConfig.ANG_CONFIG + guid, "")
result.status = true
result.content = jsonConfig
parseDomainNameAndTag(app, jsonConfig)
return result
} catch (e: Exception) {
@@ -143,12 +129,6 @@ object V2rayConfigUtil {
*/
private fun inbounds(vmess: VmessBean, v2rayConfig: V2rayConfig, app: AngApplication): Boolean {
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
// val socksPort = Utils.parseInt(app.defaultDPreference.getPrefString(SettingsActivity.PREF_SOCKS_PORT, "10808"))
// val lanconnPort = Utils.parseInt(app.defaultDPreference.getPrefString(SettingsActivity.PREF_HTTP_PORT, ""))
@@ -178,12 +158,8 @@ object V2rayConfigUtil {
try {
val outbound = v2rayConfig.outbounds[0]
val configType = EConfigType.fromInt(vmess.configType)
if (configType != null) {
outbound.protocol = configType.name.toLowerCase()
}
when (configType) {
EConfigType.VMESS -> {
when (vmess.configType) {
AppConfig.EConfigType.Vmess -> {
outbound.settings?.servers = null
val vnext = v2rayConfig.outbounds[0].settings?.vnext?.get(0)
@@ -201,8 +177,10 @@ object V2rayConfigUtil {
//远程服务器底层传输配置
outbound.streamSettings = boundStreamSettings(vmess)
outbound.protocol = "vmess"
}
EConfigType.SHADOWSOCKS -> {
AppConfig.EConfigType.Shadowsocks -> {
outbound.settings?.vnext = null
val server = outbound.settings?.servers?.get(0)
@@ -215,8 +193,10 @@ object V2rayConfigUtil {
//Mux
outbound.mux?.enabled = false
outbound.protocol = "shadowsocks"
}
EConfigType.SOCKS -> {
AppConfig.EConfigType.Socks -> {
outbound.settings?.vnext = null
val server = outbound.settings?.servers?.get(0)
@@ -225,22 +205,21 @@ object V2rayConfigUtil {
//Mux
outbound.mux?.enabled = false
outbound.protocol = "socks"
}
else -> {
}
}
val serverDomain = if (Utils.isIpv6Address(vmess.address)) {
String.format("[%s]:%s", vmess.address, vmess.port)
var serverDomain: String
if(Utils.isIpv6Address(vmess.address)) {
serverDomain = String.format("[%s]:%s", vmess.address, vmess.port)
} 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)
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) {
e.printStackTrace()
return false
@@ -261,7 +240,7 @@ object V2rayConfigUtil {
//streamSettings
when (streamSettings.network) {
"kcp" -> {
val kcpsettings = V2rayConfig.OutboundBean.StreamSettingsBean.KcpSettingsBean()
val kcpsettings = V2rayConfig.OutboundBean.StreamSettingsBean.KcpsettingsBean()
kcpsettings.mtu = 1350
kcpsettings.tti = 50
kcpsettings.uplinkCapacity = 12
@@ -269,33 +248,34 @@ object V2rayConfigUtil {
kcpsettings.congestion = false
kcpsettings.readBufferSize = 1
kcpsettings.writeBufferSize = 1
kcpsettings.header = V2rayConfig.OutboundBean.StreamSettingsBean.KcpSettingsBean.HeaderBean()
kcpsettings.header = V2rayConfig.OutboundBean.StreamSettingsBean.KcpsettingsBean.HeaderBean()
kcpsettings.header.type = vmess.headerType
streamSettings.kcpSettings = kcpsettings
streamSettings.kcpsettings = kcpsettings
}
"ws" -> {
val wssettings = V2rayConfig.OutboundBean.StreamSettingsBean.WsSettingsBean()
val wssettings = V2rayConfig.OutboundBean.StreamSettingsBean.WssettingsBean()
wssettings.connectionReuse = true
val host = vmess.requestHost.trim()
val path = vmess.path.trim()
if (!TextUtils.isEmpty(host)) {
wssettings.headers = V2rayConfig.OutboundBean.StreamSettingsBean.WsSettingsBean.HeadersBean()
wssettings.headers = V2rayConfig.OutboundBean.StreamSettingsBean.WssettingsBean.HeadersBean()
wssettings.headers.Host = host
}
if (!TextUtils.isEmpty(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
if (!TextUtils.isEmpty(host)) {
tlssettings.serverName = host
}
streamSettings.tlsSettings = tlssettings
streamSettings.tlssettings = tlssettings
}
"h2" -> {
val httpsettings = V2rayConfig.OutboundBean.StreamSettingsBean.HttpSettingsBean()
val httpsettings = V2rayConfig.OutboundBean.StreamSettingsBean.HttpsettingsBean()
val host = vmess.requestHost.trim()
val path = vmess.path.trim()
@@ -303,30 +283,31 @@ object V2rayConfigUtil {
httpsettings.host = host.split(",").map { it.trim() }
}
httpsettings.path = path
streamSettings.httpSettings = httpsettings
streamSettings.httpsettings = httpsettings
val tlssettings = V2rayConfig.OutboundBean.StreamSettingsBean.TlsSettingsBean()
val tlssettings = V2rayConfig.OutboundBean.StreamSettingsBean.TlssettingsBean()
tlssettings.allowInsecure = true
streamSettings.tlsSettings = tlssettings
streamSettings.tlssettings = tlssettings
}
"quic" -> {
val quicsettings = V2rayConfig.OutboundBean.StreamSettingsBean.QuicSettingBean()
val quicsettings = V2rayConfig.OutboundBean.StreamSettingsBean.QuicsettingBean()
val host = vmess.requestHost.trim()
val path = vmess.path.trim()
quicsettings.security = host
quicsettings.key = path
quicsettings.header = V2rayConfig.OutboundBean.StreamSettingsBean.QuicSettingBean.HeaderBean()
quicsettings.header = V2rayConfig.OutboundBean.StreamSettingsBean.QuicsettingBean.HeaderBean()
quicsettings.header.type = vmess.headerType
streamSettings.quicSettings = quicsettings
streamSettings.quicsettings = quicsettings
}
else -> {
//tcp带http伪装
if (vmess.headerType == "http") {
val tcpSettings = V2rayConfig.OutboundBean.StreamSettingsBean.TcpSettingsBean()
tcpSettings.header = V2rayConfig.OutboundBean.StreamSettingsBean.TcpSettingsBean.HeaderBean()
val tcpSettings = V2rayConfig.OutboundBean.StreamSettingsBean.TcpsettingsBean()
tcpSettings.connectionReuse = true
tcpSettings.header = V2rayConfig.OutboundBean.StreamSettingsBean.TcpsettingsBean.HeaderBean()
tcpSettings.header.type = vmess.headerType
// if (requestObj.has("headers")
@@ -566,7 +547,7 @@ object V2rayConfigUtil {
mux = null))
}
// DNS routing
// DNS routing
v2rayConfig.routing.rules.add(0, V2rayConfig.RoutingBean.RulesBean(
type = "field",
outboundTag = AppConfig.TAG_DIRECT,
@@ -632,58 +613,42 @@ object V2rayConfigUtil {
}
}
private fun parseDomainNameAndTag(app: AngApplication, jsonConfig: String) {
private fun parseDomainName(jsonConfig: String): String {
try {
val jObj = JSONObject(jsonConfig)
var domainName = ""
val tags = LinkedHashSet<String>()
var domainName: String
if (jObj.has("outbound")) {
val (domain, tag) = parseDomainNameAndTag(jObj.optJSONObject("outbound"))
domainName = domain
if (!TextUtils.isEmpty(tag)) {
tags.add(tag)
domainName = parseDomainName(jObj.optJSONObject("outbound"))
if (!TextUtils.isEmpty(domainName)) {
return domainName
}
}
if (jObj.has("outbounds")) {
for (i in 0..(jObj.optJSONArray("outbounds").length() - 1)) {
val (domain, tag) = parseDomainNameAndTag(jObj.optJSONArray("outbounds").getJSONObject(i))
if (!TextUtils.isEmpty(domain) && TextUtils.isEmpty(domainName)) {
domainName = domain
}
if (!TextUtils.isEmpty(tag)) {
tags.add(tag)
domainName = parseDomainName(jObj.optJSONArray("outbounds").getJSONObject(i))
if (!TextUtils.isEmpty(domainName)) {
return domainName
}
}
}
if (jObj.has("outboundDetour")) {
for (i in 0..(jObj.optJSONArray("outboundDetour").length() - 1)) {
val (domain, tag) = parseDomainNameAndTag(jObj.optJSONArray("outboundDetour").getJSONObject(i))
if (!TextUtils.isEmpty(domain) && TextUtils.isEmpty(domainName)) {
domainName = domain
}
if (!TextUtils.isEmpty(tag)) {
tags.add(tag)
domainName = parseDomainName(jObj.optJSONArray("outboundDetour").getJSONObject(i))
if (!TextUtils.isEmpty(domainName)) {
return domainName
}
}
}
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) {
e.printStackTrace()
}
return ""
}
private fun parseDomainNameAndTag(outbound: JSONObject): Pair<String, String> {
val tag = if (outbound.has("tag")) {
outbound.getString("tag")
} else {
""
}
private fun parseDomainName(outbound: JSONObject): String {
try {
if (outbound.has("settings")) {
val vnext: JSONArray?
var vnext: JSONArray?
if (outbound.optJSONObject("settings").has("vnext")) {
// vmess
vnext = outbound.optJSONObject("settings").optJSONArray("vnext")
@@ -691,22 +656,22 @@ object V2rayConfigUtil {
// shadowsocks or socks
vnext = outbound.optJSONObject("settings").optJSONArray("servers")
} else {
return Pair("", tag)
return ""
}
for (i in 0..(vnext.length() - 1)) {
val item = vnext.getJSONObject(i)
val address = item.getString("address")
val port = item.getString("port")
if(Utils.isIpv6Address(address)) {
return Pair(String.format("[%s]:%s", address, port), tag)
return String.format("[%s]:%s", address, port)
} else {
return Pair(String.format("%s:%s", address, port), tag)
return String.format("%s:%s", address, port)
}
}
}
} catch (e: Exception) {
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:layout_width="match_parent"
android:layout_height="@dimen/connection_test_height"
android:background="@drawable/background_test_button"
android:gravity="center|left"
android:clickable="true"
android:focusable="true">
android:background="@color/colorPrimary_text"
android:gravity="center|left">
<TextView
android:id="@+id/tv_test_state"
@@ -94,8 +92,6 @@
android:layout_marginLeft="16dp"
android:layout_marginTop="16dp"
android:src="@drawable/ic_v_idle"
android:clickable="true"
android:focusable="true"
app:layout_anchorGravity="bottom|right|end" />
</com.github.jorgecastilloprz.FABProgressCircle>
@@ -111,23 +107,7 @@
app:headerLayout="@layout/nav_header"
app:itemIconTint="@color/colorPrimary_dark"
app:itemTextColor="@color/colorPrimary"
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>
app:menu="@menu/menu_drawer" />
</android.support.v4.widget.DrawerLayout>
@@ -68,15 +68,20 @@
android:text="@string/server_lab_content"
android:textAppearance="@style/TextAppearance.AppCompat.Subhead" />
<EditText
<TextView
android:id="@+id/tv_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_margin_top_height"
android:text="" />
</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>
</ScrollView>
@@ -2,10 +2,7 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true">
android:gravity="center_vertical">
<android.support.v7.widget.AppCompatImageView
android:id="@+id/icon"
@@ -43,7 +40,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="false"
android:focusable="false"
android:paddingStart="2dp"
android:paddingLeft="2dp"
android:paddingEnd="6dp"
@@ -9,6 +9,10 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="3dp"
android:clickable="true"
android:focusable="true"
android:foreground="?android:attr/selectableItemBackground"
android:orientation="horizontal"
card_view:cardCornerRadius="5dp">
<LinearLayout
@@ -17,11 +21,7 @@
android:layout_height="@dimen/server_height"
android:layout_gravity="center"
android:gravity="center"
android:orientation="horizontal"
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:nextFocusRight="@+id/layout_share">
android:orientation="horizontal">
<LinearLayout
android:layout_width="wrap_content"
@@ -111,14 +111,10 @@
<LinearLayout
android:id="@+id/layout_share"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_height="@dimen/server_height"
android:gravity="center"
android:orientation="vertical"
android:padding="@dimen/layout_margin_spacing"
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:nextFocusLeft="@+id/info_container">
android:paddingEnd="@dimen/layout_margin_right_height">
<ImageView
android:layout_width="wrap_content"
@@ -130,13 +126,10 @@
<LinearLayout
android:id="@+id/layout_edit"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_height="@dimen/server_height"
android:gravity="center"
android:orientation="vertical"
android:padding="@dimen/layout_margin_spacing"
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true">
android:paddingEnd="@dimen/layout_margin_right_height">
<ImageView
android:layout_width="@dimen/png_height"
@@ -148,13 +141,10 @@
<LinearLayout
android:id="@+id/layout_remove"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_height="@dimen/server_height"
android:gravity="center"
android:orientation="vertical"
android:padding="@dimen/layout_margin_spacing"
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true">
android:paddingEnd="@dimen/layout_margin_right_height">
<ImageView
android:layout_width="@dimen/png_height"
@@ -6,7 +6,6 @@
android:gravity="center_vertical">
<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_height="wrap_content"
android:layout_margin="3dp"
@@ -14,8 +13,7 @@
android:focusable="true"
android:foreground="?android:attr/selectableItemBackground"
android:orientation="horizontal"
card_view:cardCornerRadius="5dp"
android:nextFocusRight="@+id/layout_edit">
card_view:cardCornerRadius="5dp">
<LinearLayout
android:id="@+id/info_container"
@@ -64,14 +62,11 @@
<LinearLayout
android:id="@+id/layout_share"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_height="@dimen/server_height"
android:gravity="center"
android:orientation="vertical"
android:padding="@dimen/layout_margin_spacing"
android:visibility="invisible"
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true">
android:paddingEnd="@dimen/layout_margin_right_height"
android:visibility="invisible">
<ImageView
android:layout_width="@dimen/png_height"
@@ -83,14 +78,10 @@
<LinearLayout
android:id="@+id/layout_edit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_height="@dimen/server_height"
android:gravity="center"
android:orientation="vertical"
android:padding="@dimen/layout_margin_spacing"
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:nextFocusLeft="@+id/item_cardview">
android:paddingEnd="@dimen/layout_margin_right_height">
<ImageView
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"?>
<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:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -7,9 +14,16 @@
android:gravity="center"
android:orientation="vertical">
<ImageView
<ImageView
android:layout_width="40dp"
android:layout_height="40dp"
android:padding="10dp"
android:src="@drawable/ic_v" />
android:src="@mipmap/ic_launcher" />
<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>
+15 -12
View File
@@ -4,7 +4,12 @@
tools:showIn="navigation_view">
<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
android:id="@+id/sub_setting"
android:icon="@drawable/ic_subscriptions_white_24dp"
@@ -16,27 +21,25 @@
</group>
<group android:id="@+id/group_id2">
<item
<item android:title="@string/title_about">
<menu>
<item
android:id="@+id/promotion"
android:icon="@drawable/ic_whatshot_white_24dp"
android:title="@string/title_pref_promotion" />
<item
<item
android:id="@+id/donate"
android:icon="@drawable/ic_attach_money_white_24dp"
android:title="@string/title_pref_donate" />
<item
<item
android:id="@+id/logcat"
android:icon="@drawable/ic_logcat_white_24dp"
android:title="@string/title_logcat" />
<item
<item
android:id="@+id/feedback"
android:icon="@drawable/ic_feedback_white_24dp"
android:title="@string/title_pref_feedback" />
<!-- place holder for version text at the bottom -->
<item
android:id="@+id/placeholder"
android:enabled="false"
android:title="" />
</group>
</menu>
</item>
</menu>
@@ -6,9 +6,4 @@
android:icon="@drawable/ic_copy_white"
android:title="@string/logcat_copy"
app:showAsAction="always" />
<item
android:id="@+id/delete"
android:icon="@drawable/ic_delete_white_24dp"
android:title="@string/logcat_delete"
app:showAsAction="ifRoom" />
</menu>
+36 -1
View File
@@ -17,6 +17,17 @@
<copyright>Copyright(C) 2008-2011 The Android Open Source Project</copyright>
<license>Apache Software License 2.0</license>
</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>
<name>Google Gson</name>
<url>https://github.com/google/gson</url>
@@ -41,9 +52,33 @@
<copyright>Copyright 2015 Square, Inc.</copyright>
<license>Apache Software License 2.0</license>
</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>
<name>RxPermissions</name>
<url>https://github.com/tbruyelle/RxPermissions</url>
<license>Apache Software License 2.0</license>
</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_widget_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_close">Close navigation drawer</string>
@@ -73,6 +73,7 @@
<!-- Preferences -->
<string name="title_settings">设置</string>
<string name="title_about">关于</string>
<string name="title_advanced">进阶设置</string>
<string name="title_pref_per_app_proxy">分应用代理</string>
@@ -82,7 +83,7 @@
<string name="summary_pref_mux_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="summary_pref_sniffing_enabled">流量探测</string>
@@ -122,8 +123,7 @@
<string name="title_pref_promotion">推广</string>
<string name="summary_pref_promotion">一些推广,点击查看详情(捐赠可去除)</string>
<string name="title_mode">模式</string>
<string name="title_mode_help">点此查看更多帮助</string>
<string name="title_pref_version">版本</string>
<string name="donate_error_setup">初始化错误:</string>
<string name="donate_error_inventory">无法查询到项目</string>
@@ -135,8 +135,7 @@
<string name="title_logcat">Logcat</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="sub_setting_remarks">备注</string>
<string name="sub_setting_url">地址(url)</string>
@@ -181,15 +180,5 @@
<item>绕过大陆地址</item>
<item>绕过局域网及大陆地址</item>
</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>
@@ -3,7 +3,7 @@
<string name="app_name">v2rayNG</string>
<string name="app_widget_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_close">Close navigation drawer</string>
@@ -17,7 +17,7 @@
<string name="toast_services_failure">啟動服務失敗</string>
<!--ServerActivity-->
<string name="title_server">設定檔</string>
<string name="title_server">組態</string>
<string name="menu_item_add_config">新增組態</string>
<string name="menu_item_save_config">儲存組態</string>
<string name="menu_item_del_config">刪除組態</string>
@@ -57,7 +57,7 @@
<string name="title_file_chooser">選取一個設定檔</string>
<string name="toast_require_file_manager">請安裝檔案總管。</string>
<string name="server_customize_config">自訂組態</string>
<string name="toast_config_file_invalid">無效設定檔</string>
<string name="toast_config_file_invalid">無效組態</string>
<string name="server_lab_content">內容</string>
<string name="toast_none_data_clipboard">剪貼簿內無資料</string>
<string name="toast_invalid_url">網址無效</string>
@@ -74,6 +74,7 @@
<!-- Preferences -->
<string name="title_settings">設定</string>
<string name="title_about">關於</string>
<string name="title_advanced">進階設定</string>
<string name="title_pref_per_app_proxy">Proxy 個別應用程式</string>
@@ -83,7 +84,7 @@
<string name="summary_pref_mux_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="summary_pref_sniffing_enabled">流量探測</string>
@@ -103,17 +104,17 @@
<string name="summary_pref_donate">向開發人員捐款</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="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="title_pref_socks_port">SOCKS5代理連接埠</string>
<string name="summary_pref_socks_port">SOCKS5代理連接埠</string>
<string name="title_pref_socks_port">SOCKS5代理端口</string>
<string name="summary_pref_socks_port">SOCKS5代理端口</string>
<string name="title_pref_http_port">HTTP代理連接埠</string>
<string name="summary_pref_http_port">HTTP代理連接埠</string>
<string name="title_pref_http_port">HTTP代理端口</string>
<string name="summary_pref_http_port">HTTP代理端口</string>
<string name="title_pref_feedback">回饋</string>
@@ -124,8 +125,7 @@
<string name="title_pref_promotion">推廣</string>
<string name="summary_pref_promotion">一些推廣,點擊查看詳情(捐款可去除)</string>
<string name="title_mode">模式</string>
<string name="title_mode_help">點此查看更多幫助</string>
<string name="title_pref_version">版本</string>
<string name="donate_error_setup">錯誤設定:</string>
<string name="donate_error_inventory">Error querying inventory</string>
@@ -137,8 +137,7 @@
<string name="title_logcat">Logcat</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="sub_setting_remarks">備註</string>
<string name="sub_setting_url">位址(url)</string>
@@ -154,7 +153,7 @@
<string name="routing_settings_delete">清除</string>
<string name="routing_settings_scan_replace">掃描並取代</string>
<string name="routing_settings_scan_append">掃描並附加</string>
<string name="routing_settings_default_rules">設置預設路由規則</string>
<string name="routing_settings_default_rules">設置默認路由規則</string>
<string name="connection_test_pending">"檢查連線能力"</string>
<string name="connection_test_testing">"測試中……"</string>
@@ -179,19 +178,8 @@
<string-array name="routing_mode">
<item>全球</item>
<item>略過域網</item>
<item>略過域網</item>
<item>略過中國大陸</item>
<item>略過域網及中國大陸</item>
<item>略過域網及中國大陸</item>
</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>
@@ -66,44 +66,4 @@
<item>IPIfNonMatch</item>
<item>IPOnDemand</item>
</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>
@@ -3,7 +3,6 @@
<dimen name="bypass_list_header_height">50dp</dimen>
<dimen name="layout_margin_top_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="png_height">24dp</dimen>
<dimen name="server_height">72dp</dimen>
+6 -18
View File
@@ -3,7 +3,7 @@
<string name="app_name">v2rayNG</string>
<string name="app_widget_name">Switch</string>
<string name="app_tile_name">Switch</string>
<string name="app_tile_first_use">First use of this feature, please use the app to add server</string>
<string name="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_close">Close navigation drawer</string>
@@ -41,7 +41,7 @@
<string name="server_lab_network">network</string>
<string name="server_lab_more_function">more function</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_stream_security">tls</string>
<string name="server_lab_allow_insecure">allowInsecure</string>
@@ -74,6 +74,7 @@
<!-- Preferences -->
<string name="title_settings">Settings</string>
<string name="title_about">About</string>
<string name="title_advanced">Advanced Settings</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="title_pref_speed_enabled">Enable speed display</string>
<string name="summary_pref_speed_enabled">Display current speed in the notification.\nNotification icon would change based on
usage.</string>
<string name="summary_pref_speed_enabled">Display current speed in the notification</string>
<string name="title_pref_sniffing_enabled">Enable Sniffing</string>
<string name="summary_pref_sniffing_enabled">Sniffing</string>
@@ -124,8 +124,7 @@
<string name="title_pref_promotion">Promotion</string>
<string name="summary_pref_promotion">Promotion,click for details(Donation can be removed)</string>
<string name="title_mode">Mode</string>
<string name="title_mode_help">Click me for more help</string>
<string name="title_pref_version">Version</string>
<string name="donate_error_setup">Error Setup:</string>
<string name="donate_error_inventory">Error querying inventory</string>
@@ -137,8 +136,7 @@
<string name="title_logcat">Logcat</string>
<string name="logcat_copy">Copy</string>
<string name="logcat_delete">Delete</string>
<string name="title_export_all">Export non-custom configs to clipboard</string>
<string name="title_export_all">Export all config to clipboard</string>
<string name="title_sub_setting">Subscription setting</string>
<string name="sub_setting_remarks">remarks</string>
<string name="sub_setting_url">url</string>
@@ -183,15 +181,5 @@
<item>Bypass mainland address</item>
<item>Bypassing LAN and mainland address</item>
</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>
@@ -13,12 +13,6 @@
<item name="windowNoTitle">true</item>
</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.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
+25 -16
View File
@@ -22,13 +22,6 @@
android:summary="@string/summary_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 android:title="@string/title_pref_routing">
@@ -85,13 +78,29 @@
android:key="pref_http_port"
android:summary="10809"
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>
</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>
+1 -1
View File
@@ -7,7 +7,7 @@ buildscript {
maven { url 'https://maven.google.com' }
}
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"
// NOTE: Do not place your application dependencies here; they belong
+4 -4
View File
@@ -1,8 +1,8 @@
apply plugin: 'com.android.library'
android {
compileSdkVersion Integer.parseInt("$compileSdkVer")
buildToolsVersion buildToolsVer
compileSdkVersion 28
buildToolsVersion '28.0.3'
defaultConfig {
minSdkVersion 17
@@ -19,7 +19,7 @@ android {
dependencies {
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.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.text.TextUtils;
import java.util.LinkedHashSet;
import java.util.Set;
/**
@@ -47,6 +45,10 @@ public class DPreference {
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) {
return PrefAccessor.getInt(mContext, mName, key, defaultValue);
}
@@ -59,26 +61,10 @@ public class DPreference {
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) {
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) {
PrefAccessor.remove(mContext, mName, key);
}
@@ -4,7 +4,6 @@ import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.LinkedHashSet;
import java.util.Set;
public class StringSetConverter {
@@ -14,8 +13,8 @@ public class StringSetConverter {
return gson.toJson(src);
}
public static LinkedHashSet<String> decode(String json) {
Type setType = new TypeToken<LinkedHashSet<String>>() {
public static Set<String> decode(String json) {
Type setType = new TypeToken<Set<String>>() {
}.getType();
return gson.fromJson(json, setType);
}
+5 -4
View File
@@ -13,9 +13,10 @@ org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryErro
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
#Fri Jun 02 14:08:42 CST 2017
kotlinVersion=1.4.10
ankoVersion=0.10.8
kotlinVersion=1.3.40
supportLibVersion=28.0.0
buildToolsVer=30.0.2
compileSdkVer=30
buildToolsVer=28.0.3
compileSdkVer=28
kotlin.incremental=true
targetSdkVer=30
targetSdkVer=28
-6
View File
@@ -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