Compare commits

..

51 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
2dust
28639cc388 Merge pull request #468 from yuhan6665/cleanup-config
Remove legacy "connectionReuse" in config
2020-07-18 14:01:38 +08:00
2dust
ca254b2aa1 Merge pull request #443 from yuhan6665/resume-speed
Fix speed display after screen turns on
2020-07-18 14:00:48 +08:00
2dust
9721879713 Merge pull request #432 from yuhan6665/drawer-ui
fix highlight issue in drawer ui
2020-07-18 13:59:47 +08:00
2dust
623c1807c5 Merge pull request #409 from yuhan6665/test-cleanup
Tcp test cleanup
2020-07-18 13:58:37 +08:00
2dust
739fa88ba7 Merge pull request #335 from yuhan6665/sub-index
Fix selected index when update subscription
2020-07-18 13:58:11 +08:00
2dust
85d6f00f8c Merge pull request #474 from yuhan6665/dns-cache-timeout
Refresh prepared domain every 30 minutes
2020-07-18 09:48:16 +08:00
yuhan6665
8986710453 Cancel async block when user test tcping again
Change async code to use Kotlin coroutine instead of Anko,
since Anko is deprecated and coroutine is the recommanded
approach now.
2020-07-16 19:42:43 -04:00
yuhan6665
f54faacbf6 Close connecting sockets when user test tcping again 2020-07-16 19:42:43 -04:00
r23
993ee0b8d2 Optimize invoke timing of store config file after dragging item. 2020-06-29 01:24:19 +08:00
yuhan6665
903352ec9c Refresh prepared domain every 30 minutes 2020-06-19 23:42:02 -04:00
yuhan6665
ad56106c08 Remove legacy "connectionReuse" in config
Also auto-refactor the data class naming to be proper camelcase
2020-06-13 23:38:33 -04: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
2dust
6577c46a31 Merge pull request #425 from yuhan6665/translation
Fix a traditional Chinese translation
2020-05-31 16:13:35 +08:00
yuhan6665
c6dab001b2 Fix traditional Chinese translation 2020-05-30 12:34:29 -04:00
yuhan6665
aea8369b8a Fix speed display after screen turns on
Also, use the tab to make the text less jumpy
2020-05-29 22:54:55 -04:00
yuhan6665
92d2cb35c4 Make drawer item unchecked
Current checked item is not consistent with the active activity.
In fact, we don't need checked state. This is a standard behavior.
You can find in apps like Google Playstore.
2020-05-24 11:13:12 -04:00
yuhan6665
6ce3d540e8 Remove config item in drawer
The drawer is attached to MainActivity. When user see the drawer, the
MainActivity must be active. Showing an menu item to launch itself is
confusing.
2020-05-24 11:09:11 -04:00
2dust
c105d84b35 Merge pull request #300 from yuhan6665/master
Fix UI touch issues for Android TV
2020-04-12 10:04:52 +08:00
yuhan6665
8b149fb52f Fix selected index when update subscription 2020-04-03 18:58:41 -04:00
2dust
3ea04c076c Merge pull request #314 from yuhan6665/chrome-os
Add tags in AndroidManifest to support PlayStore filtering
2020-03-16 16:24:58 +08:00
2dust
98475460bf Merge pull request #313 from yuhan6665/lint-fix
Use annotation to fix lint error
2020-03-16 16:24:08 +08:00
yuhan6665
68ee61a753 Add tags in AndroidManifest to support PlayStore filtering
Google Playstore is using uses-feature and supports-screens
to filter devices.
By setting all screens to true and specifing features as
"not required" should make app compatible with more devices
Particularly it includes Chrome OS devices.
2020-03-14 14:07:31 -04:00
yuhan6665
90ba9ef2b7 Use annotation to fix lint error 2020-03-14 13:03:51 -04:00
yuhan6665
0ec114322e Fix UI touch issues for Android TV 2020-03-08 12:06:44 -04:00
2dust
76f52e7aa7 Merge pull request #287 from yuhan6665/multidex
Fix multidex for Android devices below L
2020-03-03 14:52:19 +08:00
2dust
caa25ce424 Merge pull request #278 from yuhan6665/master
Add delete log button in logcat
2020-03-03 14:52:08 +08:00
yuhan6665
e33d7e9bcf Fix multidex for Android devices below L 2020-02-29 10:51:58 -05:00
yuhan6665
e120fce0b7 Add delete log button in logcat
Also, automatically scroll to bottom of the log if it is too long
2020-02-22 12:01:16 -05:00
40 changed files with 465 additions and 213 deletions

8
.gitignore vendored
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/geosite.dat
V2rayNG/app/src/main/java/com/v2ray/ang/InappBuyActivity.java
V2rayNG/gradle/wrapper/gradle-wrapper.properties
V2rayNG/gradle/wrapper/gradle-wrapper.properties
*.dat
*.jks
V2rayNG/gradle/wrapper/gradle-wrapper.properties
V2rayNG/gradle/wrapper/gradle-wrapper.properties
V2rayNG/app/release/output.json
V2rayNG/app/release/output.json
.idea/
.gradle/

View File

