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
66 changed files with 1065 additions and 1677 deletions
+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/geoip.dat
V2rayNG/app/src/main/assets/geosite.dat V2rayNG/app/src/main/assets/geosite.dat
V2rayNG/app/src/main/java/com/v2ray/ang/InappBuyActivity.java V2rayNG/app/src/main/java/com/v2ray/ang/InappBuyActivity.java
V2rayNG/gradle/wrapper/gradle-wrapper.properties
V2rayNG/gradle/wrapper/gradle-wrapper.properties
*.dat *.dat
*.jks *.jks
V2rayNG/app/release/output.json V2rayNG/gradle/wrapper/gradle-wrapper.properties
.idea/ V2rayNG/gradle/wrapper/gradle-wrapper.properties
.gradle/ V2rayNG/app/release/output.json
+4 -5
View File
@@ -5,15 +5,14 @@ import (
) )
type Status struct { type Status struct {
IsRunning bool IsRunning bool
PackageName string PackageName string
PackageCodePath string
Vpoint v2core.Server Vpoint v2core.Server
} }
func CheckVersion() int { func CheckVersion() int {
return 21 return 20
} }
func (v *Status) GetDataDir() string { func (v *Status) GetDataDir() string {
@@ -21,7 +20,7 @@ func (v *Status) GetDataDir() string {
} }
func (v *Status) GetApp(name string) 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) { func (v *Status) GetTun2socksArgs(localDNS bool, enableIPv6 bool) (ret []string) {
+2 -4
View File
@@ -44,7 +44,6 @@ type V2RayPoint struct {
closeChan chan struct{} closeChan chan struct{}
PackageName string PackageName string
PackageCodePath string
DomainName string DomainName string
ConfigureFileContent string ConfigureFileContent string
EnableLocalDNS bool EnableLocalDNS bool
@@ -68,7 +67,6 @@ func (v *V2RayPoint) RunLoop() (err error) {
defer v.v2rayOP.Unlock() defer v.v2rayOP.Unlock()
//Construct Context //Construct Context
v.status.PackageName = v.PackageName v.status.PackageName = v.PackageName
v.status.PackageCodePath = v.PackageCodePath
if !v.status.IsRunning { if !v.status.IsRunning {
v.closeChan = make(chan struct{}) v.closeChan = make(chan struct{})
@@ -118,7 +116,7 @@ func (v V2RayPoint) QueryStats(tag string, direct string) int64 {
if v.statsManager == nil { if v.statsManager == nil {
return 0 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 { if counter == nil {
return 0 return 0
} }
@@ -235,7 +233,7 @@ func (v V2RayPoint) runTun2socks() error {
v.escorter.EscortingUp() v.escorter.EscortingUp()
go v.escorter.EscortRun( go v.escorter.EscortRun(
v.status.GetApp("libtun2socks.so"), v.status.GetApp("tun2socks"),
v.status.GetTun2socksArgs(v.EnableLocalDNS, v.ForwardIpv6), "", v.status.GetTun2socksArgs(v.EnableLocalDNS, v.ForwardIpv6), "",
v.SupportSet.SendFd) v.SupportSet.SendFd)
-19
View File
@@ -1,25 +1,6 @@
# v2rayNG # 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.0-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"> <a href="https://play.google.com/store/apps/details?id=com.v2ray.ang">
<img alt="Get it on Google Play" src="https://play.google.com/intl/en_us/badges/images/generic/en_badge_web_generic.png" width="165" height="64" /> <img alt="Get it on Google Play" src="https://play.google.com/intl/en_us/badges/images/generic/en_badge_web_generic.png" width="165" height="64" />
</a> </a>
### Usage
See 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
+24 -12
View File
@@ -3,8 +3,8 @@ apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlin-android-extensions'
android { android {
compileSdkVersion Integer.parseInt("$compileSdkVer") compileSdkVersion 28
buildToolsVersion buildToolsVer buildToolsVersion '28.0.3'
compileOptions { compileOptions {
targetCompatibility = "8" targetCompatibility = "8"
@@ -20,17 +20,34 @@ android {
versionName "1.0.2" versionName "1.0.2"
} }
signingConfigs {
release {
storeFile file("../key.jks")
keyAlias 'ang'
keyPassword '123456'
storePassword '123456'
}
debug {
storeFile file("../key.jks")
keyAlias 'ang'
keyPassword '123456'
storePassword '123456'
}
}
buildTypes { buildTypes {
release { release {
minifyEnabled false minifyEnabled false
zipAlignEnabled false zipAlignEnabled false
shrinkResources false shrinkResources false
signingConfig signingConfigs.release
// proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' // proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
} }
debug { debug {
minifyEnabled false minifyEnabled false
zipAlignEnabled false zipAlignEnabled false
shrinkResources false shrinkResources false
signingConfig signingConfigs.release
} }
} }
@@ -62,14 +79,11 @@ android {
dependencies { dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs') implementation fileTree(include: ['*.jar'], dir: 'libs')
testImplementation 'junit:junit:4.13' implementation 'com.android.support.constraint:constraint-layout:1.1.3'
testImplementation 'junit:junit:4.12'
implementation project(':dpreference') implementation project(':dpreference')
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion" implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion" implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9"
// Android support library // Android support library
implementation "com.android.support:support-v4:$supportLibVersion" implementation "com.android.support:support-v4:$supportLibVersion"
implementation "com.android.support:appcompat-v7:$supportLibVersion" implementation "com.android.support:appcompat-v7:$supportLibVersion"
@@ -77,15 +91,12 @@ dependencies {
implementation "com.android.support:cardview-v7:$supportLibVersion" implementation "com.android.support:cardview-v7:$supportLibVersion"
implementation "com.android.support:preference-v7:$supportLibVersion" implementation "com.android.support:preference-v7:$supportLibVersion"
implementation "com.android.support:recyclerview-v7:$supportLibVersion" implementation "com.android.support:recyclerview-v7:$supportLibVersion"
implementation "com.android.support:multidex:1.0.3"
implementation 'com.android.support.constraint:constraint-layout:2.0.1'
// DSL // DSL
implementation "org.jetbrains.anko:anko-sdk15:$ankoVersion" implementation "org.jetbrains.anko:anko-sdk15:$ankoVersion"
implementation "org.jetbrains.anko:anko-support-v4:$ankoVersion" implementation "org.jetbrains.anko:anko-support-v4:$ankoVersion"
implementation "org.jetbrains.anko:anko-appcompat-v7:$ankoVersion" implementation "org.jetbrains.anko:anko-appcompat-v7:$ankoVersion"
implementation "org.jetbrains.anko:anko-design:$ankoVersion" implementation "org.jetbrains.anko:anko-design:$ankoVersion"
implementation 'com.google.code.gson:gson:2.8.6' implementation 'com.google.code.gson:gson:2.8.5'
implementation 'io.reactivex:rxjava:1.3.4' implementation 'io.reactivex:rxjava:1.3.4'
implementation 'io.reactivex:rxandroid:1.2.1' implementation 'io.reactivex:rxandroid:1.2.1'
implementation 'com.tbruyelle.rxpermissions:rxpermissions:0.9.4@aar' implementation 'com.tbruyelle.rxpermissions:rxpermissions:0.9.4@aar'
@@ -95,6 +106,7 @@ dependencies {
implementation 'me.dm7.barcodescanner:zxing:1.9.8' implementation 'me.dm7.barcodescanner:zxing:1.9.8'
implementation 'com.github.jorgecastilloprz:fabprogresscircle:1.01@aar' implementation 'com.github.jorgecastilloprz:fabprogresscircle:1.01@aar'
implementation 'com.beust:klaxon:3.0.1' implementation 'com.beust:klaxon:3.0.1'
implementation 'com.android.support:multidex:1.0.3'
implementation(name: 'libv2ray', ext: 'aar') implementation(name: 'libv2ray', ext: 'aar')
//implementation(name: 'tun2socks', ext: 'aar') //implementation(name: 'tun2socks', ext: 'aar')
@@ -114,4 +126,4 @@ repositories {
flatDir { flatDir {
dirs 'libs' dirs 'libs'
} }
} }
Binary file not shown.
+16 -39
View File
@@ -2,16 +2,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.v2ray.ang"> package="com.v2ray.ang">
<supports-screens
android:anyDensity="true"
android:smallScreens="true"
android:normalScreens="true"
android:largeScreens="true"
android:xlargeScreens="true"/>
<uses-feature android:name="android.hardware.camera" android:required="false"/>
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/> <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
@@ -76,11 +66,7 @@
<activity android:name=".ui.SubEditActivity" /> <activity android:name=".ui.SubEditActivity" />
<activity android:name=".ui.ScScannerActivity" /> <activity android:name=".ui.ScScannerActivity" />
<activity <activity android:name=".ui.ScSwitchActivity" />
android:name=".ui.ScSwitchActivity"
android:excludeFromRecents="true"
android:process=":RunSoLibV2RayDaemon"
android:theme="@style/AppTheme.NoActionBar.Translucent" />
<service <service
android:name=".service.V2RayVpnService" android:name=".service.V2RayVpnService"
@@ -97,30 +83,22 @@
android:value="true" /> android:value="true" />
</service> </service>
<service android:name=".service.V2RayProxyOnlyService" <!--<receiver android:name=".receiver.WidgetProvider">-->
android:exported="false" <!--<meta-data-->
android:label="@string/app_name" <!--android:name="android.appwidget.provider"-->
android:process=":RunSoLibV2RayDaemon"> <!--android:resource="@xml/app_widget_provider" />-->
</service>
<receiver android:name=".receiver.WidgetProvider" <!--<intent-filter>-->
android:process=":RunSoLibV2RayDaemon"> <!--<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />-->
<meta-data <!--<action android:name="com.v2ray.ang.action.widget.click" />-->
android:name="android.appwidget.provider" <!--</intent-filter>-->
android:resource="@xml/app_widget_provider" /> <!--</receiver>-->
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
<action android:name="com.v2ray.ang.action.widget.click" />
<action android:name="com.v2ray.ang.action.activity" />
</intent-filter>
</receiver>
<service <service
android:name=".service.QSTileService" android:name=".service.QSTileService"
android:icon="@drawable/ic_v" android:icon="@drawable/ic_v"
android:label="@string/app_tile_name" android:label="@string/app_tile_name"
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE" android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
android:process=":RunSoLibV2RayDaemon">
<intent-filter> <intent-filter>
<action android:name="android.service.quicksettings.action.QS_TILE" /> <action android:name="android.service.quicksettings.action.QS_TILE" />
</intent-filter> </intent-filter>
@@ -135,8 +113,7 @@
</intent-filter> </intent-filter>
</activity> </activity>
<receiver android:name=".receiver.TaskerReceiver" <receiver android:name=".receiver.TaskerReceiver">
android:process=":RunSoLibV2RayDaemon">
<intent-filter> <intent-filter>
<action android:name="com.twofortyfouram.locale.intent.action.FIRE_SETTING" /> <action android:name="com.twofortyfouram.locale.intent.action.FIRE_SETTING" />
</intent-filter> </intent-filter>
@@ -145,4 +122,4 @@
</application> </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.chrome
com.android.providers.downloads
com.android.providers.downloads.ui
com.android.providers.telephony
com.android.settings
com.android.vending
com.android6park.m6park
com.apkpure.aegon
com.apkupdater
com.app.pornhub
com.arthurivanets.owly
com.asahi.tida.tablet
com.authy.authy
com.avmovie
com.ballistiq.artstation
com.binance.dev
com.bitly.app
com.brave.browser
com.brave.browser_beta
com.breel.wallpapers18
com.bvanced.android.youtube
com.chrome.beta
com.chrome.canary
com.chrome.dev
com.cl.newt66y
com.cradle.iitc_mobile
com.cygames.shadowverse
com.devhd.feedly
com.devolver.reigns2
com.discord
com.downloader.video.tumblr
com.driverbrowser
com.dropbox.android
com.duolingo
com.duckduckgo.mobile.android
com.dv.adm
com.estrongs.android.pop
com.estrongs.android.pop.pro
com.evernote
com.facebook.katana
com.facebook.lite
com.facebook.mlite
com.facebook.orca
com.facebook.services
com.facebook.system
com.fastaccess.github
com.felixfilip.scpae
com.fireproofstudios.theroom4
com.firstrowria.pushnotificationtester
com.flyersoft.moonreaderp
com.fooview.android.fooview
com.fvd.eversync
com.gameloft.android.anmp.glofta8hm
com.gameloft.android.anmp.glofta9hm
com.gianlu.aria2app
com.github.yeriomin.yalpstore
com.google.android.apps.adm
com.google.android.apps.books
com.google.android.apps.docs
com.google.android.apps.docs.editors.sheets
com.google.android.apps.fitness
com.google.android.apps.googleassistant
com.google.android.apps.googlevoice
com.google.android.apps.hangoutsdialer
com.google.android.apps.inbox
com.google.android.apps.magazines
com.google.android.apps.maps
com.google.android.apps.nbu.files
com.google.android.apps.paidtasks
com.google.android.apps.pdfviewer
com.google.android.apps.photos
com.google.android.apps.plus
com.google.android.apps.translate
com.google.android.gm
com.google.android.gms
com.google.android.gms.setup
com.google.android.googlequicksearchbox com.google.android.googlequicksearchbox
com.google.android.apps.photos
com.google.android.youtube
com.google.android.gm
com.google.android.apps.plus
com.android.vending
com.google.android.inputmethod.latin
com.google.android.apps.paidtasks
com.google.android.keep
com.google.android.gms.setup
com. google.android. apps.magazines
com.google.android.videos
com. google.android.gms
com.google.android.apps.books
com.google.android.music
com.google.android.play.games
com.google.android.gsf com.google.android.gsf
com.google.android.gsf.login com.google.android.gsf.login
com.app.pornhub
com.spotify.music
org.thunderdog.challegram
com.tumblr
com.twitter.android
com.xda.labs
com.kapp.youtube.final
com.google.android.ims com.google.android.ims
com.google.android.inputmethod.latin com.wire
mark.via.gp
com.downloader.video.tumblr
com.sololearn
com.cygames.shadowverse
com.felixfilip.scpae
amanita_design.samorost3.gp
com.devolver.reigns2
com.utopia.pxview
ch.protonmail.android
com.perol.asdpl.pixivez
com.pinterest
com.paypal.android.p2pmobile
com.arthurivanets.owly
com.rubenmayayo.reddit
com.rayark.cytus2
com.rayark.pluto
com.rayark.implosion
com.fireproofstudios.theroom4
com.netflix.mediaclient
com.instagram.android
com.google.android.apps.hangoutsdialer
com.google.android.talk
com.google.android.apps.plus
com.google.android.apps.pdfviewer
com.google.android.apps.magazines
com.google.android.apps.nbu.files
com.evernote
net.tsapps.appsales
com.google.android.apps.translate
com.google.ar.lens
com.google.android.apps.adm
com.google.android.apps.googleassistant
tw.com.gamer.android.activecenter
org.telegram.plus
com.brave.browser
com.breel.wallpapers18
com.teslacoilsw.launcher
com.lastpass.lpandroid
org.kustom.widget
com.fooview.android.fooview
com.google.android.apps.docs
com.google.android.apps.maps
com.facebook.services
com.facebook.system
com.facebook.katana
com.nianticlabs.ingress.prime.qa
com.vanced.android.youtube
com.nianticproject.ingress
com.quoord.tapatalkpro.activity
org.mozilla.firefox
com.reddit.frontpage
com.google.android.apps.fitness
au.com.shiftyjelly.pocketcasts
com.google.android.gms
com.android.providers.telephony
com.resilio.sync
com.google.android.apps.googlevoice
com.discord
com.cradle.iitc_mobile
be.mygod.vpnhotspot
com.alphainventor.filemanager
com.android.providers.downloads
com.apkpure.aegon
com.ballistiq.artstation
com.bitly.app
com.chrome.canary
com.chrome.dev
com.devhd.feedly
com.dropbox.android
com.estrongs.android.pop
com.estrongs.android.pop.pro
com.fastaccess.github
com.firstrowria.pushnotificationtester
com.fvd.eversync
com.gianlu.aria2app
com.github.yeriomin.yalpstore
com.google.android.apps.docs.editors.sheets
com.google.android.instantapps.supervisor com.google.android.instantapps.supervisor
com.google.android.keep
com.google.android.music
com.google.android.ogyoutube com.google.android.ogyoutube
com.google.android.partnersetup com.google.android.partnersetup
com.google.android.play.games
com.google.android.street
com.google.android.syncadapters.calendar com.google.android.syncadapters.calendar
com.google.android.syncadapters.contacts com.google.android.syncadapters.contacts
com.google.android.talk
com.google.android.tts com.google.android.tts
com.google.android.videos
com.google.android.youtube
com.google.ar.lens
com.hochan.coldsoup com.hochan.coldsoup
com.ifttt.ifttt com.ifttt.ifttt
com.imgur.mobile com.imgur.mobile
com.innologica.inoreader com.innologica.inoreader
com.instagram.android
com.instapaper.android com.instapaper.android
com.jarvanh.vpntether com.jarvanh.vpntether
com.kapp.youtube.final
com.klinker.android.twitter_l
com.lastpass.lpandroid
com.linecorp.linelite
com.lingodeer
com.mediapods.tumbpods com.mediapods.tumbpods
com.mgoogle.android.gms com.mgoogle.android.gms
com.microsoft.emmx
com.microsoft.office.powerpoint com.microsoft.office.powerpoint
com.microsoft.skydrive
com.mixplorer com.mixplorer
com.msd.consumerchinese com.msd.consumerchinese
com.msd.professionalchinese com.msd.professionalchinese
com.mss2011c.sharehelper com.mss2011c.sharehelper
com.netflix.mediaclient
com.newin.nplayer.pro com.newin.nplayer.pro
com.nianticlabs.ingress.prime.qa
com.nianticproject.ingress
com.ninefolders.hd3
com.ninegag.android.app
com.nintendo.zara
com.nytimes.cn
com.oasisfeng.island com.oasisfeng.island
com.ocnt.liveapp.hw
com.orekie.search com.orekie.search
com.patreon.android
com.paypal.android.p2pmobile
com.perol.asdpl.pixivez
com.pinterest
com.popularapp.periodcalendar
com.popularapp.videodownloaderforinstagram com.popularapp.videodownloaderforinstagram
com.pushbullet.android com.pushbullet.android
com.quoord.tapatalkpro.activity
com.quora.android
com.rayark.cytus2
com.rayark.implosion
com.rayark.pluto
com.reddit.frontpage
com.resilio.sync
com.rhmsoft.edit com.rhmsoft.edit
com.rubenmayayo.reddit
com.sec.android.app.sbrowser
com.sec.android.app.sbrowser.beta
com.shanga.walli
com.simplehabit.simplehabitapp
com.slack com.slack
com.snaptube.premium
com.sololearn
com.sonelli.juicessh
com.spotify.music
com.tencent.huatuo com.tencent.huatuo
com.termux com.termux
com.teslacoilsw.launcher
com.theinitium.news
com.thomsonreuters.reuters
com.thunkable.android.hritvik00.freenom com.thunkable.android.hritvik00.freenom
com.topjohnwu.magisk com.topjohnwu.magisk
com.tripadvisor.tripadvisor
com.tumblr
com.twitter.android
com.u91porn com.u91porn
com.u9porn com.u9porn
com.ubisoft.dance.justdance2015companion
com.utopia.pxview
com.valvesoftware.android.steam.communimunity
com.valvesoftware.android.steam.community
com.vanced.android.youtube
com.vimeo.android.videoapp com.vimeo.android.videoapp
com.vivaldi.browser
com.vivaldi.browser.snapshot
com.vkontakte.android
com.whatsapp
com.wire
com.wuxiangai.refactor com.wuxiangai.refactor
com.xda.labs
com.xvideos.app
com.yandex.browser com.yandex.browser
com.yandex.browser.beta
com.yandex.browser.alpha
com.z28j.feel com.z28j.feel
con.medium.reader
de.apkgrabber
de.robv.android.xposed.installer de.robv.android.xposed.installer
dk.tacit.android.foldersync.full dk.tacit.android.foldersync.full
es.rafalense.telegram.themes es.rafalense.telegram.themes
es.rafalense.themes es.rafalense.themes
flipboard.app flipboard.app
fm.moon.app
fr.gouv.etalab.mastodon
github.tornaco.xposedmoduletest github.tornaco.xposedmoduletest
idm.internet.download.manager
idm.internet.download.manager.plus
io.github.javiewer
io.github.skyhacker2.magnetsearch
io.va.exposed io.va.exposed
it.mvilla.android.fenix2
jp.bokete.app.android
jp.naver.line.android
jp.pxv.android jp.pxv.android
luo.speedometergpspro
mark.via.gp
me.tshine.easymark me.tshine.easymark
net.teeha.android.url_shortener net.teeha.android.url_shortener
net.tsapps.appsales
onion.fire onion.fire
org.fdroid.fdroid org.fdroid.fdroid
org.freedownloadmanager.fdm
org.kustom.widget
org.mozilla.fennec_aurora org.mozilla.fennec_aurora
org.mozilla.fenix
org.mozilla.fenix.nightly
org.mozilla.firefox
org.mozilla.firefox_beta
org.mozilla.focus
org.schabi.newpipe org.schabi.newpipe
org.telegram.messenger org.telegram.messenger
org.telegram.multi
org.telegram.plus
org.thunderdog.challegram
org.torproject.android org.torproject.android
org.torproject.torbrowser_alpha
org.wikipedia
org.xbmc.kodi org.xbmc.kodi
pl.zdunex25.updater pl.zdunex25.updater
tv.twitch.android.app
tw.com.gamer.android.activecenter
videodownloader.downloadvideo.downloader videodownloader.downloadvideo.downloader
uk.co.bbc.learningenglish com.quora.android
com.ted.android com.lingodeer
org.wikipedia
com.ninegag.android.app
com.duolingo
com.patreon.android
com.valvesoftware.android.steam.communimunity
co.wanqu.android
jp.bokete.app.android
com.vkontakte.android
com.amazon.mshop.android.shopping
com.ubisoft.dance.justdance2015companion
com.gameloft.android.anmp.glofta8hm
com.gameloft.android.anmp.glofta9hm
com.binance.dev
com.asahi.tida.tablet
com.theinitium.news
com.driverbrowser
com.thomsonreuters.reuters
com.nytimes.cn
com.android.providers.downloads.ui
com.avmovie
bbc.mobile.news.ww
org.mozilla.focus
io.github.javiewer
com.sonelli.juicessh
con.medium.reader
com.microsoft.skydrive
com.valvesoftware.android.steam.community
com.nintendo.zara
org.torproject.torbrowser_alpha
tv.twitch.android.app
com.shanga.walli
com.whatsapp
com.wire
com.simplehabit.simplehabitapp
@@ -13,8 +13,8 @@
} }
}, },
"system": { "system": {
"statsOutboundUplink": true, "statsInboundUplink": true,
"statsOutboundDownlink": true "statsInboundDownlink": true
} }
}, },
"inbounds": [{ "inbounds": [{
@@ -43,8 +43,6 @@ public interface ItemTouchHelperAdapter {
boolean onItemMove(int fromPosition, int toPosition); boolean onItemMove(int fromPosition, int toPosition);
void onItemMoveCompleted();
/** /**
* Called when an item has been dismissed by a swipe.<br/> * Called when an item has been dismissed by a swipe.<br/>
* <br/> * <br/>
@@ -112,8 +112,6 @@ public class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback {
public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
super.clearView(recyclerView, viewHolder); super.clearView(recyclerView, viewHolder);
mAdapter.onItemMoveCompleted();
viewHolder.itemView.setAlpha(ALPHA_FULL); viewHolder.itemView.setAlpha(ALPHA_FULL);
if (viewHolder instanceof ItemTouchHelperViewHolder) { if (viewHolder instanceof ItemTouchHelperViewHolder) {
Binary file not shown.
Binary file not shown.
@@ -1,17 +1,16 @@
package com.v2ray.ang package com.v2ray.ang
import android.app.Application
//import com.squareup.leakcanary.LeakCanary //import com.squareup.leakcanary.LeakCanary
import android.support.multidex.MultiDexApplication
import com.v2ray.ang.util.AngConfigManager import com.v2ray.ang.util.AngConfigManager
import me.dozen.dpreference.DPreference import me.dozen.dpreference.DPreference
import org.jetbrains.anko.defaultSharedPreferences import org.jetbrains.anko.defaultSharedPreferences
class AngApplication : MultiDexApplication() { class AngApplication : Application() {
companion object { companion object {
const val PREF_LAST_VERSION = "pref_last_version" const val PREF_LAST_VERSION = "pref_last_version"
} }
var curIndex = -1 //Current proxy that is opened. (Used to implement restart feature)
var firstRun = false var firstRun = false
private set private set
@@ -11,10 +11,7 @@ object AppConfig {
const val PREF_CURR_CONFIG_GUID = "pref_v2ray_config_guid" const val PREF_CURR_CONFIG_GUID = "pref_v2ray_config_guid"
const val PREF_CURR_CONFIG_NAME = "pref_v2ray_config_name" const val PREF_CURR_CONFIG_NAME = "pref_v2ray_config_name"
const val PREF_CURR_CONFIG_DOMAIN = "pref_v2ray_config_domain" const val PREF_CURR_CONFIG_DOMAIN = "pref_v2ray_config_domain"
const val PREF_CURR_CONFIG_OUTBOUND_TAGS = "pref_v2ray_config_outbound_tags"
const val PREF_INAPP_BUY_IS_PREMIUM = "pref_inapp_buy_is_premium" const val PREF_INAPP_BUY_IS_PREMIUM = "pref_inapp_buy_is_premium"
const val PREF_MODE = "pref_mode"
const val VMESS_PROTOCOL: String = "vmess://" const val VMESS_PROTOCOL: String = "vmess://"
const val SS_PROTOCOL: String = "ss://" const val SS_PROTOCOL: String = "ss://"
const val SOCKS_PROTOCOL: String = "socks://" const val SOCKS_PROTOCOL: String = "socks://"
@@ -38,7 +35,6 @@ object AppConfig {
const val androidpackagenamelistUrl = "https://raw.githubusercontent.com/2dust/androidpackagenamelist/master/proxy.txt" const val androidpackagenamelistUrl = "https://raw.githubusercontent.com/2dust/androidpackagenamelist/master/proxy.txt"
const val v2rayCustomRoutingListUrl = "https://raw.githubusercontent.com/2dust/v2rayCustomRoutingList/master/" const val v2rayCustomRoutingListUrl = "https://raw.githubusercontent.com/2dust/v2rayCustomRoutingList/master/"
const val v2rayNGIssues = "https://github.com/2dust/v2rayNG/issues" const val v2rayNGIssues = "https://github.com/2dust/v2rayNG/issues"
const val v2rayNGWikiMode = "https://github.com/2dust/v2rayNG/wiki/Mode"
const val promotionUrl = "https://1.2345345.xyz/ads.html" const val promotionUrl = "https://1.2345345.xyz/ads.html"
const val DNS_AGENT = "1.1.1.1" const val DNS_AGENT = "1.1.1.1"
@@ -54,4 +50,12 @@ object AppConfig {
const val MSG_STATE_STOP = 4 const val MSG_STATE_STOP = 4
const val MSG_STATE_STOP_SUCCESS = 41 const val MSG_STATE_STOP_SUCCESS = 41
const val MSG_STATE_RESTART = 5 const val MSG_STATE_RESTART = 5
}
object EConfigType {
val Vmess = 1
val Custom = 2
val Shadowsocks = 3
val Socks = 4
}
}
@@ -1,12 +0,0 @@
package com.v2ray.ang.dto
enum class EConfigType(val value: Int) {
VMESS(1),
CUSTOM(2),
SHADOWSOCKS(3),
SOCKS(4);
companion object {
fun fromInt(value: Int) = values().firstOrNull { it.value == value }
}
}
@@ -64,21 +64,22 @@ data class V2rayConfig(
data class StreamSettingsBean(var network: String, data class StreamSettingsBean(var network: String,
var security: String, var security: String,
var tcpSettings: TcpSettingsBean?, var tcpSettings: TcpsettingsBean?,
var kcpSettings: KcpSettingsBean?, var kcpsettings: KcpsettingsBean?,
var wsSettings: WsSettingsBean?, var wssettings: WssettingsBean?,
var httpSettings: HttpSettingsBean?, var httpsettings: HttpsettingsBean?,
var tlsSettings: TlsSettingsBean?, var tlssettings: TlssettingsBean?,
var quicSettings: QuicSettingBean? var quicsettings: QuicsettingBean?
) { ) {
data class TcpSettingsBean(var header: HeaderBean = HeaderBean()) { data class TcpsettingsBean(var connectionReuse: Boolean = true,
var header: HeaderBean = HeaderBean()) {
data class HeaderBean(var type: String = "none", data class HeaderBean(var type: String = "none",
var request: Any? = null, var request: Any? = null,
var response: Any? = null) var response: Any? = null)
} }
data class KcpSettingsBean(var mtu: Int = 1350, data class KcpsettingsBean(var mtu: Int = 1350,
var tti: Int = 20, var tti: Int = 20,
var uplinkCapacity: Int = 12, var uplinkCapacity: Int = 12,
var downlinkCapacity: Int = 100, var downlinkCapacity: Int = 100,
@@ -89,43 +90,25 @@ data class V2rayConfig(
data class HeaderBean(var type: String = "none") data class HeaderBean(var type: String = "none")
} }
data class WsSettingsBean(var path: String = "", data class WssettingsBean(var connectionReuse: Boolean = true,
var path: String = "",
var headers: HeadersBean = HeadersBean()) { var headers: HeadersBean = HeadersBean()) {
data class HeadersBean(var Host: String = "") data class HeadersBean(var Host: String = "")
} }
data class HttpSettingsBean(var host: List<String> = ArrayList(), data class HttpsettingsBean(var host: List<String> = ArrayList<String>(), var path: String = "")
var path: String = "")
data class TlsSettingsBean(var allowInsecure: Boolean = true, data class TlssettingsBean(var allowInsecure: Boolean = true,
var serverName: String = "") var serverName: String = "")
data class QuicSettingBean(var security: String = "none", data class QuicsettingBean(var security: String = "none",
var key: String = "", var key: String = "",
var header: HeaderBean = HeaderBean()) { var header: HeaderBean = HeaderBean()) {
data class HeaderBean(var type: String = "none") data class HeaderBean(var type: String = "none")
} }
} }
data class MuxBean(var enabled: Boolean) data class MuxBean(var enabled: Boolean)
fun getServerAddress(): String? {
if (protocol.equals(EConfigType.VMESS.name.toLowerCase())) {
return settings?.vnext?.get(0)?.address
} else if (protocol.equals(EConfigType.SHADOWSOCKS.name.toLowerCase()) || protocol.equals(EConfigType.SOCKS.name.toLowerCase())) {
return settings?.servers?.get(0)?.address
}
return null
}
fun getServerPort(): Int? {
if (protocol.equals(EConfigType.VMESS.name.toLowerCase())) {
return settings?.vnext?.get(0)?.port
} else if (protocol.equals(EConfigType.SHADOWSOCKS.name.toLowerCase()) || protocol.equals(EConfigType.SOCKS.name.toLowerCase())) {
return settings?.servers?.get(0)?.port
}
return null
}
} }
//data class DnsBean(var servers: List<String>) //data class DnsBean(var servers: List<String>)
@@ -27,37 +27,34 @@ const val divisor = 1024F
fun Long.toSpeedString() = toTrafficString() + "/s" fun Long.toSpeedString() = toTrafficString() + "/s"
fun Long.toTrafficString(): String { fun Long.toTrafficString(): String {
if (this == 0L)
return "\t\t\t0\t B"
if (this < threshold) if (this < threshold)
return "${this.toFloat().toShortString()}\t B" return "$this B"
val kib = this / divisor val kib = this / divisor
if (kib < threshold) if (kib < threshold)
return "${kib.toShortString()}\t KB" return "${kib.toShortString()} KB"
val mib = kib / divisor val mib = kib / divisor
if (mib < threshold) if (mib < threshold)
return "${mib.toShortString()}\t MB" return "${mib.toShortString()} MB"
val gib = mib / divisor val gib = mib / divisor
if (gib < threshold) if (gib < threshold)
return "${gib.toShortString()}\t GB" return "${gib.toShortString()} GB"
val tib = gib / divisor val tib = gib / divisor
if (tib < threshold) if (tib < threshold)
return "${tib.toShortString()}\t TB" return "${tib.toShortString()} TB"
val pib = tib / divisor val pib = tib / divisor
if (pib < threshold) if (pib < threshold)
return "${pib.toShortString()}\t PB" return "${pib.toShortString()} PB"
return "∞" return "∞"
} }
private fun Float.toShortString(): String { private fun Float.toShortString(): String {
val s = "%.2f".format(this) val s = toString()
if (s.length <= 4) if (s.length <= 4)
return s return s
return s.substring(0, 4).removeSuffix(".") return s.substring(0, 4).removeSuffix(".")
@@ -3,14 +3,14 @@ package com.v2ray.ang.receiver
import android.app.PendingIntent import android.app.PendingIntent
import android.appwidget.AppWidgetManager import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProvider import android.appwidget.AppWidgetProvider
import android.content.ComponentName
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Bundle
import android.widget.RemoteViews import android.widget.RemoteViews
import com.v2ray.ang.R import com.v2ray.ang.R
import com.v2ray.ang.AppConfig import com.v2ray.ang.AppConfig
import com.v2ray.ang.service.V2RayServiceManager
import com.v2ray.ang.util.Utils import com.v2ray.ang.util.Utils
import org.jetbrains.anko.toast
class WidgetProvider : AppWidgetProvider() { class WidgetProvider : AppWidgetProvider() {
/** /**
@@ -18,20 +18,11 @@ class WidgetProvider : AppWidgetProvider() {
*/ */
override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) { override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
super.onUpdate(context, appWidgetManager, appWidgetIds) super.onUpdate(context, appWidgetManager, appWidgetIds)
updateWidgetBackground(context, appWidgetManager, appWidgetIds, V2RayServiceManager.v2rayPoint.isRunning)
}
private fun updateWidgetBackground(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray, isRunning: Boolean) {
val remoteViews = RemoteViews(context.packageName, R.layout.widget_switch) val remoteViews = RemoteViews(context.packageName, R.layout.widget_switch)
val intent = Intent(context, WidgetProvider::class.java) val intent = Intent(AppConfig.BROADCAST_ACTION_WIDGET_CLICK)
intent.action = AppConfig.BROADCAST_ACTION_WIDGET_CLICK
val pendingIntent = PendingIntent.getBroadcast(context, R.id.layout_switch, intent, PendingIntent.FLAG_UPDATE_CURRENT) val pendingIntent = PendingIntent.getBroadcast(context, R.id.layout_switch, intent, PendingIntent.FLAG_UPDATE_CURRENT)
remoteViews.setOnClickPendingIntent(R.id.layout_switch, pendingIntent) remoteViews.setOnClickPendingIntent(R.id.layout_switch, pendingIntent)
if (isRunning) {
remoteViews.setInt(R.id.layout_switch, "setBackgroundResource", R.drawable.ic_rounded_corner_theme)
} else {
remoteViews.setInt(R.id.layout_switch, "setBackgroundResource", R.drawable.ic_rounded_corner_grey)
}
for (appWidgetId in appWidgetIds) { for (appWidgetId in appWidgetIds) {
appWidgetManager.updateAppWidget(appWidgetId, remoteViews) appWidgetManager.updateAppWidget(appWidgetId, remoteViews)
@@ -39,28 +30,21 @@ class WidgetProvider : AppWidgetProvider() {
} }
/** /**
* 接收窗口小部件发送的广播 * 接收窗口小部件点击时发送的广播
*/ */
override fun onReceive(context: Context, intent: Intent) { override fun onReceive(context: Context, intent: Intent) {
super.onReceive(context, intent) super.onReceive(context, intent)
if (AppConfig.BROADCAST_ACTION_WIDGET_CLICK == intent.action) { if (AppConfig.BROADCAST_ACTION_WIDGET_CLICK == intent.action) {
if (V2RayServiceManager.v2rayPoint.isRunning) {
val isRunning = Utils.isServiceRun(context, "com.v2ray.ang.service.V2RayVpnService")
if (isRunning) {
// context.toast(R.string.toast_services_stop)
Utils.stopVService(context) Utils.stopVService(context)
} else { } else {
Utils.startVServiceFromToggle(context) // context.toast(R.string.toast_services_start)
} Utils.startVService(context)
} else if (AppConfig.BROADCAST_ACTION_ACTIVITY == intent.action) {
val manager = AppWidgetManager.getInstance(context)
when (intent.getIntExtra("key", 0)) {
AppConfig.MSG_STATE_RUNNING, AppConfig.MSG_STATE_START_SUCCESS -> {
updateWidgetBackground(context, manager, manager.getAppWidgetIds(ComponentName(context, WidgetProvider::class.java)),
true)
}
AppConfig.MSG_STATE_NOT_RUNNING, AppConfig.MSG_STATE_START_FAILURE, AppConfig.MSG_STATE_STOP_SUCCESS -> {
updateWidgetBackground(context, manager, manager.getAppWidgetIds(ComponentName(context, WidgetProvider::class.java)),
false)
}
} }
} }
} }
} }
@@ -6,6 +6,7 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.IntentFilter import android.content.IntentFilter
import android.graphics.drawable.Icon import android.graphics.drawable.Icon
import android.net.VpnService
import android.os.Build import android.os.Build
import android.service.quicksettings.Tile import android.service.quicksettings.Tile
import android.service.quicksettings.TileService import android.service.quicksettings.TileService
@@ -14,6 +15,7 @@ import com.v2ray.ang.R
import com.v2ray.ang.extension.defaultDPreference import com.v2ray.ang.extension.defaultDPreference
import com.v2ray.ang.util.MessageUtil import com.v2ray.ang.util.MessageUtil
import com.v2ray.ang.util.Utils import com.v2ray.ang.util.Utils
import org.jetbrains.anko.toast
import java.lang.ref.SoftReference import java.lang.ref.SoftReference
@@ -31,6 +33,7 @@ class QSTileService : TileService() {
qsTile?.icon = Icon.createWithResource(applicationContext, R.drawable.ic_v) qsTile?.icon = Icon.createWithResource(applicationContext, R.drawable.ic_v)
} }
qsTile?.updateTile() qsTile?.updateTile()
} }
@@ -53,7 +56,11 @@ class QSTileService : TileService() {
super.onClick() super.onClick()
when (qsTile.state) { when (qsTile.state) {
Tile.STATE_INACTIVE -> { Tile.STATE_INACTIVE -> {
Utils.startVServiceFromToggle(this) val intent = VpnService.prepare(this)
if (intent == null)
if (!Utils.startVService(this)) {
toast(R.string.app_tile_first_use)
}
} }
Tile.STATE_ACTIVE -> { Tile.STATE_ACTIVE -> {
Utils.stopVService(this) Utils.stopVService(this)
@@ -86,4 +93,4 @@ class QSTileService : TileService() {
} }
} }
} }
} }
@@ -1,15 +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
fun vpnSendFd()
}
@@ -1,47 +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 vpnSendFd() {
// do nothing
}
override fun onBind(intent: Intent?): IBinder? {
return null
}
}
@@ -1,367 +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.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)
}
}
private var lastQueryTime = 0L
private var mBuilder: NotificationCompat.Builder? = null
private var mSubscription: Subscription? = null
private var mNotificationManager: NotificationManager? = null
fun startV2Ray(context: Context, mode: String) {
val intent = if (mode == "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
}
}
override fun sendFd(): Long {
val serviceControl = serviceControl?.get() ?: return -1
try {
serviceControl.vpnSendFd()
} catch (e: Exception) {
Log.d(serviceControl.getService().packageName, e.toString())
return -1
}
return 0
}
}
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, "")
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(service.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,
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() {
val service = serviceControl?.get()?.getService() ?: return
if (mSubscription != null) {
mSubscription?.unsubscribe() //stop queryStats
mSubscription = null
val cfName = service.defaultDPreference.getPrefString(AppConfig.PREF_CURR_CONFIG_NAME, "")
updateNotification(cfName, 0, 0)
}
}
}
@@ -1,27 +1,69 @@
package com.v2ray.ang.service package com.v2ray.ang.service
import android.app.* import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.IntentFilter
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.graphics.Color
import android.net.* import android.net.*
import android.os.Build import android.net.VpnService
import android.os.ParcelFileDescriptor import android.os.*
import android.os.StrictMode
import android.support.annotation.RequiresApi import android.support.annotation.RequiresApi
import android.util.Log import android.support.v4.app.NotificationCompat
import com.v2ray.ang.AppConfig import com.v2ray.ang.AppConfig
import com.v2ray.ang.R import com.v2ray.ang.R
import com.v2ray.ang.extension.defaultDPreference import com.v2ray.ang.extension.defaultDPreference
import com.v2ray.ang.extension.toSpeedString
import com.v2ray.ang.ui.MainActivity
import com.v2ray.ang.ui.PerAppProxyActivity import com.v2ray.ang.ui.PerAppProxyActivity
import com.v2ray.ang.ui.SettingsActivity import com.v2ray.ang.ui.SettingsActivity
import com.v2ray.ang.util.MessageUtil
import com.v2ray.ang.util.Utils import com.v2ray.ang.util.Utils
import org.jetbrains.anko.doAsync 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.File
import java.io.FileDescriptor
import java.io.FileInputStream
import java.lang.ref.SoftReference import java.lang.ref.SoftReference
import android.os.Build
import android.annotation.TargetApi
import android.util.Log
import org.jetbrains.anko.doAsync
class V2RayVpnService : VpnService(), ServiceControl { class V2RayVpnService : VpnService() {
companion object {
const val NOTIFICATION_ID = 1
const val NOTIFICATION_PENDING_INTENT_CONTENT = 0
const val NOTIFICATION_PENDING_INTENT_STOP_V2RAY = 1
fun startV2Ray(context: Context) {
val intent = Intent(context.applicationContext, V2RayVpnService::class.java)
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) {
context.startForegroundService(intent)
} else {
context.startService(intent)
}
}
}
private val v2rayPoint = Libv2ray.newV2RayPoint(V2RayCallback())
private lateinit var configContent: String
private lateinit var mInterface: ParcelFileDescriptor private lateinit var mInterface: ParcelFileDescriptor
val fd: Int get() = mInterface.fd
private var mBuilder: NotificationCompat.Builder? = null
private var mSubscription: Subscription? = null
private var mNotificationManager: NotificationManager? = null
/** /**
* Unfortunately registerDefaultNetworkCallback is going to return our VPN interface: https://android.googlesource.com/platform/frameworks/base/+/dda156ab0c5d66ad82bdcf76cda07cbc0a9c8a2e * Unfortunately registerDefaultNetworkCallback is going to return our VPN interface: https://android.googlesource.com/platform/frameworks/base/+/dda156ab0c5d66ad82bdcf76cda07cbc0a9c8a2e
@@ -32,32 +74,27 @@ class V2RayVpnService : VpnService(), ServiceControl {
* *
* Source: https://android.googlesource.com/platform/frameworks/base/+/2df4c7d/services/core/java/com/android/server/ConnectivityService.java#887 * Source: https://android.googlesource.com/platform/frameworks/base/+/2df4c7d/services/core/java/com/android/server/ConnectivityService.java#887
*/ */
@delegate:RequiresApi(Build.VERSION_CODES.P) @TargetApi(28)
private val defaultNetworkRequest by lazy { private val defaultNetworkRequest = NetworkRequest.Builder()
NetworkRequest.Builder() .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) .build()
.build()
}
private val connectivity by lazy { getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager } private val connectivity by lazy { getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager }
@TargetApi(28)
@delegate:RequiresApi(Build.VERSION_CODES.P) private val defaultNetworkCallback = object : ConnectivityManager.NetworkCallback() {
private val defaultNetworkCallback by lazy { override fun onAvailable(network: Network) {
object : ConnectivityManager.NetworkCallback() { setUnderlyingNetworks(arrayOf(network))
override fun onAvailable(network: Network) { }
setUnderlyingNetworks(arrayOf(network)) override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities?) {
} // it's a good idea to refresh capabilities
override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities?) { setUnderlyingNetworks(arrayOf(network))
// it's a good idea to refresh capabilities }
setUnderlyingNetworks(arrayOf(network)) override fun onLost(network: Network) {
} setUnderlyingNetworks(null)
override fun onLost(network: Network) {
setUnderlyingNetworks(null)
}
} }
} }
private var listeningForDefaultNetwork = false private var listeningForDefaultNetwork = false
override fun onCreate() { override fun onCreate() {
@@ -65,7 +102,8 @@ class V2RayVpnService : VpnService(), ServiceControl {
val policy = StrictMode.ThreadPolicy.Builder().permitAll().build() val policy = StrictMode.ThreadPolicy.Builder().permitAll().build()
StrictMode.setThreadPolicy(policy) StrictMode.setThreadPolicy(policy)
V2RayServiceManager.serviceControl = SoftReference(this) v2rayPoint.packageName = Utils.packagePath(applicationContext)
Seq.setContext(applicationContext)
} }
override fun onRevoke() { override fun onRevoke() {
@@ -79,12 +117,12 @@ class V2RayVpnService : VpnService(), ServiceControl {
override fun onDestroy() { override fun onDestroy() {
super.onDestroy() super.onDestroy()
V2RayServiceManager.cancelNotification() cancelNotification()
} }
private fun setup(parameters: String) { fun setup(parameters: String) {
val prepare = prepare(this) val prepare = VpnService.prepare(this)
if (prepare != null) { if (prepare != null) {
return return
} }
@@ -93,7 +131,6 @@ class V2RayVpnService : VpnService(), ServiceControl {
// Configure a builder while parsing the parameters. // Configure a builder while parsing the parameters.
val builder = Builder() val builder = Builder()
val enableLocalDns = defaultDPreference.getPrefBoolean(SettingsActivity.PREF_LOCAL_DNS_ENABLED, false) val enableLocalDns = defaultDPreference.getPrefBoolean(SettingsActivity.PREF_LOCAL_DNS_ENABLED, false)
val routingMode = defaultDPreference.getPrefString(SettingsActivity.PREF_ROUTING_MODE, "0")
parameters.split(" ") parameters.split(" ")
.map { it.split(",") } .map { it.split(",") }
@@ -102,20 +139,7 @@ class V2RayVpnService : VpnService(), ServiceControl {
'm' -> builder.setMtu(java.lang.Short.parseShort(it[1]).toInt()) 'm' -> builder.setMtu(java.lang.Short.parseShort(it[1]).toInt())
's' -> builder.addSearchDomain(it[1]) 's' -> builder.addSearchDomain(it[1])
'a' -> builder.addAddress(it[1], Integer.parseInt(it[2])) 'a' -> builder.addAddress(it[1], Integer.parseInt(it[2]))
'r' -> { 'r' -> builder.addRoute(it[1], Integer.parseInt(it[2]))
if (routingMode == "1" || routingMode == "3") {
if (it[1] == "::") { //not very elegant, should move Vpn setting in Kotlin, simplify go code
builder.addRoute("2000::", 3)
} else {
resources.getStringArray(R.array.bypass_private_ip_address).forEach { cidr ->
val addr = cidr.split('/')
builder.addRoute(addr[0], addr[1].toInt())
}
}
} else {
builder.addRoute(it[1], Integer.parseInt(it[2]))
}
}
'd' -> builder.addDnsServer(it[1]) 'd' -> builder.addDnsServer(it[1])
} }
} }
@@ -149,24 +173,25 @@ class V2RayVpnService : VpnService(), ServiceControl {
try { try {
mInterface.close() mInterface.close()
} catch (ignored: Exception) { } catch (ignored: Exception) {
// ignored
} }
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
if (Build.VERSION.SDK_INT >= 28) {
connectivity.requestNetwork(defaultNetworkRequest, defaultNetworkCallback) connectivity.requestNetwork(defaultNetworkRequest, defaultNetworkCallback)
listeningForDefaultNetwork = true listeningForDefaultNetwork = true
} }
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
builder.setMetered(false)
}
// Create a new interface using the builder and save the parameters. // Create a new interface using the builder and save the parameters.
mInterface = builder.establish() mInterface = builder.establish()
sendFd() sendFd()
startSpeedNotification()
} }
private fun sendFd() { fun shutdown() {
stopV2Ray(true)
}
fun sendFd() {
val fd = mInterface.fileDescriptor val fd = mInterface.fileDescriptor
val path = File(Utils.packagePath(applicationContext), "sock_path").absolutePath val path = File(Utils.packagePath(applicationContext), "sock_path").absolutePath
@@ -174,7 +199,7 @@ class V2RayVpnService : VpnService(), ServiceControl {
var tries = 0 var tries = 0
while (true) try { while (true) try {
Thread.sleep(50L shl tries) Thread.sleep(50L shl tries)
Log.d(packageName, "sendFd tries: $tries") Log.d(packageName, "sendFd tries: " + tries.toString())
LocalSocket().use { localSocket -> LocalSocket().use { localSocket ->
localSocket.connect(LocalSocketAddress(path, LocalSocketAddress.Namespace.FILESYSTEM)) localSocket.connect(LocalSocketAddress(path, LocalSocketAddress.Namespace.FILESYSTEM))
localSocket.setFileDescriptorsForSend(arrayOf(fd)) localSocket.setFileDescriptorsForSend(arrayOf(fd))
@@ -190,57 +215,278 @@ class V2RayVpnService : VpnService(), ServiceControl {
} }
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
V2RayServiceManager.startV2rayPoint() startV2ray()
return START_STICKY return START_STICKY
//return super.onStartCommand(intent, flags, startId) //return super.onStartCommand(intent, flags, startId)
} }
private fun startV2ray() {
if (!v2rayPoint.isRunning) {
try {
val mFilter = IntentFilter(AppConfig.BROADCAST_ACTION_SERVICE)
mFilter.addAction(Intent.ACTION_SCREEN_ON)
mFilter.addAction(Intent.ACTION_SCREEN_OFF)
mFilter.addAction(Intent.ACTION_USER_PRESENT)
registerReceiver(mMsgReceive, mFilter)
} catch (e: Exception) {
}
configContent = defaultDPreference.getPrefString(AppConfig.PREF_CURR_CONFIG, "")
v2rayPoint.configureFileContent = configContent
v2rayPoint.enableLocalDNS = defaultDPreference.getPrefBoolean(SettingsActivity.PREF_LOCAL_DNS_ENABLED, false)
v2rayPoint.forwardIpv6 = defaultDPreference.getPrefBoolean(SettingsActivity.PREF_FORWARD_IPV6, false)
v2rayPoint.domainName = defaultDPreference.getPrefString(AppConfig.PREF_CURR_CONFIG_DOMAIN, "")
try {
v2rayPoint.runLoop()
} catch (e: Exception) {
Log.d(packageName, e.toString())
}
if (v2rayPoint.isRunning) {
MessageUtil.sendMsg2UI(this, AppConfig.MSG_STATE_START_SUCCESS, "")
showNotification()
} else {
MessageUtil.sendMsg2UI(this, AppConfig.MSG_STATE_START_FAILURE, "")
cancelNotification()
}
}
// showNotification()
}
private fun stopV2Ray(isForced: Boolean = true) { private fun stopV2Ray(isForced: Boolean = true) {
// val configName = defaultDPreference.getPrefString(PREF_CURR_CONFIG_GUID, "") // val configName = defaultDPreference.getPrefString(PREF_CURR_CONFIG_GUID, "")
// val emptyInfo = VpnNetworkInfo() // val emptyInfo = VpnNetworkInfo()
// val info = loadVpnNetworkInfo(configName, emptyInfo)!! + (lastNetworkInfo ?: emptyInfo) // val info = loadVpnNetworkInfo(configName, emptyInfo)!! + (lastNetworkInfo ?: emptyInfo)
// saveVpnNetworkInfo(configName, info) // saveVpnNetworkInfo(configName, info)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && listeningForDefaultNetwork) { if (Build.VERSION.SDK_INT >= 28) {
connectivity.unregisterNetworkCallback(defaultNetworkCallback) if (listeningForDefaultNetwork) {
listeningForDefaultNetwork = false connectivity.unregisterNetworkCallback(defaultNetworkCallback)
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) { if (isForced) {
//stopSelf has to be called ahead of mInterface.close(). otherwise v2ray core cannot be stooped try {
//It's strage but true. unregisterReceiver(mMsgReceive)
//This can be verified by putting stopself() behind and call stopLoop and startLoop } catch (e: Exception) {
//in a row for several times. You will find that later created v2ray core report port in use }
//which means the first v2ray core somehow failed to stop and release the port.
stopSelf()
try { try {
mInterface.close() mInterface.close()
} catch (ignored: Exception) { } catch (ignored: Exception) {
// ignored
} }
stopSelf()
} }
} }
override fun getService(): Service { private fun showNotification() {
return this val startMainIntent = Intent(applicationContext, MainActivity::class.java)
val contentPendingIntent = PendingIntent.getActivity(applicationContext,
NOTIFICATION_PENDING_INTENT_CONTENT, startMainIntent,
PendingIntent.FLAG_UPDATE_CURRENT)
val stopV2RayIntent = Intent(AppConfig.BROADCAST_ACTION_SERVICE)
stopV2RayIntent.`package` = AppConfig.ANG_PACKAGE
stopV2RayIntent.putExtra("key", AppConfig.MSG_STATE_STOP)
val stopV2RayPendingIntent = PendingIntent.getBroadcast(applicationContext,
NOTIFICATION_PENDING_INTENT_STOP_V2RAY, stopV2RayIntent,
PendingIntent.FLAG_UPDATE_CURRENT)
val channelId =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createNotificationChannel()
} else {
// If earlier version channel ID is not used
// https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context)
""
}
mBuilder = NotificationCompat.Builder(applicationContext, channelId)
.setSmallIcon(R.drawable.ic_v)
.setContentTitle(defaultDPreference.getPrefString(AppConfig.PREF_CURR_CONFIG_NAME, ""))
.setPriority(NotificationCompat.PRIORITY_MIN)
.setOngoing(true)
.setShowWhen(false)
.setOnlyAlertOnce(true)
.setContentIntent(contentPendingIntent)
.addAction(R.drawable.ic_close_grey_800_24dp,
getString(R.string.notification_action_stop_v2ray),
stopV2RayPendingIntent)
//.build()
//mBuilder?.setDefaults(NotificationCompat.FLAG_ONLY_ALERT_ONCE) //取消震动,铃声其他都不好使
startForeground(NOTIFICATION_ID, mBuilder?.build())
} }
override fun startService(parameters: String) { @RequiresApi(Build.VERSION_CODES.O)
setup(parameters) private fun createNotificationChannel(): String {
val channelId = "RAY_NG_M_CH_ID"
val channelName = "V2rayNG Background Service"
val chan = NotificationChannel(channelId,
channelName, NotificationManager.IMPORTANCE_HIGH)
chan.lightColor = Color.DKGRAY
chan.importance = NotificationManager.IMPORTANCE_NONE
chan.lockscreenVisibility = Notification.VISIBILITY_PRIVATE
getNotificationManager().createNotificationChannel(chan)
return channelId
} }
override fun stopService() { private fun cancelNotification() {
stopV2Ray(true) stopForeground(true)
mBuilder = null
mSubscription?.unsubscribe()
mSubscription = null
} }
override fun vpnProtect(socket: Int): Boolean { private fun updateNotification(contentText: String) {
return protect(socket) if (mBuilder != null) {
mBuilder?.setContentTitle(contentText)
getNotificationManager().notify(NOTIFICATION_ID, mBuilder?.build())
}
} }
override fun vpnSendFd() { private fun getNotificationManager(): NotificationManager {
sendFd() 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()
}
}
}
} }
} }
@@ -55,6 +55,7 @@ abstract class BaseDrawerActivity : BaseActivity() {
this@BaseDrawerActivity, R.anim.fade_in, R.anim.fade_out).toBundle() this@BaseDrawerActivity, R.anim.fade_in, R.anim.fade_out).toBundle()
var activityClass: Class<*>? = null var activityClass: Class<*>? = null
when (mItemToOpenWhenDrawerCloses) { when (mItemToOpenWhenDrawerCloses) {
R.id.server_profile -> activityClass = MainActivity::class.java
R.id.sub_setting -> activityClass = SubSettingActivity::class.java R.id.sub_setting -> activityClass = SubSettingActivity::class.java
R.id.settings -> activityClass = SettingsActivity::class.java R.id.settings -> activityClass = SettingsActivity::class.java
R.id.logcat -> { R.id.logcat -> {
@@ -194,7 +195,9 @@ abstract class BaseDrawerActivity : BaseActivity() {
true true
} }
if (SubSettingActivity::class.java.isAssignableFrom(javaClass)) { if (MainActivity::class.java.isAssignableFrom(javaClass)) {
navigationView.setCheckedItem(R.id.server_profile)
} else if (SubSettingActivity::class.java.isAssignableFrom(javaClass)) {
navigationView.setCheckedItem(R.id.sub_setting) navigationView.setCheckedItem(R.id.sub_setting)
} else if (SettingsActivity::class.java.isAssignableFrom(javaClass)) { } else if (SettingsActivity::class.java.isAssignableFrom(javaClass)) {
navigationView.setCheckedItem(R.id.settings) navigationView.setCheckedItem(R.id.settings)
@@ -1,8 +1,6 @@
package com.v2ray.ang.ui package com.v2ray.ang.ui
import android.os.Bundle import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.text.method.ScrollingMovementMethod import android.text.method.ScrollingMovementMethod
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
@@ -26,22 +24,15 @@ class LogcatActivity : BaseActivity() {
title = getString(R.string.title_logcat) title = getString(R.string.title_logcat)
supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayHomeAsUpEnabled(true)
logcat(false) logcat()
} }
private fun logcat(shouldFlushLog: Boolean) { private fun logcat() {
try { try {
pb_waiting.visibility = View.VISIBLE pb_waiting.visibility = View.VISIBLE
doAsync { doAsync {
if (shouldFlushLog) {
val lst = LinkedHashSet<String>()
lst.add("logcat")
lst.add("-c")
val process = Runtime.getRuntime().exec(lst.toTypedArray())
process.waitFor()
}
val lst = LinkedHashSet<String>() val lst = LinkedHashSet<String>()
lst.add("logcat") lst.add("logcat")
lst.add("-d") lst.add("-d")
@@ -58,11 +49,9 @@ class LogcatActivity : BaseActivity() {
tv_logcat.text = allText tv_logcat.text = allText
tv_logcat.movementMethod = ScrollingMovementMethod() tv_logcat.movementMethod = ScrollingMovementMethod()
pb_waiting.visibility = View.GONE pb_waiting.visibility = View.GONE
Handler(Looper.getMainLooper()).post { sv_logcat.fullScroll(View.FOCUS_DOWN) }
} }
} }
} catch (e: IOException) { } catch (e: IOException) {
e.printStackTrace()
} }
} }
@@ -77,10 +66,7 @@ class LogcatActivity : BaseActivity() {
toast(R.string.toast_success) toast(R.string.toast_success)
true true
} }
R.id.delete -> {
logcat(true)
true
}
else -> super.onOptionsItemSelected(item) else -> super.onOptionsItemSelected(item)
} }
} }
@@ -27,17 +27,12 @@ import android.support.v4.view.GravityCompat
import android.support.v7.app.ActionBarDrawerToggle import android.support.v7.app.ActionBarDrawerToggle
import android.support.v7.widget.helper.ItemTouchHelper import android.support.v7.widget.helper.ItemTouchHelper
import android.util.Log import android.util.Log
import com.v2ray.ang.BuildConfig
import com.v2ray.ang.dto.EConfigType
import com.v2ray.ang.extension.defaultDPreference
//import com.v2ray.ang.InappBuyActivity //import com.v2ray.ang.InappBuyActivity
import rx.Observable import rx.Observable
import rx.android.schedulers.AndroidSchedulers import rx.android.schedulers.AndroidSchedulers
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import com.v2ray.ang.helper.SimpleItemTouchHelperCallback import com.v2ray.ang.helper.SimpleItemTouchHelperCallback
import com.v2ray.ang.util.AngConfigManager.configs import com.v2ray.ang.util.AngConfigManager.configs
import kotlinx.coroutines.*
import libv2ray.Libv2ray
class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedListener { class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedListener {
companion object { companion object {
@@ -63,7 +58,6 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
private val adapter by lazy { MainRecyclerAdapter(this) } private val adapter by lazy { MainRecyclerAdapter(this) }
private var mItemTouchHelper: ItemTouchHelper? = null private var mItemTouchHelper: ItemTouchHelper? = null
private val testingJobs = ArrayList<Job>()
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@@ -74,15 +68,13 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
fab.setOnClickListener { fab.setOnClickListener {
if (isRunning) { if (isRunning) {
Utils.stopVService(this) Utils.stopVService(this)
} else if (defaultDPreference.getPrefString(AppConfig.PREF_MODE, "VPN") == "VPN") { } else {
val intent = VpnService.prepare(this) val intent = VpnService.prepare(this)
if (intent == null) { if (intent == null) {
startV2Ray() startV2Ray()
} else { } else {
startActivityForResult(intent, REQUEST_CODE_VPN_PREPARE) startActivityForResult(intent, REQUEST_CODE_VPN_PREPARE)
} }
} else {
startV2Ray()
} }
} }
layout_test.setOnClickListener { layout_test.setOnClickListener {
@@ -115,7 +107,6 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
drawer_layout.addDrawerListener(toggle) drawer_layout.addDrawerListener(toggle)
toggle.syncState() toggle.syncState()
nav_view.setNavigationItemSelectedListener(this) nav_view.setNavigationItemSelectedListener(this)
version.text = "v${BuildConfig.VERSION_NAME} (${Libv2ray.checkVersionX()})"
} }
fun startV2Ray() { fun startV2Ray() {
@@ -171,8 +162,8 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
importBatchConfig(data?.getStringExtra("SCAN_RESULT")) importBatchConfig(data?.getStringExtra("SCAN_RESULT"))
} }
REQUEST_FILE_CHOOSER -> { REQUEST_FILE_CHOOSER -> {
val uri = data?.data if (resultCode == RESULT_OK) {
if (resultCode == RESULT_OK && uri != null) { val uri = data!!.data
readContentFromUri(uri) readContentFromUri(uri)
} }
} }
@@ -241,7 +232,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
R.id.export_all -> { R.id.export_all -> {
if (AngConfigManager.shareAll2Clipboard() == 0) { if (AngConfigManager.shareAll2Clipboard() == 0) {
//remove toast, otherwise it will block previous warning message toast(R.string.toast_success)
} else { } else {
toast(R.string.toast_failure) toast(R.string.toast_failure)
} }
@@ -249,34 +240,19 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
} }
R.id.ping_all -> { R.id.ping_all -> {
testingJobs.forEach {
it.cancel()
}
testingJobs.clear()
Utils.closeAllTcpSockets()
for (k in 0 until configs.vmess.count()) { for (k in 0 until configs.vmess.count()) {
configs.vmess[k].testResult = "" configs.vmess[k].testResult = ""
adapter.updateConfigList() adapter.updateConfigList()
} }
for (k in 0 until configs.vmess.count()) { for (k in 0 until configs.vmess.count()) {
var serverAddress = configs.vmess[k].address if (configs.vmess[k].configType != AppConfig.EConfigType.Custom) {
var serverPort = configs.vmess[k].port doAsync {
if (configs.vmess[k].configType == EConfigType.CUSTOM.value) { configs.vmess[k].testResult = Utils.tcping(configs.vmess[k].address, configs.vmess[k].port)
val serverOutbound = V2rayConfigUtil.getCustomConfigServerOutbound(applicationContext, configs.vmess[k].guid) uiThread {
?: continue
serverAddress = serverOutbound.getServerAddress() ?: continue
serverPort = serverOutbound.getServerPort() ?: continue
}
testingJobs.add(GlobalScope.launch(Dispatchers.IO) {
configs.vmess.getOrNull(k)?.let { // check null in case array is modified during testing
it.testResult = Utils.tcping(serverAddress, serverPort)
val myJob = coroutineContext[Job]
launch(Dispatchers.Main) {
testingJobs.remove(myJob)
adapter.updateSelectedItem(k) adapter.updateSelectedItem(k)
} }
} }
}) }
} }
true true
} }
@@ -466,10 +442,9 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
.subscribe { .subscribe {
if (it) { if (it) {
try { try {
contentResolver.openInputStream(uri).use { val inputStream = contentResolver.openInputStream(uri)
val configText = it?.bufferedReader()?.readText() val configText = inputStream.bufferedReader().readText()
importCustomizeConfig(configText) importCustomizeConfig(configText)
}
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
} }
@@ -595,4 +570,4 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
drawer_layout.closeDrawer(GravityCompat.START) drawer_layout.closeDrawer(GravityCompat.START)
return true return true
} }
} }
@@ -8,19 +8,17 @@ import android.view.ViewGroup
import com.v2ray.ang.AppConfig import com.v2ray.ang.AppConfig
import com.v2ray.ang.R import com.v2ray.ang.R
import com.v2ray.ang.dto.AngConfig import com.v2ray.ang.dto.AngConfig
import com.v2ray.ang.dto.EConfigType
import com.v2ray.ang.extension.defaultDPreference
import com.v2ray.ang.helper.ItemTouchHelperAdapter import com.v2ray.ang.helper.ItemTouchHelperAdapter
import com.v2ray.ang.helper.ItemTouchHelperViewHolder import com.v2ray.ang.helper.ItemTouchHelperViewHolder
import com.v2ray.ang.util.AngConfigManager import com.v2ray.ang.util.AngConfigManager
import com.v2ray.ang.util.Utils import com.v2ray.ang.util.Utils
import com.v2ray.ang.util.V2rayConfigUtil
import kotlinx.android.synthetic.main.item_qrcode.view.* import kotlinx.android.synthetic.main.item_qrcode.view.*
import kotlinx.android.synthetic.main.item_recycler_main.view.* import kotlinx.android.synthetic.main.item_recycler_main.view.*
import org.jetbrains.anko.* import org.jetbrains.anko.*
import rx.Observable import rx.Observable
import rx.android.schedulers.AndroidSchedulers import rx.android.schedulers.AndroidSchedulers
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import com.v2ray.ang.extension.defaultDPreference
class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<MainRecyclerAdapter.BaseViewHolder>() class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<MainRecyclerAdapter.BaseViewHolder>()
, ItemTouchHelperAdapter { , ItemTouchHelperAdapter {
@@ -51,7 +49,7 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
override fun onBindViewHolder(holder: BaseViewHolder, position: Int) { override fun onBindViewHolder(holder: BaseViewHolder, position: Int) {
if (holder is MainViewHolder) { if (holder is MainViewHolder) {
val configType = EConfigType.fromInt(configs.vmess[position].configType) val configType = configs.vmess[position].configType
val remarks = configs.vmess[position].remarks val remarks = configs.vmess[position].remarks
val subid = configs.vmess[position].subid val subid = configs.vmess[position].subid
val address = configs.vmess[position].address val address = configs.vmess[position].address
@@ -69,40 +67,39 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
holder.subid.text = "S" holder.subid.text = "S"
} }
var shareOptions = share_method.asList() if (configType == AppConfig.EConfigType.Vmess) {
if (configType == EConfigType.CUSTOM) { holder.type.text = "vmess"
holder.type.text = mActivity.getString(R.string.server_customize_config)
val serverOutbound = V2rayConfigUtil.getCustomConfigServerOutbound(mActivity.applicationContext, configs.vmess[position].guid)
if (serverOutbound == null) {
holder.statistics.text = ""
} else {
holder.statistics.text = "${serverOutbound.getServerAddress()} : ${serverOutbound.getServerPort()}"
}
shareOptions = shareOptions.takeLast(1)
} else {
holder.type.text = configType?.name?.toLowerCase()
holder.statistics.text = "$address : $port" holder.statistics.text = "$address : $port"
holder.layout_share.visibility = View.VISIBLE
} else if (configType == AppConfig.EConfigType.Custom) {
holder.type.text = mActivity.getString(R.string.server_customize_config)
holder.statistics.text = ""//mActivity.getString(R.string.server_customize_config)
holder.layout_share.visibility = View.INVISIBLE
} else if (configType == AppConfig.EConfigType.Shadowsocks) {
holder.type.text = "shadowsocks"
holder.statistics.text = "$address : $port"
holder.layout_share.visibility = View.VISIBLE
} else if (configType == AppConfig.EConfigType.Socks) {
holder.type.text = "socks"
holder.statistics.text = "$address : $port"
holder.layout_share.visibility = View.VISIBLE
} }
holder.layout_share.setOnClickListener { holder.layout_share.setOnClickListener {
mActivity.selector(null, shareOptions) { dialogInterface, i -> mActivity.selector(null, share_method.asList()) { dialogInterface, i ->
try { try {
when (i) { when (i) {
0 -> { 0 -> {
if (configType == EConfigType.CUSTOM) { val iv = mActivity.layoutInflater.inflate(R.layout.item_qrcode, null)
shareFullContent(position) iv.iv_qcode.setImageBitmap(AngConfigManager.share2QRCode(position))
} else {
val iv = mActivity.layoutInflater.inflate(R.layout.item_qrcode, null)
iv.iv_qcode.setImageBitmap(AngConfigManager.share2QRCode(position))
mActivity.alert { mActivity.alert {
customView { customView {
linearLayout { linearLayout {
addView(iv) addView(iv)
}
} }
}.show() }
} }.show()
} }
1 -> { 1 -> {
if (AngConfigManager.share2Clipboard(position) == 0) { if (AngConfigManager.share2Clipboard(position) == 0) {
@@ -111,8 +108,15 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
mActivity.toast(R.string.toast_failure) mActivity.toast(R.string.toast_failure)
} }
} }
2 -> shareFullContent(position) 2 -> {
else -> mActivity.toast("else") if (AngConfigManager.shareFullContent2Clipboard(position) == 0) {
mActivity.toast(R.string.toast_success)
} else {
mActivity.toast(R.string.toast_failure)
}
}
else ->
mActivity.toast("else")
} }
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
@@ -121,13 +125,13 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
} }
holder.layout_edit.setOnClickListener { holder.layout_edit.setOnClickListener {
if (configType == EConfigType.VMESS) { if (configType == AppConfig.EConfigType.Vmess) {
mActivity.startActivity<ServerActivity>("position" to position, "isRunning" to !changeable) mActivity.startActivity<ServerActivity>("position" to position, "isRunning" to !changeable)
} else if (configType == EConfigType.CUSTOM) { } else if (configType == AppConfig.EConfigType.Custom) {
mActivity.startActivity<Server2Activity>("position" to position, "isRunning" to !changeable) mActivity.startActivity<Server2Activity>("position" to position, "isRunning" to !changeable)
} else if (configType == EConfigType.SHADOWSOCKS) { } else if (configType == AppConfig.EConfigType.Shadowsocks) {
mActivity.startActivity<Server3Activity>("position" to position, "isRunning" to !changeable) mActivity.startActivity<Server3Activity>("position" to position, "isRunning" to !changeable)
} else if (configType == EConfigType.SOCKS) { } else if (configType == AppConfig.EConfigType.Socks) {
mActivity.startActivity<Server4Activity>("position" to position, "isRunning" to !changeable) mActivity.startActivity<Server4Activity>("position" to position, "isRunning" to !changeable)
} }
} }
@@ -172,14 +176,6 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
} }
} }
private fun shareFullContent(position: Int) {
if (AngConfigManager.shareFullContent2Clipboard(position) == 0) {
mActivity.toast(R.string.toast_success)
} else {
mActivity.toast(R.string.toast_failure)
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder {
when (viewType) { when (viewType) {
VIEW_TYPE_ITEM -> VIEW_TYPE_ITEM ->
@@ -269,8 +265,4 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
updateSelectedItem(if (fromPosition < toPosition) fromPosition else toPosition) updateSelectedItem(if (fromPosition < toPosition) fromPosition else toPosition)
return true return true
} }
override fun onItemMoveCompleted() {
AngConfigManager.storeConfigFile()
}
} }
@@ -1,22 +1,104 @@
package com.v2ray.ang.ui package com.v2ray.ang.ui
import android.content.*
import android.net.VpnService
import com.v2ray.ang.R import com.v2ray.ang.R
import com.v2ray.ang.util.Utils import com.v2ray.ang.util.Utils
import android.os.Bundle import android.os.Bundle
import com.v2ray.ang.service.V2RayServiceManager import com.v2ray.ang.AppConfig
import com.v2ray.ang.util.MessageUtil
import java.lang.ref.SoftReference
import android.content.IntentFilter
import kotlinx.android.synthetic.main.activity_main.*
import rx.Observable
import rx.android.schedulers.AndroidSchedulers
import java.util.concurrent.TimeUnit
class ScSwitchActivity : BaseActivity() { class ScSwitchActivity : BaseActivity() {
companion object {
private const val REQUEST_CODE_VPN_PREPARE = 0
}
var isRunning = false
set(value) {
field = value
if (value) {
Utils.stopVService(this)
} else {
val intent = VpnService.prepare(this)
if (intent == null) {
Utils.startVService(this)
} else {
startActivityForResult(intent, REQUEST_CODE_VPN_PREPARE)
}
}
finishActivity()
}
fun finishActivity() {
try {
Observable.timer(5000, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
finish()
}
} catch (e: Exception) {
}
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
moveTaskToBack(true) moveTaskToBack(true)
setContentView(R.layout.activity_none) setContentView(R.layout.activity_none)
if (V2RayServiceManager.v2rayPoint.isRunning) { val isRunning = Utils.isServiceRun(this, "com.v2ray.ang.service.V2RayVpnService")
Utils.stopVService(this) if (isRunning) {
//Utils.stopVService(this)
mMsgReceive = ReceiveMessageHandler(this@ScSwitchActivity)
registerReceiver(mMsgReceive, IntentFilter(AppConfig.BROADCAST_ACTION_ACTIVITY))
MessageUtil.sendMsg2Service(this, AppConfig.MSG_REGISTER_CLIENT, "")
} else { } else {
Utils.startVServiceFromToggle(this) Utils.startVService(this)
finishActivity()
} }
finish()
} }
}
override fun onStop() {
super.onStop()
if (mMsgReceive != null) {
unregisterReceiver(mMsgReceive)
mMsgReceive = null
}
}
private var mMsgReceive: BroadcastReceiver? = null
private class ReceiveMessageHandler(activity: ScSwitchActivity) : BroadcastReceiver() {
internal var mReference: SoftReference<ScSwitchActivity> = SoftReference(activity)
override fun onReceive(ctx: Context?, intent: Intent?) {
val activity = mReference.get()
when (intent?.getIntExtra("key", 0)) {
AppConfig.MSG_STATE_RUNNING -> {
activity?.isRunning = true
}
AppConfig.MSG_STATE_NOT_RUNNING -> {
activity?.isRunning = false
}
// AppConfig.MSG_STATE_START_SUCCESS -> {
// activity?.toast(R.string.toast_services_success)
// activity?.isRunning = true
// }
// AppConfig.MSG_STATE_START_FAILURE -> {
// activity?.toast(R.string.toast_services_failure)
// activity?.isRunning = false
// }
// AppConfig.MSG_STATE_STOP_SUCCESS -> {
// activity?.isRunning = false
// }
}
}
}
}
@@ -119,10 +119,10 @@ class ScannerActivity : BaseActivity(), ZXingScannerView.ResultHandler {
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data) super.onActivityResult(requestCode, resultCode, data)
when (requestCode) { when (requestCode) {
REQUEST_FILE_CHOOSER -> { REQUEST_FILE_CHOOSER ->
val uri = data?.data if (resultCode == RESULT_OK) {
if (resultCode == RESULT_OK && uri != null) {
try { try {
val uri = data!!.data
val bitmap = BitmapFactory.decodeStream(contentResolver.openInputStream(uri)) val bitmap = BitmapFactory.decodeStream(contentResolver.openInputStream(uri))
val text = QRCodeDecoder.syncDecodeQRCode(bitmap) val text = QRCodeDecoder.syncDecodeQRCode(bitmap)
finished(text) finished(text)
@@ -131,7 +131,6 @@ class ScannerActivity : BaseActivity(), ZXingScannerView.ResultHandler {
toast(e.message.toString()) toast(e.message.toString())
} }
} }
}
} }
} }
} }
@@ -1,11 +1,9 @@
package com.v2ray.ang.ui package com.v2ray.ang.ui
import android.os.Bundle import android.os.Bundle
import android.text.Editable
import android.text.TextUtils import android.text.TextUtils
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import com.google.gson.Gson
import com.v2ray.ang.AppConfig import com.v2ray.ang.AppConfig
import com.v2ray.ang.R import com.v2ray.ang.R
import com.v2ray.ang.extension.defaultDPreference import com.v2ray.ang.extension.defaultDPreference
@@ -14,7 +12,6 @@ import com.v2ray.ang.util.AngConfigManager
import com.v2ray.ang.util.Utils import com.v2ray.ang.util.Utils
import kotlinx.android.synthetic.main.activity_server2.* import kotlinx.android.synthetic.main.activity_server2.*
import org.jetbrains.anko.* import org.jetbrains.anko.*
import java.lang.Exception
class Server2Activity : BaseActivity() { class Server2Activity : BaseActivity() {
@@ -53,7 +50,7 @@ class Server2Activity : BaseActivity() {
*/ */
fun bindingServer(vmess: AngConfig.VmessBean): Boolean { fun bindingServer(vmess: AngConfig.VmessBean): Boolean {
et_remarks.text = Utils.getEditable(vmess.remarks) et_remarks.text = Utils.getEditable(vmess.remarks)
tv_content.text = Editable.Factory.getInstance().newEditable(defaultDPreference.getPrefString(AppConfig.ANG_CONFIG + edit_guid, "")) tv_content.text = defaultDPreference.getPrefString(AppConfig.ANG_CONFIG + edit_guid, "")
return true return true
} }
@@ -69,40 +66,21 @@ class Server2Activity : BaseActivity() {
* save server config * save server config
*/ */
fun saveServer(): Boolean { fun saveServer(): Boolean {
var saveSuccess: Boolean
val vmess = configs.vmess[edit_index] val vmess = configs.vmess[edit_index]
vmess.remarks = et_remarks.text.toString() vmess.remarks = et_remarks.text.toString()
if (TextUtils.isEmpty(vmess.remarks)) { if (TextUtils.isEmpty(vmess.remarks)) {
toast(R.string.server_lab_remarks) toast(R.string.server_lab_remarks)
saveSuccess = false return false
} }
if (AngConfigManager.addCustomServer(vmess, edit_index) == 0) { if (AngConfigManager.addCustomServer(vmess, edit_index) == 0) {
toast(R.string.toast_success) toast(R.string.toast_success)
saveSuccess = true
} else {
toast(R.string.toast_failure)
saveSuccess = false
}
try {
Gson().fromJson<Object>(tv_content.text.toString(), Object::class.java)
} catch (e: Exception) {
e.printStackTrace()
toast(R.string.toast_malformed_josn)
saveSuccess = false
}
if (saveSuccess) {
//update config
defaultDPreference.setPrefString(AppConfig.ANG_CONFIG + edit_guid, tv_content.text.toString())
finish() finish()
return true return true
} else { } else {
toast(R.string.toast_failure)
return false return false
} }
} }
@@ -1,14 +1,25 @@
package com.v2ray.ang.ui package com.v2ray.ang.ui
import android.content.Intent
import android.content.SharedPreferences
import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.support.v7.preference.* import android.preference.CheckBoxPreference
import android.view.View import android.preference.EditTextPreference
import android.preference.Preference
import android.preference.PreferenceFragment
import com.v2ray.ang.BuildConfig
//import com.v2ray.ang.InappBuyActivity
import com.v2ray.ang.R import com.v2ray.ang.R
import com.v2ray.ang.AppConfig import com.v2ray.ang.AppConfig
import com.v2ray.ang.extension.defaultDPreference
import com.v2ray.ang.extension.onClick
import com.v2ray.ang.util.Utils import com.v2ray.ang.util.Utils
import org.jetbrains.anko.act
import org.jetbrains.anko.defaultSharedPreferences import org.jetbrains.anko.defaultSharedPreferences
import org.jetbrains.anko.startActivity import org.jetbrains.anko.startActivity
import org.jetbrains.anko.toast import org.jetbrains.anko.toast
import libv2ray.Libv2ray
class SettingsActivity : BaseActivity() { class SettingsActivity : BaseActivity() {
companion object { companion object {
@@ -18,7 +29,6 @@ class SettingsActivity : BaseActivity() {
// const val PREF_MUX_ENAimport libv2ray.Libv2rayBLED = "pref_mux_enabled" // const val PREF_MUX_ENAimport libv2ray.Libv2rayBLED = "pref_mux_enabled"
const val PREF_SPEED_ENABLED = "pref_speed_enabled" const val PREF_SPEED_ENABLED = "pref_speed_enabled"
const val PREF_SNIFFING_ENABLED = "pref_sniffing_enabled" const val PREF_SNIFFING_ENABLED = "pref_sniffing_enabled"
const val PREF_PROXY_SHARING = "pref_proxy_sharing_enabled"
const val PREF_LOCAL_DNS_ENABLED = "pref_local_dns_enabled" const val PREF_LOCAL_DNS_ENABLED = "pref_local_dns_enabled"
const val PREF_REMOTE_DNS = "pref_remote_dns" const val PREF_REMOTE_DNS = "pref_remote_dns"
const val PREF_DOMESTIC_DNS = "pref_domestic_dns" const val PREF_DOMESTIC_DNS = "pref_domestic_dns"
@@ -33,6 +43,7 @@ class SettingsActivity : BaseActivity() {
// const val PREF_LICENSES = "pref_licenses" // const val PREF_LICENSES = "pref_licenses"
// const val PREF_FEEDBACK = "pref_feedback" // const val PREF_FEEDBACK = "pref_feedback"
// const val PREF_TG_GROUP = "pref_tg_group" // const val PREF_TG_GROUP = "pref_tg_group"
const val PREF_VERSION = "pref_version"
// const val PREF_AUTO_RESTART = "pref_auto_restart" // const val PREF_AUTO_RESTART = "pref_auto_restart"
const val PREF_FORWARD_IPV6 = "pref_forward_ipv6" const val PREF_FORWARD_IPV6 = "pref_forward_ipv6"
} }
@@ -46,126 +57,32 @@ class SettingsActivity : BaseActivity() {
supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayHomeAsUpEnabled(true)
} }
class SettingsFragment : PreferenceFragmentCompat() { class SettingsFragment : PreferenceFragment(), SharedPreferences.OnSharedPreferenceChangeListener {
private val perAppProxy by lazy { findPreference(PREF_PER_APP_PROXY) as CheckBoxPreference } val perAppProxy by lazy { findPreference(PREF_PER_APP_PROXY) as CheckBoxPreference }
private val sppedEnabled by lazy { findPreference(PREF_SPEED_ENABLED) as CheckBoxPreference }
private val sniffingEnabled by lazy { findPreference(PREF_SNIFFING_ENABLED) as CheckBoxPreference }
private val proxySharing by lazy { findPreference(PREF_PROXY_SHARING) as CheckBoxPreference }
private val domainStrategy by lazy { findPreference(PREF_ROUTING_DOMAIN_STRATEGY) as ListPreference }
private val routingMode by lazy { findPreference(PREF_ROUTING_MODE) as ListPreference }
private val forwardIpv6 by lazy { findPreference(PREF_FORWARD_IPV6) as CheckBoxPreference }
private val enableLocalDns by lazy { findPreference(PREF_LOCAL_DNS_ENABLED) as CheckBoxPreference }
private val domesticDns by lazy { findPreference(PREF_DOMESTIC_DNS) as EditTextPreference }
private val remoteDns by lazy { findPreference(PREF_REMOTE_DNS) as EditTextPreference }
// val autoRestart by lazy { findPreference(PREF_AUTO_RESTART) as CheckBoxPreference } // val autoRestart by lazy { findPreference(PREF_AUTO_RESTART) as CheckBoxPreference }
val remoteDns by lazy { findPreference(PREF_REMOTE_DNS) as EditTextPreference }
val domesticDns by lazy { findPreference(PREF_DOMESTIC_DNS) as EditTextPreference }
val enableLocalDns by lazy { findPreference(PREF_LOCAL_DNS_ENABLED) as CheckBoxPreference }
val forwardIpv6 by lazy { findPreference(PREF_FORWARD_IPV6) as CheckBoxPreference }
// val socksPort by lazy { findPreference(PREF_SOCKS_PORT) as EditTextPreference } // val socksPort by lazy { findPreference(PREF_SOCKS_PORT) as EditTextPreference }
// val httpPort by lazy { findPreference(PREF_HTTP_PORT) as EditTextPreference } // val httpPort by lazy { findPreference(PREF_HTTP_PORT) as EditTextPreference }
private val routingCustom: Preference by lazy { findPreference(PREF_ROUTING_CUSTOM) } val routingCustom: Preference by lazy { findPreference(PREF_ROUTING_CUSTOM) }
// val donate: Preference by lazy { findPreference(PREF_DONATE) } // val donate: Preference by lazy { findPreference(PREF_DONATE) }
// val licenses: Preference by lazy { findPreference(PREF_LICENSES) } // val licenses: Preference by lazy { findPreference(PREF_LICENSES) }
// val feedback: Preference by lazy { findPreference(PREF_FEEDBACK) } // val feedback: Preference by lazy { findPreference(PREF_FEEDBACK) }
// val tgGroup: Preference by lazy { findPreference(PREF_TG_GROUP) } // val tgGroup: Preference by lazy { findPreference(PREF_TG_GROUP) }
val version: Preference by lazy { findPreference(PREF_VERSION) }
private val mode by lazy { findPreference(AppConfig.PREF_MODE) as ListPreference } override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
private fun restartProxy() {
Utils.stopVService(requireContext())
Utils.startVService(requireContext())
}
private fun isRunning(): Boolean {
return false //TODO no point of adding logic now since Settings will be changed soon
}
override fun onCreatePreferences(bundle: Bundle?, s: String?) {
addPreferencesFromResource(R.xml.pref_settings) addPreferencesFromResource(R.xml.pref_settings)
perAppProxy.setOnPreferenceClickListener { routingCustom.onClick {
if (isRunning()) { startActivity<RoutingSettingsActivity>()
Utils.stopVService(requireContext())
}
activity?.startActivity<PerAppProxyActivity>()
perAppProxy.isChecked = true
true
} }
sppedEnabled.setOnPreferenceClickListener {
if (isRunning())
restartProxy()
true
}
sniffingEnabled.setOnPreferenceClickListener {
if (isRunning())
restartProxy()
true
}
proxySharing.setOnPreferenceClickListener {
if (proxySharing.isChecked)
activity?.toast(R.string.toast_warning_pref_proxysharing)
if (isRunning())
restartProxy()
true
}
domainStrategy.setOnPreferenceChangeListener { _, _ ->
if (isRunning())
restartProxy()
true
}
routingMode.setOnPreferenceChangeListener { _, _ ->
if (isRunning())
restartProxy()
true
}
routingCustom.setOnPreferenceClickListener {
if (isRunning())
Utils.stopVService(requireContext())
activity?.startActivity<RoutingSettingsActivity>()
false
}
forwardIpv6.setOnPreferenceClickListener {
if (isRunning())
restartProxy()
true
}
enableLocalDns.setOnPreferenceClickListener {
if (isRunning())
restartProxy()
true
}
domesticDns.setOnPreferenceChangeListener { _, any ->
// domesticDns.summary = any as String
val nval = any as String
domesticDns.summary = if (nval == "") AppConfig.DNS_DIRECT else nval
if (isRunning())
restartProxy()
true
}
remoteDns.setOnPreferenceChangeListener { _, any ->
// remoteDns.summary = any as String
val nval = any as String
remoteDns.summary = if (nval == "") AppConfig.DNS_AGENT else nval
if (isRunning())
restartProxy()
true
}
mode.setOnPreferenceChangeListener { _, newValue ->
updatePerAppProxy(newValue.toString())
true
}
mode.dialogLayoutResource = R.layout.preference_with_help_link
// donate.onClick { // donate.onClick {
// startActivity<InappBuyActivity>() // startActivity<InappBuyActivity>()
@@ -193,7 +110,24 @@ class SettingsActivity : BaseActivity() {
// } // }
// } // }
perAppProxy.setOnPreferenceClickListener {
startActivity<PerAppProxyActivity>()
perAppProxy.isChecked = true
false
}
remoteDns.setOnPreferenceChangeListener { preference, any ->
// remoteDns.summary = any as String
val nval = any as String
remoteDns.summary = if (nval == "") AppConfig.DNS_AGENT else nval
true
}
domesticDns.setOnPreferenceChangeListener { preference, any ->
// domesticDns.summary = any as String
val nval = any as String
domesticDns.summary = if (nval == "") AppConfig.DNS_DIRECT else nval
true
}
// socksPort.setOnPreferenceChangeListener { preference, any -> // socksPort.setOnPreferenceChangeListener { preference, any ->
// socksPort.summary = any as String // socksPort.summary = any as String
// true // true
@@ -202,13 +136,16 @@ class SettingsActivity : BaseActivity() {
// httpPort.summary = any as String // httpPort.summary = any as String
// true // true
// } // }
version.summary = "${BuildConfig.VERSION_NAME} (${Libv2ray.checkVersionX()})"
} }
override fun onStart() { override fun onStart() {
super.onStart() super.onStart()
updatePerAppProxy(activity?.defaultSharedPreferences?.getString(AppConfig.PREF_MODE, "VPN"))
remoteDns.summary = activity?.defaultSharedPreferences?.getString(PREF_REMOTE_DNS, "") perAppProxy.isChecked = defaultSharedPreferences.getBoolean(PREF_PER_APP_PROXY, false)
domesticDns.summary = activity?.defaultSharedPreferences?.getString(PREF_DOMESTIC_DNS, "") remoteDns.summary = defaultSharedPreferences.getString(PREF_REMOTE_DNS, "")
domesticDns.summary = defaultSharedPreferences.getString(PREF_DOMESTIC_DNS, "")
if (remoteDns.summary == "") { if (remoteDns.summary == "") {
remoteDns.summary = AppConfig.DNS_AGENT remoteDns.summary = AppConfig.DNS_AGENT
@@ -220,21 +157,24 @@ class SettingsActivity : BaseActivity() {
// socksPort.summary = defaultSharedPreferences.getString(PREF_SOCKS_PORT, "10808") // socksPort.summary = defaultSharedPreferences.getString(PREF_SOCKS_PORT, "10808")
// lanconnPort.summary = defaultSharedPreferences.getString(PREF_HTTP_PORT, "") // lanconnPort.summary = defaultSharedPreferences.getString(PREF_HTTP_PORT, "")
defaultSharedPreferences.registerOnSharedPreferenceChangeListener(this)
} }
private fun updatePerAppProxy(mode: String?) { override fun onStop() {
val preference = activity?.defaultSharedPreferences ?: return super.onStop()
if (mode == "VPN") { defaultSharedPreferences.unregisterOnSharedPreferenceChangeListener(this)
perAppProxy.isEnabled = true }
perAppProxy.isChecked = preference.getBoolean(PREF_PER_APP_PROXY, false)
} else { override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String?) {
perAppProxy.isEnabled = false when (key) {
perAppProxy.isChecked = false // PREF_AUTO_RESTART ->
// act.defaultDPreference.setPrefBoolean(key, sharedPreferences.getBoolean(key, false))
PREF_PER_APP_PROXY ->
act.defaultDPreference.setPrefBoolean(key, sharedPreferences.getBoolean(key, false))
} }
} }
} }
fun onModeHelpClicked(view: View) { }
Utils.openUri(this, AppConfig.v2rayNGWikiMode)
}
}
@@ -2,6 +2,7 @@ package com.v2ray.ang.util
import android.graphics.Bitmap import android.graphics.Bitmap
import android.text.TextUtils import android.text.TextUtils
import android.util.Log
import com.google.gson.Gson import com.google.gson.Gson
import com.v2ray.ang.AngApplication import com.v2ray.ang.AngApplication
import com.v2ray.ang.AppConfig import com.v2ray.ang.AppConfig
@@ -14,11 +15,13 @@ import com.v2ray.ang.AppConfig.SS_PROTOCOL
import com.v2ray.ang.AppConfig.VMESS_PROTOCOL import com.v2ray.ang.AppConfig.VMESS_PROTOCOL
import com.v2ray.ang.R import com.v2ray.ang.R
import com.v2ray.ang.dto.AngConfig import com.v2ray.ang.dto.AngConfig
import com.v2ray.ang.dto.EConfigType
import com.v2ray.ang.dto.VmessQRCode import com.v2ray.ang.dto.VmessQRCode
import java.net.URI import com.v2ray.ang.extension.defaultDPreference
import org.jetbrains.anko.toast
import java.net.URLDecoder import java.net.URLDecoder
import java.util.* import java.util.*
import java.net.*
import java.math.BigInteger
object AngConfigManager { object AngConfigManager {
private lateinit var app: AngApplication private lateinit var app: AngApplication
@@ -66,7 +69,7 @@ object AngConfigManager {
fun addServer(vmess: AngConfig.VmessBean, index: Int): Int { fun addServer(vmess: AngConfig.VmessBean, index: Int): Int {
try { try {
vmess.configVersion = 2 vmess.configVersion = 2
vmess.configType = EConfigType.VMESS.value vmess.configType = AppConfig.EConfigType.Vmess
if (index >= 0) { if (index >= 0) {
//edit //edit
@@ -101,7 +104,16 @@ object AngConfigManager {
angConfig.vmess.removeAt(index) angConfig.vmess.removeAt(index)
//移除的是活动的 //移除的是活动的
adjustIndexForRemovalAt(index) if (angConfig.index == index) {
if (angConfig.vmess.count() > 0) {
angConfig.index = 0
} else {
angConfig.index = -1
}
} else if (index < angConfig.index)//移除活动之前的
{
angConfig.index--
}
storeConfigFile() storeConfigFile()
} catch (e: Exception) { } catch (e: Exception) {
@@ -111,19 +123,6 @@ object AngConfigManager {
return 0 return 0
} }
private fun adjustIndexForRemovalAt(index: Int) {
if (angConfig.index == index) {
if (angConfig.vmess.count() > 0) {
angConfig.index = 0
} else {
angConfig.index = -1
}
} else if (index < angConfig.index)//移除活动之前的
{
angConfig.index--
}
}
fun swapServer(fromPosition: Int, toPosition: Int): Int { fun swapServer(fromPosition: Int, toPosition: Int): Int {
try { try {
Collections.swap(angConfig.vmess, fromPosition, toPosition) Collections.swap(angConfig.vmess, fromPosition, toPosition)
@@ -134,7 +133,7 @@ object AngConfigManager {
} else if (index == toPosition) { } else if (index == toPosition) {
angConfig.index = fromPosition angConfig.index = fromPosition
} }
//storeConfigFile() storeConfigFile()
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
return -1 return -1
@@ -148,15 +147,13 @@ object AngConfigManager {
fun setActiveServer(index: Int): Int { fun setActiveServer(index: Int): Int {
try { try {
if (index < 0 || index > angConfig.vmess.count() - 1) { if (index < 0 || index > angConfig.vmess.count() - 1) {
app.curIndex = -1
return -1 return -1
} }
angConfig.index = index angConfig.index = index
app.curIndex = index
storeConfigFile() storeConfigFile()
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
app.curIndex = -1
return -1 return -1
} }
return 0 return 0
@@ -209,14 +206,14 @@ object AngConfigManager {
return app.defaultDPreference.getPrefString(AppConfig.PREF_CURR_CONFIG, "") return app.defaultDPreference.getPrefString(AppConfig.PREF_CURR_CONFIG, "")
} }
fun currConfigType(): EConfigType? { fun currConfigType(): Int {
if (angConfig.index < 0 if (angConfig.index < 0
|| angConfig.vmess.count() <= 0 || angConfig.vmess.count() <= 0
|| angConfig.index > angConfig.vmess.count() - 1 || angConfig.index > angConfig.vmess.count() - 1
) { ) {
return null return -1
} }
return EConfigType.fromInt(angConfig.vmess[angConfig.index].configType) return angConfig.vmess[angConfig.index].configType
} }
fun currConfigName(): String { fun currConfigName(): String {
@@ -242,7 +239,7 @@ object AngConfigManager {
/** /**
* import config form qrcode or... * import config form qrcode or...
*/ */
fun importConfig(server: String?, subid: String, removedSelectedServer: AngConfig.VmessBean?): Int { fun importConfig(server: String?, subid: String): Int {
try { try {
if (server == null || TextUtils.isEmpty(server)) { if (server == null || TextUtils.isEmpty(server)) {
return R.string.toast_none_data return R.string.toast_none_data
@@ -253,11 +250,7 @@ object AngConfigManager {
if (server.startsWith(VMESS_PROTOCOL)) { if (server.startsWith(VMESS_PROTOCOL)) {
val indexSplit = server.indexOf("?") val indexSplit = server.indexOf("?")
val newVmess = tryParseNewVmess(server) if (indexSplit > 0) {
if (newVmess != null) {
vmess = newVmess
vmess.subid = subid
} else if (indexSplit > 0) {
vmess = ResolveVmess4Kitsunebi(server) vmess = ResolveVmess4Kitsunebi(server)
} else { } else {
@@ -276,7 +269,7 @@ object AngConfigManager {
return R.string.toast_incorrect_protocol return R.string.toast_incorrect_protocol
} }
vmess.configType = EConfigType.VMESS.value vmess.configType = AppConfig.EConfigType.Vmess
vmess.security = "auto" vmess.security = "auto"
vmess.network = "tcp" vmess.network = "tcp"
vmess.headerType = "none" vmess.headerType = "none"
@@ -366,12 +359,6 @@ object AngConfigManager {
} else { } else {
return R.string.toast_incorrect_protocol return R.string.toast_incorrect_protocol
} }
if (removedSelectedServer != null &&
vmess.subid.equals(removedSelectedServer.subid) &&
vmess.address.equals(removedSelectedServer.address) &&
vmess.port.equals(removedSelectedServer.port)) {
setActiveServer(configs.vmess.count() - 1)
}
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
return -1 return -1
@@ -379,61 +366,6 @@ object AngConfigManager {
return 0 return 0
} }
fun tryParseNewVmess(uri: String): AngConfig.VmessBean? {
return runCatching {
val uri = URI(uri)
check(uri.scheme == "vmess")
val (_, protocol, tlsStr, uuid, alterId) =
Regex("(tcp|http|ws|kcp|quic)(\\+tls)?:([0-9a-z]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12})-([0-9]+)")
.matchEntire(uri.userInfo)?.groupValues
?: error("parse user info fail.")
val tls = tlsStr.isNotBlank()
val queryParam = uri.rawQuery.split("&")
.map { it.split("=").let { (k, v) -> k to URLDecoder.decode(v, "utf-8")!! } }
.toMap()
val vmess = AngConfig.VmessBean()
vmess.address = uri.host
vmess.port = uri.port
vmess.id = uuid
vmess.alterId = alterId.toInt()
vmess.streamSecurity = if (tls) "tls" else ""
vmess.remarks = uri.fragment
vmess.security = "auto"
// TODO: allowInsecure not supported
when (protocol) {
"tcp" -> {
vmess.network = "tcp"
vmess.headerType = queryParam["type"] ?: "none"
vmess.requestHost = queryParam["host"] ?: ""
}
"http" -> {
vmess.network = "h2"
vmess.path = queryParam["path"]?.takeIf { it.trim() != "/" } ?: ""
vmess.requestHost = queryParam["host"]?.split("|")?.get(0) ?: ""
}
"ws" -> {
vmess.network = "ws"
vmess.path = queryParam["path"]?.takeIf { it.trim() != "/" } ?: ""
vmess.requestHost = queryParam["host"]?.split("|")?.get(0) ?: ""
}
"kcp" -> {
vmess.network = "kcp"
vmess.headerType = queryParam["type"] ?: "none"
vmess.path = queryParam["seed"] ?: ""
}
"quic" -> {
vmess.network = "quic"
vmess.requestHost = queryParam["security"] ?: "none"
vmess.headerType = queryParam["type"] ?: "none"
vmess.path = queryParam["key"] ?: ""
}
}
vmess
}.getOrNull()
}
private fun ResolveVmess4Kitsunebi(server: String): AngConfig.VmessBean { private fun ResolveVmess4Kitsunebi(server: String): AngConfig.VmessBean {
val vmess = AngConfig.VmessBean() val vmess = AngConfig.VmessBean()
@@ -479,7 +411,7 @@ object AngConfigManager {
} }
val vmess = angConfig.vmess[index] val vmess = angConfig.vmess[index]
if (angConfig.vmess[index].configType == EConfigType.VMESS.value) { if (angConfig.vmess[index].configType == AppConfig.EConfigType.Vmess) {
val vmessQRCode = VmessQRCode() val vmessQRCode = VmessQRCode()
vmessQRCode.v = vmess.configVersion.toString() vmessQRCode.v = vmess.configVersion.toString()
@@ -497,7 +429,7 @@ object AngConfigManager {
val conf = VMESS_PROTOCOL + Utils.encode(json) val conf = VMESS_PROTOCOL + Utils.encode(json)
return conf return conf
} else if (angConfig.vmess[index].configType == EConfigType.SHADOWSOCKS.value) { } else if (angConfig.vmess[index].configType == AppConfig.EConfigType.Shadowsocks) {
val remark = "#" + Utils.urlEncode(vmess.remarks) val remark = "#" + Utils.urlEncode(vmess.remarks)
val url = String.format("%s:%s@%s:%s", val url = String.format("%s:%s@%s:%s",
vmess.security, vmess.security,
@@ -505,7 +437,7 @@ object AngConfigManager {
vmess.address, vmess.address,
vmess.port) vmess.port)
return SS_PROTOCOL + Utils.encode(url) + remark return SS_PROTOCOL + Utils.encode(url) + remark
} else if (angConfig.vmess[index].configType == EConfigType.SOCKS.value) { } else if (angConfig.vmess[index].configType == AppConfig.EConfigType.Socks) {
val remark = "#" + Utils.urlEncode(vmess.remarks) val remark = "#" + Utils.urlEncode(vmess.remarks)
val url = String.format("%s:%s", val url = String.format("%s:%s",
vmess.address, vmess.address,
@@ -614,7 +546,7 @@ object AngConfigManager {
//add //add
val vmess = AngConfig.VmessBean() val vmess = AngConfig.VmessBean()
vmess.configVersion = 2 vmess.configVersion = 2
vmess.configType = EConfigType.CUSTOM.value vmess.configType = AppConfig.EConfigType.Custom
vmess.guid = guid vmess.guid = guid
vmess.remarks = vmess.guid vmess.remarks = vmess.guid
@@ -717,7 +649,7 @@ object AngConfigManager {
fun addCustomServer(vmess: AngConfig.VmessBean, index: Int): Int { fun addCustomServer(vmess: AngConfig.VmessBean, index: Int): Int {
try { try {
vmess.configVersion = 2 vmess.configVersion = 2
vmess.configType = EConfigType.CUSTOM.value vmess.configType = AppConfig.EConfigType.Custom
if (index >= 0) { if (index >= 0) {
//edit //edit
@@ -742,7 +674,7 @@ object AngConfigManager {
fun addShadowsocksServer(vmess: AngConfig.VmessBean, index: Int): Int { fun addShadowsocksServer(vmess: AngConfig.VmessBean, index: Int): Int {
try { try {
vmess.configVersion = 2 vmess.configVersion = 2
vmess.configType = EConfigType.SHADOWSOCKS.value vmess.configType = AppConfig.EConfigType.Shadowsocks
if (index >= 0) { if (index >= 0) {
//edit //edit
@@ -767,7 +699,7 @@ object AngConfigManager {
fun addSocksServer(vmess: AngConfig.VmessBean, index: Int): Int { fun addSocksServer(vmess: AngConfig.VmessBean, index: Int): Int {
try { try {
vmess.configVersion = 2 vmess.configVersion = 2
vmess.configType = EConfigType.SOCKS.value vmess.configType = AppConfig.EConfigType.Socks
if (index >= 0) { if (index >= 0) {
//edit //edit
@@ -794,17 +726,6 @@ object AngConfigManager {
if (servers == null) { if (servers == null) {
return 0 return 0
} }
val removedSelectedServer =
if (!TextUtils.isEmpty(subid)) {
configs.vmess.getOrNull(configs.index)?.let {
if (it.subid == subid) {
return@let it
}
return@let null
}
} else {
null
}
removeServerViaSubid(subid) removeServerViaSubid(subid)
// var servers = server // var servers = server
@@ -815,7 +736,7 @@ object AngConfigManager {
var count = 0 var count = 0
servers.lines() servers.lines()
.forEach { .forEach {
val resId = importConfig(it, subid, removedSelectedServer) val resId = importConfig(it, subid)
if (resId == 0) { if (resId == 0) {
count++ count++
} }
@@ -855,7 +776,6 @@ object AngConfigManager {
for (k in configs.vmess.count() - 1 downTo 0) { for (k in configs.vmess.count() - 1 downTo 0) {
if (configs.vmess[k].subid.equals(subid)) { if (configs.vmess[k].subid.equals(subid)) {
angConfig.vmess.removeAt(k) angConfig.vmess.removeAt(k)
adjustIndexForRemovalAt(k)
} }
} }
@@ -901,4 +821,4 @@ object AngConfigManager {
return 0 return 0
} }
} }
@@ -16,7 +16,7 @@ object AppManagerUtil {
val apps = ArrayList<AppInfo>() val apps = ArrayList<AppInfo>()
for (pkg in packages) { for (pkg in packages) {
if (!pkg.hasInternetPermission && pkg.packageName != "android") continue if (!pkg.hasInternetPermission) continue
val applicationInfo = pkg.applicationInfo val applicationInfo = pkg.applicationInfo
@@ -40,4 +40,4 @@ object AppManagerUtil {
val permissions = requestedPermissions val permissions = requestedPermissions
return permissions?.any { it == Manifest.permission.INTERNET } ?: false return permissions?.any { it == Manifest.permission.INTERNET } ?: false
} }
} }
@@ -11,35 +11,42 @@ import com.google.zxing.qrcode.QRCodeWriter
import com.google.zxing.EncodeHintType import com.google.zxing.EncodeHintType
import java.util.* import java.util.*
import kotlin.collections.HashMap import kotlin.collections.HashMap
import android.app.ActivityManager
import android.content.ClipData import android.content.ClipData
import android.content.Intent import android.content.Intent
import android.content.res.AssetManager
import android.net.Uri import android.net.Uri
import android.os.SystemClock import android.os.SystemClock
import android.text.TextUtils import android.text.TextUtils
import android.text.method.ScrollingMovementMethod
import android.util.Log import android.util.Log
import android.util.Patterns import android.util.Patterns
import android.view.View
import android.webkit.URLUtil import android.webkit.URLUtil
import com.v2ray.ang.AngApplication import com.v2ray.ang.AngApplication
import com.v2ray.ang.AppConfig import com.v2ray.ang.AppConfig
import com.v2ray.ang.R import com.v2ray.ang.R
import com.v2ray.ang.dto.EConfigType
import com.v2ray.ang.extension.responseLength import com.v2ray.ang.extension.responseLength
import com.v2ray.ang.extension.v2RayApplication import com.v2ray.ang.service.V2RayVpnService
import com.v2ray.ang.service.V2RayServiceManager
import com.v2ray.ang.ui.SettingsActivity import com.v2ray.ang.ui.SettingsActivity
import kotlinx.coroutines.isActive import kotlinx.android.synthetic.main.activity_logcat.*
import me.dozen.dpreference.DPreference import me.dozen.dpreference.DPreference
import org.jetbrains.anko.toast import org.jetbrains.anko.toast
import org.jetbrains.anko.uiThread
import java.io.BufferedReader
import java.io.File
import java.io.IOException import java.io.IOException
import java.io.InputStreamReader
import java.net.* import java.net.*
import java.util.regex.Matcher
import java.util.regex.Pattern
import java.math.BigInteger
import java.util.concurrent.TimeUnit
import libv2ray.Libv2ray import libv2ray.Libv2ray
import kotlin.coroutines.coroutineContext
object Utils { object Utils {
val tcpTestingSockets = ArrayList<Socket?>()
/** /**
* convert string to editalbe for kotlin * convert string to editalbe for kotlin
* *
@@ -94,7 +101,7 @@ object Utils {
try { try {
val cmb = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager val cmb = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val clipData = ClipData.newPlainText(null, content) val clipData = ClipData.newPlainText(null, content)
cmb.setPrimaryClip(clipData) cmb.primaryClip = clipData
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
} }
@@ -272,27 +279,43 @@ object Utils {
return false return false
} }
fun startVServiceFromToggle(context: Context): Boolean {
val result = startVService(context) /**
if (!result) { * 判断服务是否后台运行
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 result return isRun
} }
/** /**
* startVService * startVService
*/ */
fun startVService(context: Context): Boolean { fun startVService(context: Context): Boolean {
if (context.v2RayApplication.defaultDPreference.getPrefBoolean(SettingsActivity.PREF_PROXY_SHARING, false)) { context.toast(R.string.toast_services_start)
context.toast(R.string.toast_warning_pref_proxysharing_short)
}else{
context.toast(R.string.toast_services_start)
}
if (AngConfigManager.genStoreV2rayConfig(-1)) { if (AngConfigManager.genStoreV2rayConfig(-1)) {
val configContent = AngConfigManager.currGeneratedV2rayConfig() val configContent = AngConfigManager.currGeneratedV2rayConfig()
val configType = AngConfigManager.currConfigType() val configType = AngConfigManager.currConfigType()
if (configType == EConfigType.CUSTOM) { if (configType == AppConfig.EConfigType.Custom) {
try { try {
Libv2ray.testConfig(configContent) Libv2ray.testConfig(configContent)
} catch (e: Exception) { } catch (e: Exception) {
@@ -300,7 +323,7 @@ object Utils {
return false return false
} }
} }
V2RayServiceManager.startV2Ray(context, context.v2RayApplication.defaultDPreference.getPrefString(AppConfig.PREF_MODE, "VPN")) V2RayVpnService.startV2Ray(context)
return true return true
} else { } else {
return false return false
@@ -312,7 +335,6 @@ object Utils {
*/ */
fun startVService(context: Context, guid: String): Boolean { fun startVService(context: Context, guid: String): Boolean {
val index = AngConfigManager.getIndexViaGuid(guid) val index = AngConfigManager.getIndexViaGuid(guid)
context.v2RayApplication.curIndex=index
return startVService(context, index) return startVService(context, index)
} }
@@ -420,6 +442,7 @@ object Utils {
return path return path
} }
/** /**
* readTextFromAssets * readTextFromAssets
*/ */
@@ -454,13 +477,10 @@ object Utils {
/** /**
* tcping * tcping
*/ */
suspend fun tcping(url: String, port: Int): String { fun tcping(url: String, port: Int): String {
var time = -1L var time = -1L
for (k in 0 until 2) { for (k in 0 until 2) {
val one = socketConnectTime(url, port) val one = socketConnectTime(url, port)
if (!coroutineContext.isActive) {
break
}
if (one != -1L ) if (one != -1L )
if(time == -1L || one < time) { if(time == -1L || one < time) {
time = one time = one
@@ -471,35 +491,19 @@ object Utils {
fun socketConnectTime(url: String, port: Int): Long { fun socketConnectTime(url: String, port: Int): Long {
try { try {
val socket = Socket()
synchronized(this) {
tcpTestingSockets.add(socket)
}
val start = System.currentTimeMillis() val start = System.currentTimeMillis()
socket.connect(InetSocketAddress(url, port)) val socket = Socket(url, port)
val time = System.currentTimeMillis() - start val time = System.currentTimeMillis() - start
synchronized(this) {
tcpTestingSockets.remove(socket)
}
socket.close() socket.close()
return time return time
} catch (e: UnknownHostException) { } catch (e: UnknownHostException) {
e.printStackTrace() e.printStackTrace()
} catch (e: IOException) { } catch (e: IOException) {
Log.d(AppConfig.ANG_PACKAGE, "socketConnectTime IOException: $e") e.printStackTrace()
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
} }
return -1 return -1
} }
fun closeAllTcpSockets() {
synchronized(this) {
tcpTestingSockets.forEach {
it?.close()
}
tcpTestingSockets.clear()
}
}
} }
@@ -1,6 +1,5 @@
package com.v2ray.ang.util package com.v2ray.ang.util
import android.content.Context
import android.text.TextUtils import android.text.TextUtils
import android.util.Log import android.util.Log
import com.google.gson.Gson import com.google.gson.Gson
@@ -10,15 +9,12 @@ import com.v2ray.ang.AngApplication
import com.v2ray.ang.AppConfig import com.v2ray.ang.AppConfig
import com.v2ray.ang.dto.AngConfig.VmessBean import com.v2ray.ang.dto.AngConfig.VmessBean
import com.v2ray.ang.dto.V2rayConfig import com.v2ray.ang.dto.V2rayConfig
import com.v2ray.ang.extension.defaultDPreference
import com.v2ray.ang.ui.SettingsActivity import com.v2ray.ang.ui.SettingsActivity
import org.json.JSONException import org.json.JSONException
import org.json.JSONObject import org.json.JSONObject
import org.json.JSONArray import org.json.JSONArray
import com.google.gson.JsonObject import com.google.gson.JsonObject
import com.v2ray.ang.dto.EConfigType
import com.v2ray.ang.extension.defaultDPreference
import kotlin.collections.ArrayList
import kotlin.collections.LinkedHashSet
object V2rayConfigUtil { object V2rayConfigUtil {
private val requestObj: JsonObject by lazy { private val requestObj: JsonObject by lazy {
@@ -45,10 +41,19 @@ object V2rayConfigUtil {
// return result // return result
// } // }
if (vmess.configType == EConfigType.CUSTOM.value) { if (vmess.configType == AppConfig.EConfigType.Vmess) {
result = getV2rayConfigType2(app, vmess)
} else {
result = getV2rayConfigType1(app, vmess) result = getV2rayConfigType1(app, vmess)
} else if (vmess.configType == AppConfig.EConfigType.Custom) {
result = getV2rayConfigType2(app, vmess)
} else if (vmess.configType == AppConfig.EConfigType.Shadowsocks) {
result = getV2rayConfigType1(app, vmess)
} else if (vmess.configType == AppConfig.EConfigType.Socks) {
result = getV2rayConfigType1(app, vmess)
}
val domainName = parseDomainName(result.content)
if (!TextUtils.isEmpty(domainName)) {
app.defaultDPreference.setPrefString(AppConfig.PREF_CURR_CONFIG_DOMAIN, domainName)
} }
Log.d("V2rayConfigUtilGoLog", result.content) Log.d("V2rayConfigUtilGoLog", result.content)
@@ -59,22 +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 = Gson().fromJson(jsonConfig, V2rayConfig::class.java) ?: return null
for (outbound in v2rayConfig.outbounds) {
if (outbound.protocol.equals(EConfigType.VMESS.name.toLowerCase()) ||
outbound.protocol.equals(EConfigType.SHADOWSOCKS.name.toLowerCase()) ||
outbound.protocol.equals(EConfigType.SOCKS.name.toLowerCase())) {
return outbound
}
}
return null
}
/** /**
* 生成v2ray的客户端配置文件 * 生成v2ray的客户端配置文件
*/ */
@@ -127,7 +116,6 @@ object V2rayConfigUtil {
val jsonConfig = app.defaultDPreference.getPrefString(AppConfig.ANG_CONFIG + guid, "") val jsonConfig = app.defaultDPreference.getPrefString(AppConfig.ANG_CONFIG + guid, "")
result.status = true result.status = true
result.content = jsonConfig result.content = jsonConfig
parseDomainNameAndTag(app, jsonConfig)
return result return result
} catch (e: Exception) { } catch (e: Exception) {
@@ -141,12 +129,6 @@ object V2rayConfigUtil {
*/ */
private fun inbounds(vmess: VmessBean, v2rayConfig: V2rayConfig, app: AngApplication): Boolean { private fun inbounds(vmess: VmessBean, v2rayConfig: V2rayConfig, app: AngApplication): Boolean {
try { try {
v2rayConfig.inbounds.forEach { curInbound ->
if (!app.defaultDPreference.getPrefBoolean(SettingsActivity.PREF_PROXY_SHARING, false)) {
//bind all inbounds to localhost if the user requests
curInbound.listen = "127.0.0.1"
}
}
v2rayConfig.inbounds[0].port = 10808 v2rayConfig.inbounds[0].port = 10808
// val socksPort = Utils.parseInt(app.defaultDPreference.getPrefString(SettingsActivity.PREF_SOCKS_PORT, "10808")) // val socksPort = Utils.parseInt(app.defaultDPreference.getPrefString(SettingsActivity.PREF_SOCKS_PORT, "10808"))
// val lanconnPort = Utils.parseInt(app.defaultDPreference.getPrefString(SettingsActivity.PREF_HTTP_PORT, "")) // val lanconnPort = Utils.parseInt(app.defaultDPreference.getPrefString(SettingsActivity.PREF_HTTP_PORT, ""))
@@ -176,12 +158,8 @@ object V2rayConfigUtil {
try { try {
val outbound = v2rayConfig.outbounds[0] val outbound = v2rayConfig.outbounds[0]
val configType = EConfigType.fromInt(vmess.configType) when (vmess.configType) {
if (configType != null) { AppConfig.EConfigType.Vmess -> {
outbound.protocol = configType.name.toLowerCase()
}
when (configType) {
EConfigType.VMESS -> {
outbound.settings?.servers = null outbound.settings?.servers = null
val vnext = v2rayConfig.outbounds[0].settings?.vnext?.get(0) val vnext = v2rayConfig.outbounds[0].settings?.vnext?.get(0)
@@ -199,8 +177,10 @@ object V2rayConfigUtil {
//远程服务器底层传输配置 //远程服务器底层传输配置
outbound.streamSettings = boundStreamSettings(vmess) outbound.streamSettings = boundStreamSettings(vmess)
outbound.protocol = "vmess"
} }
EConfigType.SHADOWSOCKS -> { AppConfig.EConfigType.Shadowsocks -> {
outbound.settings?.vnext = null outbound.settings?.vnext = null
val server = outbound.settings?.servers?.get(0) val server = outbound.settings?.servers?.get(0)
@@ -213,8 +193,10 @@ object V2rayConfigUtil {
//Mux //Mux
outbound.mux?.enabled = false outbound.mux?.enabled = false
outbound.protocol = "shadowsocks"
} }
EConfigType.SOCKS -> { AppConfig.EConfigType.Socks -> {
outbound.settings?.vnext = null outbound.settings?.vnext = null
val server = outbound.settings?.servers?.get(0) val server = outbound.settings?.servers?.get(0)
@@ -223,22 +205,21 @@ object V2rayConfigUtil {
//Mux //Mux
outbound.mux?.enabled = false outbound.mux?.enabled = false
outbound.protocol = "socks"
}
else -> {
} }
} }
val serverDomain = if (Utils.isIpv6Address(vmess.address)) { var serverDomain: String
String.format("[%s]:%s", vmess.address, vmess.port) if(Utils.isIpv6Address(vmess.address)) {
serverDomain = String.format("[%s]:%s", vmess.address, vmess.port)
} else { } else {
String.format("%s:%s", vmess.address, vmess.port) serverDomain = String.format("%s:%s", vmess.address, vmess.port)
} }
app.defaultDPreference.setPrefString(AppConfig.PREF_CURR_CONFIG_DOMAIN, serverDomain) app.defaultDPreference.setPrefString(AppConfig.PREF_CURR_CONFIG_DOMAIN, serverDomain)
val tags = LinkedHashSet<String>()
v2rayConfig.outbounds.forEach {
if (!TextUtils.isEmpty(it.tag)) {
tags.add(it.tag)
}
}
app.defaultDPreference.setPrefStringOrderedSet(AppConfig.PREF_CURR_CONFIG_OUTBOUND_TAGS, tags)
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
return false return false
@@ -259,7 +240,7 @@ object V2rayConfigUtil {
//streamSettings //streamSettings
when (streamSettings.network) { when (streamSettings.network) {
"kcp" -> { "kcp" -> {
val kcpsettings = V2rayConfig.OutboundBean.StreamSettingsBean.KcpSettingsBean() val kcpsettings = V2rayConfig.OutboundBean.StreamSettingsBean.KcpsettingsBean()
kcpsettings.mtu = 1350 kcpsettings.mtu = 1350
kcpsettings.tti = 50 kcpsettings.tti = 50
kcpsettings.uplinkCapacity = 12 kcpsettings.uplinkCapacity = 12
@@ -267,33 +248,34 @@ object V2rayConfigUtil {
kcpsettings.congestion = false kcpsettings.congestion = false
kcpsettings.readBufferSize = 1 kcpsettings.readBufferSize = 1
kcpsettings.writeBufferSize = 1 kcpsettings.writeBufferSize = 1
kcpsettings.header = V2rayConfig.OutboundBean.StreamSettingsBean.KcpSettingsBean.HeaderBean() kcpsettings.header = V2rayConfig.OutboundBean.StreamSettingsBean.KcpsettingsBean.HeaderBean()
kcpsettings.header.type = vmess.headerType kcpsettings.header.type = vmess.headerType
streamSettings.kcpSettings = kcpsettings streamSettings.kcpsettings = kcpsettings
} }
"ws" -> { "ws" -> {
val wssettings = V2rayConfig.OutboundBean.StreamSettingsBean.WsSettingsBean() val wssettings = V2rayConfig.OutboundBean.StreamSettingsBean.WssettingsBean()
wssettings.connectionReuse = true
val host = vmess.requestHost.trim() val host = vmess.requestHost.trim()
val path = vmess.path.trim() val path = vmess.path.trim()
if (!TextUtils.isEmpty(host)) { if (!TextUtils.isEmpty(host)) {
wssettings.headers = V2rayConfig.OutboundBean.StreamSettingsBean.WsSettingsBean.HeadersBean() wssettings.headers = V2rayConfig.OutboundBean.StreamSettingsBean.WssettingsBean.HeadersBean()
wssettings.headers.Host = host wssettings.headers.Host = host
} }
if (!TextUtils.isEmpty(path)) { if (!TextUtils.isEmpty(path)) {
wssettings.path = path wssettings.path = path
} }
streamSettings.wsSettings = wssettings streamSettings.wssettings = wssettings
val tlssettings = V2rayConfig.OutboundBean.StreamSettingsBean.TlsSettingsBean() val tlssettings = V2rayConfig.OutboundBean.StreamSettingsBean.TlssettingsBean()
tlssettings.allowInsecure = true tlssettings.allowInsecure = true
if (!TextUtils.isEmpty(host)) { if (!TextUtils.isEmpty(host)) {
tlssettings.serverName = host tlssettings.serverName = host
} }
streamSettings.tlsSettings = tlssettings streamSettings.tlssettings = tlssettings
} }
"h2" -> { "h2" -> {
val httpsettings = V2rayConfig.OutboundBean.StreamSettingsBean.HttpSettingsBean() val httpsettings = V2rayConfig.OutboundBean.StreamSettingsBean.HttpsettingsBean()
val host = vmess.requestHost.trim() val host = vmess.requestHost.trim()
val path = vmess.path.trim() val path = vmess.path.trim()
@@ -301,30 +283,31 @@ object V2rayConfigUtil {
httpsettings.host = host.split(",").map { it.trim() } httpsettings.host = host.split(",").map { it.trim() }
} }
httpsettings.path = path httpsettings.path = path
streamSettings.httpSettings = httpsettings streamSettings.httpsettings = httpsettings
val tlssettings = V2rayConfig.OutboundBean.StreamSettingsBean.TlsSettingsBean() val tlssettings = V2rayConfig.OutboundBean.StreamSettingsBean.TlssettingsBean()
tlssettings.allowInsecure = true tlssettings.allowInsecure = true
streamSettings.tlsSettings = tlssettings streamSettings.tlssettings = tlssettings
} }
"quic" -> { "quic" -> {
val quicsettings = V2rayConfig.OutboundBean.StreamSettingsBean.QuicSettingBean() val quicsettings = V2rayConfig.OutboundBean.StreamSettingsBean.QuicsettingBean()
val host = vmess.requestHost.trim() val host = vmess.requestHost.trim()
val path = vmess.path.trim() val path = vmess.path.trim()
quicsettings.security = host quicsettings.security = host
quicsettings.key = path quicsettings.key = path
quicsettings.header = V2rayConfig.OutboundBean.StreamSettingsBean.QuicSettingBean.HeaderBean() quicsettings.header = V2rayConfig.OutboundBean.StreamSettingsBean.QuicsettingBean.HeaderBean()
quicsettings.header.type = vmess.headerType quicsettings.header.type = vmess.headerType
streamSettings.quicSettings = quicsettings streamSettings.quicsettings = quicsettings
} }
else -> { else -> {
//tcp带http伪装 //tcp带http伪装
if (vmess.headerType == "http") { if (vmess.headerType == "http") {
val tcpSettings = V2rayConfig.OutboundBean.StreamSettingsBean.TcpSettingsBean() val tcpSettings = V2rayConfig.OutboundBean.StreamSettingsBean.TcpsettingsBean()
tcpSettings.header = V2rayConfig.OutboundBean.StreamSettingsBean.TcpSettingsBean.HeaderBean() tcpSettings.connectionReuse = true
tcpSettings.header = V2rayConfig.OutboundBean.StreamSettingsBean.TcpsettingsBean.HeaderBean()
tcpSettings.header.type = vmess.headerType tcpSettings.header.type = vmess.headerType
// if (requestObj.has("headers") // if (requestObj.has("headers")
@@ -564,7 +547,7 @@ object V2rayConfigUtil {
mux = null)) mux = null))
} }
// DNS routing // DNS routing
v2rayConfig.routing.rules.add(0, V2rayConfig.RoutingBean.RulesBean( v2rayConfig.routing.rules.add(0, V2rayConfig.RoutingBean.RulesBean(
type = "field", type = "field",
outboundTag = AppConfig.TAG_DIRECT, outboundTag = AppConfig.TAG_DIRECT,
@@ -630,58 +613,42 @@ object V2rayConfigUtil {
} }
} }
private fun parseDomainNameAndTag(app: AngApplication, jsonConfig: String) { private fun parseDomainName(jsonConfig: String): String {
try { try {
val jObj = JSONObject(jsonConfig) val jObj = JSONObject(jsonConfig)
var domainName = "" var domainName: String
val tags = LinkedHashSet<String>()
if (jObj.has("outbound")) { if (jObj.has("outbound")) {
val (domain, tag) = parseDomainNameAndTag(jObj.optJSONObject("outbound")) domainName = parseDomainName(jObj.optJSONObject("outbound"))
domainName = domain if (!TextUtils.isEmpty(domainName)) {
if (!TextUtils.isEmpty(tag)) { return domainName
tags.add(tag)
} }
} }
if (jObj.has("outbounds")) { if (jObj.has("outbounds")) {
for (i in 0..(jObj.optJSONArray("outbounds").length() - 1)) { for (i in 0..(jObj.optJSONArray("outbounds").length() - 1)) {
val (domain, tag) = parseDomainNameAndTag(jObj.optJSONArray("outbounds").getJSONObject(i)) domainName = parseDomainName(jObj.optJSONArray("outbounds").getJSONObject(i))
if (!TextUtils.isEmpty(domain) && TextUtils.isEmpty(domainName)) { if (!TextUtils.isEmpty(domainName)) {
domainName = domain return domainName
}
if (!TextUtils.isEmpty(tag)) {
tags.add(tag)
} }
} }
} }
if (jObj.has("outboundDetour")) { if (jObj.has("outboundDetour")) {
for (i in 0..(jObj.optJSONArray("outboundDetour").length() - 1)) { for (i in 0..(jObj.optJSONArray("outboundDetour").length() - 1)) {
val (domain, tag) = parseDomainNameAndTag(jObj.optJSONArray("outboundDetour").getJSONObject(i)) domainName = parseDomainName(jObj.optJSONArray("outboundDetour").getJSONObject(i))
if (!TextUtils.isEmpty(domain) && TextUtils.isEmpty(domainName)) { if (!TextUtils.isEmpty(domainName)) {
domainName = domain return domainName
}
if (!TextUtils.isEmpty(tag)) {
tags.add(tag)
} }
} }
} }
if (!TextUtils.isEmpty(domainName)) {
app.defaultDPreference.setPrefString(AppConfig.PREF_CURR_CONFIG_DOMAIN, domainName)
}
app.defaultDPreference.setPrefStringOrderedSet(AppConfig.PREF_CURR_CONFIG_OUTBOUND_TAGS, tags)
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
} }
return ""
} }
private fun parseDomainNameAndTag(outbound: JSONObject): Pair<String, String> { private fun parseDomainName(outbound: JSONObject): String {
val tag = if (outbound.has("tag")) {
outbound.getString("tag")
} else {
""
}
try { try {
if (outbound.has("settings")) { if (outbound.has("settings")) {
val vnext: JSONArray? var vnext: JSONArray?
if (outbound.optJSONObject("settings").has("vnext")) { if (outbound.optJSONObject("settings").has("vnext")) {
// vmess // vmess
vnext = outbound.optJSONObject("settings").optJSONArray("vnext") vnext = outbound.optJSONObject("settings").optJSONArray("vnext")
@@ -689,22 +656,22 @@ object V2rayConfigUtil {
// shadowsocks or socks // shadowsocks or socks
vnext = outbound.optJSONObject("settings").optJSONArray("servers") vnext = outbound.optJSONObject("settings").optJSONArray("servers")
} else { } else {
return Pair("", tag) return ""
} }
for (i in 0..(vnext.length() - 1)) { for (i in 0..(vnext.length() - 1)) {
val item = vnext.getJSONObject(i) val item = vnext.getJSONObject(i)
val address = item.getString("address") val address = item.getString("address")
val port = item.getString("port") val port = item.getString("port")
if(Utils.isIpv6Address(address)) { if(Utils.isIpv6Address(address)) {
return Pair(String.format("[%s]:%s", address, port), tag) return String.format("[%s]:%s", address, port)
} else { } else {
return Pair(String.format("%s:%s", address, port), tag) return String.format("%s:%s", address, port)
} }
} }
} }
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
} }
return Pair("", tag) return ""
} }
} }
@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true"
android:drawable="@color/secondary_text" />
<item android:state_focused="true"
android:drawable="@color/secondary_text" />
<item android:state_hovered="true"
android:drawable="@color/secondary_text" />
<item android:drawable="@color/colorPrimary_text" />
</selector>
@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/accent"/>
<corners android:radius="20dp"/>
<padding android:left="0dp" android:top="0dp" android:right="0dp" android:bottom="0dp" />
</shape>
@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#009963"/>
<corners android:radius="20dp"/>
<padding android:left="0dp" android:top="0dp" android:right="0dp" android:bottom="0dp" />
</shape>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

@@ -54,10 +54,8 @@
android:id="@+id/layout_test" android:id="@+id/layout_test"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="@dimen/connection_test_height" android:layout_height="@dimen/connection_test_height"
android:background="@drawable/background_test_button" android:background="@color/colorPrimary_text"
android:gravity="center|left" android:gravity="center|left">
android:clickable="true"
android:focusable="true">
<TextView <TextView
android:id="@+id/tv_test_state" android:id="@+id/tv_test_state"
@@ -94,8 +92,6 @@
android:layout_marginLeft="16dp" android:layout_marginLeft="16dp"
android:layout_marginTop="16dp" android:layout_marginTop="16dp"
android:src="@drawable/ic_v_idle" android:src="@drawable/ic_v_idle"
android:clickable="true"
android:focusable="true"
app:layout_anchorGravity="bottom|right|end" /> app:layout_anchorGravity="bottom|right|end" />
</com.github.jorgecastilloprz.FABProgressCircle> </com.github.jorgecastilloprz.FABProgressCircle>
@@ -111,23 +107,7 @@
app:headerLayout="@layout/nav_header" app:headerLayout="@layout/nav_header"
app:itemIconTint="@color/colorPrimary_dark" app:itemIconTint="@color/colorPrimary_dark"
app:itemTextColor="@color/colorPrimary" app:itemTextColor="@color/colorPrimary"
app:menu="@menu/menu_drawer" > app:menu="@menu/menu_drawer" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:background="@color/white"
android:padding="14dp">
<TextView
android:id="@+id/version"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textColor="@color/accent" />
</LinearLayout>
</android.support.design.widget.NavigationView>
</android.support.v4.widget.DrawerLayout> </android.support.v4.widget.DrawerLayout>
@@ -68,15 +68,20 @@
android:text="@string/server_lab_content" android:text="@string/server_lab_content"
android:textAppearance="@style/TextAppearance.AppCompat.Subhead" /> android:textAppearance="@style/TextAppearance.AppCompat.Subhead" />
<EditText <TextView
android:id="@+id/tv_content" android:id="@+id/tv_content"
android:layout_width="match_parent" android:layout_width="wrap_content"
android:layout_height="match_parent" android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_margin_top_height" android:layout_marginTop="@dimen/layout_margin_top_height"
android:text="" /> android:text="" />
</LinearLayout> </LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_margin_top_height"
android:layout_marginBottom="@dimen/layout_margin_top_height"
android:orientation="vertical" />
</LinearLayout> </LinearLayout>
</ScrollView> </ScrollView>
@@ -2,10 +2,7 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center_vertical" android:gravity="center_vertical">
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true">
<android.support.v7.widget.AppCompatImageView <android.support.v7.widget.AppCompatImageView
android:id="@+id/icon" android:id="@+id/icon"
@@ -43,7 +40,6 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:clickable="false" android:clickable="false"
android:focusable="false"
android:paddingStart="2dp" android:paddingStart="2dp"
android:paddingLeft="2dp" android:paddingLeft="2dp"
android:paddingEnd="6dp" android:paddingEnd="6dp"
@@ -9,6 +9,10 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="3dp" android:layout_margin="3dp"
android:clickable="true"
android:focusable="true"
android:foreground="?android:attr/selectableItemBackground"
android:orientation="horizontal"
card_view:cardCornerRadius="5dp"> card_view:cardCornerRadius="5dp">
<LinearLayout <LinearLayout
@@ -17,11 +21,7 @@
android:layout_height="@dimen/server_height" android:layout_height="@dimen/server_height"
android:layout_gravity="center" android:layout_gravity="center"
android:gravity="center" android:gravity="center"
android:orientation="horizontal" android:orientation="horizontal">
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:nextFocusRight="@+id/layout_share">
<LinearLayout <LinearLayout
android:layout_width="wrap_content" android:layout_width="wrap_content"
@@ -111,14 +111,10 @@
<LinearLayout <LinearLayout
android:id="@+id/layout_share" android:id="@+id/layout_share"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="match_parent" android:layout_height="@dimen/server_height"
android:gravity="center" android:gravity="center"
android:orientation="vertical" android:orientation="vertical"
android:padding="@dimen/layout_margin_spacing" android:paddingEnd="@dimen/layout_margin_right_height">
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:nextFocusLeft="@+id/info_container">
<ImageView <ImageView
android:layout_width="wrap_content" android:layout_width="wrap_content"
@@ -130,13 +126,10 @@
<LinearLayout <LinearLayout
android:id="@+id/layout_edit" android:id="@+id/layout_edit"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="match_parent" android:layout_height="@dimen/server_height"
android:gravity="center" android:gravity="center"
android:orientation="vertical" android:orientation="vertical"
android:padding="@dimen/layout_margin_spacing" android:paddingEnd="@dimen/layout_margin_right_height">
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true">
<ImageView <ImageView
android:layout_width="@dimen/png_height" android:layout_width="@dimen/png_height"
@@ -148,13 +141,10 @@
<LinearLayout <LinearLayout
android:id="@+id/layout_remove" android:id="@+id/layout_remove"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="match_parent" android:layout_height="@dimen/server_height"
android:gravity="center" android:gravity="center"
android:orientation="vertical" android:orientation="vertical"
android:padding="@dimen/layout_margin_spacing" android:paddingEnd="@dimen/layout_margin_right_height">
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true">
<ImageView <ImageView
android:layout_width="@dimen/png_height" android:layout_width="@dimen/png_height"
@@ -6,7 +6,6 @@
android:gravity="center_vertical"> android:gravity="center_vertical">
<android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto" <android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:id="@+id/item_cardview"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="3dp" android:layout_margin="3dp"
@@ -14,8 +13,7 @@
android:focusable="true" android:focusable="true"
android:foreground="?android:attr/selectableItemBackground" android:foreground="?android:attr/selectableItemBackground"
android:orientation="horizontal" android:orientation="horizontal"
card_view:cardCornerRadius="5dp" card_view:cardCornerRadius="5dp">
android:nextFocusRight="@+id/layout_edit">
<LinearLayout <LinearLayout
android:id="@+id/info_container" android:id="@+id/info_container"
@@ -64,14 +62,11 @@
<LinearLayout <LinearLayout
android:id="@+id/layout_share" android:id="@+id/layout_share"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="@dimen/server_height"
android:gravity="center" android:gravity="center"
android:orientation="vertical" android:orientation="vertical"
android:padding="@dimen/layout_margin_spacing" android:paddingEnd="@dimen/layout_margin_right_height"
android:visibility="invisible" android:visibility="invisible">
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true">
<ImageView <ImageView
android:layout_width="@dimen/png_height" android:layout_width="@dimen/png_height"
@@ -83,14 +78,10 @@
<LinearLayout <LinearLayout
android:id="@+id/layout_edit" android:id="@+id/layout_edit"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="@dimen/server_height"
android:gravity="center" android:gravity="center"
android:orientation="vertical" android:orientation="vertical"
android:padding="@dimen/layout_margin_spacing" android:paddingEnd="@dimen/layout_margin_right_height">
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:nextFocusLeft="@+id/item_cardview">
<ImageView <ImageView
android:layout_width="@dimen/png_height" android:layout_width="@dimen/png_height"
@@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Button xmlns:android="http://schemas.android.com/apk/res/android"
style="@style/Widget.AppCompat.Button.Borderless"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:onClick="onModeHelpClicked"
android:text="@string/title_mode_help"
android:textAlignment="textStart"
android:textStyle="italic" />
@@ -1,5 +1,12 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:orientation="vertical">
<LinearLayout
android:id="@+id/layout_switch" android:id="@+id/layout_switch"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@@ -7,9 +14,16 @@
android:gravity="center" android:gravity="center"
android:orientation="vertical"> android:orientation="vertical">
<ImageView <ImageView
android:layout_width="40dp" android:layout_width="40dp"
android:layout_height="40dp" android:layout_height="40dp"
android:padding="10dp" android:src="@mipmap/ic_launcher" />
android:src="@drawable/ic_v" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:text="@string/app_widget_name"
android:textAppearance="@style/TextAppearance.AppCompat.Small" />
</LinearLayout>
</LinearLayout> </LinearLayout>
+15 -12
View File
@@ -4,7 +4,12 @@
tools:showIn="navigation_view"> tools:showIn="navigation_view">
<group <group
android:id="@+id/group_main"> android:id="@+id/group_main"
android:checkableBehavior="single">
<item
android:id="@+id/server_profile"
android:icon="@drawable/ic_description_white_24dp"
android:title="@string/title_server" />
<item <item
android:id="@+id/sub_setting" android:id="@+id/sub_setting"
android:icon="@drawable/ic_subscriptions_white_24dp" android:icon="@drawable/ic_subscriptions_white_24dp"
@@ -16,27 +21,25 @@
</group> </group>
<group android:id="@+id/group_id2"> <item android:title="@string/title_about">
<item <menu>
<item
android:id="@+id/promotion" android:id="@+id/promotion"
android:icon="@drawable/ic_whatshot_white_24dp" android:icon="@drawable/ic_whatshot_white_24dp"
android:title="@string/title_pref_promotion" /> android:title="@string/title_pref_promotion" />
<item <item
android:id="@+id/donate" android:id="@+id/donate"
android:icon="@drawable/ic_attach_money_white_24dp" android:icon="@drawable/ic_attach_money_white_24dp"
android:title="@string/title_pref_donate" /> android:title="@string/title_pref_donate" />
<item <item
android:id="@+id/logcat" android:id="@+id/logcat"
android:icon="@drawable/ic_logcat_white_24dp" android:icon="@drawable/ic_logcat_white_24dp"
android:title="@string/title_logcat" /> android:title="@string/title_logcat" />
<item <item
android:id="@+id/feedback" android:id="@+id/feedback"
android:icon="@drawable/ic_feedback_white_24dp" android:icon="@drawable/ic_feedback_white_24dp"
android:title="@string/title_pref_feedback" /> android:title="@string/title_pref_feedback" />
<!-- place holder for version text at the bottom --> </menu>
<item </item>
android:id="@+id/placeholder"
android:enabled="false"
android:title="" />
</group>
</menu> </menu>
@@ -6,9 +6,4 @@
android:icon="@drawable/ic_copy_white" android:icon="@drawable/ic_copy_white"
android:title="@string/logcat_copy" android:title="@string/logcat_copy"
app:showAsAction="always" /> app:showAsAction="always" />
<item
android:id="@+id/delete"
android:icon="@drawable/ic_delete_white_24dp"
android:title="@string/logcat_delete"
app:showAsAction="ifRoom" />
</menu> </menu>
@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- https://stackoverflow.com/a/52960668/5495739 -->
<resources xmlns:tools="http://schemas.android.com/tools">
<bool name="config_materialPreferenceIconSpaceReserved" tools:ignore="MissingDefaultResource,PrivateResource">false</bool>
<dimen name="preference_category_padding_start" tools:ignore="MissingDefaultResource,PrivateResource">0dp</dimen>
</resources>
@@ -3,7 +3,7 @@
<string name="app_name">v2rayNG</string> <string name="app_name">v2rayNG</string>
<string name="app_widget_name">开关</string> <string name="app_widget_name">开关</string>
<string name="app_tile_name">开关</string> <string name="app_tile_name">开关</string>
<string name="app_tile_first_use">初次使用此功能请先用APP添加配置</string> <string name="app_tile_first_use">初次使用此功能请先用APP激活VPN</string>
<string name="navigation_drawer_open">Open navigation drawer</string> <string name="navigation_drawer_open">Open navigation drawer</string>
<string name="navigation_drawer_close">Close navigation drawer</string> <string name="navigation_drawer_close">Close navigation drawer</string>
@@ -73,6 +73,7 @@
<!-- Preferences --> <!-- Preferences -->
<string name="title_settings">设置</string> <string name="title_settings">设置</string>
<string name="title_about">关于</string>
<string name="title_advanced">进阶设置</string> <string name="title_advanced">进阶设置</string>
<string name="title_pref_per_app_proxy">分应用代理</string> <string name="title_pref_per_app_proxy">分应用代理</string>
@@ -82,7 +83,7 @@
<string name="summary_pref_mux_enabled">开启可能会加速,关闭可能会减少断流</string> <string name="summary_pref_mux_enabled">开启可能会加速,关闭可能会减少断流</string>
<string name="title_pref_speed_enabled">启用速度显示</string> <string name="title_pref_speed_enabled">启用速度显示</string>
<string name="summary_pref_speed_enabled">在通知中显示当前速度\n小图标显示流量的路由情况</string> <string name="summary_pref_speed_enabled">在通知中显示当前速度</string>
<string name="title_pref_sniffing_enabled">启用流量探测</string> <string name="title_pref_sniffing_enabled">启用流量探测</string>
<string name="summary_pref_sniffing_enabled">流量探测</string> <string name="summary_pref_sniffing_enabled">流量探测</string>
@@ -122,8 +123,7 @@
<string name="title_pref_promotion">推广</string> <string name="title_pref_promotion">推广</string>
<string name="summary_pref_promotion">一些推广,点击查看详情(捐赠可去除)</string> <string name="summary_pref_promotion">一些推广,点击查看详情(捐赠可去除)</string>
<string name="title_mode">模式</string> <string name="title_pref_version">版本</string>
<string name="title_mode_help">点此查看更多帮助</string>
<string name="donate_error_setup">初始化错误:</string> <string name="donate_error_setup">初始化错误:</string>
<string name="donate_error_inventory">无法查询到项目</string> <string name="donate_error_inventory">无法查询到项目</string>
@@ -135,8 +135,7 @@
<string name="title_logcat">Logcat</string> <string name="title_logcat">Logcat</string>
<string name="logcat_copy">复制</string> <string name="logcat_copy">复制</string>
<string name="logcat_delete">删除</string> <string name="title_export_all">导出全部配置至剪贴板</string>
<string name="title_export_all">导出全部(非自定义)配置至剪贴板</string>
<string name="title_sub_setting">订阅设置</string> <string name="title_sub_setting">订阅设置</string>
<string name="sub_setting_remarks">备注</string> <string name="sub_setting_remarks">备注</string>
<string name="sub_setting_url">地址(url)</string> <string name="sub_setting_url">地址(url)</string>
@@ -181,15 +180,5 @@
<item>绕过大陆地址</item> <item>绕过大陆地址</item>
<item>绕过局域网及大陆地址</item> <item>绕过局域网及大陆地址</item>
</string-array> </string-array>
<string name="title_pref_proxy_sharing_enabled">代理共享</string>
<string name="summary_pref_proxy_sharing_enabled">绑定代理入口ip到0.0.0.0</string>
<string name="toast_warning_pref_proxysharing">其他设备可以使用socks/http协议通过您的IP地址连接到代理\nHttp 代理: http://您的ip:10809\nSocks 代理: socks(4/5)://您的ip:10808\n仅在受信任的网络中启用以避免未经授权的连接</string>
<string name="toast_warning_pref_proxysharing_short">代理共享已启用,请确保处于受信网络</string>
<string name="toast_malformed_josn">配置格式错误</string>
<string-array name="mode_entries">
<item>VPN</item>
<item>仅代理</item>
</string-array>
</resources> </resources>
@@ -3,7 +3,7 @@
<string name="app_name">v2rayNG</string> <string name="app_name">v2rayNG</string>
<string name="app_widget_name">切換</string> <string name="app_widget_name">切換</string>
<string name="app_tile_name">切換</string> <string name="app_tile_name">切換</string>
<string name="app_tile_first_use">首次使用此功能,請使用此應用程式新增組態</string> <string name="app_tile_first_use">首次使用此功能,請使用此應用程式來啟用 VPN</string>
<string name="navigation_drawer_open">Open navigation drawer</string> <string name="navigation_drawer_open">Open navigation drawer</string>
<string name="navigation_drawer_close">Close navigation drawer</string> <string name="navigation_drawer_close">Close navigation drawer</string>
@@ -17,7 +17,7 @@
<string name="toast_services_failure">啟動服務失敗</string> <string name="toast_services_failure">啟動服務失敗</string>
<!--ServerActivity--> <!--ServerActivity-->
<string name="title_server">設定檔</string> <string name="title_server">組態</string>
<string name="menu_item_add_config">新增組態</string> <string name="menu_item_add_config">新增組態</string>
<string name="menu_item_save_config">儲存組態</string> <string name="menu_item_save_config">儲存組態</string>
<string name="menu_item_del_config">刪除組態</string> <string name="menu_item_del_config">刪除組態</string>
@@ -57,7 +57,7 @@
<string name="title_file_chooser">選取一個設定檔</string> <string name="title_file_chooser">選取一個設定檔</string>
<string name="toast_require_file_manager">請安裝檔案總管。</string> <string name="toast_require_file_manager">請安裝檔案總管。</string>
<string name="server_customize_config">自訂組態</string> <string name="server_customize_config">自訂組態</string>
<string name="toast_config_file_invalid">無效設定檔</string> <string name="toast_config_file_invalid">無效組態</string>
<string name="server_lab_content">內容</string> <string name="server_lab_content">內容</string>
<string name="toast_none_data_clipboard">剪貼簿內無資料</string> <string name="toast_none_data_clipboard">剪貼簿內無資料</string>
<string name="toast_invalid_url">網址無效</string> <string name="toast_invalid_url">網址無效</string>
@@ -74,6 +74,7 @@
<!-- Preferences --> <!-- Preferences -->
<string name="title_settings">設定</string> <string name="title_settings">設定</string>
<string name="title_about">關於</string>
<string name="title_advanced">進階設定</string> <string name="title_advanced">進階設定</string>
<string name="title_pref_per_app_proxy">Proxy 個別應用程式</string> <string name="title_pref_per_app_proxy">Proxy 個別應用程式</string>
@@ -83,7 +84,7 @@
<string name="summary_pref_mux_enabled">啟用或許會加快網路速度,切換或許會閃爍</string> <string name="summary_pref_mux_enabled">啟用或許會加快網路速度,切換或許會閃爍</string>
<string name="title_pref_speed_enabled">啟用速度顯示</string> <string name="title_pref_speed_enabled">啟用速度顯示</string>
<string name="summary_pref_speed_enabled">在通知中顯示當前速度\n小圖標顯示流量的路由情況</string> <string name="summary_pref_speed_enabled">在通知中顯示當前速度</string>
<string name="title_pref_sniffing_enabled">啟用流量探測</string> <string name="title_pref_sniffing_enabled">啟用流量探測</string>
<string name="summary_pref_sniffing_enabled">流量探測</string> <string name="summary_pref_sniffing_enabled">流量探測</string>
@@ -103,17 +104,17 @@
<string name="summary_pref_donate">向開發人員捐款</string> <string name="summary_pref_donate">向開發人員捐款</string>
<string name="donate_detail">新增一些實驗性進階功能</string> <string name="donate_detail">新增一些實驗性進階功能</string>
<string name="title_pref_remote_dns">遠端DNS (可選)</string> <string name="title_pref_remote_dns">遠端DNS (可选)</string>
<string name="summary_pref_remote_dns">DNS</string> <string name="summary_pref_remote_dns">DNS</string>
<string name="title_pref_domestic_dns">境内DNS (可選,僅本地DNS模式下生效)</string> <string name="title_pref_domestic_dns">境内DNS (可选,僅本地DNS模式下生效)</string>
<string name="summary_pref_domestic_dns">DNS</string> <string name="summary_pref_domestic_dns">DNS</string>
<string name="title_pref_socks_port">SOCKS5代理連接埠</string> <string name="title_pref_socks_port">SOCKS5代理端口</string>
<string name="summary_pref_socks_port">SOCKS5代理連接埠</string> <string name="summary_pref_socks_port">SOCKS5代理端口</string>
<string name="title_pref_http_port">HTTP代理連接埠</string> <string name="title_pref_http_port">HTTP代理端口</string>
<string name="summary_pref_http_port">HTTP代理連接埠</string> <string name="summary_pref_http_port">HTTP代理端口</string>
<string name="title_pref_feedback">回饋</string> <string name="title_pref_feedback">回饋</string>
@@ -124,8 +125,7 @@
<string name="title_pref_promotion">推廣</string> <string name="title_pref_promotion">推廣</string>
<string name="summary_pref_promotion">一些推廣,點擊查看詳情(捐款可去除)</string> <string name="summary_pref_promotion">一些推廣,點擊查看詳情(捐款可去除)</string>
<string name="title_mode">模式</string> <string name="title_pref_version">版本</string>
<string name="title_mode_help">點此查看更多幫助</string>
<string name="donate_error_setup">錯誤設定:</string> <string name="donate_error_setup">錯誤設定:</string>
<string name="donate_error_inventory">Error querying inventory</string> <string name="donate_error_inventory">Error querying inventory</string>
@@ -137,8 +137,7 @@
<string name="title_logcat">Logcat</string> <string name="title_logcat">Logcat</string>
<string name="logcat_copy">複製</string> <string name="logcat_copy">複製</string>
<string name="logcat_delete">刪除</string> <string name="title_export_all">匯出全部配置至剪貼簿</string>
<string name="title_export_all">匯出全部(非自訂)配置至剪貼簿</string>
<string name="title_sub_setting">訂閱設定</string> <string name="title_sub_setting">訂閱設定</string>
<string name="sub_setting_remarks">備註</string> <string name="sub_setting_remarks">備註</string>
<string name="sub_setting_url">位址(url)</string> <string name="sub_setting_url">位址(url)</string>
@@ -154,7 +153,7 @@
<string name="routing_settings_delete">清除</string> <string name="routing_settings_delete">清除</string>
<string name="routing_settings_scan_replace">掃描並取代</string> <string name="routing_settings_scan_replace">掃描並取代</string>
<string name="routing_settings_scan_append">掃描並附加</string> <string name="routing_settings_scan_append">掃描並附加</string>
<string name="routing_settings_default_rules">設置預設路由規則</string> <string name="routing_settings_default_rules">設置默認路由規則</string>
<string name="connection_test_pending">"檢查連線能力"</string> <string name="connection_test_pending">"檢查連線能力"</string>
<string name="connection_test_testing">"測試中……"</string> <string name="connection_test_testing">"測試中……"</string>
@@ -179,19 +178,8 @@
<string-array name="routing_mode"> <string-array name="routing_mode">
<item>全球</item> <item>全球</item>
<item>略過區域網路</item> <item>略過局域網</item>
<item>略過中國大陸</item> <item>略過中國大陸</item>
<item>略過區域網路及中國大陸</item> <item>略過局域網及中國大陸</item>
</string-array> </string-array>
<string name="title_pref_proxy_sharing_enabled">代理共享</string>
<string name="summary_pref_proxy_sharing_enabled">綁定代理入口ip到0.0.0.0</string>
<string name="toast_warning_pref_proxysharing">其他設備可以使用socks/http協定通過您的IP地址連接到代理\nHttp 代理: http://您的ip:10809\nSocks 代理: socks(4/5)://您的ip:10808\n僅在受信任的網路中啟用以避免未經授權的連接</string>
<string name="toast_warning_pref_proxysharing_short">代理共享已啟用,請確保處於受信網路</string>
<string name="toast_malformed_josn">配置格式錯誤</string>
<string-array name="mode_entries">
<item>VPN</item>
<item>僅代理</item>
</string-array>
</resources> </resources>
@@ -66,44 +66,4 @@
<item>IPIfNonMatch</item> <item>IPIfNonMatch</item>
<item>IPOnDemand</item> <item>IPOnDemand</item>
</string-array> </string-array>
<string-array name="mode_value" translatable="false">
<item>VPN</item>
<item>Proxy only</item>
</string-array>
<!-- minimum list https://serverfault.com/a/304791 -->
<string-array name="bypass_private_ip_address" translatable="false">
<item>0.0.0.0/5</item>
<item>8.0.0.0/7</item>
<item>11.0.0.0/8</item>
<item>12.0.0.0/6</item>
<item>16.0.0.0/4</item>
<item>32.0.0.0/3</item>
<item>64.0.0.0/2</item>
<item>128.0.0.0/3</item>
<item>160.0.0.0/5</item>
<item>168.0.0.0/6</item>
<item>172.0.0.0/12</item>
<item>172.32.0.0/11</item>
<item>172.64.0.0/10</item>
<item>172.128.0.0/9</item>
<item>173.0.0.0/8</item>
<item>174.0.0.0/7</item>
<item>176.0.0.0/4</item>
<item>192.0.0.0/9</item>
<item>192.128.0.0/11</item>
<item>192.160.0.0/13</item>
<item>192.169.0.0/16</item>
<item>192.170.0.0/15</item>
<item>192.172.0.0/14</item>
<item>192.176.0.0/12</item>
<item>192.192.0.0/10</item>
<item>193.0.0.0/8</item>
<item>194.0.0.0/7</item>
<item>196.0.0.0/6</item>
<item>200.0.0.0/5</item>
<item>208.0.0.0/4</item>
<item>224.0.0.0/3</item>
</string-array>
</resources> </resources>
@@ -3,7 +3,6 @@
<dimen name="bypass_list_header_height">50dp</dimen> <dimen name="bypass_list_header_height">50dp</dimen>
<dimen name="layout_margin_top_height">16dp</dimen> <dimen name="layout_margin_top_height">16dp</dimen>
<dimen name="layout_margin_right_height">16dp</dimen> <dimen name="layout_margin_right_height">16dp</dimen>
<dimen name="layout_margin_spacing">8dp</dimen>
<dimen name="edit_height">50dp</dimen> <dimen name="edit_height">50dp</dimen>
<dimen name="png_height">24dp</dimen> <dimen name="png_height">24dp</dimen>
<dimen name="server_height">72dp</dimen> <dimen name="server_height">72dp</dimen>
+6 -18
View File
@@ -3,7 +3,7 @@
<string name="app_name">v2rayNG</string> <string name="app_name">v2rayNG</string>
<string name="app_widget_name">Switch</string> <string name="app_widget_name">Switch</string>
<string name="app_tile_name">Switch</string> <string name="app_tile_name">Switch</string>
<string name="app_tile_first_use">First use of this feature, please use the app to add server</string> <string name="app_tile_first_use">First use of this feature, please use the app to activate VPN</string>
<string name="navigation_drawer_open">Open navigation drawer</string> <string name="navigation_drawer_open">Open navigation drawer</string>
<string name="navigation_drawer_close">Close navigation drawer</string> <string name="navigation_drawer_close">Close navigation drawer</string>
@@ -41,7 +41,7 @@
<string name="server_lab_network">network</string> <string name="server_lab_network">network</string>
<string name="server_lab_more_function">more function</string> <string name="server_lab_more_function">more function</string>
<string name="server_lab_head_type">head type</string> <string name="server_lab_head_type">head type</string>
<string name="server_lab_request_host">request host(host/ws host/h2 host)/QUIC security</string> <string name="server_lab_request_host">request host(host/ws host/h2 host)/QUIC securty</string>
<string name="server_lab_path">path(ws path/h2 path)/QUIC key</string> <string name="server_lab_path">path(ws path/h2 path)/QUIC key</string>
<string name="server_lab_stream_security">tls</string> <string name="server_lab_stream_security">tls</string>
<string name="server_lab_allow_insecure">allowInsecure</string> <string name="server_lab_allow_insecure">allowInsecure</string>
@@ -74,6 +74,7 @@
<!-- Preferences --> <!-- Preferences -->
<string name="title_settings">Settings</string> <string name="title_settings">Settings</string>
<string name="title_about">About</string>
<string name="title_advanced">Advanced Settings</string> <string name="title_advanced">Advanced Settings</string>
<string name="title_pref_per_app_proxy">Per-app proxy</string> <string name="title_pref_per_app_proxy">Per-app proxy</string>
@@ -83,8 +84,7 @@
<string name="summary_pref_mux_enabled">Enable maybe speed up network and switch network maybe flash</string> <string name="summary_pref_mux_enabled">Enable maybe speed up network and switch network maybe flash</string>
<string name="title_pref_speed_enabled">Enable speed display</string> <string name="title_pref_speed_enabled">Enable speed display</string>
<string name="summary_pref_speed_enabled">Display current speed in the notification.\nNotification icon would change based on <string name="summary_pref_speed_enabled">Display current speed in the notification</string>
usage.</string>
<string name="title_pref_sniffing_enabled">Enable Sniffing</string> <string name="title_pref_sniffing_enabled">Enable Sniffing</string>
<string name="summary_pref_sniffing_enabled">Sniffing</string> <string name="summary_pref_sniffing_enabled">Sniffing</string>
@@ -124,8 +124,7 @@
<string name="title_pref_promotion">Promotion</string> <string name="title_pref_promotion">Promotion</string>
<string name="summary_pref_promotion">Promotion,click for details(Donation can be removed)</string> <string name="summary_pref_promotion">Promotion,click for details(Donation can be removed)</string>
<string name="title_mode">Mode</string> <string name="title_pref_version">Version</string>
<string name="title_mode_help">Click me for more help</string>
<string name="donate_error_setup">Error Setup:</string> <string name="donate_error_setup">Error Setup:</string>
<string name="donate_error_inventory">Error querying inventory</string> <string name="donate_error_inventory">Error querying inventory</string>
@@ -137,8 +136,7 @@
<string name="title_logcat">Logcat</string> <string name="title_logcat">Logcat</string>
<string name="logcat_copy">Copy</string> <string name="logcat_copy">Copy</string>
<string name="logcat_delete">Delete</string> <string name="title_export_all">Export all config to clipboard</string>
<string name="title_export_all">Export non-custom configs to clipboard</string>
<string name="title_sub_setting">Subscription setting</string> <string name="title_sub_setting">Subscription setting</string>
<string name="sub_setting_remarks">remarks</string> <string name="sub_setting_remarks">remarks</string>
<string name="sub_setting_url">url</string> <string name="sub_setting_url">url</string>
@@ -183,15 +181,5 @@
<item>Bypass mainland address</item> <item>Bypass mainland address</item>
<item>Bypassing LAN and mainland address</item> <item>Bypassing LAN and mainland address</item>
</string-array> </string-array>
<string name="title_pref_proxy_sharing_enabled">Proxy sharing</string>
<string name="summary_pref_proxy_sharing_enabled">Bind inbound to 0.0.0.0</string>
<string name="toast_warning_pref_proxysharing">Other devices can connect to proxy by your ip address through socks/http protocol\nHttp Proxy: http://yourIP:10809\nSocks Proxy: socks(4/5)://yourIP:10808\nOnly enable in trusted network to avoid unauthorized connection</string>
<string name="toast_warning_pref_proxysharing_short">Proxy sharing enabled\nMake sure you are in a trusted network</string>
<string name="toast_malformed_josn">Config malformed</string>
<string-array name="mode_entries">
<item>VPN</item>
<item>Proxy only</item>
</string-array>
</resources> </resources>
@@ -13,12 +13,6 @@
<item name="windowNoTitle">true</item> <item name="windowNoTitle">true</item>
</style> </style>
<style name="AppTheme.NoActionBar.Translucent">
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:colorBackgroundCacheHint">@null</item>
<item name="android:windowIsTranslucent">true</item>
</style>
<style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" /> <style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" /> <style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
+25 -16
View File
@@ -22,13 +22,6 @@
android:summary="@string/summary_pref_sniffing_enabled" android:summary="@string/summary_pref_sniffing_enabled"
android:title="@string/title_pref_sniffing_enabled" /> android:title="@string/title_pref_sniffing_enabled" />
<CheckBoxPreference
android:defaultValue="false"
android:key="pref_proxy_sharing_enabled"
android:onClick="proxySharingOnClick"
android:summary="@string/summary_pref_proxy_sharing_enabled"
android:title="@string/title_pref_proxy_sharing_enabled" />
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory android:title="@string/title_pref_routing"> <PreferenceCategory android:title="@string/title_pref_routing">
@@ -85,13 +78,29 @@
android:key="pref_http_port" android:key="pref_http_port"
android:summary="10809" android:summary="10809"
android:title="@string/title_pref_http_port" /> android:title="@string/title_pref_http_port" />
<ListPreference
android:defaultValue="VPN"
android:entries="@array/mode_entries"
android:entryValues="@array/mode_value"
android:key="pref_mode"
android:summary="%s"
android:title="@string/title_mode" />
</PreferenceCategory> </PreferenceCategory>
</PreferenceScreen>
<PreferenceCategory android:title="@string/title_about">
<!--<Preference-->
<!--android:key="pref_donate"-->
<!--android:summary="@string/summary_pref_donate"-->
<!--android:title="@string/title_pref_donate" />-->
<!--<Preference-->
<!--android:key="pref_licenses"-->
<!--android:title="@string/notices_title" />-->
<!--<Preference-->
<!--android:key="pref_feedback"-->
<!--android:summary="@string/summary_pref_feedback"-->
<!--android:title="@string/title_pref_feedback" />-->
<!--<Preference-->
<!--android:key="pref_tg_group"-->
<!--android:title="@string/summary_pref_tg_group" />-->
<Preference
android:key="pref_version"
android:title="@string/title_pref_version" />
</PreferenceCategory>
</PreferenceScreen>
+1 -1
View File
@@ -7,7 +7,7 @@ buildscript {
maven { url 'https://maven.google.com' } maven { url 'https://maven.google.com' }
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:4.0.1' classpath 'com.android.tools.build:gradle:3.1.3'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong
+4 -4
View File
@@ -1,8 +1,8 @@
apply plugin: 'com.android.library' apply plugin: 'com.android.library'
android { android {
compileSdkVersion Integer.parseInt("$compileSdkVer") compileSdkVersion 28
buildToolsVersion buildToolsVer buildToolsVersion '28.0.3'
defaultConfig { defaultConfig {
minSdkVersion 17 minSdkVersion 17
@@ -19,7 +19,7 @@ android {
dependencies { dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs') implementation fileTree(include: ['*.jar'], dir: 'libs')
testImplementation 'junit:junit:4.13' testImplementation 'junit:junit:4.12'
implementation "com.android.support:support-annotations:$supportLibVersion" implementation "com.android.support:support-annotations:$supportLibVersion"
implementation 'com.google.code.gson:gson:2.8.6' implementation 'com.google.code.gson:gson:2.7'
} }
@@ -2,9 +2,7 @@ package me.dozen.dpreference;
import android.content.Context; import android.content.Context;
import android.text.TextUtils;
import java.util.LinkedHashSet;
import java.util.Set; import java.util.Set;
/** /**
@@ -47,6 +45,10 @@ public class DPreference {
PrefAccessor.setInt(mContext, mName, key, value); PrefAccessor.setInt(mContext, mName, key, value);
} }
public void setPrefStringSet(final String key, final Set<String> value) {
PrefAccessor.setStringSet(mContext, mName, key, value);
}
public int getPrefInt(final String key, final int defaultValue) { public int getPrefInt(final String key, final int defaultValue) {
return PrefAccessor.getInt(mContext, mName, key, defaultValue); return PrefAccessor.getInt(mContext, mName, key, defaultValue);
} }
@@ -59,26 +61,10 @@ public class DPreference {
return PrefAccessor.getLong(mContext, mName, key, defaultValue); return PrefAccessor.getLong(mContext, mName, key, defaultValue);
} }
public void setPrefStringSet(final String key, final Set<String> value) {
PrefAccessor.setStringSet(mContext, mName, key, value);
}
public Set<String> getPrefStringSet(final String key, final Set<String> defaultValue) { public Set<String> getPrefStringSet(final String key, final Set<String> defaultValue) {
return PrefAccessor.getStringSet(mContext, mName, key, defaultValue); return PrefAccessor.getStringSet(mContext, mName, key, defaultValue);
} }
public void setPrefStringOrderedSet(final String key, final LinkedHashSet<String> value) {
PrefAccessor.setString(mContext, mName, key, StringSetConverter.encode(value));
}
public LinkedHashSet<String> getPrefStringOrderedSet(final String key, final LinkedHashSet<String> defaultValue) {
String value = PrefAccessor.getString(mContext, mName, key, "");
if (TextUtils.isEmpty(value)) {
return defaultValue;
}
return StringSetConverter.decode(value);
}
public void removePreference(final String key) { public void removePreference(final String key) {
PrefAccessor.remove(mContext, mName, key); PrefAccessor.remove(mContext, mName, key);
} }
@@ -4,7 +4,6 @@ import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken; import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.LinkedHashSet;
import java.util.Set; import java.util.Set;
public class StringSetConverter { public class StringSetConverter {
@@ -14,8 +13,8 @@ public class StringSetConverter {
return gson.toJson(src); return gson.toJson(src);
} }
public static LinkedHashSet<String> decode(String json) { public static Set<String> decode(String json) {
Type setType = new TypeToken<LinkedHashSet<String>>() { Type setType = new TypeToken<Set<String>>() {
}.getType(); }.getType();
return gson.fromJson(json, setType); return gson.fromJson(json, setType);
} }
+4 -4
View File
@@ -14,9 +14,9 @@ org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryErro
# org.gradle.parallel=true # org.gradle.parallel=true
#Fri Jun 02 14:08:42 CST 2017 #Fri Jun 02 14:08:42 CST 2017
ankoVersion=0.10.8 ankoVersion=0.10.8
kotlinVersion=1.4.0 kotlinVersion=1.3.40
supportLibVersion=28.0.0 supportLibVersion=28.0.0
buildToolsVer=29.0.3 buildToolsVer=28.0.3
compileSdkVer=29 compileSdkVer=28
kotlin.incremental=true kotlin.incremental=true
targetSdkVer=29 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