Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 870950e807 | |||
| e798cb3f42 | |||
| b970f0bcff | |||
| 93b9a56428 | |||
| eb8bd13266 | |||
| d850c88c63 | |||
| 6bee795c0d | |||
| d7d7e029e0 | |||
| 8efdab43d7 | |||
| 5e0235cf70 | |||
| 9f668b3da7 | |||
| b2437279dc | |||
| 180b5efd93 | |||
| dc31380cc2 | |||
| 1361e0dacf | |||
| a45eb66bd1 | |||
| 508102cebe | |||
| 1d86dbb9f3 | |||
| 20ca554be2 | |||
| 25ba455656 | |||
| 17e7c62d53 | |||
| 6f0f2fdeda | |||
| 3c9c9b5a4c | |||
| e13024d6bb | |||
| 9d58edd31f | |||
| af7dfc3a43 | |||
| ca554e6ac4 | |||
| ec391d8689 |
+3
-5
@@ -2,10 +2,8 @@ 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/gradle/wrapper/gradle-wrapper.properties
|
V2rayNG/app/release/output.json
|
||||||
V2rayNG/gradle/wrapper/gradle-wrapper.properties
|
.idea/
|
||||||
V2rayNG/app/release/output.json
|
.gradle/
|
||||||
@@ -5,14 +5,15 @@ 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 20
|
return 21
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *Status) GetDataDir() string {
|
func (v *Status) GetDataDir() string {
|
||||||
@@ -20,7 +21,7 @@ func (v *Status) GetDataDir() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (v *Status) GetApp(name string) string {
|
func (v *Status) GetApp(name string) string {
|
||||||
return v.PackageName + name
|
return v.PackageCodePath + name
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *Status) GetTun2socksArgs(localDNS bool, enableIPv6 bool) (ret []string) {
|
func (v *Status) GetTun2socksArgs(localDNS bool, enableIPv6 bool) (ret []string) {
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ type resolved struct {
|
|||||||
domain string
|
domain string
|
||||||
IPs []net.IP
|
IPs []net.IP
|
||||||
Port int
|
Port int
|
||||||
lastResolved time.Time
|
|
||||||
ipIdx uint8
|
ipIdx uint8
|
||||||
ipLock sync.Mutex
|
ipLock sync.Mutex
|
||||||
lastSwitched time.Time
|
lastSwitched time.Time
|
||||||
@@ -98,7 +97,7 @@ func (d *ProtectedDialer) PrepareResolveChan() {
|
|||||||
d.resolveChan = make(chan struct{})
|
d.resolveChan = make(chan struct{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *ProtectedDialer) ResolveChan() chan struct{} {
|
func (d *ProtectedDialer) ResolveChan() <-chan struct{} {
|
||||||
return d.resolveChan
|
return d.resolveChan
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,10 +137,9 @@ func (d *ProtectedDialer) lookupAddr(addr string) (*resolved, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
rs := &resolved{
|
rs := &resolved{
|
||||||
domain: host,
|
domain: host,
|
||||||
IPs: IPs,
|
IPs: IPs,
|
||||||
Port: portnum,
|
Port: portnum,
|
||||||
lastResolved: time.Now(),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return rs, nil
|
return rs, nil
|
||||||
@@ -152,6 +150,7 @@ func (d *ProtectedDialer) PrepareDomain(domainName string, closeCh <-chan struct
|
|||||||
log.Printf("Preparing Domain: %s", domainName)
|
log.Printf("Preparing Domain: %s", domainName)
|
||||||
d.currentServer = domainName
|
d.currentServer = domainName
|
||||||
|
|
||||||
|
defer close(d.resolveChan)
|
||||||
maxRetry := 10
|
maxRetry := 10
|
||||||
for {
|
for {
|
||||||
if maxRetry == 0 {
|
if maxRetry == 0 {
|
||||||
@@ -213,10 +212,6 @@ func (d *ProtectedDialer) Dial(ctx context.Context,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if time.Now().Sub(d.vServer.lastResolved) > time.Minute * 30 {
|
|
||||||
d.PrepareDomain(Address, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
fd, err := d.getFd(dest.Network)
|
fd, err := d.getFd(dest.Network)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ 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
|
||||||
@@ -67,14 +68,12 @@ 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{})
|
||||||
v.dialer.PrepareResolveChan()
|
v.dialer.PrepareResolveChan()
|
||||||
go func() {
|
go v.dialer.PrepareDomain(v.DomainName, v.closeChan)
|
||||||
v.dialer.PrepareDomain(v.DomainName, v.closeChan)
|
|
||||||
close(v.dialer.ResolveChan())
|
|
||||||
}()
|
|
||||||
go func() {
|
go func() {
|
||||||
select {
|
select {
|
||||||
// wait until resolved
|
// wait until resolved
|
||||||
@@ -236,7 +235,7 @@ func (v V2RayPoint) runTun2socks() error {
|
|||||||
|
|
||||||
v.escorter.EscortingUp()
|
v.escorter.EscortingUp()
|
||||||
go v.escorter.EscortRun(
|
go v.escorter.EscortRun(
|
||||||
v.status.GetApp("tun2socks"),
|
v.status.GetApp("libtun2socks.so"),
|
||||||
v.status.GetTun2socksArgs(v.EnableLocalDNS, v.ForwardIpv6), "",
|
v.status.GetTun2socksArgs(v.EnableLocalDNS, v.ForwardIpv6), "",
|
||||||
v.SupportSet.SendFd)
|
v.SupportSet.SendFd)
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ apply plugin: 'kotlin-android'
|
|||||||
apply plugin: 'kotlin-android-extensions'
|
apply plugin: 'kotlin-android-extensions'
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 28
|
compileSdkVersion Integer.parseInt("$compileSdkVer")
|
||||||
buildToolsVersion '28.0.3'
|
buildToolsVersion buildToolsVer
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
targetCompatibility = "8"
|
targetCompatibility = "8"
|
||||||
@@ -20,34 +20,17 @@ 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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,4 +114,4 @@ repositories {
|
|||||||
flatDir {
|
flatDir {
|
||||||
dirs 'libs'
|
dirs 'libs'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
@@ -76,7 +76,10 @@
|
|||||||
|
|
||||||
<activity android:name=".ui.SubEditActivity" />
|
<activity android:name=".ui.SubEditActivity" />
|
||||||
<activity android:name=".ui.ScScannerActivity" />
|
<activity android:name=".ui.ScScannerActivity" />
|
||||||
<activity android:name=".ui.ScSwitchActivity" />
|
<activity
|
||||||
|
android:name=".ui.ScSwitchActivity"
|
||||||
|
android:excludeFromRecents="true"
|
||||||
|
android:theme="@style/AppTheme.NoActionBar.Translucent" />
|
||||||
|
|
||||||
<service
|
<service
|
||||||
android:name=".service.V2RayVpnService"
|
android:name=".service.V2RayVpnService"
|
||||||
@@ -132,4 +135,4 @@
|
|||||||
|
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|||||||
@@ -1,196 +1,241 @@
|
|||||||
com.android.chrome
|
|
||||||
com.google.android.googlequicksearchbox
|
|
||||||
com.google.android.apps.photos
|
|
||||||
com.google.android.youtube
|
|
||||||
com.google.android.gm
|
|
||||||
com.google.android.apps.plus
|
|
||||||
com.android.vending
|
|
||||||
com.google.android.inputmethod.latin
|
|
||||||
com.google.android.apps.paidtasks
|
|
||||||
com.google.android.keep
|
|
||||||
com.google.android.gms.setup
|
|
||||||
com. google.android. apps.magazines
|
|
||||||
com.google.android.videos
|
|
||||||
com. google.android.gms
|
|
||||||
com.google.android.apps.books
|
|
||||||
com.google.android.music
|
|
||||||
com.google.android.play.games
|
|
||||||
com.google.android.gsf
|
|
||||||
com.google.android.gsf.login
|
|
||||||
com.app.pornhub
|
|
||||||
com.spotify.music
|
|
||||||
org.thunderdog.challegram
|
|
||||||
com.tumblr
|
|
||||||
com.twitter.android
|
|
||||||
com.xda.labs
|
|
||||||
com.kapp.youtube.final
|
|
||||||
com.google.android.ims
|
|
||||||
com.wire
|
|
||||||
mark.via.gp
|
|
||||||
com.downloader.video.tumblr
|
|
||||||
com.sololearn
|
|
||||||
com.cygames.shadowverse
|
|
||||||
com.felixfilip.scpae
|
|
||||||
amanita_design.samorost3.gp
|
amanita_design.samorost3.gp
|
||||||
com.devolver.reigns2
|
android
|
||||||
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
|
au.com.shiftyjelly.pocketcasts
|
||||||
com.google.android.gms
|
bbc.mobile.news.ww
|
||||||
com.android.providers.telephony
|
|
||||||
com.resilio.sync
|
|
||||||
com.google.android.apps.googlevoice
|
|
||||||
com.discord
|
|
||||||
com.cradle.iitc_mobile
|
|
||||||
be.mygod.vpnhotspot
|
be.mygod.vpnhotspot
|
||||||
|
ch.protonmail.android
|
||||||
|
co.wanqu.android
|
||||||
com.alphainventor.filemanager
|
com.alphainventor.filemanager
|
||||||
|
com.amazon.kindle
|
||||||
|
com.amazon.mshop.android.shopping
|
||||||
|
com.android.chrome
|
||||||
com.android.providers.downloads
|
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.apkpure.aegon
|
||||||
|
com.apkupdater
|
||||||
|
com.app.pornhub
|
||||||
|
com.arthurivanets.owly
|
||||||
|
com.asahi.tida.tablet
|
||||||
|
com.authy.authy
|
||||||
|
com.avmovie
|
||||||
com.ballistiq.artstation
|
com.ballistiq.artstation
|
||||||
|
com.binance.dev
|
||||||
com.bitly.app
|
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.canary
|
||||||
com.chrome.dev
|
com.chrome.dev
|
||||||
|
com.cl.newt66y
|
||||||
|
com.cradle.iitc_mobile
|
||||||
|
com.cygames.shadowverse
|
||||||
com.devhd.feedly
|
com.devhd.feedly
|
||||||
|
com.devolver.reigns2
|
||||||
|
com.discord
|
||||||
|
com.downloader.video.tumblr
|
||||||
|
com.driverbrowser
|
||||||
com.dropbox.android
|
com.dropbox.android
|
||||||
|
com.duolingo
|
||||||
|
com.duckduckgo.mobile.android
|
||||||
|
com.dv.adm
|
||||||
com.estrongs.android.pop
|
com.estrongs.android.pop
|
||||||
com.estrongs.android.pop.pro
|
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.fastaccess.github
|
||||||
|
com.felixfilip.scpae
|
||||||
|
com.fireproofstudios.theroom4
|
||||||
com.firstrowria.pushnotificationtester
|
com.firstrowria.pushnotificationtester
|
||||||
|
com.flyersoft.moonreaderp
|
||||||
|
com.fooview.android.fooview
|
||||||
com.fvd.eversync
|
com.fvd.eversync
|
||||||
|
com.gameloft.android.anmp.glofta8hm
|
||||||
|
com.gameloft.android.anmp.glofta9hm
|
||||||
com.gianlu.aria2app
|
com.gianlu.aria2app
|
||||||
com.github.yeriomin.yalpstore
|
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.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.gsf
|
||||||
|
com.google.android.gsf.login
|
||||||
|
com.google.android.ims
|
||||||
|
com.google.android.inputmethod.latin
|
||||||
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
|
||||||
videodownloader.downloadvideo.downloader
|
|
||||||
com.quora.android
|
|
||||||
com.lingodeer
|
|
||||||
org.wikipedia
|
|
||||||
com.ninegag.android.app
|
|
||||||
com.duolingo
|
|
||||||
com.patreon.android
|
|
||||||
com.valvesoftware.android.steam.communimunity
|
|
||||||
co.wanqu.android
|
|
||||||
jp.bokete.app.android
|
|
||||||
com.vkontakte.android
|
|
||||||
com.amazon.mshop.android.shopping
|
|
||||||
com.ubisoft.dance.justdance2015companion
|
|
||||||
com.gameloft.android.anmp.glofta8hm
|
|
||||||
com.gameloft.android.anmp.glofta9hm
|
|
||||||
com.binance.dev
|
|
||||||
com.asahi.tida.tablet
|
|
||||||
com.theinitium.news
|
|
||||||
com.driverbrowser
|
|
||||||
com.thomsonreuters.reuters
|
|
||||||
com.nytimes.cn
|
|
||||||
com.android.providers.downloads.ui
|
|
||||||
com.avmovie
|
|
||||||
bbc.mobile.news.ww
|
|
||||||
org.mozilla.focus
|
|
||||||
io.github.javiewer
|
|
||||||
com.sonelli.juicessh
|
|
||||||
con.medium.reader
|
|
||||||
com.microsoft.skydrive
|
|
||||||
com.valvesoftware.android.steam.community
|
|
||||||
com.nintendo.zara
|
|
||||||
org.torproject.torbrowser_alpha
|
|
||||||
tv.twitch.android.app
|
tv.twitch.android.app
|
||||||
com.shanga.walli
|
tw.com.gamer.android.activecenter
|
||||||
com.whatsapp
|
videodownloader.downloadvideo.downloader
|
||||||
com.wire
|
uk.co.bbc.learningenglish
|
||||||
com.simplehabit.simplehabitapp
|
com.ted.android
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -11,6 +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 VMESS_PROTOCOL: String = "vmess://"
|
const val VMESS_PROTOCOL: String = "vmess://"
|
||||||
const val SS_PROTOCOL: String = "ss://"
|
const val SS_PROTOCOL: String = "ss://"
|
||||||
@@ -50,12 +51,4 @@ 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
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
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 }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -108,6 +108,24 @@ data class V2rayConfig(
|
|||||||
}
|
}
|
||||||
|
|
||||||
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>)
|
||||||
|
|||||||
@@ -11,11 +11,14 @@ import android.content.IntentFilter
|
|||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.net.*
|
import android.net.*
|
||||||
import android.net.VpnService
|
import android.os.Build
|
||||||
import android.os.*
|
import android.os.ParcelFileDescriptor
|
||||||
|
import android.os.StrictMode
|
||||||
import android.support.annotation.RequiresApi
|
import android.support.annotation.RequiresApi
|
||||||
import android.support.v4.app.NotificationCompat
|
import android.support.v4.app.NotificationCompat
|
||||||
|
import android.util.Log
|
||||||
import com.v2ray.ang.AppConfig
|
import com.v2ray.ang.AppConfig
|
||||||
|
import com.v2ray.ang.AppConfig.TAG_DIRECT
|
||||||
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.extension.toSpeedString
|
||||||
@@ -24,16 +27,14 @@ 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.MessageUtil
|
||||||
import com.v2ray.ang.util.Utils
|
import com.v2ray.ang.util.Utils
|
||||||
|
import go.Seq
|
||||||
import libv2ray.Libv2ray
|
import libv2ray.Libv2ray
|
||||||
import libv2ray.V2RayVPNServiceSupportsSet
|
import libv2ray.V2RayVPNServiceSupportsSet
|
||||||
|
import org.jetbrains.anko.doAsync
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
import rx.Subscription
|
import rx.Subscription
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.lang.ref.SoftReference
|
import java.lang.ref.SoftReference
|
||||||
import android.os.Build
|
|
||||||
import android.util.Log
|
|
||||||
import go.Seq
|
|
||||||
import org.jetbrains.anko.doAsync
|
|
||||||
|
|
||||||
class V2RayVpnService : VpnService() {
|
class V2RayVpnService : VpnService() {
|
||||||
companion object {
|
companion object {
|
||||||
@@ -103,6 +104,7 @@ class V2RayVpnService : VpnService() {
|
|||||||
val policy = StrictMode.ThreadPolicy.Builder().permitAll().build()
|
val policy = StrictMode.ThreadPolicy.Builder().permitAll().build()
|
||||||
StrictMode.setThreadPolicy(policy)
|
StrictMode.setThreadPolicy(policy)
|
||||||
v2rayPoint.packageName = Utils.packagePath(applicationContext)
|
v2rayPoint.packageName = Utils.packagePath(applicationContext)
|
||||||
|
v2rayPoint.packageCodePath = applicationContext.applicationInfo.nativeLibraryDir + "/"
|
||||||
Seq.setContext(applicationContext)
|
Seq.setContext(applicationContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -384,6 +386,7 @@ class V2RayVpnService : VpnService() {
|
|||||||
mBuilder?.setSmallIcon(R.drawable.ic_stat_direct)
|
mBuilder?.setSmallIcon(R.drawable.ic_stat_direct)
|
||||||
}
|
}
|
||||||
mBuilder?.setStyle(NotificationCompat.BigTextStyle().bigText(contentText))
|
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())
|
getNotificationManager().notify(NOTIFICATION_ID, mBuilder?.build())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -399,24 +402,34 @@ class V2RayVpnService : VpnService() {
|
|||||||
if (mSubscription == null &&
|
if (mSubscription == null &&
|
||||||
v2rayPoint.isRunning &&
|
v2rayPoint.isRunning &&
|
||||||
defaultDPreference.getPrefBoolean(SettingsActivity.PREF_SPEED_ENABLED, false)) {
|
defaultDPreference.getPrefBoolean(SettingsActivity.PREF_SPEED_ENABLED, false)) {
|
||||||
val cf_name = defaultDPreference.getPrefString(AppConfig.PREF_CURR_CONFIG_NAME, "")
|
|
||||||
var last_zero_speed = false
|
var last_zero_speed = false
|
||||||
|
val outboundTags = defaultDPreference.getPrefStringOrderedSet(AppConfig.PREF_CURR_CONFIG_OUTBOUND_TAGS, LinkedHashSet())
|
||||||
|
outboundTags.remove(TAG_DIRECT)
|
||||||
|
|
||||||
mSubscription = Observable.interval(3, java.util.concurrent.TimeUnit.SECONDS)
|
mSubscription = Observable.interval(3, java.util.concurrent.TimeUnit.SECONDS)
|
||||||
.subscribe {
|
.subscribe {
|
||||||
val proxyUplink = v2rayPoint.queryStats("proxy", "uplink")
|
|
||||||
val proxyDownlink = v2rayPoint.queryStats("proxy", "downlink")
|
|
||||||
val directUplink = v2rayPoint.queryStats("direct", "uplink")
|
|
||||||
val directDownlink = v2rayPoint.queryStats("direct", "downlink")
|
|
||||||
val zero_speed = (proxyUplink == 0L && proxyDownlink == 0L && directUplink == 0L && directDownlink == 0L)
|
|
||||||
val queryTime = System.currentTimeMillis()
|
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 zero_speed = (proxyTotal == 0L && directUplink == 0L && directDownlink == 0L)
|
||||||
if (!zero_speed || !last_zero_speed) {
|
if (!zero_speed || !last_zero_speed) {
|
||||||
val sinceLastQueryInSeconds = (queryTime - lastQueryTime) / 1000.0
|
if (proxyTotal == 0L) {
|
||||||
updateNotification("proxy\t• ${(proxyUplink / sinceLastQueryInSeconds).toLong().toSpeedString()}↑ " +
|
appendSpeedString(text, outboundTags.firstOrNull(), 0.0, 0.0)
|
||||||
"${(proxyDownlink / sinceLastQueryInSeconds).toLong().toSpeedString()}↓\n" +
|
}
|
||||||
"direct\t• ${(directUplink / sinceLastQueryInSeconds).toLong().toSpeedString()}↑ " +
|
appendSpeedString(text, TAG_DIRECT, directUplink / sinceLastQueryInSeconds,
|
||||||
"${(directDownlink / sinceLastQueryInSeconds).toLong().toSpeedString()}↓",
|
directDownlink / sinceLastQueryInSeconds)
|
||||||
proxyDownlink + proxyUplink, directDownlink + directUplink)
|
updateNotification(text.toString(), proxyTotal, directDownlink + directUplink)
|
||||||
}
|
}
|
||||||
last_zero_speed = zero_speed
|
last_zero_speed = zero_speed
|
||||||
lastQueryTime = queryTime
|
lastQueryTime = queryTime
|
||||||
@@ -424,6 +437,15 @@ class V2RayVpnService : VpnService() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun appendSpeedString(text: StringBuilder, name: String?, up: Double, down: Double) {
|
||||||
|
var n = name ?: "no tag"
|
||||||
|
n = n.substring(0, Math.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() {
|
fun stopSpeedNotification() {
|
||||||
if (mSubscription != null) {
|
if (mSubscription != null) {
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ 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.dto.EConfigType
|
||||||
//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
|
||||||
@@ -164,8 +165,8 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
|||||||
importBatchConfig(data?.getStringExtra("SCAN_RESULT"))
|
importBatchConfig(data?.getStringExtra("SCAN_RESULT"))
|
||||||
}
|
}
|
||||||
REQUEST_FILE_CHOOSER -> {
|
REQUEST_FILE_CHOOSER -> {
|
||||||
if (resultCode == RESULT_OK) {
|
val uri = data?.data
|
||||||
val uri = data!!.data
|
if (resultCode == RESULT_OK && uri != null) {
|
||||||
readContentFromUri(uri)
|
readContentFromUri(uri)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -252,16 +253,24 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
|||||||
adapter.updateConfigList()
|
adapter.updateConfigList()
|
||||||
}
|
}
|
||||||
for (k in 0 until configs.vmess.count()) {
|
for (k in 0 until configs.vmess.count()) {
|
||||||
if (configs.vmess[k].configType != AppConfig.EConfigType.Custom) {
|
var serverAddress = configs.vmess[k].address
|
||||||
testingJobs.add(GlobalScope.launch(Dispatchers.IO) {
|
var serverPort = configs.vmess[k].port
|
||||||
configs.vmess[k].testResult = Utils.tcping(configs.vmess[k].address, configs.vmess[k].port)
|
if (configs.vmess[k].configType == EConfigType.CUSTOM.value) {
|
||||||
|
val serverOutbound = V2rayConfigUtil.getCustomConfigServerOutbound(applicationContext, configs.vmess[k].guid)
|
||||||
|
?: 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]
|
val myJob = coroutineContext[Job]
|
||||||
launch(Dispatchers.Main) {
|
launch(Dispatchers.Main) {
|
||||||
testingJobs.remove(myJob)
|
testingJobs.remove(myJob)
|
||||||
adapter.updateSelectedItem(k)
|
adapter.updateSelectedItem(k)
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
@@ -451,9 +460,10 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
|||||||
.subscribe {
|
.subscribe {
|
||||||
if (it) {
|
if (it) {
|
||||||
try {
|
try {
|
||||||
val inputStream = contentResolver.openInputStream(uri)
|
contentResolver.openInputStream(uri).use {
|
||||||
val configText = inputStream.bufferedReader().readText()
|
val configText = it?.bufferedReader()?.readText()
|
||||||
importCustomizeConfig(configText)
|
importCustomizeConfig(configText)
|
||||||
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,17 +8,19 @@ 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 {
|
||||||
@@ -49,7 +51,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 = configs.vmess[position].configType
|
val configType = EConfigType.fromInt(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
|
||||||
@@ -67,39 +69,40 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
|
|||||||
holder.subid.text = "S"
|
holder.subid.text = "S"
|
||||||
}
|
}
|
||||||
|
|
||||||
if (configType == AppConfig.EConfigType.Vmess) {
|
var shareOptions = share_method.asList()
|
||||||
holder.type.text = "vmess"
|
if (configType == EConfigType.CUSTOM) {
|
||||||
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.type.text = mActivity.getString(R.string.server_customize_config)
|
||||||
holder.statistics.text = ""//mActivity.getString(R.string.server_customize_config)
|
val serverOutbound = V2rayConfigUtil.getCustomConfigServerOutbound(mActivity.applicationContext, configs.vmess[position].guid)
|
||||||
holder.layout_share.visibility = View.INVISIBLE
|
if (serverOutbound == null) {
|
||||||
} else if (configType == AppConfig.EConfigType.Shadowsocks) {
|
holder.statistics.text = ""
|
||||||
holder.type.text = "shadowsocks"
|
} 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.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, share_method.asList()) { dialogInterface, i ->
|
mActivity.selector(null, shareOptions) { dialogInterface, i ->
|
||||||
try {
|
try {
|
||||||
when (i) {
|
when (i) {
|
||||||
0 -> {
|
0 -> {
|
||||||
val iv = mActivity.layoutInflater.inflate(R.layout.item_qrcode, null)
|
if (configType == EConfigType.CUSTOM) {
|
||||||
iv.iv_qcode.setImageBitmap(AngConfigManager.share2QRCode(position))
|
shareFullContent(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) {
|
||||||
@@ -108,15 +111,8 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
|
|||||||
mActivity.toast(R.string.toast_failure)
|
mActivity.toast(R.string.toast_failure)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
2 -> {
|
2 -> shareFullContent(position)
|
||||||
if (AngConfigManager.shareFullContent2Clipboard(position) == 0) {
|
else -> mActivity.toast("else")
|
||||||
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()
|
||||||
@@ -125,13 +121,13 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
|
|||||||
}
|
}
|
||||||
|
|
||||||
holder.layout_edit.setOnClickListener {
|
holder.layout_edit.setOnClickListener {
|
||||||
if (configType == AppConfig.EConfigType.Vmess) {
|
if (configType == EConfigType.VMESS) {
|
||||||
mActivity.startActivity<ServerActivity>("position" to position, "isRunning" to !changeable)
|
mActivity.startActivity<ServerActivity>("position" to position, "isRunning" to !changeable)
|
||||||
} else if (configType == AppConfig.EConfigType.Custom) {
|
} else if (configType == EConfigType.CUSTOM) {
|
||||||
mActivity.startActivity<Server2Activity>("position" to position, "isRunning" to !changeable)
|
mActivity.startActivity<Server2Activity>("position" to position, "isRunning" to !changeable)
|
||||||
} else if (configType == AppConfig.EConfigType.Shadowsocks) {
|
} else if (configType == EConfigType.SHADOWSOCKS) {
|
||||||
mActivity.startActivity<Server3Activity>("position" to position, "isRunning" to !changeable)
|
mActivity.startActivity<Server3Activity>("position" to position, "isRunning" to !changeable)
|
||||||
} else if (configType == AppConfig.EConfigType.Socks) {
|
} else if (configType == EConfigType.SOCKS) {
|
||||||
mActivity.startActivity<Server4Activity>("position" to position, "isRunning" to !changeable)
|
mActivity.startActivity<Server4Activity>("position" to position, "isRunning" to !changeable)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -176,6 +172,14 @@ 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 ->
|
||||||
|
|||||||
@@ -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 -> {
|
||||||
if (resultCode == RESULT_OK) {
|
val uri = data?.data
|
||||||
|
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,6 +131,7 @@ class ScannerActivity : BaseActivity(), ZXingScannerView.ResultHandler {
|
|||||||
toast(e.message.toString())
|
toast(e.message.toString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ 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 java.net.URI
|
||||||
import java.net.URLDecoder
|
import java.net.URLDecoder
|
||||||
@@ -65,7 +66,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 = AppConfig.EConfigType.Vmess
|
vmess.configType = EConfigType.VMESS.value
|
||||||
|
|
||||||
if (index >= 0) {
|
if (index >= 0) {
|
||||||
//edit
|
//edit
|
||||||
@@ -208,14 +209,14 @@ object AngConfigManager {
|
|||||||
return app.defaultDPreference.getPrefString(AppConfig.PREF_CURR_CONFIG, "")
|
return app.defaultDPreference.getPrefString(AppConfig.PREF_CURR_CONFIG, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun currConfigType(): Int {
|
fun currConfigType(): EConfigType? {
|
||||||
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 -1
|
return null
|
||||||
}
|
}
|
||||||
return angConfig.vmess[angConfig.index].configType
|
return EConfigType.fromInt(angConfig.vmess[angConfig.index].configType)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun currConfigName(): String {
|
fun currConfigName(): String {
|
||||||
@@ -275,7 +276,7 @@ object AngConfigManager {
|
|||||||
return R.string.toast_incorrect_protocol
|
return R.string.toast_incorrect_protocol
|
||||||
}
|
}
|
||||||
|
|
||||||
vmess.configType = AppConfig.EConfigType.Vmess
|
vmess.configType = EConfigType.VMESS.value
|
||||||
vmess.security = "auto"
|
vmess.security = "auto"
|
||||||
vmess.network = "tcp"
|
vmess.network = "tcp"
|
||||||
vmess.headerType = "none"
|
vmess.headerType = "none"
|
||||||
@@ -478,7 +479,7 @@ object AngConfigManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val vmess = angConfig.vmess[index]
|
val vmess = angConfig.vmess[index]
|
||||||
if (angConfig.vmess[index].configType == AppConfig.EConfigType.Vmess) {
|
if (angConfig.vmess[index].configType == EConfigType.VMESS.value) {
|
||||||
|
|
||||||
val vmessQRCode = VmessQRCode()
|
val vmessQRCode = VmessQRCode()
|
||||||
vmessQRCode.v = vmess.configVersion.toString()
|
vmessQRCode.v = vmess.configVersion.toString()
|
||||||
@@ -496,7 +497,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 == AppConfig.EConfigType.Shadowsocks) {
|
} else if (angConfig.vmess[index].configType == EConfigType.SHADOWSOCKS.value) {
|
||||||
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,
|
||||||
@@ -504,7 +505,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 == AppConfig.EConfigType.Socks) {
|
} else if (angConfig.vmess[index].configType == EConfigType.SOCKS.value) {
|
||||||
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,
|
||||||
@@ -613,7 +614,7 @@ object AngConfigManager {
|
|||||||
//add
|
//add
|
||||||
val vmess = AngConfig.VmessBean()
|
val vmess = AngConfig.VmessBean()
|
||||||
vmess.configVersion = 2
|
vmess.configVersion = 2
|
||||||
vmess.configType = AppConfig.EConfigType.Custom
|
vmess.configType = EConfigType.CUSTOM.value
|
||||||
vmess.guid = guid
|
vmess.guid = guid
|
||||||
vmess.remarks = vmess.guid
|
vmess.remarks = vmess.guid
|
||||||
|
|
||||||
@@ -716,7 +717,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 = AppConfig.EConfigType.Custom
|
vmess.configType = EConfigType.CUSTOM.value
|
||||||
|
|
||||||
if (index >= 0) {
|
if (index >= 0) {
|
||||||
//edit
|
//edit
|
||||||
@@ -741,7 +742,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 = AppConfig.EConfigType.Shadowsocks
|
vmess.configType = EConfigType.SHADOWSOCKS.value
|
||||||
|
|
||||||
if (index >= 0) {
|
if (index >= 0) {
|
||||||
//edit
|
//edit
|
||||||
@@ -766,7 +767,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 = AppConfig.EConfigType.Socks
|
vmess.configType = EConfigType.SOCKS.value
|
||||||
|
|
||||||
if (index >= 0) {
|
if (index >= 0) {
|
||||||
//edit
|
//edit
|
||||||
@@ -794,10 +795,16 @@ object AngConfigManager {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
val removedSelectedServer =
|
val removedSelectedServer =
|
||||||
if (!TextUtils.isEmpty(subid) && configs.vmess.count() > 0 && configs.vmess[configs.index].subid.equals(subid))
|
if (!TextUtils.isEmpty(subid)) {
|
||||||
configs.vmess[configs.index]
|
configs.vmess.getOrNull(configs.index)?.let {
|
||||||
else
|
if (it.subid == subid) {
|
||||||
|
return@let it
|
||||||
|
}
|
||||||
|
return@let null
|
||||||
|
}
|
||||||
|
} else {
|
||||||
null
|
null
|
||||||
|
}
|
||||||
removeServerViaSubid(subid)
|
removeServerViaSubid(subid)
|
||||||
|
|
||||||
// var servers = server
|
// var servers = server
|
||||||
|
|||||||
@@ -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) continue
|
if (!pkg.hasInternetPermission && pkg.packageName != "android") 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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,36 +14,25 @@ import kotlin.collections.HashMap
|
|||||||
import android.app.ActivityManager
|
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.extension.v2RayApplication
|
||||||
import com.v2ray.ang.service.V2RayVpnService
|
import com.v2ray.ang.service.V2RayVpnService
|
||||||
import com.v2ray.ang.ui.SettingsActivity
|
import com.v2ray.ang.ui.SettingsActivity
|
||||||
import kotlinx.android.synthetic.main.activity_logcat.*
|
|
||||||
import kotlinx.coroutines.isActive
|
import kotlinx.coroutines.isActive
|
||||||
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
|
import kotlin.coroutines.coroutineContext
|
||||||
|
|
||||||
@@ -106,7 +95,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.primaryClip = clipData
|
cmb.setPrimaryClip(clipData)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
@@ -324,7 +313,7 @@ object Utils {
|
|||||||
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 == AppConfig.EConfigType.Custom) {
|
if (configType == EConfigType.CUSTOM) {
|
||||||
try {
|
try {
|
||||||
Libv2ray.testConfig(configContent)
|
Libv2ray.testConfig(configContent)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
@@ -452,7 +441,6 @@ object Utils {
|
|||||||
return path
|
return path
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* readTextFromAssets
|
* readTextFromAssets
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
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
|
||||||
@@ -14,6 +15,10 @@ 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 {
|
||||||
@@ -40,19 +45,10 @@ object V2rayConfigUtil {
|
|||||||
// return result
|
// return result
|
||||||
// }
|
// }
|
||||||
|
|
||||||
if (vmess.configType == AppConfig.EConfigType.Vmess) {
|
if (vmess.configType == EConfigType.CUSTOM.value) {
|
||||||
result = getV2rayConfigType1(app, vmess)
|
|
||||||
} else if (vmess.configType == AppConfig.EConfigType.Custom) {
|
|
||||||
result = getV2rayConfigType2(app, vmess)
|
result = getV2rayConfigType2(app, vmess)
|
||||||
} else if (vmess.configType == AppConfig.EConfigType.Shadowsocks) {
|
} else {
|
||||||
result = getV2rayConfigType1(app, vmess)
|
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)
|
||||||
@@ -63,6 +59,22 @@ 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的客户端配置文件
|
||||||
*/
|
*/
|
||||||
@@ -115,6 +127,7 @@ 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) {
|
||||||
@@ -163,8 +176,12 @@ object V2rayConfigUtil {
|
|||||||
try {
|
try {
|
||||||
val outbound = v2rayConfig.outbounds[0]
|
val outbound = v2rayConfig.outbounds[0]
|
||||||
|
|
||||||
when (vmess.configType) {
|
val configType = EConfigType.fromInt(vmess.configType)
|
||||||
AppConfig.EConfigType.Vmess -> {
|
if (configType != null) {
|
||||||
|
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)
|
||||||
@@ -182,10 +199,8 @@ object V2rayConfigUtil {
|
|||||||
|
|
||||||
//远程服务器底层传输配置
|
//远程服务器底层传输配置
|
||||||
outbound.streamSettings = boundStreamSettings(vmess)
|
outbound.streamSettings = boundStreamSettings(vmess)
|
||||||
|
|
||||||
outbound.protocol = "vmess"
|
|
||||||
}
|
}
|
||||||
AppConfig.EConfigType.Shadowsocks -> {
|
EConfigType.SHADOWSOCKS -> {
|
||||||
outbound.settings?.vnext = null
|
outbound.settings?.vnext = null
|
||||||
|
|
||||||
val server = outbound.settings?.servers?.get(0)
|
val server = outbound.settings?.servers?.get(0)
|
||||||
@@ -198,10 +213,8 @@ object V2rayConfigUtil {
|
|||||||
|
|
||||||
//Mux
|
//Mux
|
||||||
outbound.mux?.enabled = false
|
outbound.mux?.enabled = false
|
||||||
|
|
||||||
outbound.protocol = "shadowsocks"
|
|
||||||
}
|
}
|
||||||
AppConfig.EConfigType.Socks -> {
|
EConfigType.SOCKS -> {
|
||||||
outbound.settings?.vnext = null
|
outbound.settings?.vnext = null
|
||||||
|
|
||||||
val server = outbound.settings?.servers?.get(0)
|
val server = outbound.settings?.servers?.get(0)
|
||||||
@@ -210,21 +223,22 @@ object V2rayConfigUtil {
|
|||||||
|
|
||||||
//Mux
|
//Mux
|
||||||
outbound.mux?.enabled = false
|
outbound.mux?.enabled = false
|
||||||
|
|
||||||
outbound.protocol = "socks"
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var serverDomain: String
|
val serverDomain = if (Utils.isIpv6Address(vmess.address)) {
|
||||||
if(Utils.isIpv6Address(vmess.address)) {
|
String.format("[%s]:%s", vmess.address, vmess.port)
|
||||||
serverDomain = String.format("[%s]:%s", vmess.address, vmess.port)
|
|
||||||
} else {
|
} else {
|
||||||
serverDomain = String.format("%s:%s", vmess.address, vmess.port)
|
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
|
||||||
@@ -616,42 +630,58 @@ object V2rayConfigUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun parseDomainName(jsonConfig: String): String {
|
private fun parseDomainNameAndTag(app: AngApplication, jsonConfig: String) {
|
||||||
try {
|
try {
|
||||||
val jObj = JSONObject(jsonConfig)
|
val jObj = JSONObject(jsonConfig)
|
||||||
var domainName: String
|
var domainName = ""
|
||||||
|
val tags = LinkedHashSet<String>()
|
||||||
if (jObj.has("outbound")) {
|
if (jObj.has("outbound")) {
|
||||||
domainName = parseDomainName(jObj.optJSONObject("outbound"))
|
val (domain, tag) = parseDomainNameAndTag(jObj.optJSONObject("outbound"))
|
||||||
if (!TextUtils.isEmpty(domainName)) {
|
domainName = domain
|
||||||
return domainName
|
if (!TextUtils.isEmpty(tag)) {
|
||||||
|
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)) {
|
||||||
domainName = parseDomainName(jObj.optJSONArray("outbounds").getJSONObject(i))
|
val (domain, tag) = parseDomainNameAndTag(jObj.optJSONArray("outbounds").getJSONObject(i))
|
||||||
if (!TextUtils.isEmpty(domainName)) {
|
if (!TextUtils.isEmpty(domain) && TextUtils.isEmpty(domainName)) {
|
||||||
return domainName
|
domainName = domain
|
||||||
|
}
|
||||||
|
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)) {
|
||||||
domainName = parseDomainName(jObj.optJSONArray("outboundDetour").getJSONObject(i))
|
val (domain, tag) = parseDomainNameAndTag(jObj.optJSONArray("outboundDetour").getJSONObject(i))
|
||||||
if (!TextUtils.isEmpty(domainName)) {
|
if (!TextUtils.isEmpty(domain) && TextUtils.isEmpty(domainName)) {
|
||||||
return domainName
|
domainName = domain
|
||||||
|
}
|
||||||
|
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 parseDomainName(outbound: JSONObject): String {
|
private fun parseDomainNameAndTag(outbound: JSONObject): Pair<String, String> {
|
||||||
|
val tag = if (outbound.has("tag")) {
|
||||||
|
outbound.getString("tag")
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
if (outbound.has("settings")) {
|
if (outbound.has("settings")) {
|
||||||
var vnext: JSONArray?
|
val 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")
|
||||||
@@ -659,22 +689,22 @@ object V2rayConfigUtil {
|
|||||||
// shadowsocks or socks
|
// shadowsocks or socks
|
||||||
vnext = outbound.optJSONObject("settings").optJSONArray("servers")
|
vnext = outbound.optJSONObject("settings").optJSONArray("servers")
|
||||||
} else {
|
} else {
|
||||||
return ""
|
return Pair("", tag)
|
||||||
}
|
}
|
||||||
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 String.format("[%s]:%s", address, port)
|
return Pair(String.format("[%s]:%s", address, port), tag)
|
||||||
} else {
|
} else {
|
||||||
return String.format("%s:%s", address, port)
|
return Pair(String.format("%s:%s", address, port), tag)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
return ""
|
return Pair("", tag)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -136,7 +136,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="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>
|
||||||
|
|||||||
@@ -138,7 +138,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="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>
|
||||||
|
|||||||
@@ -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 securty</string>
|
<string name="server_lab_request_host">request host(host/ws host/h2 host)/QUIC security</string>
|
||||||
<string name="server_lab_path">path(ws path/h2 path)/QUIC key</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>
|
||||||
@@ -138,7 +138,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="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>
|
||||||
|
|||||||
@@ -13,6 +13,12 @@
|
|||||||
<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" />
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
apply plugin: 'com.android.library'
|
apply plugin: 'com.android.library'
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 28
|
compileSdkVersion Integer.parseInt("$compileSdkVer")
|
||||||
buildToolsVersion '28.0.3'
|
buildToolsVersion buildToolsVer
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 17
|
minSdkVersion 17
|
||||||
|
|||||||
@@ -2,7 +2,9 @@ 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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -45,10 +47,6 @@ 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);
|
||||||
}
|
}
|
||||||
@@ -61,10 +59,26 @@ 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,6 +4,7 @@ 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 {
|
||||||
@@ -13,8 +14,8 @@ public class StringSetConverter {
|
|||||||
return gson.toJson(src);
|
return gson.toJson(src);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Set<String> decode(String json) {
|
public static LinkedHashSet<String> decode(String json) {
|
||||||
Type setType = new TypeToken<Set<String>>() {
|
Type setType = new TypeToken<LinkedHashSet<String>>() {
|
||||||
}.getType();
|
}.getType();
|
||||||
return gson.fromJson(json, setType);
|
return gson.fromJson(json, setType);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryErro
|
|||||||
ankoVersion=0.10.8
|
ankoVersion=0.10.8
|
||||||
kotlinVersion=1.3.40
|
kotlinVersion=1.3.40
|
||||||
supportLibVersion=28.0.0
|
supportLibVersion=28.0.0
|
||||||
buildToolsVer=28.0.3
|
buildToolsVer=29.0.3
|
||||||
compileSdkVer=28
|
compileSdkVer=29
|
||||||
kotlin.incremental=true
|
kotlin.incremental=true
|
||||||
targetSdkVer=28
|
targetSdkVer=29
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
#Tue Feb 25 12:40:41 CST 2020
|
||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5.1-all.zip
|
||||||
Reference in New Issue
Block a user