@@ -116,7 +116,7 @@ func (v V2RayPoint) QueryStats(tag string, direct string) int64 {
if v.statsManager == nil {
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 {
return 0
}

View File

@@ -20,34 +20,17 @@ android {
versionName "1.0.2"
}
signingConfigs {
release {
storeFile file("../key.jks")
keyAlias 'ang'
keyPassword '123456'
storePassword '123456'
}
debug {
storeFile file("../key.jks")
keyAlias 'ang'
keyPassword '123456'
storePassword '123456'
}
}
buildTypes {
release {
minifyEnabled false
zipAlignEnabled false
shrinkResources false
signingConfig signingConfigs.release
// proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
debug {
minifyEnabled false
zipAlignEnabled false
shrinkResources false
signingConfig signingConfigs.release
}
}
@@ -84,6 +67,10 @@ dependencies {
implementation project(':dpreference')
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.2.2"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.2.2" // 1.3.x has compile error:
// More than one file was found with OS independent path 'META-INF/proguard/coroutines.pro'
// Android support library
implementation "com.android.support:support-v4:$supportLibVersion"
implementation "com.android.support:appcompat-v7:$supportLibVersion"
@@ -91,6 +78,7 @@ dependencies {
implementation "com.android.support:cardview-v7:$supportLibVersion"
implementation "com.android.support:preference-v7:$supportLibVersion"
implementation "com.android.support:recyclerview-v7:$supportLibVersion"
implementation "com.android.support:multidex:1.0.3"
// DSL
implementation "org.jetbrains.anko:anko-sdk15:$ankoVersion"
implementation "org.jetbrains.anko:anko-support-v4:$ankoVersion"
@@ -126,4 +114,4 @@ repositories {
flatDir {
dirs 'libs'
}
}
}

View File

@@ -2,6 +2,16 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.v2ray.ang">
<supports-screens
android:anyDensity="true"
android:smallScreens="true"
android:normalScreens="true"
android:largeScreens="true"
android:xlargeScreens="true"/>
<uses-feature android:name="android.hardware.camera" android:required="false"/>
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
<uses-permission android:name="android.permission.INTERNET" />
@@ -66,7 +76,10 @@
<activity android:name=".ui.SubEditActivity" />
<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
android:name=".service.V2RayVpnService"
@@ -83,16 +96,16 @@
android:value="true" />
</service>
<!--<receiver android:name=".receiver.WidgetProvider">-->
<!--<meta-data-->
<!--android:name="android.appwidget.provider"-->
<!--android:resource="@xml/app_widget_provider" />-->
<!--<intent-filter>-->
<!--<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />-->
<!--<action android:name="com.v2ray.ang.action.widget.click" />-->
<!--</intent-filter>-->
<!--</receiver>-->
<receiver android:name=".receiver.WidgetProvider"
android:process=":RunSoLibV2RayDaemon">
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/app_widget_provider" />
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
<action android:name="com.v2ray.ang.action.widget.click" />
</intent-filter>
</receiver>
<service
android:name=".service.QSTileService"

View File

@@ -1,3 +1,4 @@
android
com.android.chrome
com.google.android.googlequicksearchbox
com.google.android.apps.photos
@@ -193,4 +194,4 @@ tv.twitch.android.app
com.shanga.walli
com.whatsapp
com.wire
com.simplehabit.simplehabitapp
com.simplehabit.simplehabitapp

View File

@@ -13,8 +13,8 @@
}
},
"system": {
"statsInboundUplink": true,
"statsInboundDownlink": true
"statsOutboundUplink": true,
"statsOutboundDownlink": true
}
},
"inbounds": [{

View File

@@ -43,6 +43,8 @@ public interface ItemTouchHelperAdapter {
boolean onItemMove(int fromPosition, int toPosition);
void onItemMoveCompleted();
/**
* Called when an item has been dismissed by a swipe.<br/>
* <br/>

View File

@@ -112,6 +112,8 @@ public class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback {
public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
super.clearView(recyclerView, viewHolder);
mAdapter.onItemMoveCompleted();
viewHolder.itemView.setAlpha(ALPHA_FULL);
if (viewHolder instanceof ItemTouchHelperViewHolder) {

View File

@@ -1,12 +1,12 @@
package com.v2ray.ang
import android.app.Application
//import com.squareup.leakcanary.LeakCanary
import android.support.multidex.MultiDexApplication
import com.v2ray.ang.util.AngConfigManager
import me.dozen.dpreference.DPreference
import org.jetbrains.anko.defaultSharedPreferences
class AngApplication : Application() {
class AngApplication : MultiDexApplication() {
companion object {
const val PREF_LAST_VERSION = "pref_last_version"
}

View File

@@ -64,22 +64,21 @@ data class V2rayConfig(
data class StreamSettingsBean(var network: String,
var security: String,
var tcpSettings: TcpsettingsBean?,
var kcpsettings: KcpsettingsBean?,
var wssettings: WssettingsBean?,
var httpsettings: HttpsettingsBean?,
var tlssettings: TlssettingsBean?,
var quicsettings: QuicsettingBean?
var tcpSettings: TcpSettingsBean?,
var kcpSettings: KcpSettingsBean?,
var wsSettings: WsSettingsBean?,
var httpSettings: HttpSettingsBean?,
var tlsSettings: TlsSettingsBean?,
var quicSettings: QuicSettingBean?
) {
data class TcpsettingsBean(var connectionReuse: Boolean = true,
var header: HeaderBean = HeaderBean()) {
data class TcpSettingsBean(var header: HeaderBean = HeaderBean()) {
data class HeaderBean(var type: String = "none",
var request: Any? = null,
var response: Any? = null)
}
data class KcpsettingsBean(var mtu: Int = 1350,
data class KcpSettingsBean(var mtu: Int = 1350,
var tti: Int = 20,
var uplinkCapacity: Int = 12,
var downlinkCapacity: Int = 100,
@@ -90,20 +89,20 @@ data class V2rayConfig(
data class HeaderBean(var type: String = "none")
}
data class WssettingsBean(var connectionReuse: Boolean = true,
var path: String = "",
data class WsSettingsBean(var path: String = "",
var headers: HeadersBean = HeadersBean()) {
data class HeadersBean(var Host: String = "")
}
data class HttpsettingsBean(var host: List<String> = ArrayList<String>(), var path: String = "")
data class HttpSettingsBean(var host: List<String> = ArrayList(),
var path: String = "")
data class TlssettingsBean(var allowInsecure: Boolean = true,
data class TlsSettingsBean(var allowInsecure: Boolean = true,
var serverName: String = "")
data class QuicsettingBean(var security: String = "none",
var key: String = "",
var header: HeaderBean = HeaderBean()) {
data class QuicSettingBean(var security: String = "none",
var key: String = "",
var header: HeaderBean = HeaderBean()) {
data class HeaderBean(var type: String = "none")
}
}

View File

@@ -27,34 +27,37 @@ const val divisor = 1024F
fun Long.toSpeedString() = toTrafficString() + "/s"
fun Long.toTrafficString(): String {
if (this == 0L)
return "\t\t\t0\t B"
if (this < threshold)
return "$this B"
return "${this.toFloat().toShortString()}\t B"
val kib = this / divisor
if (kib < threshold)
return "${kib.toShortString()} KB"
return "${kib.toShortString()}\t KB"
val mib = kib / divisor
if (mib < threshold)
return "${mib.toShortString()} MB"
return "${mib.toShortString()}\t MB"
val gib = mib / divisor
if (gib < threshold)
return "${gib.toShortString()} GB"
return "${gib.toShortString()}\t GB"
val tib = gib / divisor
if (tib < threshold)
return "${tib.toShortString()} TB"
return "${tib.toShortString()}\t TB"
val pib = tib / divisor
if (pib < threshold)
return "${pib.toShortString()} PB"
return "${pib.toShortString()}\t PB"
return ""
}
private fun Float.toShortString(): String {
val s = toString()
val s = "%.2f".format(this)
if (s.length <= 4)
return s
return s.substring(0, 4).removeSuffix(".")

View File

@@ -3,14 +3,13 @@ package com.v2ray.ang.receiver
import android.app.PendingIntent
import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProvider
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.widget.RemoteViews
import com.v2ray.ang.R
import com.v2ray.ang.AppConfig
import com.v2ray.ang.util.Utils
import org.jetbrains.anko.toast
class WidgetProvider : AppWidgetProvider() {
/**
@@ -19,10 +18,21 @@ class WidgetProvider : AppWidgetProvider() {
override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
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 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)
remoteViews.setOnClickPendingIntent(R.id.layout_switch, pendingIntent)
if (isRunning) {
remoteViews.setInt(R.id.layout_switch, "setBackgroundResource", R.drawable.ic_rounded_corner_theme);
} else {
remoteViews.setInt(R.id.layout_switch, "setBackgroundResource", R.drawable.ic_rounded_corner_grey);
}
for (appWidgetId in appWidgetIds) {
appWidgetManager.updateAppWidget(appWidgetId, remoteViews)
@@ -44,7 +54,9 @@ class WidgetProvider : AppWidgetProvider() {
// context.toast(R.string.toast_services_start)
Utils.startVService(context)
}
val manager = AppWidgetManager.getInstance(context)
updateWidgetBackground(context, manager, manager.getAppWidgetIds(ComponentName(context, WidgetProvider::class.java)),
!isRunning);
}
}
}

View File

@@ -28,14 +28,9 @@ import libv2ray.Libv2ray
import libv2ray.V2RayVPNServiceSupportsSet
import rx.Observable
import rx.Subscription
import java.net.InetAddress
import java.io.IOException
import java.io.File
import java.io.FileDescriptor
import java.io.FileInputStream
import java.lang.ref.SoftReference
import android.os.Build
import android.annotation.TargetApi
import android.util.Log
import go.Seq
import org.jetbrains.anko.doAsync
@@ -45,6 +40,7 @@ class V2RayVpnService : VpnService() {
const val NOTIFICATION_ID = 1
const val NOTIFICATION_PENDING_INTENT_CONTENT = 0
const val NOTIFICATION_PENDING_INTENT_STOP_V2RAY = 1
const val NOTIFICATION_ICON_THRESHOLD = 3000
fun startV2Ray(context: Context) {
val intent = Intent(context.applicationContext, V2RayVpnService::class.java)
@@ -57,6 +53,7 @@ class V2RayVpnService : VpnService() {
}
private val v2rayPoint = Libv2ray.newV2RayPoint(V2RayCallback())
private var lastQueryTime = 0L
private lateinit var configContent: String
private lateinit var mInterface: ParcelFileDescriptor
val fd: Int get() = mInterface.fd
@@ -74,25 +71,28 @@ class V2RayVpnService : VpnService() {
*
* Source: https://android.googlesource.com/platform/frameworks/base/+/2df4c7d/services/core/java/com/android/server/ConnectivityService.java#887
*/
@TargetApi(28)
private val defaultNetworkRequest = NetworkRequest.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
.build()
private val defaultNetworkRequest by lazy @RequiresApi(Build.VERSION_CODES.P) {
NetworkRequest.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
.build()
}
private val connectivity by lazy { getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager }
@TargetApi(28)
private val defaultNetworkCallback = object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
setUnderlyingNetworks(arrayOf(network))
}
override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities?) {
// it's a good idea to refresh capabilities
setUnderlyingNetworks(arrayOf(network))
}
override fun onLost(network: Network) {
setUnderlyingNetworks(null)
private val defaultNetworkCallback by lazy @RequiresApi(Build.VERSION_CODES.P) {
object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
setUnderlyingNetworks(arrayOf(network))
}
override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities?) {
// it's a good idea to refresh capabilities
setUnderlyingNetworks(arrayOf(network))
}
override fun onLost(network: Network) {
setUnderlyingNetworks(null)
}
}
}
private var listeningForDefaultNetwork = false
@@ -131,6 +131,7 @@ class V2RayVpnService : VpnService() {
// Configure a builder while parsing the parameters.
val builder = Builder()
val enableLocalDns = defaultDPreference.getPrefBoolean(SettingsActivity.PREF_LOCAL_DNS_ENABLED, false)
val routingMode = defaultDPreference.getPrefString(SettingsActivity.PREF_ROUTING_MODE, "0")
parameters.split(" ")
.map { it.split(",") }
@@ -139,7 +140,20 @@ class V2RayVpnService : VpnService() {
'm' -> builder.setMtu(java.lang.Short.parseShort(it[1]).toInt())
's' -> builder.addSearchDomain(it[1])
'a' -> builder.addAddress(it[1], Integer.parseInt(it[2]))
'r' -> 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])
}
}
@@ -176,7 +190,7 @@ class V2RayVpnService : VpnService() {
}
if (Build.VERSION.SDK_INT >= 28) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
connectivity.requestNetwork(defaultNetworkRequest, defaultNetworkCallback)
listeningForDefaultNetwork = true
}
@@ -184,6 +198,7 @@ class V2RayVpnService : VpnService() {
// Create a new interface using the builder and save the parameters.
mInterface = builder.establish()
sendFd()
lastQueryTime = System.currentTimeMillis()
startSpeedNotification()
}
@@ -260,7 +275,7 @@ class V2RayVpnService : VpnService() {
// val emptyInfo = VpnNetworkInfo()
// val info = loadVpnNetworkInfo(configName, emptyInfo)!! + (lastNetworkInfo ?: emptyInfo)
// saveVpnNetworkInfo(configName, info)
if (Build.VERSION.SDK_INT >= 28) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
if (listeningForDefaultNetwork) {
connectivity.unregisterNetworkCallback(defaultNetworkCallback)
listeningForDefaultNetwork = false
@@ -359,9 +374,17 @@ class V2RayVpnService : VpnService() {
mSubscription = null
}
private fun updateNotification(contentText: String) {
private fun updateNotification(contentText: String, proxyTraffic: Long, directTraffic: Long) {
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())
}
}
@@ -382,13 +405,22 @@ class V2RayVpnService : VpnService() {
mSubscription = Observable.interval(3, java.util.concurrent.TimeUnit.SECONDS)
.subscribe {
val uplink = v2rayPoint.queryStats("socks", "uplink")
val downlink = v2rayPoint.queryStats("socks", "downlink")
val zero_speed = (uplink == 0L && downlink == 0L)
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()
if (!zero_speed || !last_zero_speed) {
updateNotification("${cf_name}${(uplink / 3).toSpeedString()}${(downlink / 3).toSpeedString()}")
val sinceLastQueryInSeconds = (queryTime - lastQueryTime) / 1000.0
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
lastQueryTime = queryTime
}
}
}
@@ -400,7 +432,7 @@ class V2RayVpnService : VpnService() {
mSubscription = null
val cf_name = defaultDPreference.getPrefString(AppConfig.PREF_CURR_CONFIG_NAME, "")
updateNotification(cf_name)
updateNotification(cf_name, 0, 0)
}
}

