Compare commits

..

24 Commits

Author SHA1 Message Date
2dust
b4f02c9bd6 Merge pull request #894 from Humilton/master
Support keyboard navigation for fab
2021-02-21 12:56:05 +08:00
hubaozhong
e567719f5b Support keyboard navigation for fab
Signed-off-by: hubaozhong <d63hbz@gmail.com>
2021-02-18 23:01:31 +08:00
2dust
8407fc5825 Merge pull request #866 from yuhan6665/trim-r
Fix the string list trim
2021-02-01 08:05:37 +08:00
yuhan6665
a3e49dcc3d Fix the string list trim 2021-01-30 21:59:54 -05:00
2dust
7b47bbe99a Merge pull request #846 from yuhan6665/list-ui
Add subscription remarks in server list
2021-01-16 13:18:27 +08:00
yuhan6665
0fb2165015 Add subscription remarks in server list 2021-01-15 20:44:28 -05:00
2dust
03eeeb9b62 Merge pull request #827 from yuhan6665/master
Update readme
2021-01-02 09:18:45 +08:00
yuhan6665
038daf5fda Update readme 2021-01-01 09:21:10 -05:00
2dust
bfd1387d9b Merge pull request #817 from yuhan6665/fix-manual-update
Fix manual update
2020-12-26 12:03:38 +08:00
yuhan6665
5afec5cf25 Update full config when edit manually 2020-12-25 18:34:58 -05:00
yuhan6665
ec29bdf5bf Refactor for re-use genStoreV2rayConfig() 2020-12-25 18:34:58 -05:00
2dust
57efab093f Merge pull request #803 from yuhan6665/fix-712
Fix toast BadTokenException in OS 7.1.2
2020-12-14 08:21:51 +08:00
2dust
9c92a64811 Merge pull request #801 from yuhan6665/fix-oculus
Fix widget manager null pointer in Oculus
2020-12-14 08:21:41 +08:00
yuhan6665
7ddc82d5cd Fix toast BadTokenException in OS 7.1.2
Apparently recent changes with ViewModel affect the internal of
Activity which lead to toast throwing BadTokenException in OS
7.1.2.
The error is not easily catchable. This library use reflection
to override a key function in WindowManager to catch the error.
I have audit the code of the library.

See https://github.com/PureWriter/ToastCompat for more details
2020-12-13 18:45:04 -05:00
yuhan6665
c286ba18a8 Fix widget manager null pointer in Oculus 2020-12-12 23:11:00 -05:00
2dust
867b5fc880 Merge pull request #795 from rurirei/v2init
init update
2020-12-10 08:25:10 +08:00
rurirei
e8a7fa5320 create init 2020-12-09 17:35:09 +08:00
rurirei
f2f9e55286 do not init all 2020-12-09 17:32:50 +08:00
2dust
4a1c62a67c Merge pull request #787 from yuhan6665/fix-selected
Fix update full config when settings change
2020-12-06 13:48:03 +08:00
yuhan6665
c9a6a459d4 Fix update full config when settings change
Now daemon process does not reference the node list at all and
only depend on a couple of settings like PREF_CURR_CONFIG..
2020-12-05 23:42:44 -05:00
2dust
21fdcf4ccf Merge pull request #783 from yuhan6665/fix-selected
Fix select node of two processes in sync
2020-12-05 18:20:11 +08:00
yuhan6665
7c7a623ae5 Fix select node of two processes in sync
As proxy only mode is added in 1.4.0, I moved the toggle components to the daemon process
When user start service from toggle, the full config is generated from a cached list.
However, this cache is not in sync with the main process list.
In fact, we don't need to generate full config right before the service start. We only
need to when active node is changed.
This way, code logic and daemon process is kept simple
2020-12-04 22:37:31 -05:00
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
24 changed files with 246 additions and 153 deletions

View File

