Compare commits

..

25 Commits

Author SHA1 Message Date
2dust 20ca554be2 Merge pull request #544 from yuhan6665/android-system
Add "Android System" in per-app vpn
2020-08-09 12:57:49 +08:00
yuhan6665 25ba455656 Add "Android System" in per-app vpn 2020-08-08 23:49:06 -04:00
2dust 17e7c62d53 Merge pull request #542 from yuhan6665/master
Sanitize project configuration
2020-08-08 15:35:16 +08:00
yuhan6665 6f0f2fdeda Sanitize project configuration
Make project compilable out of box. This commit address
the following:
- remove release key
- fix gitignore for Android Studio temp files
- add gradle wrapper properties
2020-08-07 21:37:00 -04:00
2dust 3c9c9b5a4c Merge pull request #531 from yuhan6665/emui-notification
Fix notification for Emui 4.1
2020-08-06 20:40:45 +08:00
2dust e13024d6bb Merge pull request #526 from Vixb1122/master
exclude ScSwitchActivity from recent list
2020-08-06 20:39:08 +08:00
2dust 9d58edd31f Merge pull request #537 from yuhan6665/revert
Revert "Refresh prepared domain every 30 minutes"
2020-08-06 20:38:50 +08:00
yuhan6665 af7dfc3a43 Revert "Refresh prepared domain every 30 minutes"
This reverts commit 903352ec9c.
2020-08-05 20:22:01 -04:00
yuhan6665 ca554e6ac4 Fix notification for Emui 4.1 2020-08-02 08:42:04 -04:00
xuezhixin ec391d8689 exclude ScSwitchActivity from recent list 2020-07-29 16:47:40 +08:00
2dust 6afd4d0549 Update AngConfigManager.kt 2020-07-27 21:00:34 +08:00
2dust 957cf85362 Merge pull request #517 from yuhan6665/bypass-private
Bypass private IP at VPN service
2020-07-25 19:42:12 +08:00
2dust 881152e10a Merge pull request #516 from cpdyj/master
add support to new version vmess link
2020-07-25 19:41:46 +08:00
2dust 6e47ebb27a Merge pull request #484 from DevRyz3n/drag-item-store-config
Optimize invoke timing of store config file after dragging item.
2020-07-25 19:41:13 +08:00
2dust a731e6c360 Merge pull request #453 from yuhan6665/widget
Widget
2020-07-25 19:40:39 +08:00
yuhan6665 d1466ba4b2 Bypass private IP at VPN service 2020-07-24 18:12:20 -04:00
iseki 617f28f399 Update AngConfigManager.kt 2020-07-24 11:23:46 +08:00
iseki c0ec0d9404 Update AngConfigManager.kt 2020-07-24 11:21:20 +08:00
iseki 3419dc8837 add support to new version vmess link
some information can be found at: https://github.com/v2ray/discussion/issues/720
2020-07-23 00:51:08 +08:00
2dust 786aaf823a Merge pull request #511 from yuhan6665/outbound-speed
Outbound speed
2020-07-19 08:19:03 +08:00
yuhan6665 1144183da4 Update notification icon based on traffic
Notification icon would be a good indication for current traffic
- going through proxy
- connect directly
- not going through v2rayNG
2020-07-18 18:48:47 -04:00
yuhan6665 5bf2fda990 Measure traffic from outbounds
With v2ray-core 4.26.0, traffic can be measured from outbounds.
Stats is shown separately for proxy and direct traffic.