View File

@@ -55,7 +55,6 @@ abstract class BaseDrawerActivity : BaseActivity() {
this@BaseDrawerActivity, R.anim.fade_in, R.anim.fade_out).toBundle()
var activityClass: Class<*>? = null
when (mItemToOpenWhenDrawerCloses) {
R.id.server_profile -> activityClass = MainActivity::class.java
R.id.sub_setting -> activityClass = SubSettingActivity::class.java
R.id.settings -> activityClass = SettingsActivity::class.java
R.id.logcat -> {
@@ -195,9 +194,7 @@ abstract class BaseDrawerActivity : BaseActivity() {
true
}
if (MainActivity::class.java.isAssignableFrom(javaClass)) {
navigationView.setCheckedItem(R.id.server_profile)
} else if (SubSettingActivity::class.java.isAssignableFrom(javaClass)) {
if (SubSettingActivity::class.java.isAssignableFrom(javaClass)) {
navigationView.setCheckedItem(R.id.sub_setting)
} else if (SettingsActivity::class.java.isAssignableFrom(javaClass)) {
navigationView.setCheckedItem(R.id.settings)

View File

@@ -1,6 +1,8 @@
package com.v2ray.ang.ui
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.text.method.ScrollingMovementMethod
import android.view.Menu
import android.view.MenuItem
@@ -24,15 +26,22 @@ class LogcatActivity : BaseActivity() {
title = getString(R.string.title_logcat)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
logcat()
logcat(false)
}
private fun logcat() {
private fun logcat(shouldFlushLog: Boolean) {
try {
pb_waiting.visibility = View.VISIBLE
doAsync {
if (shouldFlushLog) {
val lst = LinkedHashSet<String>()
lst.add("logcat")
lst.add("-c")
val process = Runtime.getRuntime().exec(lst.toTypedArray())
process.waitFor()
}
val lst = LinkedHashSet<String>()
lst.add("logcat")
lst.add("-d")
@@ -49,9 +58,11 @@ class LogcatActivity : BaseActivity() {
tv_logcat.text = allText
tv_logcat.movementMethod = ScrollingMovementMethod()
pb_waiting.visibility = View.GONE
Handler(Looper.getMainLooper()).post { sv_logcat.fullScroll(View.FOCUS_DOWN) }
}
}
} catch (e: IOException) {
e.printStackTrace()
}
}
@@ -66,7 +77,10 @@ class LogcatActivity : BaseActivity() {
toast(R.string.toast_success)
true
}
R.id.delete -> {
logcat(true)
true
}
else -> super.onOptionsItemSelected(item)
}
}

View File

@@ -33,6 +33,7 @@ import rx.android.schedulers.AndroidSchedulers
import java.util.concurrent.TimeUnit
import com.v2ray.ang.helper.SimpleItemTouchHelperCallback
import com.v2ray.ang.util.AngConfigManager.configs
import kotlinx.coroutines.*
class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedListener {
companion object {
@@ -58,6 +59,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
private val adapter by lazy { MainRecyclerAdapter(this) }
private var mItemTouchHelper: ItemTouchHelper? = null
private val testingJobs = ArrayList<Job>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -240,18 +242,25 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
}
R.id.ping_all -> {
testingJobs.forEach {
it.cancel()
}
testingJobs.clear()
Utils.closeAllTcpSockets()
for (k in 0 until configs.vmess.count()) {
configs.vmess[k].testResult = ""
adapter.updateConfigList()
}
for (k in 0 until configs.vmess.count()) {
if (configs.vmess[k].configType != AppConfig.EConfigType.Custom) {
doAsync {
testingJobs.add(GlobalScope.launch(Dispatchers.IO) {
configs.vmess[k].testResult = Utils.tcping(configs.vmess[k].address, configs.vmess[k].port)
uiThread {
val myJob = coroutineContext[Job]
launch(Dispatchers.Main) {
testingJobs.remove(myJob)
adapter.updateSelectedItem(k)
}
}
})
}
}
true
@@ -570,4 +579,4 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
drawer_layout.closeDrawer(GravityCompat.START)
return true
}
}
}

View File

@@ -265,4 +265,8 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
updateSelectedItem(if (fromPosition < toPosition) fromPosition else toPosition)
return true
}
override fun onItemMoveCompleted() {
AngConfigManager.storeConfigFile()
}
}

View File

@@ -2,7 +2,6 @@ package com.v2ray.ang.util
import android.graphics.Bitmap
import android.text.TextUtils
import android.util.Log
import com.google.gson.Gson
import com.v2ray.ang.AngApplication
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.dto.AngConfig
import com.v2ray.ang.dto.VmessQRCode
import com.v2ray.ang.extension.defaultDPreference
import org.jetbrains.anko.toast
import java.net.URI
import java.net.URLDecoder
import java.util.*
import java.net.*
import java.math.BigInteger
object AngConfigManager {
private lateinit var app: AngApplication
@@ -104,16 +100,7 @@ object AngConfigManager {
angConfig.vmess.removeAt(index)
//移除的是活动的
if (angConfig.index == index) {
if (angConfig.vmess.count() > 0) {
angConfig.index = 0
} else {
angConfig.index = -1
}
} else if (index < angConfig.index)//移除活动之前的
{
angConfig.index--
}
adjustIndexForRemovalAt(index)
storeConfigFile()
} catch (e: Exception) {
@@ -123,6 +110,19 @@ object AngConfigManager {
return 0
}
private fun adjustIndexForRemovalAt(index: Int) {
if (angConfig.index == index) {
if (angConfig.vmess.count() > 0) {
angConfig.index = 0
} else {
angConfig.index = -1
}
} else if (index < angConfig.index)//移除活动之前的
{
angConfig.index--
}
}
fun swapServer(fromPosition: Int, toPosition: Int): Int {
try {
Collections.swap(angConfig.vmess, fromPosition, toPosition)
@@ -133,7 +133,7 @@ object AngConfigManager {
} else if (index == toPosition) {
angConfig.index = fromPosition
}
storeConfigFile()
//storeConfigFile()
} catch (e: Exception) {
e.printStackTrace()
return -1
@@ -241,7 +241,7 @@ object AngConfigManager {
/**
* import config form qrcode or...
*/
fun importConfig(server: String?, subid: String): Int {
fun importConfig(server: String?, subid: String, removedSelectedServer: AngConfig.VmessBean?): Int {
try {
if (server == null || TextUtils.isEmpty(server)) {
return R.string.toast_none_data
@@ -252,7 +252,11 @@ object AngConfigManager {
if (server.startsWith(VMESS_PROTOCOL)) {
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)
} else {
@@ -361,6 +365,12 @@ object AngConfigManager {
} else {
return R.string.toast_incorrect_protocol
}
if (removedSelectedServer != null &&
vmess.subid.equals(removedSelectedServer.subid) &&
vmess.address.equals(removedSelectedServer.address) &&
vmess.port.equals(removedSelectedServer.port)) {
setActiveServer(configs.vmess.count() - 1)
}
} catch (e: Exception) {
e.printStackTrace()
return -1
@@ -368,6 +378,61 @@ object AngConfigManager {
return 0
}
fun tryParseNewVmess(uri: String): AngConfig.VmessBean? {
return runCatching {
val uri = URI(uri)
check(uri.scheme == "vmess")
val (_, protocol, tlsStr, uuid, alterId) =
Regex("(tcp|http|ws|kcp|quic)(\\+tls)?:([0-9a-z]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12})-([0-9]+)")
.matchEntire(uri.userInfo)?.groupValues
?: error("parse user info fail.")
val tls = tlsStr.isNotBlank()
val queryParam = uri.rawQuery.split("&")
.map { it.split("=").let { (k, v) -> k to URLDecoder.decode(v, "utf-8")!! } }
.toMap()
val vmess = AngConfig.VmessBean()
vmess.address = uri.host
vmess.port = uri.port
vmess.id = uuid
vmess.alterId = alterId.toInt()
vmess.streamSecurity = if (tls) "tls" else ""
vmess.remarks = uri.fragment
vmess.security = "auto"
// TODO: allowInsecure not supported
when (protocol) {
"tcp" -> {
vmess.network = "tcp"
vmess.headerType = queryParam["type"] ?: "none"
vmess.requestHost = queryParam["host"] ?: ""
}
"http" -> {
vmess.network = "h2"
vmess.path = queryParam["path"]?.takeIf { it.trim() != "/" } ?: ""
vmess.requestHost = queryParam["host"]?.split("|")?.get(0) ?: ""
}
"ws" -> {
vmess.network = "ws"
vmess.path = queryParam["path"]?.takeIf { it.trim() != "/" } ?: ""
vmess.requestHost = queryParam["host"]?.split("|")?.get(0) ?: ""
}
"kcp" -> {
vmess.network = "kcp"
vmess.headerType = queryParam["type"] ?: "none"
vmess.path = queryParam["seed"] ?: ""
}
"quic" -> {
vmess.network = "quic"
vmess.requestHost = queryParam["security"] ?: "none"
vmess.headerType = queryParam["type"] ?: "none"
vmess.path = queryParam["key"] ?: ""
}
}
vmess
}.getOrNull()
}
private fun ResolveVmess4Kitsunebi(server: String): AngConfig.VmessBean {
val vmess = AngConfig.VmessBean()
@@ -728,6 +793,11 @@ object AngConfigManager {
if (servers == null) {
return 0
}
val removedSelectedServer =
if (!TextUtils.isEmpty(subid) && configs.vmess.count() > 0 && configs.vmess[configs.index].subid.equals(subid))
configs.vmess[configs.index]
else
null
removeServerViaSubid(subid)
// var servers = server
@@ -738,7 +808,7 @@ object AngConfigManager {
var count = 0
servers.lines()
.forEach {
val resId = importConfig(it, subid)
val resId = importConfig(it, subid, removedSelectedServer)
if (resId == 0) {
count++
}
@@ -778,6 +848,7 @@ object AngConfigManager {
for (k in configs.vmess.count() - 1 downTo 0) {
if (configs.vmess[k].subid.equals(subid)) {
angConfig.vmess.removeAt(k)
adjustIndexForRemovalAt(k)
}
}
@@ -823,4 +894,4 @@ object AngConfigManager {
return 0
}
}
}

View File

@@ -16,7 +16,7 @@ object AppManagerUtil {
val apps = ArrayList<AppInfo>()
for (pkg in packages) {
if (!pkg.hasInternetPermission) continue
if (!pkg.hasInternetPermission && pkg.packageName != "android") continue
val applicationInfo = pkg.applicationInfo
@@ -40,4 +40,4 @@ object AppManagerUtil {
val permissions = requestedPermissions
return permissions?.any { it == Manifest.permission.INTERNET } ?: false
}
}
}

View File

@@ -31,6 +31,7 @@ import com.v2ray.ang.extension.v2RayApplication
import com.v2ray.ang.service.V2RayVpnService
import com.v2ray.ang.ui.SettingsActivity
import kotlinx.android.synthetic.main.activity_logcat.*
import kotlinx.coroutines.isActive
import me.dozen.dpreference.DPreference
import org.jetbrains.anko.toast
import org.jetbrains.anko.uiThread
@@ -44,10 +45,13 @@ import java.util.regex.Pattern
import java.math.BigInteger
import java.util.concurrent.TimeUnit
import libv2ray.Libv2ray
import kotlin.coroutines.coroutineContext
object Utils {
val tcpTestingSockets = ArrayList<Socket?>()
/**
* convert string to editalbe for kotlin
*
@@ -483,10 +487,13 @@ object Utils {
/**
* tcping
*/
fun tcping(url: String, port: Int): String {
suspend fun tcping(url: String, port: Int): String {
var time = -1L
for (k in 0 until 2) {
val one = socketConnectTime(url, port)
if (!coroutineContext.isActive) {
break
}
if (one != -1L )
if(time == -1L || one < time) {
time = one
@@ -497,19 +504,35 @@ object Utils {
fun socketConnectTime(url: String, port: Int): Long {
try {
val socket = Socket()
synchronized(this) {
tcpTestingSockets.add(socket)
}
val start = System.currentTimeMillis()
val socket = Socket(url, port)
socket.connect(InetSocketAddress(url, port))
val time = System.currentTimeMillis() - start
synchronized(this) {
tcpTestingSockets.remove(socket)
}
socket.close()
return time
} catch (e: UnknownHostException) {
e.printStackTrace()
} catch (e: IOException) {
e.printStackTrace()
Log.d(AppConfig.ANG_PACKAGE, "socketConnectTime IOException: $e")
} catch (e: Exception) {
e.printStackTrace()
}
return -1
}
fun closeAllTcpSockets() {
synchronized(this) {
tcpTestingSockets.forEach {
it?.close()
}
tcpTestingSockets.clear()
}
}
}

View File

@@ -9,7 +9,6 @@ import com.v2ray.ang.AngApplication
import com.v2ray.ang.AppConfig
import com.v2ray.ang.dto.AngConfig.VmessBean
import com.v2ray.ang.dto.V2rayConfig
import com.v2ray.ang.extension.defaultDPreference
import com.v2ray.ang.ui.SettingsActivity
import org.json.JSONException
import org.json.JSONObject
@@ -246,7 +245,7 @@ object V2rayConfigUtil {
//streamSettings
when (streamSettings.network) {
"kcp" -> {
val kcpsettings = V2rayConfig.OutboundBean.StreamSettingsBean.KcpsettingsBean()
val kcpsettings = V2rayConfig.OutboundBean.StreamSettingsBean.KcpSettingsBean()
kcpsettings.mtu = 1350
kcpsettings.tti = 50
kcpsettings.uplinkCapacity = 12
@@ -254,34 +253,33 @@ object V2rayConfigUtil {
kcpsettings.congestion = false
kcpsettings.readBufferSize = 1
kcpsettings.writeBufferSize = 1
kcpsettings.header = V2rayConfig.OutboundBean.StreamSettingsBean.KcpsettingsBean.HeaderBean()
kcpsettings.header = V2rayConfig.OutboundBean.StreamSettingsBean.KcpSettingsBean.HeaderBean()
kcpsettings.header.type = vmess.headerType
streamSettings.kcpsettings = kcpsettings
streamSettings.kcpSettings = kcpsettings
}
"ws" -> {
val wssettings = V2rayConfig.OutboundBean.StreamSettingsBean.WssettingsBean()
wssettings.connectionReuse = true
val wssettings = V2rayConfig.OutboundBean.StreamSettingsBean.WsSettingsBean()
val host = vmess.requestHost.trim()
val path = vmess.path.trim()
if (!TextUtils.isEmpty(host)) {
wssettings.headers = V2rayConfig.OutboundBean.StreamSettingsBean.WssettingsBean.HeadersBean()
wssettings.headers = V2rayConfig.OutboundBean.StreamSettingsBean.WsSettingsBean.HeadersBean()
wssettings.headers.Host = host
}
if (!TextUtils.isEmpty(path)) {
wssettings.path = path
}
streamSettings.wssettings = wssettings
streamSettings.wsSettings = wssettings
val tlssettings = V2rayConfig.OutboundBean.StreamSettingsBean.TlssettingsBean()
val tlssettings = V2rayConfig.OutboundBean.StreamSettingsBean.TlsSettingsBean()
tlssettings.allowInsecure = true
if (!TextUtils.isEmpty(host)) {
tlssettings.serverName = host
}
streamSettings.tlssettings = tlssettings
streamSettings.tlsSettings = tlssettings
}
"h2" -> {
val httpsettings = V2rayConfig.OutboundBean.StreamSettingsBean.HttpsettingsBean()
val httpsettings = V2rayConfig.OutboundBean.StreamSettingsBean.HttpSettingsBean()
val host = vmess.requestHost.trim()
val path = vmess.path.trim()
@@ -289,31 +287,30 @@ object V2rayConfigUtil {
httpsettings.host = host.split(",").map { it.trim() }
}
httpsettings.path = path
streamSettings.httpsettings = httpsettings
streamSettings.httpSettings = httpsettings
val tlssettings = V2rayConfig.OutboundBean.StreamSettingsBean.TlssettingsBean()
val tlssettings = V2rayConfig.OutboundBean.StreamSettingsBean.TlsSettingsBean()
tlssettings.allowInsecure = true
streamSettings.tlssettings = tlssettings
streamSettings.tlsSettings = tlssettings
}
"quic" -> {
val quicsettings = V2rayConfig.OutboundBean.StreamSettingsBean.QuicsettingBean()
val quicsettings = V2rayConfig.OutboundBean.StreamSettingsBean.QuicSettingBean()
val host = vmess.requestHost.trim()
val path = vmess.path.trim()
quicsettings.security = host
quicsettings.key = path
quicsettings.header = V2rayConfig.OutboundBean.StreamSettingsBean.QuicsettingBean.HeaderBean()
quicsettings.header = V2rayConfig.OutboundBean.StreamSettingsBean.QuicSettingBean.HeaderBean()
quicsettings.header.type = vmess.headerType
streamSettings.quicsettings = quicsettings
streamSettings.quicSettings = quicsettings
}
else -> {
//tcp带http伪装
if (vmess.headerType == "http") {
val tcpSettings = V2rayConfig.OutboundBean.StreamSettingsBean.TcpsettingsBean()
tcpSettings.connectionReuse = true
tcpSettings.header = V2rayConfig.OutboundBean.StreamSettingsBean.TcpsettingsBean.HeaderBean()
val tcpSettings = V2rayConfig.OutboundBean.StreamSettingsBean.TcpSettingsBean()
tcpSettings.header = V2rayConfig.OutboundBean.StreamSettingsBean.TcpSettingsBean.HeaderBean()
tcpSettings.header.type = vmess.headerType
// if (requestObj.has("headers")

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true"
android:drawable="@color/secondary_text" />
<item android:state_focused="true"
android:drawable="@color/secondary_text" />
<item android:state_hovered="true"
android:drawable="@color/secondary_text" />
<item android:drawable="@color/colorPrimary_text" />
</selector>

View File

@@ -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>

View File

@@ -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

View File

@@ -54,8 +54,10 @@
android:id="@+id/layout_test"
android:layout_width="match_parent"
android:layout_height="@dimen/connection_test_height"
android:background="@color/colorPrimary_text"
android:gravity="center|left">
android:background="@drawable/background_test_button"
android:gravity="center|left"
android:clickable="true"
android:focusable="true">
<TextView
android:id="@+id/tv_test_state"
@@ -92,6 +94,8 @@
android:layout_marginLeft="16dp"
android:layout_marginTop="16dp"
android:src="@drawable/ic_v_idle"
android:clickable="true"
android:focusable="true"
app:layout_anchorGravity="bottom|right|end" />
</com.github.jorgecastilloprz.FABProgressCircle>

View File

@@ -2,7 +2,10 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical">
android:gravity="center_vertical"
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true">
<android.support.v7.widget.AppCompatImageView
android:id="@+id/icon"
@@ -40,6 +43,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="false"
android:focusable="false"
android:paddingStart="2dp"
android:paddingLeft="2dp"
android:paddingEnd="6dp"

View File

@@ -9,10 +9,6 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="3dp"
android:clickable="true"
android:focusable="true"
android:foreground="?android:attr/selectableItemBackground"
android:orientation="horizontal"
card_view:cardCornerRadius="5dp">
<LinearLayout
@@ -21,7 +17,11 @@
android:layout_height="@dimen/server_height"
android:layout_gravity="center"
android:gravity="center"
android:orientation="horizontal">
android:orientation="horizontal"
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:nextFocusRight="@+id/layout_share">
<LinearLayout
android:layout_width="wrap_content"
@@ -111,10 +111,14 @@
<LinearLayout
android:id="@+id/layout_share"
android:layout_width="wrap_content"
android:layout_height="@dimen/server_height"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:paddingEnd="@dimen/layout_margin_right_height">
android:padding="@dimen/layout_margin_spacing"
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:nextFocusLeft="@+id/info_container">
<ImageView
android:layout_width="wrap_content"
@@ -126,10 +130,13 @@
<LinearLayout
android:id="@+id/layout_edit"
android:layout_width="wrap_content"
android:layout_height="@dimen/server_height"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:paddingEnd="@dimen/layout_margin_right_height">
android:padding="@dimen/layout_margin_spacing"
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true">
<ImageView
android:layout_width="@dimen/png_height"
@@ -141,10 +148,13 @@
<LinearLayout
android:id="@+id/layout_remove"
android:layout_width="wrap_content"
android:layout_height="@dimen/server_height"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:paddingEnd="@dimen/layout_margin_right_height">
android:padding="@dimen/layout_margin_spacing"
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true">
<ImageView
android:layout_width="@dimen/png_height"

View File

@@ -6,6 +6,7 @@
android:gravity="center_vertical">
<android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:id="@+id/item_cardview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="3dp"
@@ -13,7 +14,8 @@
android:focusable="true"
android:foreground="?android:attr/selectableItemBackground"
android:orientation="horizontal"
card_view:cardCornerRadius="5dp">
card_view:cardCornerRadius="5dp"
android:nextFocusRight="@+id/layout_edit">
<LinearLayout
android:id="@+id/info_container"
@@ -62,11 +64,14 @@
<LinearLayout
android:id="@+id/layout_share"
android:layout_width="wrap_content"
android:layout_height="@dimen/server_height"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical"
android:paddingEnd="@dimen/layout_margin_right_height"
android:visibility="invisible">
android:padding="@dimen/layout_margin_spacing"
android:visibility="invisible"
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true">
<ImageView
android:layout_width="@dimen/png_height"
@@ -78,10 +83,14 @@
<LinearLayout
android:id="@+id/layout_edit"
android:layout_width="wrap_content"
android:layout_height="@dimen/server_height"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical"
android:paddingEnd="@dimen/layout_margin_right_height">
android:padding="@dimen/layout_margin_spacing"
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:nextFocusLeft="@+id/item_cardview">
<ImageView
android:layout_width="@dimen/png_height"

View File

@@ -1,12 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:orientation="vertical">
<LinearLayout
android:id="@+id/layout_switch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -14,16 +7,9 @@
android:gravity="center"
android:orientation="vertical">
<ImageView
<ImageView
android:layout_width="40dp"
android:layout_height="40dp"
android:src="@mipmap/ic_launcher" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:text="@string/app_widget_name"
android:textAppearance="@style/TextAppearance.AppCompat.Small" />
</LinearLayout>
android:padding="10dp"
android:src="@drawable/ic_v" />
</LinearLayout>

View File

@@ -4,12 +4,7 @@
tools:showIn="navigation_view">
<group
android:id="@+id/group_main"
android:checkableBehavior="single">
<item
android:id="@+id/server_profile"
android:icon="@drawable/ic_description_white_24dp"
android:title="@string/title_server" />
android:id="@+id/group_main">
<item
android:id="@+id/sub_setting"
android:icon="@drawable/ic_subscriptions_white_24dp"

View File

@@ -6,4 +6,9 @@
android:icon="@drawable/ic_copy_white"
android:title="@string/logcat_copy"
app:showAsAction="always" />
<item
android:id="@+id/delete"
android:icon="@drawable/ic_delete_white_24dp"
android:title="@string/logcat_delete"
app:showAsAction="ifRoom" />
</menu>

View File

@@ -83,7 +83,7 @@
<string name="summary_pref_mux_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="summary_pref_sniffing_enabled">流量探测</string>
@@ -135,6 +135,7 @@
<string name="title_logcat">Logcat</string>
<string name="logcat_copy">复制</string>
<string name="logcat_delete">删除</string>
<string name="title_export_all">导出全部配置至剪贴板</string>
<string name="title_sub_setting">订阅设置</string>
<string name="sub_setting_remarks">备注</string>

View File

@@ -17,7 +17,7 @@
<string name="toast_services_failure">啟動服務失敗</string>
<!--ServerActivity-->
<string name="title_server">組態</string>
<string name="title_server">設定檔</string>
<string name="menu_item_add_config">新增組態</string>
<string name="menu_item_save_config">儲存組態</string>
<string name="menu_item_del_config">刪除組態</string>
@@ -57,7 +57,7 @@
<string name="title_file_chooser">選取一個設定檔</string>
<string name="toast_require_file_manager">請安裝檔案總管。</string>
<string name="server_customize_config">自訂組態</string>
<string name="toast_config_file_invalid">無效組態</string>
<string name="toast_config_file_invalid">無效設定檔</string>
<string name="server_lab_content">內容</string>
<string name="toast_none_data_clipboard">剪貼簿內無資料</string>
<string name="toast_invalid_url">網址無效</string>
@@ -84,7 +84,7 @@
<string name="summary_pref_mux_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="summary_pref_sniffing_enabled">流量探測</string>
@@ -104,17 +104,17 @@
<string name="summary_pref_donate">向開發人員捐款</string>
<string name="donate_detail">新增一些實驗性進階功能</string>
<string name="title_pref_remote_dns">遠端DNS (可)</string>
<string name="title_pref_remote_dns">遠端DNS (可)</string>
<string name="summary_pref_remote_dns">DNS</string>
<string name="title_pref_domestic_dns">境内DNS (可僅本地DNS模式下生效)</string>
<string name="title_pref_domestic_dns">境内DNS (可僅本地DNS模式下生效)</string>
<string name="summary_pref_domestic_dns">DNS</string>
<string name="title_pref_socks_port">SOCKS5代理端口</string>
<string name="summary_pref_socks_port">SOCKS5代理端口</string>
<string name="title_pref_socks_port">SOCKS5代理連接埠</string>
<string name="summary_pref_socks_port">SOCKS5代理連接埠</string>
<string name="title_pref_http_port">HTTP代理端口</string>
<string name="summary_pref_http_port">HTTP代理端口</string>
<string name="title_pref_http_port">HTTP代理連接埠</string>
<string name="summary_pref_http_port">HTTP代理連接埠</string>
<string name="title_pref_feedback">回饋</string>
@@ -137,6 +137,7 @@
<string name="title_logcat">Logcat</string>
<string name="logcat_copy">複製</string>
<string name="logcat_delete">刪除</string>
<string name="title_export_all">匯出全部配置至剪貼簿</string>
<string name="title_sub_setting">訂閱設定</string>
<string name="sub_setting_remarks">備註</string>
@@ -153,7 +154,7 @@
<string name="routing_settings_delete">清除</string>
<string name="routing_settings_scan_replace">掃描並取代</string>
<string name="routing_settings_scan_append">掃描並附加</string>
<string name="routing_settings_default_rules">設置默認路由規則</string>
<string name="routing_settings_default_rules">設置預設路由規則</string>
<string name="connection_test_pending">"檢查連線能力"</string>
<string name="connection_test_testing">"測試中……"</string>
@@ -178,13 +179,13 @@
<string-array name="routing_mode">
<item>全球</item>
<item>略過域網</item>
<item>略過域網</item>
<item>略過中國大陸</item>
<item>略過域網及中國大陸</item>
<item>略過域網及中國大陸</item>
</string-array>
<string name="title_pref_proxy_sharing_enabled">代理共享</string>
<string name="summary_pref_proxy_sharing_enabled">綁定代理入口ip到0.0.0.0</string>
<string name="toast_warning_pref_proxysharing">其他設備可以使用socks/http協通過您的IP地址連接到代理\nHttp 代理: http://您的ip:10809\nSocks 代理: socks(4/5)://您的ip:10808\n僅在受信任的網中啟用以避免未經授權的連接</string>
<string name="toast_warning_pref_proxysharing_short">代理共享已啟用,請確保處於受信網</string>
<string name="toast_warning_pref_proxysharing">其他設備可以使用socks/http協通過您的IP地址連接到代理\nHttp 代理: http://您的ip:10809\nSocks 代理: socks(4/5)://您的ip:10808\n僅在受信任的網中啟用以避免未經授權的連接</string>
<string name="toast_warning_pref_proxysharing_short">代理共享已啟用,請確保處於受信網</string>
<string name="toast_malformed_josn">配置格式錯誤</string>
</resources>

View File

@@ -66,4 +66,39 @@
<item>IPIfNonMatch</item>
<item>IPOnDemand</item>
</string-array>
<!-- minimum list https://serverfault.com/a/304791 -->
<string-array name="bypass_private_ip_address" translatable="false">
<item>0.0.0.0/5</item>
<item>8.0.0.0/7</item>
<item>11.0.0.0/8</item>
<item>12.0.0.0/6</item>
<item>16.0.0.0/4</item>
<item>32.0.0.0/3</item>
<item>64.0.0.0/2</item>
<item>128.0.0.0/3</item>
<item>160.0.0.0/5</item>
<item>168.0.0.0/6</item>
<item>172.0.0.0/12</item>
<item>172.32.0.0/11</item>
<item>172.64.0.0/10</item>
<item>172.128.0.0/9</item>
<item>173.0.0.0/8</item>
<item>174.0.0.0/7</item>
<item>176.0.0.0/4</item>
<item>192.0.0.0/9</item>
<item>192.128.0.0/11</item>
<item>192.160.0.0/13</item>
<item>192.169.0.0/16</item>
<item>192.170.0.0/15</item>
<item>192.172.0.0/14</item>
<item>192.176.0.0/12</item>
<item>192.192.0.0/10</item>
<item>193.0.0.0/8</item>
<item>194.0.0.0/7</item>
<item>196.0.0.0/6</item>
<item>200.0.0.0/5</item>
<item>208.0.0.0/4</item>
<item>224.0.0.0/3</item>
</string-array>
</resources>

View File

@@ -3,6 +3,7 @@
<dimen name="bypass_list_header_height">50dp</dimen>
<dimen name="layout_margin_top_height">16dp</dimen>
<dimen name="layout_margin_right_height">16dp</dimen>
<dimen name="layout_margin_spacing">8dp</dimen>
<dimen name="edit_height">50dp</dimen>
<dimen name="png_height">24dp</dimen>
<dimen name="server_height">72dp</dimen>

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="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="summary_pref_sniffing_enabled">Sniffing</string>
@@ -136,6 +137,7 @@
<string name="title_logcat">Logcat</string>
<string name="logcat_copy">Copy</string>
<string name="logcat_delete">Delete</string>
<string name="title_export_all">Export all config to clipboard</string>
<string name="title_sub_setting">Subscription setting</string>
<string name="sub_setting_remarks">remarks</string>

View File

@@ -13,6 +13,12 @@
<item name="windowNoTitle">true</item>
</style>
<style name="AppTheme.NoActionBar.Translucent">
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:colorBackgroundCacheHint">@null</item>
<item name="android:windowIsTranslucent">true</item>
</style>
<style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />

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