diff --git a/V2rayNG/app/src/main/AndroidManifest.xml b/V2rayNG/app/src/main/AndroidManifest.xml
index 3d627cf7..71ddbab5 100644
--- a/V2rayNG/app/src/main/AndroidManifest.xml
+++ b/V2rayNG/app/src/main/AndroidManifest.xml
@@ -96,6 +96,12 @@
android:value="true" />
+
+
+
+
+ android:name=".service.QSTileService"
+ android:icon="@drawable/ic_v"
+ android:label="@string/app_tile_name"
+ android:permission="android.permission.BIND_QUICK_SETTINGS_TILE"
+ android:process=":RunSoLibV2RayDaemon">
@@ -126,7 +134,8 @@
-
+
diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/AppConfig.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/AppConfig.kt
index eff2c8c2..2956e477 100644
--- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/AppConfig.kt
+++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/AppConfig.kt
@@ -13,6 +13,8 @@ object AppConfig {
const val PREF_CURR_CONFIG_DOMAIN = "pref_v2ray_config_domain"
const val PREF_CURR_CONFIG_OUTBOUND_TAGS = "pref_v2ray_config_outbound_tags"
const val PREF_INAPP_BUY_IS_PREMIUM = "pref_inapp_buy_is_premium"
+ const val PREF_MODE = "pref_mode"
+
const val VMESS_PROTOCOL: String = "vmess://"
const val SS_PROTOCOL: String = "ss://"
const val SOCKS_PROTOCOL: String = "socks://"
diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/receiver/WidgetProvider.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/receiver/WidgetProvider.kt
index dfda98b9..0f79a90b 100644
--- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/receiver/WidgetProvider.kt
+++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/receiver/WidgetProvider.kt
@@ -9,6 +9,7 @@ import android.content.Intent
import android.widget.RemoteViews
import com.v2ray.ang.R
import com.v2ray.ang.AppConfig
+import com.v2ray.ang.service.V2RayServiceManager
import com.v2ray.ang.util.Utils
class WidgetProvider : AppWidgetProvider() {
@@ -17,21 +18,19 @@ 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)
+ updateWidgetBackground(context, appWidgetManager, appWidgetIds, V2RayServiceManager.v2rayPoint.isRunning)
}
private fun updateWidgetBackground(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray, isRunning: Boolean) {
val remoteViews = RemoteViews(context.packageName, R.layout.widget_switch)
val intent = Intent(context, WidgetProvider::class.java)
- intent.setAction(AppConfig.BROADCAST_ACTION_WIDGET_CLICK)
+ intent.action = 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);
+ 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);
+ remoteViews.setInt(R.id.layout_switch, "setBackgroundResource", R.drawable.ic_rounded_corner_grey)
}
for (appWidgetId in appWidgetIds) {
@@ -40,23 +39,28 @@ class WidgetProvider : AppWidgetProvider() {
}
/**
- * 接收窗口小部件点击时发送的广播
+ * 接收窗口小部件发送的广播
*/
override fun onReceive(context: Context, intent: Intent) {
super.onReceive(context, intent)
if (AppConfig.BROADCAST_ACTION_WIDGET_CLICK == intent.action) {
-
- val isRunning = Utils.isServiceRun(context, "com.v2ray.ang.service.V2RayVpnService")
- if (isRunning) {
-// context.toast(R.string.toast_services_stop)
+ if (V2RayServiceManager.v2rayPoint.isRunning) {
Utils.stopVService(context)
} else {
-// context.toast(R.string.toast_services_start)
- Utils.startVService(context)
+ Utils.startVServiceFromToggle(context)
}
+ } else if (AppConfig.BROADCAST_ACTION_ACTIVITY == intent.action) {
val manager = AppWidgetManager.getInstance(context)
- updateWidgetBackground(context, manager, manager.getAppWidgetIds(ComponentName(context, WidgetProvider::class.java)),
- !isRunning);
+ 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)
+ }
+ }
}
}
}
diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/QSTileService.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/QSTileService.kt
index cbb82425..3c9320e9 100644
--- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/QSTileService.kt
+++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/QSTileService.kt
@@ -6,7 +6,6 @@ import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.graphics.drawable.Icon
-import android.net.VpnService
import android.os.Build
import android.service.quicksettings.Tile
import android.service.quicksettings.TileService
@@ -15,7 +14,6 @@ import com.v2ray.ang.R
import com.v2ray.ang.extension.defaultDPreference
import com.v2ray.ang.util.MessageUtil
import com.v2ray.ang.util.Utils
-import org.jetbrains.anko.toast
import java.lang.ref.SoftReference
@@ -33,7 +31,6 @@ class QSTileService : TileService() {
qsTile?.icon = Icon.createWithResource(applicationContext, R.drawable.ic_v)
}
-
qsTile?.updateTile()
}
@@ -56,11 +53,7 @@ class QSTileService : TileService() {
super.onClick()
when (qsTile.state) {
Tile.STATE_INACTIVE -> {
- val intent = VpnService.prepare(this)
- if (intent == null)
- if (!Utils.startVService(this)) {
- toast(R.string.app_tile_first_use)
- }
+ Utils.startVServiceFromToggle(this)
}
Tile.STATE_ACTIVE -> {
Utils.stopVService(this)
@@ -93,4 +86,4 @@ class QSTileService : TileService() {
}
}
}
-}
\ No newline at end of file
+}
diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/ServiceControl.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/ServiceControl.kt
new file mode 100644
index 00000000..6fd22c79
--- /dev/null
+++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/ServiceControl.kt
@@ -0,0 +1,15 @@
+package com.v2ray.ang.service
+
+import android.app.Service
+
+interface ServiceControl {
+ fun getService(): Service
+
+ fun startService(parameters: String)
+
+ fun stopService()
+
+ fun vpnProtect(socket: Int): Boolean
+
+ fun vpnSendFd()
+}
diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayProxyOnlyService.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayProxyOnlyService.kt
new file mode 100644
index 00000000..3c01aefb
--- /dev/null
+++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayProxyOnlyService.kt
@@ -0,0 +1,47 @@
+package com.v2ray.ang.service
+
+import android.app.Service
+import android.content.Intent
+import android.os.IBinder
+import java.lang.ref.SoftReference
+
+class V2RayProxyOnlyService : Service(), ServiceControl {
+ override fun onCreate() {
+ super.onCreate()
+ V2RayServiceManager.serviceControl = SoftReference(this)
+ }
+
+ override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
+ V2RayServiceManager.startV2rayPoint()
+ return START_STICKY
+ }
+
+ override fun onDestroy() {
+ super.onDestroy()
+ V2RayServiceManager.stopV2rayPoint()
+ }
+
+ override fun getService(): Service {
+ return this
+ }
+
+ override fun startService(parameters: String) {
+ // do nothing
+ }
+
+ override fun stopService() {
+ stopSelf()
+ }
+
+ override fun vpnProtect(socket: Int): Boolean {
+ return true
+ }
+
+ override fun vpnSendFd() {
+ // do nothing
+ }
+
+ override fun onBind(intent: Intent?): IBinder? {
+ return null
+ }
+}
diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayServiceManager.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayServiceManager.kt
new file mode 100644
index 00000000..a85e8467
--- /dev/null
+++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayServiceManager.kt
@@ -0,0 +1,367 @@
+package com.v2ray.ang.service
+
+import android.app.Notification
+import android.app.NotificationChannel
+import android.app.NotificationManager
+import android.app.PendingIntent
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.graphics.Color
+import android.os.Build
+import android.support.annotation.RequiresApi
+import android.support.v4.app.NotificationCompat
+import android.util.Log
+import com.v2ray.ang.AppConfig
+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.ui.MainActivity
+import com.v2ray.ang.ui.SettingsActivity
+import com.v2ray.ang.util.MessageUtil
+import com.v2ray.ang.util.Utils
+import go.Seq
+import libv2ray.Libv2ray
+import libv2ray.V2RayPoint
+import libv2ray.V2RayVPNServiceSupportsSet
+import rx.Observable
+import rx.Subscription
+import java.lang.ref.SoftReference
+import kotlin.math.min
+
+object V2RayServiceManager {
+ private const val NOTIFICATION_ID = 1
+ private const val NOTIFICATION_PENDING_INTENT_CONTENT = 0
+ private const val NOTIFICATION_PENDING_INTENT_STOP_V2RAY = 1
+ private const val NOTIFICATION_ICON_THRESHOLD = 3000
+
+ val v2rayPoint: V2RayPoint = Libv2ray.newV2RayPoint(V2RayCallback())
+ private val mMsgReceive = ReceiveMessageHandler()
+
+ var serviceControl: SoftReference? = null
+ set(value) {
+ field = value
+ val context = value?.get()?.getService()?.applicationContext
+ context?.let {
+ v2rayPoint.packageName = Utils.packagePath(context)
+ v2rayPoint.packageCodePath = context.applicationInfo.nativeLibraryDir + "/"
+ Seq.setContext(context)
+ }
+ }
+
+ 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") {
+ Intent(context.applicationContext, V2RayVpnService::class.java)
+ } else {
+ Intent(context.applicationContext, V2RayProxyOnlyService::class.java)
+ }
+ if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) {
+ context.startForegroundService(intent)
+ } else {
+ context.startService(intent)
+ }
+ }
+
+ private class V2RayCallback : V2RayVPNServiceSupportsSet {
+ override fun shutdown(): Long {
+ val serviceControl = serviceControl?.get() ?: return -1
+ // called by go
+ // shutdown the whole vpn service
+ return try {
+ serviceControl.stopService()
+ 0
+ } catch (e: Exception) {
+ Log.d(serviceControl.getService().packageName, e.toString())
+ -1
+ }
+ }
+
+ override fun prepare(): Long {
+ return 0
+ }
+
+ override fun protect(l: Long): Long {
+ val serviceControl = serviceControl?.get() ?: return 1
+ return if (serviceControl.vpnProtect(l.toInt())) 0 else 1
+ }
+
+ override fun onEmitStatus(l: Long, s: String?): Long {
+ //Logger.d(s)
+ return 0
+ }
+
+ override fun setup(s: String): Long {
+ val serviceControl = serviceControl?.get() ?: return -1
+ //Logger.d(s)
+ return try {
+ serviceControl.startService(s)
+ lastQueryTime = System.currentTimeMillis()
+ startSpeedNotification()
+ 0
+ } catch (e: Exception) {
+ Log.d(serviceControl.getService().packageName, e.toString())
+ -1
+ }
+ }
+
+ 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() {
+ val service = serviceControl?.get()?.getService() ?: return
+ if (!v2rayPoint.isRunning) {
+
+ try {
+ val mFilter = IntentFilter(AppConfig.BROADCAST_ACTION_SERVICE)
+ mFilter.addAction(Intent.ACTION_SCREEN_ON)
+ mFilter.addAction(Intent.ACTION_SCREEN_OFF)
+ mFilter.addAction(Intent.ACTION_USER_PRESENT)
+ service.registerReceiver(mMsgReceive, mFilter)
+ } catch (e: Exception) {
+ Log.d(service.packageName, e.toString())
+ }
+
+ v2rayPoint.configureFileContent = service.defaultDPreference.getPrefString(AppConfig.PREF_CURR_CONFIG, "")
+ 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, "")
+
+ try {
+ v2rayPoint.runLoop()
+ } catch (e: Exception) {
+ Log.d(service.packageName, e.toString())
+ }
+
+ if (v2rayPoint.isRunning) {
+ MessageUtil.sendMsg2UI(service, AppConfig.MSG_STATE_START_SUCCESS, "")
+ showNotification()
+ } else {
+ MessageUtil.sendMsg2UI(service, AppConfig.MSG_STATE_START_FAILURE, "")
+ cancelNotification()
+ }
+ }
+ }
+
+ fun stopV2rayPoint() {
+ val service = serviceControl?.get()?.getService() ?: return
+
+ if (v2rayPoint.isRunning) {
+ try {
+ v2rayPoint.stopLoop()
+ } catch (e: Exception) {
+ Log.d(service.packageName, e.toString())
+ }
+ }
+
+ MessageUtil.sendMsg2UI(service, AppConfig.MSG_STATE_STOP_SUCCESS, "")
+ cancelNotification()
+
+ try {
+ service.unregisterReceiver(mMsgReceive)
+ } catch (e: Exception) {
+ Log.d(service.packageName, e.toString())
+ }
+ }
+
+ private class ReceiveMessageHandler : BroadcastReceiver() {
+ override fun onReceive(ctx: Context?, intent: Intent?) {
+ val serviceControl = serviceControl?.get() ?: return
+ when (intent?.getIntExtra("key", 0)) {
+ AppConfig.MSG_REGISTER_CLIENT -> {
+ //Logger.e("ReceiveMessageHandler", intent?.getIntExtra("key", 0).toString())
+ if (v2rayPoint.isRunning) {
+ MessageUtil.sendMsg2UI(serviceControl.getService(), AppConfig.MSG_STATE_RUNNING, "")
+ } else {
+ MessageUtil.sendMsg2UI(serviceControl.getService(), AppConfig.MSG_STATE_NOT_RUNNING, "")
+ }
+ }
+ AppConfig.MSG_UNREGISTER_CLIENT -> {
+ // nothing to do
+ }
+ AppConfig.MSG_STATE_START -> {
+ // nothing to do
+ }
+ AppConfig.MSG_STATE_STOP -> {
+ serviceControl.stopService()
+ }
+ AppConfig.MSG_STATE_RESTART -> {
+ startV2rayPoint()
+ }
+ }
+
+ when (intent?.action) {
+ Intent.ACTION_SCREEN_OFF -> {
+ Log.d(AppConfig.ANG_PACKAGE, "SCREEN_OFF, stop querying stats")
+ stopSpeedNotification()
+ }
+ Intent.ACTION_SCREEN_ON -> {
+ Log.d(AppConfig.ANG_PACKAGE, "SCREEN_ON, start querying stats")
+ startSpeedNotification()
+ }
+ }
+ }
+ }
+
+ private fun showNotification() {
+ val service = serviceControl?.get()?.getService() ?: return
+ val startMainIntent = Intent(service, MainActivity::class.java)
+ val contentPendingIntent = PendingIntent.getActivity(service,
+ NOTIFICATION_PENDING_INTENT_CONTENT, startMainIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT)
+
+ val stopV2RayIntent = Intent(AppConfig.BROADCAST_ACTION_SERVICE)
+ stopV2RayIntent.`package` = AppConfig.ANG_PACKAGE
+ stopV2RayIntent.putExtra("key", AppConfig.MSG_STATE_STOP)
+
+ val stopV2RayPendingIntent = PendingIntent.getBroadcast(service,
+ NOTIFICATION_PENDING_INTENT_STOP_V2RAY, stopV2RayIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT)
+
+ val channelId =
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ createNotificationChannel()
+ } else {
+ // If earlier version channel ID is not used
+ // https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context)
+ ""
+ }
+
+ mBuilder = NotificationCompat.Builder(service, channelId)
+ .setSmallIcon(R.drawable.ic_v)
+ .setContentTitle(service.defaultDPreference.getPrefString(AppConfig.PREF_CURR_CONFIG_NAME, ""))
+ .setPriority(NotificationCompat.PRIORITY_MIN)
+ .setOngoing(true)
+ .setShowWhen(false)
+ .setOnlyAlertOnce(true)
+ .setContentIntent(contentPendingIntent)
+ .addAction(R.drawable.ic_close_grey_800_24dp,
+ service.getString(R.string.notification_action_stop_v2ray),
+ stopV2RayPendingIntent)
+ //.build()
+
+ //mBuilder?.setDefaults(NotificationCompat.FLAG_ONLY_ALERT_ONCE) //取消震动,铃声其他都不好使
+
+ service.startForeground(NOTIFICATION_ID, mBuilder?.build())
+ }
+
+ @RequiresApi(Build.VERSION_CODES.O)
+ private fun createNotificationChannel(): String {
+ val channelId = "RAY_NG_M_CH_ID"
+ val channelName = "V2rayNG Background Service"
+ val chan = NotificationChannel(channelId,
+ channelName, NotificationManager.IMPORTANCE_HIGH)
+ chan.lightColor = Color.DKGRAY
+ chan.importance = NotificationManager.IMPORTANCE_NONE
+ chan.lockscreenVisibility = Notification.VISIBILITY_PRIVATE
+ getNotificationManager()?.createNotificationChannel(chan)
+ return channelId
+ }
+
+ fun cancelNotification() {
+ val service = serviceControl?.get()?.getService() ?: return
+ service.stopForeground(true)
+ mBuilder = null
+ mSubscription?.unsubscribe()
+ mSubscription = null
+ }
+
+ private fun updateNotification(contentText: String, proxyTraffic: Long, directTraffic: Long) {
+ if (mBuilder != null) {
+ 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())
+ }
+ }
+
+ private fun getNotificationManager(): NotificationManager? {
+ if (mNotificationManager == null) {
+ val service = serviceControl?.get()?.getService() ?: return null
+ mNotificationManager = service.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
+ }
+ return mNotificationManager
+ }
+
+ fun startSpeedNotification() {
+ val service = serviceControl?.get()?.getService() ?: return
+ if (mSubscription == null &&
+ v2rayPoint.isRunning &&
+ service.defaultDPreference.getPrefBoolean(SettingsActivity.PREF_SPEED_ENABLED, false)) {
+ var lastZeroSpeed = false
+ val outboundTags = service.defaultDPreference.getPrefStringOrderedSet(AppConfig.PREF_CURR_CONFIG_OUTBOUND_TAGS, LinkedHashSet())
+ outboundTags.remove(TAG_DIRECT)
+
+ mSubscription = Observable.interval(3, java.util.concurrent.TimeUnit.SECONDS)
+ .subscribe {
+ val queryTime = System.currentTimeMillis()
+ val sinceLastQueryInSeconds = (queryTime - lastQueryTime) / 1000.0
+ var proxyTotal = 0L
+ val text = StringBuilder()
+ outboundTags.forEach {
+ val up = v2rayPoint.queryStats(it, "uplink")
+ val down = v2rayPoint.queryStats(it, "downlink")
+ if (up + down > 0) {
+ appendSpeedString(text, it, up / sinceLastQueryInSeconds, down / sinceLastQueryInSeconds)
+ proxyTotal += up + down
+ }
+ }
+ val directUplink = v2rayPoint.queryStats(TAG_DIRECT, "uplink")
+ val directDownlink = v2rayPoint.queryStats(TAG_DIRECT, "downlink")
+ val zeroSpeed = (proxyTotal == 0L && directUplink == 0L && directDownlink == 0L)
+ if (!zeroSpeed || !lastZeroSpeed) {
+ if (proxyTotal == 0L) {
+ appendSpeedString(text, outboundTags.firstOrNull(), 0.0, 0.0)
+ }
+ appendSpeedString(text, TAG_DIRECT, directUplink / sinceLastQueryInSeconds,
+ directDownlink / sinceLastQueryInSeconds)
+ updateNotification(text.toString(), proxyTotal, directDownlink + directUplink)
+ }
+ lastZeroSpeed = zeroSpeed
+ lastQueryTime = queryTime
+ }
+ }
+ }
+
+ private fun appendSpeedString(text: StringBuilder, name: String?, up: Double, down: Double) {
+ var n = name ?: "no tag"
+ n = n.substring(0, min(n.length, 6))
+ text.append(n)
+ for (i in n.length..6 step 2) {
+ text.append("\t")
+ }
+ text.append("• ${up.toLong().toSpeedString()}↑ ${down.toLong().toSpeedString()}↓\n")
+ }
+
+ fun stopSpeedNotification() {
+ 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)
+ }
+ }
+}
diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayVpnService.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayVpnService.kt
index 1bd13151..6dc41713 100644
--- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayVpnService.kt
+++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayVpnService.kt
@@ -1,67 +1,27 @@
package com.v2ray.ang.service
-import android.app.Notification
-import android.app.NotificationChannel
-import android.app.NotificationManager
-import android.app.PendingIntent
-import android.content.BroadcastReceiver
+import android.app.*
import android.content.Context
import android.content.Intent
-import android.content.IntentFilter
import android.content.pm.PackageManager
-import android.graphics.Color
import android.net.*
import android.os.Build
import android.os.ParcelFileDescriptor
import android.os.StrictMode
import android.support.annotation.RequiresApi
-import android.support.v4.app.NotificationCompat
import android.util.Log
import com.v2ray.ang.AppConfig
-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.ui.MainActivity
import com.v2ray.ang.ui.PerAppProxyActivity
import com.v2ray.ang.ui.SettingsActivity
-import com.v2ray.ang.util.MessageUtil
import com.v2ray.ang.util.Utils
-import go.Seq
-import libv2ray.Libv2ray
-import libv2ray.V2RayVPNServiceSupportsSet
import org.jetbrains.anko.doAsync
-import rx.Observable
-import rx.Subscription
import java.io.File
import java.lang.ref.SoftReference
-class V2RayVpnService : VpnService() {
- companion object {
- 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)
- if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) {
- context.startForegroundService(intent)
- } else {
- context.startService(intent)
- }
- }
- }
-
- private val v2rayPoint = Libv2ray.newV2RayPoint(V2RayCallback())
- private var lastQueryTime = 0L
- private lateinit var configContent: String
+class V2RayVpnService : VpnService(), ServiceControl {
private lateinit var mInterface: ParcelFileDescriptor
- val fd: Int get() = mInterface.fd
- private var mBuilder: NotificationCompat.Builder? = null
- private var mSubscription: Subscription? = null
- private var mNotificationManager: NotificationManager? = null
-
/**
* Unfortunately registerDefaultNetworkCallback is going to return our VPN interface: https://android.googlesource.com/platform/frameworks/base/+/dda156ab0c5d66ad82bdcf76cda07cbc0a9c8a2e
@@ -79,7 +39,6 @@ class V2RayVpnService : VpnService() {
.build()
}
-
private val connectivity by lazy { getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager }
private val defaultNetworkCallback by lazy @RequiresApi(Build.VERSION_CODES.P) {
@@ -96,6 +55,7 @@ class V2RayVpnService : VpnService() {
}
}
}
+
private var listeningForDefaultNetwork = false
override fun onCreate() {
@@ -103,9 +63,7 @@ class V2RayVpnService : VpnService() {
val policy = StrictMode.ThreadPolicy.Builder().permitAll().build()
StrictMode.setThreadPolicy(policy)
- v2rayPoint.packageName = Utils.packagePath(applicationContext)
- v2rayPoint.packageCodePath = applicationContext.applicationInfo.nativeLibraryDir + "/"
- Seq.setContext(applicationContext)
+ V2RayServiceManager.serviceControl = SoftReference(this)
}
override fun onRevoke() {
@@ -119,12 +77,12 @@ class V2RayVpnService : VpnService() {
override fun onDestroy() {
super.onDestroy()
- cancelNotification()
+ V2RayServiceManager.cancelNotification()
}
- fun setup(parameters: String) {
+ private fun setup(parameters: String) {
- val prepare = VpnService.prepare(this)
+ val prepare = prepare(this)
if (prepare != null) {
return
}
@@ -147,8 +105,8 @@ class V2RayVpnService : VpnService() {
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('/')
+ resources.getStringArray(R.array.bypass_private_ip_address).forEach { cidr ->
+ val addr = cidr.split('/')
builder.addRoute(addr[0], addr[1].toInt())
}
}
@@ -189,9 +147,9 @@ class V2RayVpnService : VpnService() {
try {
mInterface.close()
} catch (ignored: Exception) {
+ // ignored
}
-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
connectivity.requestNetwork(defaultNetworkRequest, defaultNetworkCallback)
listeningForDefaultNetwork = true
@@ -204,15 +162,9 @@ class V2RayVpnService : VpnService() {
// Create a new interface using the builder and save the parameters.
mInterface = builder.establish()
sendFd()
- lastQueryTime = System.currentTimeMillis()
- startSpeedNotification()
}
- fun shutdown() {
- stopV2Ray(true)
- }
-
- fun sendFd() {
+ private fun sendFd() {
val fd = mInterface.fileDescriptor
val path = File(Utils.packagePath(applicationContext), "sock_path").absolutePath
@@ -220,7 +172,7 @@ class V2RayVpnService : VpnService() {
var tries = 0
while (true) try {
Thread.sleep(50L shl tries)
- Log.d(packageName, "sendFd tries: " + tries.toString())
+ Log.d(packageName, "sendFd tries: $tries")
LocalSocket().use { localSocket ->
localSocket.connect(LocalSocketAddress(path, LocalSocketAddress.Namespace.FILESYSTEM))
localSocket.setFileDescriptorsForSend(arrayOf(fd))
@@ -236,74 +188,24 @@ class V2RayVpnService : VpnService() {
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
- startV2ray()
+ V2RayServiceManager.startV2rayPoint()
return START_STICKY
//return super.onStartCommand(intent, flags, startId)
}
- private fun startV2ray() {
- if (!v2rayPoint.isRunning) {
-
- try {
- val mFilter = IntentFilter(AppConfig.BROADCAST_ACTION_SERVICE)
- mFilter.addAction(Intent.ACTION_SCREEN_ON)
- mFilter.addAction(Intent.ACTION_SCREEN_OFF)
- mFilter.addAction(Intent.ACTION_USER_PRESENT)
- registerReceiver(mMsgReceive, mFilter)
- } catch (e: Exception) {
- }
-
- configContent = defaultDPreference.getPrefString(AppConfig.PREF_CURR_CONFIG, "")
- v2rayPoint.configureFileContent = configContent
- v2rayPoint.enableLocalDNS = defaultDPreference.getPrefBoolean(SettingsActivity.PREF_LOCAL_DNS_ENABLED, false)
- v2rayPoint.forwardIpv6 = defaultDPreference.getPrefBoolean(SettingsActivity.PREF_FORWARD_IPV6, false)
- v2rayPoint.domainName = defaultDPreference.getPrefString(AppConfig.PREF_CURR_CONFIG_DOMAIN, "")
-
- try {
- v2rayPoint.runLoop()
- } catch (e: Exception) {
- Log.d(packageName, e.toString())
- }
-
- if (v2rayPoint.isRunning) {
- MessageUtil.sendMsg2UI(this, AppConfig.MSG_STATE_START_SUCCESS, "")
- showNotification()
- } else {
- MessageUtil.sendMsg2UI(this, AppConfig.MSG_STATE_START_FAILURE, "")
- cancelNotification()
- }
- }
- // showNotification()
- }
-
private fun stopV2Ray(isForced: Boolean = true) {
// val configName = defaultDPreference.getPrefString(PREF_CURR_CONFIG_GUID, "")
// val emptyInfo = VpnNetworkInfo()
// val info = loadVpnNetworkInfo(configName, emptyInfo)!! + (lastNetworkInfo ?: emptyInfo)
// saveVpnNetworkInfo(configName, info)
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
- if (listeningForDefaultNetwork) {
- connectivity.unregisterNetworkCallback(defaultNetworkCallback)
- listeningForDefaultNetwork = false
- }
- }
- if (v2rayPoint.isRunning) {
- try {
- v2rayPoint.stopLoop()
- } catch (e: Exception) {
- Log.d(packageName, e.toString())
- }
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && listeningForDefaultNetwork) {
+ connectivity.unregisterNetworkCallback(defaultNetworkCallback)
+ listeningForDefaultNetwork = false
}
- MessageUtil.sendMsg2UI(this, AppConfig.MSG_STATE_STOP_SUCCESS, "")
- cancelNotification()
+ V2RayServiceManager.stopV2rayPoint()
if (isForced) {
- try {
- unregisterReceiver(mMsgReceive)
- } catch (e: Exception) {
- }
-
//stopSelf has to be called ahead of mInterface.close(). otherwise v2ray core cannot be stooped
//It's strage but true.
//This can be verified by putting stopself() behind and call stopLoop and startLoop
@@ -314,242 +216,29 @@ class V2RayVpnService : VpnService() {
try {
mInterface.close()
} catch (ignored: Exception) {
+ // ignored
}
}
}
- private fun showNotification() {
- val startMainIntent = Intent(applicationContext, MainActivity::class.java)
- val contentPendingIntent = PendingIntent.getActivity(applicationContext,
- NOTIFICATION_PENDING_INTENT_CONTENT, startMainIntent,
- PendingIntent.FLAG_UPDATE_CURRENT)
-
- val stopV2RayIntent = Intent(AppConfig.BROADCAST_ACTION_SERVICE)
- stopV2RayIntent.`package` = AppConfig.ANG_PACKAGE
- stopV2RayIntent.putExtra("key", AppConfig.MSG_STATE_STOP)
-
- val stopV2RayPendingIntent = PendingIntent.getBroadcast(applicationContext,
- NOTIFICATION_PENDING_INTENT_STOP_V2RAY, stopV2RayIntent,
- PendingIntent.FLAG_UPDATE_CURRENT)
-
- val channelId =
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- createNotificationChannel()
- } else {
- // If earlier version channel ID is not used
- // https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context)
- ""
- }
-
- mBuilder = NotificationCompat.Builder(applicationContext, channelId)
- .setSmallIcon(R.drawable.ic_v)
- .setContentTitle(defaultDPreference.getPrefString(AppConfig.PREF_CURR_CONFIG_NAME, ""))
- .setPriority(NotificationCompat.PRIORITY_MIN)
- .setOngoing(true)
- .setShowWhen(false)
- .setOnlyAlertOnce(true)
- .setContentIntent(contentPendingIntent)
- .addAction(R.drawable.ic_close_grey_800_24dp,
- getString(R.string.notification_action_stop_v2ray),
- stopV2RayPendingIntent)
- //.build()
-
- //mBuilder?.setDefaults(NotificationCompat.FLAG_ONLY_ALERT_ONCE) //取消震动,铃声其他都不好使
-
- startForeground(NOTIFICATION_ID, mBuilder?.build())
+ override fun getService(): Service {
+ return this
}
- @RequiresApi(Build.VERSION_CODES.O)
- private fun createNotificationChannel(): String {
- val channelId = "RAY_NG_M_CH_ID"
- val channelName = "V2rayNG Background Service"
- val chan = NotificationChannel(channelId,
- channelName, NotificationManager.IMPORTANCE_HIGH)
- chan.lightColor = Color.DKGRAY
- chan.importance = NotificationManager.IMPORTANCE_NONE
- chan.lockscreenVisibility = Notification.VISIBILITY_PRIVATE
- getNotificationManager().createNotificationChannel(chan)
- return channelId
+ override fun startService(parameters: String) {
+ setup(parameters)
}
- private fun cancelNotification() {
- stopForeground(true)
- mBuilder = null
- mSubscription?.unsubscribe()
- mSubscription = null
+ override fun stopService() {
+ stopV2Ray(true)
}
- private fun updateNotification(contentText: String, proxyTraffic: Long, directTraffic: Long) {
- if (mBuilder != null) {
- 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())
- }
+ override fun vpnProtect(socket: Int): Boolean {
+ return protect(socket)
}
- private fun getNotificationManager(): NotificationManager {
- if (mNotificationManager == null) {
- mNotificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
- }
- return mNotificationManager!!
- }
-
- fun startSpeedNotification() {
- if (mSubscription == null &&
- v2rayPoint.isRunning &&
- defaultDPreference.getPrefBoolean(SettingsActivity.PREF_SPEED_ENABLED, false)) {
- var last_zero_speed = false
- val outboundTags = defaultDPreference.getPrefStringOrderedSet(AppConfig.PREF_CURR_CONFIG_OUTBOUND_TAGS, LinkedHashSet())
- outboundTags.remove(TAG_DIRECT)
-
- mSubscription = Observable.interval(3, java.util.concurrent.TimeUnit.SECONDS)
- .subscribe {
- val queryTime = System.currentTimeMillis()
- val sinceLastQueryInSeconds = (queryTime - lastQueryTime) / 1000.0
- var proxyTotal = 0L
- val text = StringBuilder()
- outboundTags.forEach {
- val up = v2rayPoint.queryStats(it, "uplink")
- val down = v2rayPoint.queryStats(it, "downlink")
- if (up + down > 0) {
- appendSpeedString(text, it, up / sinceLastQueryInSeconds, down / sinceLastQueryInSeconds)
- proxyTotal += up + down
- }
- }
- val directUplink = v2rayPoint.queryStats(TAG_DIRECT, "uplink")
- val directDownlink = v2rayPoint.queryStats(TAG_DIRECT, "downlink")
- val zero_speed = (proxyTotal == 0L && directUplink == 0L && directDownlink == 0L)
- if (!zero_speed || !last_zero_speed) {
- if (proxyTotal == 0L) {
- appendSpeedString(text, outboundTags.firstOrNull(), 0.0, 0.0)
- }
- appendSpeedString(text, TAG_DIRECT, directUplink / sinceLastQueryInSeconds,
- directDownlink / sinceLastQueryInSeconds)
- updateNotification(text.toString(), proxyTotal, directDownlink + directUplink)
- }
- last_zero_speed = zero_speed
- lastQueryTime = queryTime
- }
- }
- }
-
- private fun appendSpeedString(text: StringBuilder, name: String?, up: Double, down: Double) {
- var n = name ?: "no tag"
- n = n.substring(0, Math.min(n.length, 6))
- text.append(n)
- for (i in n.length..6 step 2) {
- text.append("\t")
- }
- text.append("• ${up.toLong().toSpeedString()}↑ ${down.toLong().toSpeedString()}↓\n")
- }
-
- fun stopSpeedNotification() {
- if (mSubscription != null) {
- mSubscription?.unsubscribe() //stop queryStats
- mSubscription = null
-
- val cf_name = defaultDPreference.getPrefString(AppConfig.PREF_CURR_CONFIG_NAME, "")
- updateNotification(cf_name, 0, 0)
- }
- }
-
- private inner class V2RayCallback : V2RayVPNServiceSupportsSet {
- override fun shutdown(): Long {
- // called by go
- // shutdown the whole vpn service
- try {
- this@V2RayVpnService.shutdown()
- return 0
- } catch (e: Exception) {
- Log.d(packageName, e.toString())
- return -1
- }
- }
-
- override fun prepare(): Long {
- return 0
- }
-
- override fun protect(l: Long) = (if (this@V2RayVpnService.protect(l.toInt())) 0 else 1).toLong()
-
- override fun onEmitStatus(l: Long, s: String?): Long {
- //Logger.d(s)
- return 0
- }
-
- override fun setup(s: String): Long {
- //Logger.d(s)
- try {
- this@V2RayVpnService.setup(s)
- return 0
- } catch (e: Exception) {
- Log.d(packageName, e.toString())
- return -1
- }
- }
-
- override fun sendFd(): Long {
- try {
- this@V2RayVpnService.sendFd()
- } catch (e: Exception) {
- Log.d(packageName, e.toString())
- return -1
- }
- return 0
- }
- }
-
- private var mMsgReceive = ReceiveMessageHandler(this@V2RayVpnService)
-
- private class ReceiveMessageHandler(vpnService: V2RayVpnService) : BroadcastReceiver() {
- internal var mReference: SoftReference = SoftReference(vpnService)
-
- override fun onReceive(ctx: Context?, intent: Intent?) {
- val vpnService = mReference.get()
- when (intent?.getIntExtra("key", 0)) {
- AppConfig.MSG_REGISTER_CLIENT -> {
- //Logger.e("ReceiveMessageHandler", intent?.getIntExtra("key", 0).toString())
-
- val isRunning = vpnService?.v2rayPoint!!.isRunning
- && VpnService.prepare(vpnService) == null
- if (isRunning) {
- MessageUtil.sendMsg2UI(vpnService, AppConfig.MSG_STATE_RUNNING, "")
- } else {
- MessageUtil.sendMsg2UI(vpnService, AppConfig.MSG_STATE_NOT_RUNNING, "")
- }
- }
- AppConfig.MSG_UNREGISTER_CLIENT -> {
-// vpnService?.mMsgSend = null
- }
- AppConfig.MSG_STATE_START -> {
- //nothing to do
- }
- AppConfig.MSG_STATE_STOP -> {
- vpnService?.stopV2Ray()
- }
- AppConfig.MSG_STATE_RESTART -> {
- vpnService?.startV2ray()
- }
- }
-
- when (intent?.action) {
- Intent.ACTION_SCREEN_OFF -> {
- Log.d(AppConfig.ANG_PACKAGE, "SCREEN_OFF, stop querying stats")
- vpnService?.stopSpeedNotification()
- }
- Intent.ACTION_SCREEN_ON -> {
- Log.d(AppConfig.ANG_PACKAGE, "SCREEN_ON, start querying stats")
- vpnService?.startSpeedNotification()
- }
- }
- }
+ override fun vpnSendFd() {
+ sendFd()
}
}
diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt
index 080858a7..b09b2a0f 100644
--- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt
+++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt
@@ -28,6 +28,7 @@ import android.support.v7.app.ActionBarDrawerToggle
import android.support.v7.widget.helper.ItemTouchHelper
import android.util.Log
import com.v2ray.ang.dto.EConfigType
+import com.v2ray.ang.extension.defaultDPreference
//import com.v2ray.ang.InappBuyActivity
import rx.Observable
import rx.android.schedulers.AndroidSchedulers
@@ -71,13 +72,15 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
fab.setOnClickListener {
if (isRunning) {
Utils.stopVService(this)
- } else {
+ } else if (defaultDPreference.getPrefString(AppConfig.PREF_MODE, "VPN") == "VPN") {
val intent = VpnService.prepare(this)
if (intent == null) {
startV2Ray()
} else {
startActivityForResult(intent, REQUEST_CODE_VPN_PREPARE)
}
+ } else {
+ startV2Ray()
}
}
layout_test.setOnClickListener {
diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ScSwitchActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ScSwitchActivity.kt
index 5b023055..3d3101ef 100644
--- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ScSwitchActivity.kt
+++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ScSwitchActivity.kt
@@ -1,104 +1,22 @@
package com.v2ray.ang.ui
-import android.content.*
-import android.net.VpnService
import com.v2ray.ang.R
import com.v2ray.ang.util.Utils
import android.os.Bundle
-import com.v2ray.ang.AppConfig
-import com.v2ray.ang.util.MessageUtil
-import java.lang.ref.SoftReference
-import android.content.IntentFilter
-import kotlinx.android.synthetic.main.activity_main.*
-import rx.Observable
-import rx.android.schedulers.AndroidSchedulers
-import java.util.concurrent.TimeUnit
+import com.v2ray.ang.service.V2RayServiceManager
class ScSwitchActivity : BaseActivity() {
- companion object {
- private const val REQUEST_CODE_VPN_PREPARE = 0
- }
-
- var isRunning = false
- set(value) {
- field = value
- if (value) {
- Utils.stopVService(this)
- } else {
- val intent = VpnService.prepare(this)
- if (intent == null) {
- Utils.startVService(this)
- } else {
- startActivityForResult(intent, REQUEST_CODE_VPN_PREPARE)
- }
- }
- finishActivity()
- }
-
- fun finishActivity() {
- try {
- Observable.timer(5000, TimeUnit.MILLISECONDS)
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe {
- finish()
- }
- } catch (e: Exception) {
- }
- }
-
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
moveTaskToBack(true)
setContentView(R.layout.activity_none)
- val isRunning = Utils.isServiceRun(this, "com.v2ray.ang.service.V2RayVpnService")
- if (isRunning) {
- //Utils.stopVService(this)
- mMsgReceive = ReceiveMessageHandler(this@ScSwitchActivity)
- registerReceiver(mMsgReceive, IntentFilter(AppConfig.BROADCAST_ACTION_ACTIVITY))
- MessageUtil.sendMsg2Service(this, AppConfig.MSG_REGISTER_CLIENT, "")
-
+ if (V2RayServiceManager.v2rayPoint.isRunning) {
+ Utils.stopVService(this)
} else {
- Utils.startVService(this)
- finishActivity()
+ Utils.startVServiceFromToggle(this)
}
+ finish()
}
-
- override fun onStop() {
- super.onStop()
- if (mMsgReceive != null) {
- unregisterReceiver(mMsgReceive)
- mMsgReceive = null
- }
- }
-
- private var mMsgReceive: BroadcastReceiver? = null
-
- private class ReceiveMessageHandler(activity: ScSwitchActivity) : BroadcastReceiver() {
- internal var mReference: SoftReference = 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
-// }
- }
- }
- }
-
-}
\ No newline at end of file
+}
diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/SettingsActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/SettingsActivity.kt
index f83ae661..0bf13dff 100644
--- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/SettingsActivity.kt
+++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/SettingsActivity.kt
@@ -88,7 +88,7 @@ class SettingsActivity : BaseActivity() {
}
private fun isRunning(): Boolean {
- return Utils.isServiceRun(activity, "com.v2ray.ang.service.V2RayVpnService")
+ return false //TODO no point of adding logic now since Settings will be changed soon
}
override fun onCreate(savedInstanceState: Bundle?) {
@@ -247,4 +247,4 @@ class SettingsActivity : BaseActivity() {
}
}
-}
\ No newline at end of file
+}
diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/Utils.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/Utils.kt
index 1320277d..efcdde4d 100644
--- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/Utils.kt
+++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/Utils.kt
@@ -11,7 +11,6 @@ import com.google.zxing.qrcode.QRCodeWriter
import com.google.zxing.EncodeHintType
import java.util.*
import kotlin.collections.HashMap
-import android.app.ActivityManager
import android.content.ClipData
import android.content.Intent
import android.net.Uri
@@ -26,7 +25,7 @@ import com.v2ray.ang.R
import com.v2ray.ang.dto.EConfigType
import com.v2ray.ang.extension.responseLength
import com.v2ray.ang.extension.v2RayApplication
-import com.v2ray.ang.service.V2RayVpnService
+import com.v2ray.ang.service.V2RayServiceManager
import com.v2ray.ang.ui.SettingsActivity
import kotlinx.coroutines.isActive
import me.dozen.dpreference.DPreference
@@ -273,32 +272,12 @@ object Utils {
return false
}
-
- /**
- * 判断服务是否后台运行
-
- * @param context
- * * Context
- * *
- * @param className
- * * 判断的服务名字
- * *
- * @return true 在运行 false 不在运行
- */
- fun isServiceRun(context: Context, className: String): Boolean {
- var isRun = false
- val activityManager = context
- .getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
- val serviceList = activityManager
- .getRunningServices(999)
- val size = serviceList.size
- for (i in 0..size - 1) {
- if (serviceList[i].service.className == className) {
- isRun = true
- break
- }
+ fun startVServiceFromToggle(context: Context): Boolean {
+ val result = startVService(context)
+ if (!result) {
+ context.toast(R.string.app_tile_first_use)
}
- return isRun
+ return result
}
/**
@@ -321,7 +300,7 @@ object Utils {
return false
}
}
- V2RayVpnService.startV2Ray(context)
+ V2RayServiceManager.startV2Ray(context, context.v2RayApplication.defaultDPreference.getPrefString(AppConfig.PREF_MODE, "VPN"))
return true
} else {
return false
diff --git a/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml b/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml
index b08a711a..592c2fb8 100644
--- a/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml
+++ b/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml
@@ -3,7 +3,7 @@
v2rayNG
开关
开关
- 初次使用此功能请先用APP激活VPN
+ 初次使用此功能请先用APP添加配置
Open navigation drawer
Close navigation drawer
@@ -123,6 +123,7 @@
推广
一些推广,点击查看详情(捐赠可去除)
+ 模式
版本
初始化错误:
diff --git a/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml b/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml
index d7310a34..14714e24 100644
--- a/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml
+++ b/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml
@@ -3,7 +3,7 @@
v2rayNG
切換
切換
- 首次使用此功能,請使用此應用程式來啟用 VPN
+ 首次使用此功能,請使用此應用程式新增組態
Open navigation drawer
Close navigation drawer
@@ -125,6 +125,7 @@
推廣
一些推廣,點擊查看詳情(捐款可去除)
+ 模式
版本
錯誤設定:
diff --git a/V2rayNG/app/src/main/res/values/arrays.xml b/V2rayNG/app/src/main/res/values/arrays.xml
index 11dfa77b..b75fae68 100644
--- a/V2rayNG/app/src/main/res/values/arrays.xml
+++ b/V2rayNG/app/src/main/res/values/arrays.xml
@@ -67,6 +67,11 @@
- IPOnDemand
+
+ - VPN
+ - Proxy only
+
+
- 0.0.0.0/5
diff --git a/V2rayNG/app/src/main/res/values/strings.xml b/V2rayNG/app/src/main/res/values/strings.xml
index 129d0264..ee72e193 100644
--- a/V2rayNG/app/src/main/res/values/strings.xml
+++ b/V2rayNG/app/src/main/res/values/strings.xml
@@ -3,7 +3,7 @@
v2rayNG
Switch
Switch
- First use of this feature, please use the app to activate VPN
+ First use of this feature, please use the app to add server
Open navigation drawer
Close navigation drawer
@@ -125,6 +125,7 @@
Promotion
Promotion,click for details(Donation can be removed)
+ Mode
Version
Error Setup:
diff --git a/V2rayNG/app/src/main/res/xml/pref_settings.xml b/V2rayNG/app/src/main/res/xml/pref_settings.xml
index 6dcf3c16..19aa1e3f 100644
--- a/V2rayNG/app/src/main/res/xml/pref_settings.xml
+++ b/V2rayNG/app/src/main/res/xml/pref_settings.xml
@@ -85,6 +85,14 @@
android:key="pref_http_port"
android:summary="10809"
android:title="@string/title_pref_http_port" />
+
+
@@ -110,4 +118,4 @@
android:key="pref_version"
android:title="@string/title_pref_version" />
-
\ No newline at end of file
+