Compare commits

...

32 Commits
1.4.7 ... 1.5.1

Author SHA1 Message Date
2dust
b3074e9697 Merge pull request #763 from yuhan6665/memory-optimization
Slightly improve memory by reduce unnecessary DPreference usage
2020-11-29 09:31:54 +08:00
yuhan6665
513ebcfa23 Slightly improve memory by reduce unnecessary DPreference usage
See more details in _Ext.kt.
In the future, change will be made to our config storage, so that
the service started through TileService/Widget/ScSwitchActivity will
also not launch main process. That will greatly reduce memory usage
2020-11-28 18:08:50 -05:00
2dust
50d9057f1a Merge pull request #750 from yuhan6665/viewmodel-test
Add ViewModel
2020-11-16 13:22:08 +08:00
2dust
2a563e7884 Merge pull request #747 from liyufan/new_template
Use new issue template
2020-11-16 13:21:40 +08:00
2dust
c69cd18842 Merge pull request #732 from rurirei/isTun2socksRunningi
isTun2socksRunning bool
2020-11-16 13:20:48 +08:00
2dust
7f2ced85a8 Merge pull request #730 from rurirei/stoploop
stoploop update
2020-11-16 13:18:46 +08:00
yuhan6665
6c5eef99b5 Move tests and broadcast listener to ViewModel 2020-11-15 18:37:46 -05:00
yuhan6665
d7c3bae8cc Add MainViewModel
ViewModel is the recommend approach for asynchronous loading
for Activity.
The variable stays even if the Activity is killed temporarily.
2020-11-15 18:37:46 -05:00
LYF
57c98f7c50 Use new issue template 2020-11-13 22:21:35 +08:00
rurirei
49be23c56a bool in return 2020-11-08 11:40:02 +08:00
rurirei
9b658e9a22 IsTun2socksRunning bool 2020-11-08 11:34:49 +08:00
rurirei
aaa84d081f stoploop update 2020-11-08 10:53:23 +08:00
2dust
5628cbee3a Merge pull request #725 from mzz2017/patch-1
fix: the problem that importing SS URL not support standard format
2020-11-05 14:30:56 +08:00
2dust
3dd663d927 Merge pull request #698 from yuhan6665/master
Release AndroidLibV2rayLite 22
2020-11-05 14:30:41 +08:00
mzz
9e49a2dbd9 fix: the problem that importing SS URL not support standard format 2020-11-02 12:37:26 +08:00
2dust
6c199c1687 Merge pull request #662 from yuhan6665/ext-geo
Add external geofile replacement help
2020-10-24 13:35:21 +08:00
yuhan6665
39af5fdb86 Release AndroidLibV2rayLite 22 2020-10-21 21:31:23 -04:00
2dust
7b2794f6be Merge pull request #695 from rurirei/proxyOnly
proxyOnly update
2020-10-22 07:59:19 +08:00
2dust
411d9e5c9a Merge pull request #691 from rurirei/api30
target 30
2020-10-22 07:58:41 +08:00
Rurirei
a57aee9424 proxyOnly update
* do not run Tun2socks under proxyOnly
2020-10-21 15:19:55 +08:00
Rurirei
4602afc67e call stopV2ray on onDestroy 2020-10-21 15:14:44 +08:00
Rurirei
ceb29840f2 BuildTools 30 fix 2020-10-20 09:49:16 +08:00
Rurirei
1c1f130ca7 update badge 2020-10-20 09:42:48 +08:00
Rurirei
afa0eb9375 package visibility 2020-10-20 07:20:37 +08:00
Rurirei
49b682f0f3 target 30
* update dependencies
2020-10-20 07:10:19 +08:00
Rurirei
d5def3bf2f useless anko 2020-10-20 07:06:36 +08:00
2dust
51b97b64f2 Merge pull request #670 from yuhan6665/fix-gson
Fix crash when custom config cannot be parsed by Gson
2020-10-15 08:18:59 +08:00
2dust
3af9ce1a1a Merge pull request #657 from rurirei/native
native fixes
2020-10-15 08:18:48 +08:00
yuhan6665
a5d3dda941 Fix crash when custom config cannot be parsed by Gson 2020-10-14 11:28:32 -04:00
yuhan6665
71a3e300d8 Add external geofile replacement help 2020-10-09 21:34:15 -04:00
Rurirei
030b9a3900 native fixes
* call sendFd() before tun2socks start
2020-10-08 15:50:51 +08:00
Rurirei
24d105a53c catch logs to checkout this error 2020-10-08 15:49:30 +08:00
22 changed files with 261 additions and 209 deletions