@@ -17,7 +17,6 @@ import (
v2filesystem "v2ray.com/core/common/platform/filesystem"
v2stats "v2ray.com/core/features/stats"
v2serial "v2ray.com/core/infra/conf/serial"
_ "v2ray.com/core/main/distro/all"
v2internet "v2ray.com/core/transport/internet"
v2applog "v2ray.com/core/app/log"

View File

@@ -0,0 +1,58 @@
package libv2ray
import (
// The following are necessary as they register handlers in their init functions.
// Required features. Can't remove unless there is replacements.
_ "v2ray.com/core/app/dispatcher"
_ "v2ray.com/core/app/proxyman/inbound"
_ "v2ray.com/core/app/proxyman/outbound"
// Other optional features.
_ "v2ray.com/core/app/dns"
_ "v2ray.com/core/app/log"
_ "v2ray.com/core/app/policy"
_ "v2ray.com/core/app/router"
_ "v2ray.com/core/app/stats"
// Inbound and outbound proxies.
_ "v2ray.com/core/proxy/blackhole"
_ "v2ray.com/core/proxy/dns"
_ "v2ray.com/core/proxy/dokodemo"
_ "v2ray.com/core/proxy/freedom"
_ "v2ray.com/core/proxy/http"
_ "v2ray.com/core/proxy/mtproto"
_ "v2ray.com/core/proxy/shadowsocks"
_ "v2ray.com/core/proxy/socks"
_ "v2ray.com/core/proxy/trojan"
_ "v2ray.com/core/proxy/vless/inbound"
_ "v2ray.com/core/proxy/vless/outbound"
_ "v2ray.com/core/proxy/vmess/inbound"
_ "v2ray.com/core/proxy/vmess/outbound"
// Transport
_ "v2ray.com/core/transport/internet/http"
_ "v2ray.com/core/transport/internet/kcp"
_ "v2ray.com/core/transport/internet/quic"
_ "v2ray.com/core/transport/internet/tcp"
_ "v2ray.com/core/transport/internet/tls"
_ "v2ray.com/core/transport/internet/udp"
_ "v2ray.com/core/transport/internet/websocket"
// Transport headers
_ "v2ray.com/core/transport/internet/headers/http"
_ "v2ray.com/core/transport/internet/headers/noop"
_ "v2ray.com/core/transport/internet/headers/srtp"
_ "v2ray.com/core/transport/internet/headers/tls"
_ "v2ray.com/core/transport/internet/headers/utp"
_ "v2ray.com/core/transport/internet/headers/wechat"
_ "v2ray.com/core/transport/internet/headers/wireguard"
// JSON config support. Choose only one from the two below.
// The following line loads JSON from v2ctl
// _ "v2ray.com/core/main/json"
// The following line loads JSON internally
_ "v2ray.com/core/main/jsonem"
// Load config from file or http(s)
// _ "v2ray.com/core/main/confloader/external"
)

View File

@@ -1,12 +1,13 @@
# v2rayNG
A V2Ray client for Android
A V2Ray client for Android, support [Xray core](https://github.com/XTLS/Xray-core) and [v2fly core](https://github.com/v2fly/v2ray-core)
[![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.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)
[![Chat on Telegram](https://img.shields.io/badge/Chat%20on-Telegram-brightgreen.svg)](https://t.me/v2rayn)
<a href="https://play.google.com/store/apps/details?id=com.v2ray.ang">
<img alt="Get it on Google Play" src="https://play.google.com/intl/en_us/badges/images/generic/en_badge_web_generic.png" width="165" height="64" />

View File

@@ -96,6 +96,7 @@ dependencies {
implementation 'me.dm7.barcodescanner:core:1.9.8'
implementation 'me.dm7.barcodescanner:zxing:1.9.8'
implementation 'com.github.jorgecastilloprz:fabprogresscircle:1.01@aar'
implementation 'me.drakeet.support:toastcompat:1.1.0'
implementation(name: 'libv2ray', ext: 'aar')
//implementation(name: 'tun2socks', ext: 'aar')

View File

@@ -5,6 +5,7 @@ import android.os.Build
import android.widget.Toast
import com.v2ray.ang.AngApplication
import me.dozen.dpreference.DPreference
import me.drakeet.support.toast.ToastCompat
import org.json.JSONObject
import java.net.URLConnection
@@ -15,16 +16,20 @@ 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
inline fun Context.toast(message: Int): Toast = Toast
inline fun Context.toast(message: Int): Toast = ToastCompat
.makeText(this, message, Toast.LENGTH_SHORT)
.apply {
show()
}
inline fun Context.toast(message: CharSequence): Toast = Toast
inline fun Context.toast(message: CharSequence): Toast = ToastCompat
.makeText(this, message, Toast.LENGTH_SHORT)
.apply {
show()

View File

@@ -21,7 +21,7 @@ class TaskerReceiver : BroadcastReceiver() {
return
} else if (switch) {
if (guid == AppConfig.TASKER_DEFAULT_GUID) {
Utils.startVService(context)
Utils.startVServiceFromToggle(context)
} else {
Utils.startVService(context, guid)
}

View File

@@ -50,15 +50,16 @@ class WidgetProvider : AppWidgetProvider() {
Utils.startVServiceFromToggle(context)
}
} else if (AppConfig.BROADCAST_ACTION_ACTIVITY == intent.action) {
val manager = AppWidgetManager.getInstance(context)
when (intent.getIntExtra("key", 0)) {
AppConfig.MSG_STATE_RUNNING, AppConfig.MSG_STATE_START_SUCCESS -> {
updateWidgetBackground(context, manager, manager.getAppWidgetIds(ComponentName(context, WidgetProvider::class.java)),
true)
}
AppConfig.MSG_STATE_NOT_RUNNING, AppConfig.MSG_STATE_START_FAILURE, AppConfig.MSG_STATE_STOP_SUCCESS -> {
updateWidgetBackground(context, manager, manager.getAppWidgetIds(ComponentName(context, WidgetProvider::class.java)),
false)
AppWidgetManager.getInstance(context)?.let { manager ->
when (intent.getIntExtra("key", 0)) {
AppConfig.MSG_STATE_RUNNING, AppConfig.MSG_STATE_START_SUCCESS -> {
updateWidgetBackground(context, manager, manager.getAppWidgetIds(ComponentName(context, WidgetProvider::class.java)),
true)
}
AppConfig.MSG_STATE_NOT_RUNNING, AppConfig.MSG_STATE_START_FAILURE, AppConfig.MSG_STATE_STOP_SUCCESS -> {
updateWidgetBackground(context, manager, manager.getAppWidgetIds(ComponentName(context, WidgetProvider::class.java)),
false)
}
}
}
}

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

@@ -18,6 +18,8 @@ import com.v2ray.ang.AppConfig.TAG_DIRECT
import com.v2ray.ang.R
import com.v2ray.ang.extension.defaultDPreference
import com.v2ray.ang.extension.toSpeedString
import com.v2ray.ang.extension.toast
import com.v2ray.ang.extension.v2RayApplication
import com.v2ray.ang.ui.MainActivity
import com.v2ray.ang.ui.SettingsActivity
import com.v2ray.ang.util.MessageUtil
@@ -50,14 +52,20 @@ object V2RayServiceManager {
Seq.setContext(context)
}
}
var currentConfigName = "NG"
private var lastQueryTime = 0L
private var mBuilder: NotificationCompat.Builder? = null
private var mSubscription: Subscription? = null
private var mNotificationManager: NotificationManager? = null
fun startV2Ray(context: Context, mode: String) {
val intent = if (mode == "VPN") {
fun startV2Ray(context: Context) {
if (context.v2RayApplication.defaultDPreference.getPrefBoolean(SettingsActivity.PREF_PROXY_SHARING, false)) {
context.toast(R.string.toast_warning_pref_proxysharing_short)
}else{
context.toast(R.string.toast_services_start)
}
val intent = if (context.v2RayApplication.defaultDPreference.getPrefString(AppConfig.PREF_MODE, "VPN") == "VPN") {
Intent(context.applicationContext, V2RayVpnService::class.java)
} else {
Intent(context.applicationContext, V2RayProxyOnlyService::class.java)
@@ -132,6 +140,7 @@ object V2RayServiceManager {
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()
@@ -235,7 +244,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)
@@ -346,13 +355,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
@@ -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)) {

View File

@@ -128,7 +128,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
}
showCircle()
// toast(R.string.toast_services_start)
if (!Utils.startVService(this)) {
if (!Utils.startVService(this, AngConfigManager.configs.index)) {
hideCircle()
}
}

View File

@@ -64,11 +64,13 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
holder.radio.isChecked = (position == configs.index)
holder.itemView.setBackgroundColor(Color.TRANSPARENT)
holder.test_result.text = test_result
if (TextUtils.isEmpty(subid)) {
holder.subid.text = ""
} else {
holder.subid.text = "S"
holder.subscription.text = ""
if (!TextUtils.isEmpty(subid)) {
for (sub in configs.subItem) {
if (sub.id == subid) {
holder.subscription.text = sub.remarks
}
}
}
var shareOptions = share_method.asList()
@@ -143,16 +145,14 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
} else {
mActivity.showCircle()
Utils.stopVService(mActivity)
AngConfigManager.setActiveServer(position)
Observable.timer(500, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
mActivity.showCircle()
if (!Utils.startVService(mActivity)) {
if (!Utils.startVService(mActivity, position)) {
mActivity.hideCircle()
}
}
}
notifyDataSetChanged()
}
@@ -213,7 +213,7 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
open class BaseViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
class MainViewHolder(itemView: View) : BaseViewHolder(itemView), ItemTouchHelperViewHolder {
val subid = itemView.tv_subid
val subscription = itemView.tv_subscription
val radio = itemView.btn_radio!!
val name = itemView.tv_name!!
val test_result = itemView.tv_test_result!!

View File

@@ -69,40 +69,32 @@ class Server2Activity : BaseActivity() {
* save server config
*/
fun saveServer(): Boolean {
var saveSuccess: Boolean
val vmess = configs.vmess[edit_index]
vmess.remarks = et_remarks.text.toString()
if (TextUtils.isEmpty(vmess.remarks)) {
toast(R.string.server_lab_remarks)
saveSuccess = false
return false
}
if (AngConfigManager.addCustomServer(vmess, edit_index) == 0) {
toast(R.string.toast_success)
saveSuccess = true
} else {
toast(R.string.toast_failure)
saveSuccess = false
}
try {
Gson().fromJson<Object>(tv_content.text.toString(), Object::class.java)
} catch (e: Exception) {
e.printStackTrace()
toast(R.string.toast_malformed_josn)
saveSuccess = false
return false
}
if (saveSuccess) {
if (AngConfigManager.addCustomServer(vmess, edit_index) == 0) {
//update config
defaultDPreference.setPrefString(AppConfig.ANG_CONFIG + edit_guid, tv_content.text.toString())
AngConfigManager.genStoreV2rayConfigIfActive(edit_index)
toast(R.string.toast_success)
finish()
return true
} else {
toast(R.string.toast_failure)
return false
}
}

View File

@@ -112,6 +112,7 @@ class Server3Activity : BaseActivity() {
}
if (AngConfigManager.addShadowsocksServer(vmess, edit_index) == 0) {
AngConfigManager.genStoreV2rayConfigIfActive(edit_index)
toast(R.string.toast_success)
finish()
return true

View File

@@ -96,6 +96,7 @@ class Server4Activity : BaseActivity() {
}
if (AngConfigManager.addSocksServer(vmess, edit_index) == 0) {
AngConfigManager.genStoreV2rayConfigIfActive(edit_index)
toast(R.string.toast_success)
finish()
return true

View File

@@ -155,6 +155,7 @@ class ServerActivity : BaseActivity() {
}
if (AngConfigManager.addServer(vmess, edit_index) == 0) {
AngConfigManager.genStoreV2rayConfigIfActive(edit_index)
toast(R.string.toast_success)
finish()
return true

View File

@@ -1,5 +1,6 @@
package com.v2ray.ang.ui
import android.arch.lifecycle.ViewModelProviders
import android.content.Intent
import android.os.Bundle
import android.support.v7.preference.*
@@ -7,7 +8,9 @@ import android.view.View
import com.v2ray.ang.R
import com.v2ray.ang.AppConfig
import com.v2ray.ang.extension.toast
import com.v2ray.ang.util.AngConfigManager
import com.v2ray.ang.util.Utils
import com.v2ray.ang.viewmodel.SettingsViewModel
class SettingsActivity : BaseActivity() {
companion object {
@@ -36,6 +39,8 @@ class SettingsActivity : BaseActivity() {
const val PREF_FORWARD_IPV6 = "pref_forward_ipv6"
}
private val settingsViewModel by lazy { ViewModelProviders.of(this).get(SettingsViewModel::class.java) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_settings)
@@ -43,6 +48,8 @@ class SettingsActivity : BaseActivity() {
title = getString(R.string.title_settings)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
settingsViewModel.startListenPreferenceChange()
}
class SettingsFragment : PreferenceFragmentCompat() {
@@ -74,7 +81,7 @@ class SettingsActivity : BaseActivity() {
private fun restartProxy() {
Utils.stopVService(requireContext())
Utils.startVService(requireContext())
Utils.startVService(requireContext(), AngConfigManager.configs.index)
}
private fun isRunning(): Boolean {

View File

@@ -2,6 +2,7 @@ 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
@@ -154,6 +155,10 @@ object AngConfigManager {
angConfig.index = index
app.curIndex = index
storeConfigFile()
if (!genStoreV2rayConfig()) {
Log.d(AppConfig.ANG_PACKAGE, "set active index $index but generate full configuration failed!")
return -1
}
} catch (e: Exception) {
e.printStackTrace()
app.curIndex = -1
@@ -174,35 +179,32 @@ object AngConfigManager {
}
}
fun genStoreV2rayConfigIfActive(index: Int) {
if (index == configs.index) {
if (!genStoreV2rayConfig()) {
Log.d(AppConfig.ANG_PACKAGE, "update config $index but generate full configuration failed!")
}
}
}
/**
* gen and store v2ray config file
*/
fun genStoreV2rayConfig(index: Int): Boolean {
fun genStoreV2rayConfig(): Boolean {
try {
if (angConfig.index < 0
|| angConfig.vmess.count() <= 0
|| angConfig.index > angConfig.vmess.count() - 1
) {
return false
}
var index2 = angConfig.index
if (index >= 0) {
index2 = index
}
val result = V2rayConfigUtil.getV2rayConfig(app, angConfig.vmess[index2])
if (result.status) {
app.defaultDPreference.setPrefString(PREF_CURR_CONFIG, result.content)
app.defaultDPreference.setPrefString(PREF_CURR_CONFIG_GUID, currConfigGuid())
app.defaultDPreference.setPrefString(PREF_CURR_CONFIG_NAME, currConfigName())
return true
} else {
return false
angConfig.vmess.getOrNull(angConfig.index)?.let {
val result = V2rayConfigUtil.getV2rayConfig(app, it)
if (result.status) {
app.defaultDPreference.setPrefString(PREF_CURR_CONFIG, result.content)
app.defaultDPreference.setPrefString(PREF_CURR_CONFIG_GUID, currConfigGuid())
app.defaultDPreference.setPrefString(PREF_CURR_CONFIG_NAME, currConfigName())
return true
}
}
} catch (e: Exception) {
e.printStackTrace()
return false
}
return false
}
fun currGeneratedV2rayConfig(): String {
@@ -586,9 +588,9 @@ object AngConfigManager {
*/
fun shareFullContent2Clipboard(index: Int): Int {
try {
if (AngConfigManager.genStoreV2rayConfig(index)) {
val configContent = app.defaultDPreference.getPrefString(AppConfig.PREF_CURR_CONFIG, "")
Utils.setClipboard(app.applicationContext, configContent)
val result = V2rayConfigUtil.getV2rayConfig(app, angConfig.vmess[index])
if (result.status) {
Utils.setClipboard(app.applicationContext, result.content)
} else {
return -1
}

View File

@@ -22,7 +22,7 @@ import android.webkit.URLUtil
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.defaultDPreference
import com.v2ray.ang.extension.responseLength
import com.v2ray.ang.extension.toast
import com.v2ray.ang.extension.v2RayApplication
@@ -32,7 +32,6 @@ import kotlinx.coroutines.isActive
import me.dozen.dpreference.DPreference
import java.io.IOException
import java.net.*
import libv2ray.Libv2ray
import kotlin.coroutines.coroutineContext
object Utils {
@@ -272,38 +271,13 @@ object Utils {
}
fun startVServiceFromToggle(context: Context): Boolean {
val result = startVService(context)
if (!result) {
val result = context.defaultDPreference.getPrefString(AppConfig.PREF_CURR_CONFIG, "")
if (result.isBlank()) {
context.toast(R.string.app_tile_first_use)
}
return result
}
/**
* startVService
*/
fun startVService(context: Context): Boolean {
if (context.v2RayApplication.defaultDPreference.getPrefBoolean(SettingsActivity.PREF_PROXY_SHARING, false)) {
context.toast(R.string.toast_warning_pref_proxysharing_short)
}else{
context.toast(R.string.toast_services_start)
}
if (AngConfigManager.genStoreV2rayConfig(-1)) {
val configContent = AngConfigManager.currGeneratedV2rayConfig()
val configType = AngConfigManager.currConfigType()
if (configType == EConfigType.CUSTOM) {
try {
Libv2ray.testConfig(configContent)
} catch (e: Exception) {
context.toast(e.toString())
return false
}
}
V2RayServiceManager.startV2Ray(context, context.v2RayApplication.defaultDPreference.getPrefString(AppConfig.PREF_MODE, "VPN"))
return true
} else {
return false
}
V2RayServiceManager.startV2Ray(context)
return true
}
/**
@@ -319,8 +293,11 @@ object Utils {
* startVService
*/
fun startVService(context: Context, index: Int): Boolean {
AngConfigManager.setActiveServer(index)
return startVService(context)
if (AngConfigManager.setActiveServer(index) < 0) {
return false
}
V2RayServiceManager.startV2Ray(context)
return true
}
/**

View File

@@ -458,12 +458,10 @@ object V2rayConfigUtil {
rulesIP.outboundTag = tag
rulesIP.ip = ArrayList<String>()
userRule.trim().replace("\n", "")
.split(",")
.forEach {
userRule.split(",").map { it.trim() }.forEach {
if (Utils.isIpAddress(it) || it.startsWith("geoip:")) {
rulesIP.ip?.add(it)
} else if (it.isNotBlank() || it.isNotEmpty())
} else if (it.isNotEmpty())
// if (Utils.isValidUrl(it)
// || it.startsWith("geosite:")
// || it.startsWith("regexp:")
@@ -487,9 +485,8 @@ object V2rayConfigUtil {
private fun userRule2Domian(userRule: String): ArrayList<String> {
val domain = ArrayList<String>()
userRule.trim().replace("\n", "").split(",").forEach {
if ((it.startsWith("geosite:") || it.startsWith("domain:")) &&
it.isNotBlank() && it.isNotEmpty()) {
userRule.split(",").map { it.trim() }.forEach {
if (it.startsWith("geosite:") || it.startsWith("domain:")) {
domain.add(it)
}
}

View File

@@ -0,0 +1,46 @@
package com.v2ray.ang.viewmodel
import android.app.Application
import android.arch.lifecycle.AndroidViewModel
import android.content.SharedPreferences
import android.support.v7.preference.PreferenceManager
import android.util.Log
import com.v2ray.ang.AppConfig
import com.v2ray.ang.ui.SettingsActivity.Companion
import com.v2ray.ang.util.AngConfigManager
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
class SettingsViewModel(application: Application) : AndroidViewModel(application), SharedPreferences.OnSharedPreferenceChangeListener {
fun startListenPreferenceChange() {
PreferenceManager.getDefaultSharedPreferences(getApplication()).registerOnSharedPreferenceChangeListener(this)
}
override fun onCleared() {
PreferenceManager.getDefaultSharedPreferences(getApplication()).unregisterOnSharedPreferenceChangeListener(this)
Log.i(AppConfig.ANG_PACKAGE, "Settings ViewModel is cleared")
super.onCleared()
}
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String?) {
Log.d(AppConfig.ANG_PACKAGE, "Observe settings changed: $key")
when(key) {
Companion.PREF_SNIFFING_ENABLED,
Companion.PREF_PROXY_SHARING,
Companion.PREF_LOCAL_DNS_ENABLED,
Companion.PREF_REMOTE_DNS,
Companion.PREF_DOMESTIC_DNS,
Companion.PREF_ROUTING_DOMAIN_STRATEGY,
Companion.PREF_ROUTING_MODE,
AppConfig.PREF_V2RAY_ROUTING_AGENT,
AppConfig.PREF_V2RAY_ROUTING_BLOCKED,
AppConfig.PREF_V2RAY_ROUTING_DIRECT -> {
GlobalScope.launch {
if (!AngConfigManager.genStoreV2rayConfig()) {
Log.d(AppConfig.ANG_PACKAGE, "$key changed but generate full configuration failed!")
}
}
}
}
}
}

View File

@@ -48,7 +48,8 @@
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
android:layout_weight="1"
android:nextFocusRight="@+id/fab" />
<LinearLayout
android:id="@+id/layout_test"
@@ -96,7 +97,8 @@
android:src="@drawable/ic_v_idle"
android:clickable="true"
android:focusable="true"
app:layout_anchorGravity="bottom|right|end" />
app:layout_anchorGravity="bottom|right|end"
android:nextFocusLeft="@+id/recycler_view" />
</com.github.jorgecastilloprz.FABProgressCircle>
</android.support.design.widget.CoordinatorLayout>

View File

@@ -1,15 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/item_bg"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical">
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/item_bg"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical">
<android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="3dp"
card_view:cardCornerRadius="5dp">
app:cardCornerRadius="5dp">
<LinearLayout
android:id="@+id/info_container"
@@ -18,34 +20,19 @@
android:layout_gravity="center"
android:gravity="center"
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"
android:layout_height="match_parent"
android:layout_gravity="center"
android:orientation="vertical">
<TextView
android:id="@+id/tv_subid"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="8dp"
android:textAppearance="@style/TextAppearance.AppCompat.Small" />
<android.support.v7.widget.AppCompatRadioButton
<android.support.v7.widget.AppCompatRadioButton
android:id="@+id/btn_radio"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:clickable="false"
android:focusable="false"
android:focusableInTouchMode="false" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
@@ -85,20 +72,31 @@
android:orientation="vertical">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:orientation="vertical"
android:paddingEnd="5dp">
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingEnd="5dp">
<TextView
android:id="@+id/tv_test_result"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:lines="1"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
android:textColor="@color/colorPing"
android:textSize="10sp" />
android:id="@+id/tv_subscription"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:lines="1"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
android:textColor="@color/colorSubscription"
android:textSize="10sp"
tools:text="Sub" />
<TextView
android:id="@+id/tv_test_result"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:lines="1"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
android:textColor="@color/colorPing"
android:textSize="10sp"
tools:text="214ms" />
</LinearLayout>
<LinearLayout
@@ -185,4 +183,4 @@
</LinearLayout>
</android.support.v7.widget.CardView>
</LinearLayout>
</LinearLayout>

View File

@@ -9,4 +9,5 @@
<color name="icons">#FFFFFF</color>
<color name="divider">#BDBDBD</color>
<color name="colorPing">#185534</color>
<color name="colorSubscription">#247BA0</color>
</resources>