In the future, it is possible to add stats for each server node and
even historic usage graph.
2020-07-18 18:47:59 -04:00
r23 993ee0b8d2 Optimize invoke timing of store config file after dragging item. 2020-06-29 01:24:19 +08:00
yuhan6665 91b8284afd Improve widget UI
Change widget color when clicked
2020-06-06 00:18:19 -04:00
yuhan6665 ef9e0cc0d2 Add back widget, fix implicit Intent 2020-06-06 00:18:19 -04:00
25 changed files with 223 additions and 96 deletions
+3 -5
View File
@@ -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 -10
View File
@@ -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
+2 -5
View File
@@ -71,10 +71,7 @@ func (v *V2RayPoint) RunLoop() (err error) {
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
@@ -119,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("inbound>>>%s>>>traffic>>>%s", tag, direct)) counter := v.statsManager.GetCounter(fmt.Sprintf("outbound>>>%s>>>traffic>>>%s", tag, direct))
if counter == nil { if counter == nil {
return 0 return 0
} }
+1 -18
View File
@@ -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'
} }
} }
+14 -11
View File
@@ -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"
@@ -93,16 +96,16 @@
android:value="true" /> android:value="true" />
</service> </service>
<!--<receiver android:name=".receiver.WidgetProvider">--> <receiver android:name=".receiver.WidgetProvider"
<!--<meta-data--> android:process=":RunSoLibV2RayDaemon">
<!--android:name="android.appwidget.provider"--> <meta-data
<!--android:resource="@xml/app_widget_provider" />--> android:name="android.appwidget.provider"
android:resource="@xml/app_widget_provider" />
<!--<intent-filter>--> <intent-filter>
<!--<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />--> <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
<!--<action android:name="com.v2ray.ang.action.widget.click" />--> <action android:name="com.v2ray.ang.action.widget.click" />
<!--</intent-filter>--> </intent-filter>
<!--</receiver>--> </receiver>
<service <service
android:name=".service.QSTileService" android:name=".service.QSTileService"
@@ -1,3 +1,4 @@
android
com.android.chrome com.android.chrome
com.google.android.googlequicksearchbox com.google.android.googlequicksearchbox
com.google.android.apps.photos com.google.android.apps.photos
@@ -193,4 +194,4 @@ tv.twitch.android.app
com.shanga.walli com.shanga.walli
com.whatsapp com.whatsapp
com.wire com.wire
com.simplehabit.simplehabitapp com.simplehabit.simplehabitapp
@@ -13,8 +13,8 @@
} }
}, },
"system": { "system": {
"statsInboundUplink": true, "statsOutboundUplink": true,
"statsInboundDownlink": true "statsOutboundDownlink": true
} }
}, },
"inbounds": [{ "inbounds": [{
@@ -43,6 +43,8 @@ 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,6 +112,8 @@ 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) {
@@ -3,14 +3,13 @@ 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.util.Utils import com.v2ray.ang.util.Utils
import org.jetbrains.anko.toast
class WidgetProvider : AppWidgetProvider() { class WidgetProvider : AppWidgetProvider() {
/** /**
@@ -19,10 +18,21 @@ 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)
val isRunning = Utils.isServiceRun(context, "com.v2ray.ang.service.V2RayVpnService")
updateWidgetBackground(context, appWidgetManager, appWidgetIds, 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(AppConfig.BROADCAST_ACTION_WIDGET_CLICK) val intent = Intent(context, WidgetProvider::class.java)
intent.setAction(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)
@@ -44,7 +54,9 @@ class WidgetProvider : AppWidgetProvider() {
// context.toast(R.string.toast_services_start) // context.toast(R.string.toast_services_start)
Utils.startVService(context) Utils.startVService(context)
} }
val manager = AppWidgetManager.getInstance(context)
updateWidgetBackground(context, manager, manager.getAppWidgetIds(ComponentName(context, WidgetProvider::class.java)),
!isRunning);
} }
} }
} }
@@ -40,6 +40,7 @@ class V2RayVpnService : VpnService() {
const val NOTIFICATION_ID = 1 const val NOTIFICATION_ID = 1
const val NOTIFICATION_PENDING_INTENT_CONTENT = 0 const val NOTIFICATION_PENDING_INTENT_CONTENT = 0
const val NOTIFICATION_PENDING_INTENT_STOP_V2RAY = 1 const val NOTIFICATION_PENDING_INTENT_STOP_V2RAY = 1
const val NOTIFICATION_ICON_THRESHOLD = 3000
fun startV2Ray(context: Context) { fun startV2Ray(context: Context) {
val intent = Intent(context.applicationContext, V2RayVpnService::class.java) val intent = Intent(context.applicationContext, V2RayVpnService::class.java)
@@ -130,6 +131,7 @@ class V2RayVpnService : VpnService() {
// 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(",") }
@@ -138,7 +140,20 @@ class V2RayVpnService : VpnService() {
'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' -> builder.addRoute(it[1], Integer.parseInt(it[2])) 'r' -> {
if (routingMode == "1" || routingMode == "3") {
if (it[1] == "::") { //not very elegant, should move Vpn setting in Kotlin, simplify go code
builder.addRoute("2000::", 3)
} else {
resources.getStringArray(R.array.bypass_private_ip_address).forEach {
val addr = it.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])
} }
} }
@@ -359,9 +374,17 @@ class V2RayVpnService : VpnService() {
mSubscription = null mSubscription = null
} }
private fun updateNotification(contentText: String) { private fun updateNotification(contentText: String, proxyTraffic: Long, directTraffic: Long) {
if (mBuilder != null) { if (mBuilder != null) {
mBuilder?.setContentTitle(contentText) 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()) getNotificationManager().notify(NOTIFICATION_ID, mBuilder?.build())
} }
} }
@@ -382,13 +405,19 @@ class V2RayVpnService : VpnService() {
mSubscription = Observable.interval(3, java.util.concurrent.TimeUnit.SECONDS) mSubscription = Observable.interval(3, java.util.concurrent.TimeUnit.SECONDS)
.subscribe { .subscribe {
val uplink = v2rayPoint.queryStats("socks", "uplink") val proxyUplink = v2rayPoint.queryStats("proxy", "uplink")
val downlink = v2rayPoint.queryStats("socks", "downlink") val proxyDownlink = v2rayPoint.queryStats("proxy", "downlink")
val zero_speed = (uplink == 0L && downlink == 0L) 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()
if (!zero_speed || !last_zero_speed) { if (!zero_speed || !last_zero_speed) {
updateNotification("${cf_name} • ${(uplink * 1000 / (queryTime - lastQueryTime)).toSpeedString()}↑" + val sinceLastQueryInSeconds = (queryTime - lastQueryTime) / 1000.0
" ${(downlink * 1000 / (queryTime - lastQueryTime)).toSpeedString()}↓") updateNotification("proxy\t• ${(proxyUplink / sinceLastQueryInSeconds).toLong().toSpeedString()}↑ " +
"${(proxyDownlink / sinceLastQueryInSeconds).toLong().toSpeedString()}↓\n" +
"direct\t• ${(directUplink / sinceLastQueryInSeconds).toLong().toSpeedString()}↑ " +
"${(directDownlink / sinceLastQueryInSeconds).toLong().toSpeedString()}↓",
proxyDownlink + proxyUplink, directDownlink + directUplink)
} }
last_zero_speed = zero_speed last_zero_speed = zero_speed
lastQueryTime = queryTime lastQueryTime = queryTime
@@ -403,7 +432,7 @@ class V2RayVpnService : VpnService() {
mSubscription = null mSubscription = null
val cf_name = defaultDPreference.getPrefString(AppConfig.PREF_CURR_CONFIG_NAME, "") val cf_name = defaultDPreference.getPrefString(AppConfig.PREF_CURR_CONFIG_NAME, "")
updateNotification(cf_name) updateNotification(cf_name, 0, 0)
} }
} }
@@ -265,4 +265,8 @@ 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()
}
} }
@@ -2,7 +2,6 @@ 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
@@ -16,12 +15,9 @@ 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.VmessQRCode import com.v2ray.ang.dto.VmessQRCode
import com.v2ray.ang.extension.defaultDPreference import java.net.URI
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
@@ -137,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
@@ -256,7 +252,11 @@ object AngConfigManager {
if (server.startsWith(VMESS_PROTOCOL)) { if (server.startsWith(VMESS_PROTOCOL)) {
val indexSplit = server.indexOf("?") val indexSplit = server.indexOf("?")
if (indexSplit > 0) { val newVmess = tryParseNewVmess(server)
if (newVmess != null) {
vmess = newVmess
vmess.subid = subid
} else if (indexSplit > 0) {
vmess = ResolveVmess4Kitsunebi(server) vmess = ResolveVmess4Kitsunebi(server)
} else { } else {
@@ -378,6 +378,61 @@ 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()
@@ -739,7 +794,7 @@ object AngConfigManager {
return 0 return 0
} }
val removedSelectedServer = val removedSelectedServer =
if (!TextUtils.isEmpty(subid) && configs.vmess[configs.index].subid.equals(subid)) if (!TextUtils.isEmpty(subid) && configs.vmess.count() > 0 && configs.vmess[configs.index].subid.equals(subid))
configs.vmess[configs.index] configs.vmess[configs.index]
else else
null null
@@ -839,4 +894,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) 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
} }
} }
@@ -0,0 +1,6 @@
<?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>
@@ -0,0 +1,6 @@
<?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.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

@@ -1,12 +1,5 @@
<?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"
@@ -14,16 +7,9 @@
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:src="@mipmap/ic_launcher" /> android:padding="10dp"
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>
@@ -83,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">在通知中显示当前速度</string> <string name="summary_pref_speed_enabled">在通知中显示当前速度\n小图标显示流量的路由情况</string>
<string name="title_pref_sniffing_enabled">启用流量探测</string> <string name="title_pref_sniffing_enabled">启用流量探测</string>
<string name="summary_pref_sniffing_enabled">流量探测</string> <string name="summary_pref_sniffing_enabled">流量探测</string>
@@ -84,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">在通知中顯示當前速度</string> <string name="summary_pref_speed_enabled">在通知中顯示當前速度\n小圖標顯示流量的路由情況</string>
<string name="title_pref_sniffing_enabled">啟用流量探測</string> <string name="title_pref_sniffing_enabled">啟用流量探測</string>
<string name="summary_pref_sniffing_enabled">流量探測</string> <string name="summary_pref_sniffing_enabled">流量探測</string>
@@ -66,4 +66,39 @@
<item>IPIfNonMatch</item> <item>IPIfNonMatch</item>
<item>IPOnDemand</item> <item>IPOnDemand</item>
</string-array> </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>
+2 -1
View File
@@ -84,7 +84,8 @@
<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</string> <string name="summary_pref_speed_enabled">Display current speed in the notification.\nNotification icon would change based on
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>
@@ -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" />
+6
View File
@@ -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