View File

@@ -1,3 +1,8 @@
---
name: v2rayNG程序问题
about: 创建一个报告来帮助我们改进
---
在提出问题前请先自行排除服务器端问题,同时也请通过搜索确认是否有人提出过相同问题。
@@ -14,7 +19,8 @@
### 日志信息
<details>
通过 `adb logcat -s com.v2ray.ang GoLog V2rayConfigUtilGoLog Main` 获取日志。请自行删减日志中可能出现的敏感信息。
通过`adb logcat -s com.v2ray.ang GoLog V2rayConfigUtilGoLog Main`获取日志。请自行删减日志中可能出现的敏感信息。
如果问题可重现,建议先执行`adb logcat -c`清空系统日志再执行上述命令,再操作重现问题。
```

5
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: V2Ray程序问题
url: https://github.com/v2fly/v2ray-core/
about: 如果您有V2Ray而非v2rayNG的问题请至这个链接讨论。

View File

@@ -6,6 +6,7 @@ import (
type Status struct {
IsRunning bool
IsTRunning bool
PackageName string
PackageCodePath string
@@ -13,7 +14,7 @@ type Status struct {
}
func CheckVersion() int {
return 21
return 22
}
func (v *Status) GetDataDir() string {

View File

@@ -3,14 +3,13 @@ package Escort
import (
"os"
"os/exec"
"time"
"log"
"github.com/2dust/AndroidLibV2rayLite/CoreI"
)
func (v *Escorting) EscortRun(proc string, pt []string, additionalEnv string, sendFd func() int) {
func (v *Escorting) EscortRun(proc string, pt []string, additionalEnv string) {
log.Println(proc, pt)
count := 0
for count <= 42 {
@@ -37,13 +36,6 @@ func (v *Escorting) EscortRun(proc string, pt []string, additionalEnv string, se
*v.escortProcess = append(*v.escortProcess, cmd.Process)
log.Println("EscortRun Waiting....")
if count > 0 {
go func() {
time.Sleep(time.Second)
sendFd()
}()
}
if err := cmd.Wait(); err != nil {
log.Println("EscortRun cmd.Wait err:", err)
}

View File

@@ -48,6 +48,7 @@ type V2RayPoint struct {
ConfigureFileContent string
EnableLocalDNS bool
ForwardIpv6 bool
ProxyOnly bool
}
/*V2RayVPNServiceSupportsSet To support Android VPN mode*/
@@ -57,7 +58,6 @@ type V2RayVPNServiceSupportsSet interface {
Shutdown() int
Protect(int) int
OnEmitStatus(int, string) int
SendFd() int
}
/*RunLoop Run V2Ray main loop
@@ -81,7 +81,6 @@ func (v *V2RayPoint) RunLoop() (err error) {
if !v.dialer.IsVServerReady() {
log.Println("vServer cannot resolved, shutdown")
v.StopLoop()
v.SupportSet.Shutdown()
}
// stop waiting if manually closed
@@ -100,9 +99,7 @@ func (v *V2RayPoint) StopLoop() (err error) {
v.v2rayOP.Lock()
defer v.v2rayOP.Unlock()
if v.status.IsRunning {
close(v.closeChan)
v.shutdownInit()
v.SupportSet.OnEmitStatus(0, "Closed")
}
return
}
@@ -112,6 +109,10 @@ func (v *V2RayPoint) GetIsRunning() bool {
return v.status.IsRunning
}
func (v *V2RayPoint) GetIsTRunning() bool {
return v.status.IsTRunning
}
//Delegate Funcation
func (v V2RayPoint) QueryStats(tag string, direct string) int64 {
if v.statsManager == nil {
@@ -125,24 +126,19 @@ func (v V2RayPoint) QueryStats(tag string, direct string) int64 {
}
func (v *V2RayPoint) shutdownInit() {
v.status.IsRunning = false
close(v.closeChan)
v.statsManager = nil
v.status.Vpoint.Close()
v.status.Vpoint = nil
v.statsManager = nil
v.status.IsRunning = false
v.escorter.EscortingDown()
v.SupportSet.Shutdown()
v.SupportSet.OnEmitStatus(0, "Closed")
}
func (v *V2RayPoint) pointloop() error {
if err := v.runTun2socks(); err != nil {
log.Println(err)
return err
}
log.Printf("EnableLocalDNS: %v\nForwardIpv6: %v\nDomainName: %s",
v.EnableLocalDNS,
v.ForwardIpv6,
v.DomainName)
log.Println("loading v2ray config")
config, err := v2serial.LoadJSONConfig(strings.NewReader(v.ConfigureFileContent))
if err != nil {
@@ -170,6 +166,21 @@ func (v *V2RayPoint) pointloop() error {
v.SupportSet.Prepare()
v.SupportSet.Setup(v.status.GetVPNSetupArg(v.EnableLocalDNS, v.ForwardIpv6))
v.SupportSet.OnEmitStatus(0, "Running")
v.status.IsTRunning = false
if !v.ProxyOnly {
if err := v.runTun2socks(); err != nil {
log.Println(err)
return err
}
v.status.IsTRunning = true
log.Printf("EnableLocalDNS: %v\nForwardIpv6: %v\nDomainName: %s",
v.EnableLocalDNS,
v.ForwardIpv6,
v.DomainName)
}
return nil
}
@@ -229,8 +240,7 @@ func (v V2RayPoint) runTun2socks() error {
v.escorter.EscortingUp()
go v.escorter.EscortRun(
v.status.GetApp("libtun2socks.so"),
v.status.GetTun2socksArgs(v.EnableLocalDNS, v.ForwardIpv6), "",
v.SupportSet.SendFd)
v.status.GetTun2socksArgs(v.EnableLocalDNS, v.ForwardIpv6), "")
return nil
}

View File

@@ -3,7 +3,7 @@
A V2Ray client for Android
[![API](https://img.shields.io/badge/API-17%2B-yellow.svg?style=flat)](https://developer.android.com/about/versions/jelly-bean#android-4.2)
[![Kotlin Version](https://img.shields.io/badge/Kotlin-1.4.0-blue.svg)](https://kotlinlang.org)
[![Kotlin Version](https://img.shields.io/badge/Kotlin-1.4.10-blue.svg)](https://kotlinlang.org)
[![GitHub commit activity](https://img.shields.io/github/commit-activity/m/2dust/v2rayNG)](https://github.com/2dust/v2rayNG/commits/master)
[![CodeFactor](https://www.codefactor.io/repository/github/2dust/v2rayng/badge)](https://www.codefactor.io/repository/github/2dust/v2rayng)
[![GitHub Releases](https://img.shields.io/github/downloads/2dust/v2rayNG/latest/total?logo=github)](https://github.com/2dust/v2rayNG/releases)
@@ -14,7 +14,16 @@ A V2Ray client for Android
### Usage
See our [wiki](https://github.com/2dust/v2rayNG/wiki)
#### Geoip and Geosite
v2rayNG release already embedded domain file `geoip.dat` and `geosite.dat`. However it is (probably) not the latest and not the most complete list.
For power user, the embedded files can be easily replaced with the following steps:
1. Launch v2rayNG (v1.4.9+)
2. Find existing geoip.dat and geosite.dat in `Android/data/com.v2ray.ang/files/assets` (path may differ on some Android device)
3. Replace them with the latest [domain list](https://github.com/v2fly/domain-list-community) and [ip list](https://github.com/v2fly/geoip)
4. Enhanced version can be found in this [repo](https://github.com/Loyalsoldier/v2ray-rules-dat) (recommend to use `geosite:geolocation-!cn` for proxy dns and routing)
5. It is also possible to use third party dat file in the same folder, like [h2y](https://guide.v2fly.org/routing/sitedata.html#%E5%A4%96%E7%BD%AE%E7%9A%84%E5%9F%9F%E5%90%8D%E6%96%87%E4%BB%B6)
#### See more in our [wiki](https://github.com/2dust/v2rayNG/wiki)
### Development guide

View File

@@ -38,6 +38,10 @@ android {
main.java.srcDirs += 'src/main/kotlin'
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8
}
splits {
abi {
enable true
@@ -70,6 +74,10 @@ dependencies {
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9"
// Androidx ktx
implementation 'android.arch.lifecycle:extensions:1.1.1'
implementation 'android.arch.lifecycle:livedata:1.1.1'
// Android support library
implementation "com.android.support:support-v4:$supportLibVersion"
implementation "com.android.support:appcompat-v7:$supportLibVersion"

Binary file not shown.

View File

@@ -44,9 +44,6 @@
static void throwUninitializedPropertyAccessException(java.lang.String);
}
-dontwarn org.jetbrains.anko.internals.**
-keep class org.jetbrains.anko.internals.** { *;}
-dontwarn rx.internal.util.unsafe.**
-keep class rx.internal.util.unsafe.** { *;}

View File

@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.v2ray.ang">
<supports-screens
@@ -12,6 +13,11 @@
<uses-feature android:name="android.hardware.camera" android:required="false"/>
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/>
<!-- https://developer.android.com/about/versions/11/privacy/package-visibility -->
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"
tools:ignore="QueryAllPackagesPermission" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
<uses-permission android:name="android.permission.INTERNET" />
@@ -21,12 +27,14 @@
<uses-permission android:name="com.android.vending.BILLING" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<!-- <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> -->
<application
android:name=".AngApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:extractNativeLibs="true"
android:theme="@style/AppTheme">
<activity
android:name=".ui.MainActivity"
@@ -34,20 +42,16 @@
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
<intent-filter>
<action android:name="android.service.quicksettings.action.QS_TILE_PREFERENCES" />
</intent-filter>
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/shortcuts" />

View File

@@ -15,6 +15,10 @@ import java.net.URLConnection
val Context.v2RayApplication: AngApplication
get() = applicationContext as AngApplication
// Usage note: DPreference use Android ContentProvider to redirect multi process access to main process.
// Currently, RunSoLibV2RayDaemon process will run proxy core, keep minimum configuration and long running
// in the background, support toggle on/off. That means it should NOT use DPreference after the initial
// creation and setup of the service
val Context.defaultDPreference: DPreference
get() = v2RayApplication.defaultDPreference

View File

@@ -11,12 +11,10 @@ import android.service.quicksettings.Tile
import android.service.quicksettings.TileService
import com.v2ray.ang.AppConfig
import com.v2ray.ang.R
import com.v2ray.ang.extension.defaultDPreference
import com.v2ray.ang.util.MessageUtil
import com.v2ray.ang.util.Utils
import java.lang.ref.SoftReference
@TargetApi(Build.VERSION_CODES.N)
class QSTileService : TileService() {
@@ -27,7 +25,7 @@ class QSTileService : TileService() {
qsTile?.icon = Icon.createWithResource(applicationContext, R.drawable.ic_v_idle)
} else if (state == Tile.STATE_ACTIVE) {
qsTile?.state = Tile.STATE_ACTIVE
qsTile?.label = defaultDPreference.getPrefString(AppConfig.PREF_CURR_CONFIG_NAME, "NG")
qsTile?.label = V2RayServiceManager.currentConfigName
qsTile?.icon = Icon.createWithResource(applicationContext, R.drawable.ic_v)
}

View File

@@ -11,5 +11,4 @@ interface ServiceControl {
fun vpnProtect(socket: Int): Boolean
fun vpnSendFd()
}

View File

@@ -37,10 +37,6 @@ class V2RayProxyOnlyService : Service(), ServiceControl {
return true
}
override fun vpnSendFd() {
// do nothing
}
override fun onBind(intent: Intent?): IBinder? {
return null
}

View File

@@ -50,6 +50,7 @@ object V2RayServiceManager {
Seq.setContext(context)
}
}
var currentConfigName = "NG"
private var lastQueryTime = 0L
private var mBuilder: NotificationCompat.Builder? = null
@@ -111,16 +112,6 @@ object V2RayServiceManager {
}
}
override fun sendFd(): Long {
val serviceControl = serviceControl?.get() ?: return -1
try {
serviceControl.vpnSendFd()
} catch (e: Exception) {
Log.d(serviceControl.getService().packageName, e.toString())
return -1
}
return 0
}
}
fun startV2rayPoint() {
@@ -141,6 +132,8 @@ object V2RayServiceManager {
v2rayPoint.enableLocalDNS = service.defaultDPreference.getPrefBoolean(SettingsActivity.PREF_LOCAL_DNS_ENABLED, false)
v2rayPoint.forwardIpv6 = service.defaultDPreference.getPrefBoolean(SettingsActivity.PREF_FORWARD_IPV6, false)
v2rayPoint.domainName = service.defaultDPreference.getPrefString(AppConfig.PREF_CURR_CONFIG_DOMAIN, "")
v2rayPoint.proxyOnly = service.defaultDPreference.getPrefString(AppConfig.PREF_MODE, "VPN") != "VPN"
currentConfigName = service.defaultDPreference.getPrefString(AppConfig.PREF_CURR_CONFIG_NAME, "NG")
try {
v2rayPoint.runLoop()
@@ -244,7 +237,7 @@ object V2RayServiceManager {
mBuilder = NotificationCompat.Builder(service, channelId)
.setSmallIcon(R.drawable.ic_v)
.setContentTitle(service.defaultDPreference.getPrefString(AppConfig.PREF_CURR_CONFIG_NAME, ""))
.setContentTitle(currentConfigName)
.setPriority(NotificationCompat.PRIORITY_MIN)
.setOngoing(true)
.setShowWhen(false)
@@ -355,13 +348,10 @@ object V2RayServiceManager {
}
fun stopSpeedNotification() {
val service = serviceControl?.get()?.getService() ?: return
if (mSubscription != null) {
mSubscription?.unsubscribe() //stop queryStats
mSubscription = null
val cfName = service.defaultDPreference.getPrefString(AppConfig.PREF_CURR_CONFIG_NAME, "")
updateNotification(cfName, 0, 0)
updateNotification(currentConfigName, 0, 0)
}
}
}

View File

@@ -10,7 +10,6 @@ import android.os.ParcelFileDescriptor
import android.os.StrictMode
import android.support.annotation.RequiresApi
import android.util.Log
import com.v2ray.ang.AppConfig
import com.v2ray.ang.R
import com.v2ray.ang.extension.defaultDPreference
import com.v2ray.ang.ui.PerAppProxyActivity
@@ -50,7 +49,7 @@ class V2RayVpnService : VpnService(), ServiceControl {
override fun onAvailable(network: Network) {
setUnderlyingNetworks(arrayOf(network))
}
override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities?) {
override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
// it's a good idea to refresh capabilities
setUnderlyingNetworks(arrayOf(network))
}
@@ -79,7 +78,7 @@ class V2RayVpnService : VpnService(), ServiceControl {
override fun onDestroy() {
super.onDestroy()
V2RayServiceManager.cancelNotification()
stopV2Ray()
}
private fun setup(parameters: String) {
@@ -127,7 +126,7 @@ class V2RayVpnService : VpnService(), ServiceControl {
}
}
builder.setSession(defaultDPreference.getPrefString(AppConfig.PREF_CURR_CONFIG_NAME, ""))
builder.setSession(V2RayServiceManager.currentConfigName)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP &&
defaultDPreference.getPrefBoolean(SettingsActivity.PREF_PER_APP_PROXY, false)) {
@@ -155,8 +154,8 @@ class V2RayVpnService : VpnService(), ServiceControl {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
try {
connectivity.requestNetwork(defaultNetworkRequest, defaultNetworkCallback)
} catch (ignored: Exception) {
// ignored
} catch (e: Exception) {
e.printStackTrace()
}
}
@@ -172,6 +171,8 @@ class V2RayVpnService : VpnService(), ServiceControl {
e.printStackTrace()
stopV2Ray()
}
sendFd()
}
private fun sendFd() {
@@ -181,7 +182,7 @@ class V2RayVpnService : VpnService(), ServiceControl {
GlobalScope.launch(Dispatchers.IO) {
var tries = 0
while (true) try {
Thread.sleep(50L shl tries)
Thread.sleep(1000L shl tries)
Log.d(packageName, "sendFd tries: $tries")
LocalSocket().use { localSocket ->
localSocket.connect(LocalSocketAddress(path, LocalSocketAddress.Namespace.FILESYSTEM))
@@ -251,7 +252,4 @@ class V2RayVpnService : VpnService(), ServiceControl {
return protect(socket)
}
override fun vpnSendFd() {
sendFd()
}
}

View File

@@ -1,42 +1,41 @@
package com.v2ray.ang.ui
import android.Manifest
import android.content.*
import android.arch.lifecycle.ViewModelProviders
import android.content.Intent
import android.net.Uri
import android.net.VpnService
import android.support.v7.widget.LinearLayoutManager
import android.view.Menu
import android.view.MenuItem
import com.tbruyelle.rxpermissions.RxPermissions
import com.v2ray.ang.R
import com.v2ray.ang.util.AngConfigManager
import com.v2ray.ang.util.Utils
import kotlinx.android.synthetic.main.activity_main.*
import android.os.Bundle
import android.text.TextUtils
import android.view.KeyEvent
import com.v2ray.ang.AppConfig
import com.v2ray.ang.util.MessageUtil
import com.v2ray.ang.util.V2rayConfigUtil
import java.lang.ref.SoftReference
import java.net.URL
import android.content.IntentFilter
import android.support.design.widget.NavigationView
import android.support.v4.view.GravityCompat
import android.support.v7.app.ActionBarDrawerToggle
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.helper.ItemTouchHelper
import android.text.TextUtils
import android.util.Log
import android.view.KeyEvent
import android.view.Menu
import android.view.MenuItem
import com.tbruyelle.rxpermissions.RxPermissions
import com.v2ray.ang.AppConfig
import com.v2ray.ang.BuildConfig
import com.v2ray.ang.dto.EConfigType
import com.v2ray.ang.R
import com.v2ray.ang.extension.defaultDPreference
import com.v2ray.ang.extension.toast
import com.v2ray.ang.helper.SimpleItemTouchHelperCallback
import com.v2ray.ang.util.AngConfigManager
import com.v2ray.ang.util.Utils
import com.v2ray.ang.util.V2rayConfigUtil
import com.v2ray.ang.viewmodel.MainViewModel
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import libv2ray.Libv2ray
import rx.Observable
import rx.android.schedulers.AndroidSchedulers
import java.net.URL
import java.util.concurrent.TimeUnit
import com.v2ray.ang.helper.SimpleItemTouchHelperCallback
import com.v2ray.ang.util.AngConfigManager.configs
import kotlinx.coroutines.*
import libv2ray.Libv2ray
class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedListener {
companion object {
@@ -46,23 +45,9 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
private const val REQUEST_SCAN_URL = 3
}
var isRunning = false
set(value) {
field = value
adapter.changeable = !value
if (value) {
fab.setImageResource(R.drawable.ic_v)
tv_test_state.text = getString(R.string.connection_connected)
} else {
fab.setImageResource(R.drawable.ic_v_idle)
tv_test_state.text = getString(R.string.connection_not_connected)
}
hideCircle()
}
private val adapter by lazy { MainRecyclerAdapter(this) }
private var mItemTouchHelper: ItemTouchHelper? = null
private val tcpingTestScope by lazy { CoroutineScope(Dispatchers.IO) }
private val mainViewModel: MainViewModel by lazy { ViewModelProviders.of(this).get(MainViewModel::class.java) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -71,7 +56,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
setSupportActionBar(toolbar)
fab.setOnClickListener {
if (isRunning) {
if (mainViewModel.isRunning.value == true) {
Utils.stopVService(this)
} else if (defaultDPreference.getPrefString(AppConfig.PREF_MODE, "VPN") == "VPN") {
val intent = VpnService.prepare(this)
@@ -85,16 +70,9 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
}
}
layout_test.setOnClickListener {
if (isRunning) {
val socksPort = 10808//Utils.parseInt(defaultDPreference.getPrefString(SettingsActivity.PREF_SOCKS_PORT, "10808"))
if (mainViewModel.isRunning.value == true) {
tv_test_state.text = getString(R.string.connection_test_testing)
GlobalScope.launch(Dispatchers.IO) {
val result = Utils.testConnection(this@MainActivity, socksPort)
launch(Dispatchers.Main) {
tv_test_state.text = Utils.getEditable(result)
}
}
mainViewModel.testCurrentServerRealPing()
} else {
// tv_test_state.text = getString(R.string.connection_test_fail)
}
@@ -115,6 +93,33 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
toggle.syncState()
nav_view.setNavigationItemSelectedListener(this)
version.text = "v${BuildConfig.VERSION_NAME} (${Libv2ray.checkVersionX()})"
setupViewModelObserver()
}
private fun setupViewModelObserver() {
mainViewModel.updateListAction.observe(this, {
val index = it ?: return@observe
if (index >= 0) {
adapter.updateSelectedItem(index)
} else {
adapter.updateConfigList()
}
})
mainViewModel.updateTestResultAction.observe(this, { tv_test_state.text = it })
mainViewModel.isRunning.observe(this, {
val isRunning = it ?: return@observe
adapter.changeable = !isRunning
if (isRunning) {
fab.setImageResource(R.drawable.ic_v)
tv_test_state.text = getString(R.string.connection_connected)
} else {
fab.setImageResource(R.drawable.ic_v_idle)
tv_test_state.text = getString(R.string.connection_not_connected)
}
hideCircle()
})
mainViewModel.startListenBroadcast()
}
fun startV2Ray() {
@@ -128,27 +133,6 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
}
}
override fun onStart() {
super.onStart()
isRunning = false
// val intent = Intent(this.applicationContext, V2RayVpnService::class.java)
// intent.`package` = AppConfig.ANG_PACKAGE
// bindService(intent, mConnection, BIND_AUTO_CREATE)
mMsgReceive = ReceiveMessageHandler(this@MainActivity)
registerReceiver(mMsgReceive, IntentFilter(AppConfig.BROADCAST_ACTION_ACTIVITY))
MessageUtil.sendMsg2Service(this, AppConfig.MSG_REGISTER_CLIENT, "")
}
override fun onStop() {
super.onStop()
if (mMsgReceive != null) {
unregisterReceiver(mMsgReceive)
mMsgReceive = null
}
}
public override fun onResume() {
super.onResume()
adapter.updateConfigList()
@@ -188,7 +172,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
}
private fun getOptionIntent() = Intent().putExtra("position", -1)
.putExtra("isRunning", isRunning)
.putExtra("isRunning", mainViewModel.isRunning.value == true)
override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
R.id.import_qrcode -> {
@@ -251,30 +235,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
}
R.id.ping_all -> {
tcpingTestScope.coroutineContext[Job]?.cancelChildren()
Utils.closeAllTcpSockets()
for (k in 0 until configs.vmess.count()) {
configs.vmess[k].testResult = ""
adapter.updateConfigList()
}
for (k in 0 until configs.vmess.count()) {
var serverAddress = configs.vmess[k].address
var serverPort = 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
}
tcpingTestScope.launch {
configs.vmess.getOrNull(k)?.let { // check null in case array is modified during testing
it.testResult = Utils.tcping(serverAddress, serverPort)
launch(Dispatchers.Main) {
adapter.updateSelectedItem(k)
}
}
}
}
mainViewModel.testAllTcping()
true
}
@@ -514,35 +475,6 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
// }
// }
private
var mMsgReceive: BroadcastReceiver? = null
private class ReceiveMessageHandler(activity: MainActivity) : BroadcastReceiver() {
internal var mReference: SoftReference<MainActivity> = SoftReference(activity)
override fun onReceive(ctx: Context?, intent: Intent?) {
val activity = mReference.get()
when (intent?.getIntExtra("key", 0)) {
AppConfig.MSG_STATE_RUNNING -> {
activity?.isRunning = true
}
AppConfig.MSG_STATE_NOT_RUNNING -> {
activity?.isRunning = false
}
AppConfig.MSG_STATE_START_SUCCESS -> {
activity?.toast(R.string.toast_services_success)
activity?.isRunning = true
}
AppConfig.MSG_STATE_START_FAILURE -> {
activity?.toast(R.string.toast_services_failure)
activity?.isRunning = false
}
AppConfig.MSG_STATE_STOP_SUCCESS -> {
activity?.isRunning = false
}
}
}
}
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
if (keyCode == KeyEvent.KEYCODE_BACK) {
moveTaskToBack(false)
@@ -585,7 +517,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
}
R.id.settings -> {
startActivity(Intent(this, SettingsActivity::class.java)
.putExtra("isRunning", isRunning))
.putExtra("isRunning", mainViewModel.isRunning.value == true))
}
R.id.feedback -> {
Utils.openUri(this, AppConfig.v2rayNGIssues)

View File

@@ -318,7 +318,7 @@ object AngConfigManager {
result = Utils.decode(result)
}
val legacyPattern = "^(.+?):(.*)@(.+?):(\\d+?)$".toRegex()
val legacyPattern = "^(.+?):(.*)@(.+?):(\\d+?)/?$".toRegex()
val match = legacyPattern.matchEntire(result)
if (match == null) {
return R.string.toast_incorrect_protocol

View File

@@ -3,9 +3,7 @@ package com.v2ray.ang.util
import android.content.Context
import android.text.TextUtils
import android.util.Log
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.JsonArray
import com.google.gson.*
import com.v2ray.ang.AngApplication
import com.v2ray.ang.AppConfig
import com.v2ray.ang.dto.AngConfig.VmessBean
@@ -14,7 +12,6 @@ import com.v2ray.ang.ui.SettingsActivity
import org.json.JSONException
import org.json.JSONObject
import org.json.JSONArray
import com.google.gson.JsonObject
import com.v2ray.ang.dto.EConfigType
import com.v2ray.ang.extension.defaultDPreference
import kotlin.collections.ArrayList
@@ -64,11 +61,16 @@ object V2rayConfigUtil {
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())) {
val v2rayConfig: V2rayConfig? = try {
Gson().fromJson(jsonConfig, V2rayConfig::class.java)
} catch (e: JsonSyntaxException) {
e.printStackTrace()
null
}
v2rayConfig?.outbounds?.forEach { outbound ->
if (outbound.protocol.equals(EConfigType.VMESS.name, true) ||
outbound.protocol.equals(EConfigType.SHADOWSOCKS.name, true) ||
outbound.protocol.equals(EConfigType.SOCKS.name, true)) {
return outbound
}
}

View File

@@ -0,0 +1,101 @@
package com.v2ray.ang.viewmodel
import android.app.Application
import android.arch.lifecycle.AndroidViewModel
import android.arch.lifecycle.MutableLiveData
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.util.Log
import com.v2ray.ang.AngApplication
import com.v2ray.ang.AppConfig
import com.v2ray.ang.R
import com.v2ray.ang.dto.EConfigType
import com.v2ray.ang.extension.toast
import com.v2ray.ang.util.AngConfigManager
import com.v2ray.ang.util.MessageUtil
import com.v2ray.ang.util.Utils
import com.v2ray.ang.util.V2rayConfigUtil
import kotlinx.coroutines.*
class MainViewModel(application: Application) : AndroidViewModel(application) {
val isRunning by lazy { MutableLiveData<Boolean>() }
val updateListAction by lazy { MutableLiveData<Int>() }
val updateTestResultAction by lazy { MutableLiveData<String>() }
private val tcpingTestScope by lazy { CoroutineScope(Dispatchers.IO) }
fun startListenBroadcast() {
isRunning.value = false
getApplication<AngApplication>().registerReceiver(mMsgReceiver, IntentFilter(AppConfig.BROADCAST_ACTION_ACTIVITY))
MessageUtil.sendMsg2Service(getApplication(), AppConfig.MSG_REGISTER_CLIENT, "")
}
override fun onCleared() {
getApplication<AngApplication>().unregisterReceiver(mMsgReceiver)
Log.i(AppConfig.ANG_PACKAGE, "Main ViewModel is cleared")
super.onCleared()
}
fun testAllTcping() {
tcpingTestScope.coroutineContext[Job]?.cancelChildren()
Utils.closeAllTcpSockets()
for (k in 0 until AngConfigManager.configs.vmess.count()) {
AngConfigManager.configs.vmess[k].testResult = ""
updateListAction.value = -1 // update all
}
for (k in 0 until AngConfigManager.configs.vmess.count()) {
var serverAddress = AngConfigManager.configs.vmess[k].address
var serverPort = AngConfigManager.configs.vmess[k].port
if (AngConfigManager.configs.vmess[k].configType == EConfigType.CUSTOM.value) {
val serverOutbound = V2rayConfigUtil.getCustomConfigServerOutbound(getApplication(),
AngConfigManager.configs.vmess[k].guid) ?: continue
serverAddress = serverOutbound.getServerAddress() ?: continue
serverPort = serverOutbound.getServerPort() ?: continue
}
tcpingTestScope.launch {
AngConfigManager.configs.vmess.getOrNull(k)?.let { // check null in case array is modified during testing
it.testResult = Utils.tcping(serverAddress, serverPort)
launch(Dispatchers.Main) {
updateListAction.value = k
}
}
}
}
}
fun testCurrentServerRealPing() {
val socksPort = 10808//Utils.parseInt(defaultDPreference.getPrefString(SettingsActivity.PREF_SOCKS_PORT, "10808"))
GlobalScope.launch(Dispatchers.IO) {
val result = Utils.testConnection(getApplication(), socksPort)
launch(Dispatchers.Main) {
updateTestResultAction.value = result
}
}
}
private val mMsgReceiver = object : BroadcastReceiver() {
override fun onReceive(ctx: Context?, intent: Intent?) {
when (intent?.getIntExtra("key", 0)) {
AppConfig.MSG_STATE_RUNNING -> {
isRunning.value = true
}
AppConfig.MSG_STATE_NOT_RUNNING -> {
isRunning.value = false
}
AppConfig.MSG_STATE_START_SUCCESS -> {
getApplication<AngApplication>().toast(R.string.toast_services_success)
isRunning.value = true
}
AppConfig.MSG_STATE_START_FAILURE -> {
getApplication<AngApplication>().toast(R.string.toast_services_failure)
isRunning.value = false
}
AppConfig.MSG_STATE_STOP_SUCCESS -> {
isRunning.value = false
}
}
}
}
}

View File

@@ -7,7 +7,7 @@ buildscript {
maven { url 'https://maven.google.com' }
}
dependencies {
classpath 'com.android.tools.build:gradle:4.0.1'
classpath 'com.android.tools.build:gradle:4.1.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
// NOTE: Do not place your application dependencies here; they belong

View File

@@ -13,9 +13,9 @@ org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryErro
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
#Fri Jun 02 14:08:42 CST 2017
kotlinVersion=1.4.0
kotlinVersion=1.4.10
supportLibVersion=28.0.0
buildToolsVer=29.0.3
compileSdkVer=29
buildToolsVer=30.0.2
compileSdkVer=30
kotlin.incremental=true
targetSdkVer=29
targetSdkVer=30