From 375a209beb2405d6a52eeac298770d82b02588f3 Mon Sep 17 00:00:00 2001 From: yuhan6665 <1588741+yuhan6665@users.noreply.github.com> Date: Sun, 14 Feb 2021 18:54:51 -0500 Subject: [PATCH] Move main storage references to MMKV - Move current index to MMKV - Move ServiceManager to MMKV - Fix isRunning in ServerActivity - Drop support for VMESS qrcode v1 - Change all protocols importing --- .../kotlin/com/v2ray/ang/dto/ServerConfig.kt | 27 +- .../kotlin/com/v2ray/ang/dto/V2rayConfig.kt | 70 +- .../com/v2ray/ang/receiver/TaskerReceiver.kt | 8 +- .../com/v2ray/ang/service/QSTileService.kt | 2 +- .../v2ray/ang/service/V2RayServiceManager.kt | 32 +- .../com/v2ray/ang/service/V2RayVpnService.kt | 2 +- .../kotlin/com/v2ray/ang/ui/MainActivity.kt | 46 +- .../com/v2ray/ang/ui/MainRecyclerAdapter.kt | 66 +- .../kotlin/com/v2ray/ang/ui/ServerActivity.kt | 85 +-- .../ang/ui/ServerCustomConfigActivity.kt | 6 +- .../com/v2ray/ang/ui/SettingsActivity.kt | 4 +- .../kotlin/com/v2ray/ang/ui/TaskerActivity.kt | 15 +- .../com/v2ray/ang/util/AngConfigManager.kt | 679 ++++-------------- .../kotlin/com/v2ray/ang/util/MmkvManager.kt | 32 +- .../main/kotlin/com/v2ray/ang/util/Utils.kt | 29 +- .../com/v2ray/ang/util/V2rayConfigUtil.kt | 65 +- .../com/v2ray/ang/viewmodel/MainViewModel.kt | 29 +- 17 files changed, 463 insertions(+), 734 deletions(-) diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/ServerConfig.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/ServerConfig.kt index eff2f0f8..5c2bdba4 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/ServerConfig.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/ServerConfig.kt @@ -1,9 +1,14 @@ package com.v2ray.ang.dto +import com.v2ray.ang.AppConfig.TAG_AGENT +import com.v2ray.ang.AppConfig.TAG_BLOCKED +import com.v2ray.ang.AppConfig.TAG_DIRECT +import com.v2ray.ang.util.Utils + data class ServerConfig( val configVersion: Int = 3, val configType: EConfigType, - val subscriptionId: String = "", + var subscriptionId: String = "", val addedTime: Long = System.currentTimeMillis(), var remarks: String = "", val outboundBean: V2rayConfig.OutboundBean? = null, @@ -48,4 +53,24 @@ data class ServerConfig( } return fullConfig?.getProxyOutbound() } + + fun getAllOutboundTags(): MutableList { + if (configType != EConfigType.CUSTOM) { + return mutableListOf(TAG_AGENT, TAG_DIRECT, TAG_BLOCKED) + } + fullConfig?.let { config -> + return config.outbounds.map { it.tag }.toMutableList() + } + return mutableListOf() + } + + fun getV2rayPointDomainAndPort(): String { + val address = getProxyOutbound()?.getServerAddress().orEmpty() + val port = getProxyOutbound()?.getServerPort() + return if (Utils.isIpv6Address(address)) { + String.format("[%s]:%s", address, port) + } else { + String.format("%s:%s", address, port) + } + } } diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/V2rayConfig.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/V2rayConfig.kt index bd37b416..e49dc15f 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/V2rayConfig.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/V2rayConfig.kt @@ -1,5 +1,7 @@ package com.v2ray.ang.dto +import android.text.TextUtils + data class V2rayConfig( val stats: Any? = null, val log: LogBean, @@ -112,7 +114,7 @@ data class V2rayConfig( var readBufferSize: Int = 1, var writeBufferSize: Int = 1, var header: HeaderBean = HeaderBean(), - var seed: String? = "") { + var seed: String? = null) { data class HeaderBean(var type: String = "none") } @@ -132,6 +134,72 @@ data class V2rayConfig( var header: HeaderBean = HeaderBean()) { data class HeaderBean(var type: String = "none") } + + fun populateTransportSettings(transport: String, headerType: String?, host: String?, path: String?, seed: String?, + quicSecurity: String?, key: String?): String { + var sni = "" + network = transport + when (network) { + "tcp" -> if (headerType == HTTP) { + val tcpSetting = TcpSettingsBean() + tcpSetting.header.type = HTTP + if (!TextUtils.isEmpty(host) || !TextUtils.isEmpty(path)) { + val requestObj = TcpSettingsBean.HeaderBean.RequestBean() + requestObj.headers.Host = (host ?: "").split(",").map { it.trim() } + requestObj.path = (path ?: "").split(",").map { it.trim() } + tcpSetting.header.request = requestObj + sni = requestObj.headers.Host.getOrNull(0) ?: sni + } + tcpSettings = tcpSetting + } + "kcp" -> { + val kcpsetting = KcpSettingsBean() + kcpsetting.header.type = headerType ?: "none" + if (seed.isNullOrEmpty()) { + kcpsetting.seed = null + } else { + kcpsetting.seed = seed + } + kcpSettings = kcpsetting + } + "ws" -> { + val wssetting = WsSettingsBean() + wssetting.headers.Host = host ?: "" + sni = wssetting.headers.Host + wssetting.path = path ?: "/" + wsSettings = wssetting + } + "h2", "http" -> { + network = "h2" + val h2Setting = HttpSettingsBean() + h2Setting.host = (host ?: "").split(",").map { it.trim() } + sni = h2Setting.host.getOrNull(0) ?: sni + h2Setting.path = path ?: "/" + httpSettings = h2Setting + } + "quic" -> { + val quicsetting = QuicSettingBean() + quicsetting.security = quicSecurity ?: "none" + quicsetting.key = key ?: "" + quicsetting.header.type = headerType ?: "none" + quicSettings = quicsetting + } + } + return sni + } + + fun populateTlsSettings(streamSecurity: String, allowInsecure: Boolean, sni: String) { + security = streamSecurity + val tlsSetting = TlsSettingsBean( + allowInsecure = allowInsecure, + serverName = sni + ) + if (security == TLS) { + tlsSettings = tlsSetting + } else if (security == XTLS) { + xtlsSettings = tlsSetting + } + } } data class MuxBean(var enabled: Boolean, var concurrency: Int = 8) diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/receiver/TaskerReceiver.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/receiver/TaskerReceiver.kt index 74f69416..08a3184a 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/receiver/TaskerReceiver.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/receiver/TaskerReceiver.kt @@ -5,11 +5,16 @@ import android.content.Context import android.content.Intent import android.text.TextUtils import com.google.zxing.WriterException +import com.tencent.mmkv.MMKV import com.v2ray.ang.AppConfig +import com.v2ray.ang.service.V2RayServiceManager +import com.v2ray.ang.util.MmkvManager import com.v2ray.ang.util.Utils class TaskerReceiver : BroadcastReceiver() { + private val mainStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_MAIN, MMKV.MULTI_PROCESS_MODE) } + override fun onReceive(context: Context, intent: Intent?) { try { @@ -23,7 +28,8 @@ class TaskerReceiver : BroadcastReceiver() { if (guid == AppConfig.TASKER_DEFAULT_GUID) { Utils.startVServiceFromToggle(context) } else { - Utils.startVService(context, guid) + mainStorage?.encode(MmkvManager.KEY_SELECTED_SERVER, guid) + V2RayServiceManager.startV2Ray(context) } } else { Utils.stopVService(context) 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 72994f10..b7e07e91 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 @@ -25,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 = V2RayServiceManager.currentConfigName + qsTile?.label = V2RayServiceManager.currentConfig?.remarks qsTile?.icon = Icon.createWithResource(applicationContext, R.drawable.ic_v) } 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 index 0f8b05fb..b5fce5ce 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayServiceManager.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayServiceManager.kt @@ -13,16 +13,20 @@ import android.os.Build import android.support.annotation.RequiresApi import android.support.v4.app.NotificationCompat import android.util.Log +import com.tencent.mmkv.MMKV import com.v2ray.ang.AppConfig import com.v2ray.ang.AppConfig.TAG_DIRECT import com.v2ray.ang.R +import com.v2ray.ang.dto.ServerConfig 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.util.MessageUtil +import com.v2ray.ang.util.MmkvManager import com.v2ray.ang.util.Utils +import com.v2ray.ang.util.V2rayConfigUtil import go.Seq import libv2ray.Libv2ray import libv2ray.V2RayPoint @@ -40,6 +44,7 @@ object V2RayServiceManager { val v2rayPoint: V2RayPoint = Libv2ray.newV2RayPoint(V2RayCallback()) private val mMsgReceive = ReceiveMessageHandler() + private val mainStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_MAIN, MMKV.MULTI_PROCESS_MODE) } var serviceControl: SoftReference? = null set(value) { @@ -51,7 +56,7 @@ object V2RayServiceManager { Seq.setContext(context) } } - var currentConfigName = "NG" + var currentConfig: ServerConfig? = null private var lastQueryTime = 0L private var mBuilder: NotificationCompat.Builder? = null @@ -122,7 +127,12 @@ object V2RayServiceManager { fun startV2rayPoint() { val service = serviceControl?.get()?.getService() ?: return + val guid = mainStorage?.decodeString(MmkvManager.KEY_SELECTED_SERVER) ?: return + val config = MmkvManager.decodeServerConfig(guid) ?: return if (!v2rayPoint.isRunning) { + val result = V2rayConfigUtil.getV2rayConfig(service, guid) + if (!result.status) + return try { val mFilter = IntentFilter(AppConfig.BROADCAST_ACTION_SERVICE) @@ -134,12 +144,12 @@ object V2RayServiceManager { Log.d(service.packageName, e.toString()) } - v2rayPoint.configureFileContent = service.defaultDPreference.getPrefString(AppConfig.PREF_CURR_CONFIG, "") + v2rayPoint.configureFileContent = result.content + v2rayPoint.domainName = config.getV2rayPointDomainAndPort() + currentConfig = config v2rayPoint.enableLocalDNS = service.defaultDPreference.getPrefBoolean(AppConfig.PREF_LOCAL_DNS_ENABLED, false) v2rayPoint.forwardIpv6 = service.defaultDPreference.getPrefBoolean(AppConfig.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() @@ -243,7 +253,7 @@ object V2RayServiceManager { mBuilder = NotificationCompat.Builder(service, channelId) .setSmallIcon(R.drawable.ic_v) - .setContentTitle(currentConfigName) + .setContentTitle(currentConfig?.remarks) .setPriority(NotificationCompat.PRIORITY_MIN) .setOngoing(true) .setShowWhen(false) @@ -280,7 +290,7 @@ object V2RayServiceManager { mSubscription = null } - private fun updateNotification(contentText: String, proxyTraffic: Long, directTraffic: Long) { + 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) @@ -309,8 +319,8 @@ object V2RayServiceManager { v2rayPoint.isRunning && service.defaultDPreference.getPrefBoolean(AppConfig.PREF_SPEED_ENABLED, false)) { var lastZeroSpeed = false - val outboundTags = service.defaultDPreference.getPrefStringOrderedSet(AppConfig.PREF_CURR_CONFIG_OUTBOUND_TAGS, LinkedHashSet()) - outboundTags.remove(TAG_DIRECT) + val outboundTags = currentConfig?.getAllOutboundTags() + outboundTags?.remove(TAG_DIRECT) mSubscription = Observable.interval(3, java.util.concurrent.TimeUnit.SECONDS) .subscribe { @@ -318,7 +328,7 @@ object V2RayServiceManager { val sinceLastQueryInSeconds = (queryTime - lastQueryTime) / 1000.0 var proxyTotal = 0L val text = StringBuilder() - outboundTags.forEach { + outboundTags?.forEach { val up = v2rayPoint.queryStats(it, "uplink") val down = v2rayPoint.queryStats(it, "downlink") if (up + down > 0) { @@ -331,7 +341,7 @@ object V2RayServiceManager { val zeroSpeed = (proxyTotal == 0L && directUplink == 0L && directDownlink == 0L) if (!zeroSpeed || !lastZeroSpeed) { if (proxyTotal == 0L) { - appendSpeedString(text, outboundTags.firstOrNull(), 0.0, 0.0) + appendSpeedString(text, outboundTags?.firstOrNull(), 0.0, 0.0) } appendSpeedString(text, TAG_DIRECT, directUplink / sinceLastQueryInSeconds, directDownlink / sinceLastQueryInSeconds) @@ -357,7 +367,7 @@ object V2RayServiceManager { if (mSubscription != null) { mSubscription?.unsubscribe() //stop queryStats mSubscription = null - updateNotification(currentConfigName, 0, 0) + updateNotification(currentConfig?.remarks, 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 181b6c43..dc92d15f 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 @@ -126,7 +126,7 @@ class V2RayVpnService : VpnService(), ServiceControl { } } - builder.setSession(V2RayServiceManager.currentConfigName) + builder.setSession(V2RayServiceManager.currentConfig?.remarks.orEmpty()) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && defaultDPreference.getPrefBoolean(AppConfig.PREF_PER_APP_PROXY, false)) { 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 db93eae4..54ae616c 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 @@ -17,6 +17,7 @@ import android.view.KeyEvent import android.view.Menu import android.view.MenuItem import com.tbruyelle.rxpermissions.RxPermissions +import com.tencent.mmkv.MMKV import com.v2ray.ang.AppConfig import com.v2ray.ang.BuildConfig import com.v2ray.ang.R @@ -24,7 +25,9 @@ import com.v2ray.ang.dto.EConfigType import com.v2ray.ang.extension.defaultDPreference import com.v2ray.ang.extension.toast import com.v2ray.ang.helper.SimpleItemTouchHelperCallback +import com.v2ray.ang.service.V2RayServiceManager import com.v2ray.ang.util.AngConfigManager +import com.v2ray.ang.util.MmkvManager import com.v2ray.ang.util.Utils import com.v2ray.ang.util.V2rayConfigUtil import com.v2ray.ang.viewmodel.MainViewModel @@ -47,6 +50,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList } private val adapter by lazy { MainRecyclerAdapter(this) } + private val mainStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_MAIN, MMKV.MULTI_PROCESS_MODE) } private var mItemTouchHelper: ItemTouchHelper? = null val mainViewModel: MainViewModel by lazy { ViewModelProviders.of(this).get(MainViewModel::class.java) } @@ -110,7 +114,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList mainViewModel.updateTestResultAction.observe(this, { tv_test_state.text = it }) mainViewModel.isRunning.observe(this, { val isRunning = it ?: return@observe - adapter.changeable = !isRunning + adapter.isRunning = isRunning if (isRunning) { fab.setImageResource(R.drawable.ic_v) tv_test_state.text = getString(R.string.connection_connected) @@ -126,19 +130,18 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList } fun startV2Ray() { - if (AngConfigManager.configs.index < 0) { + if (mainStorage?.decodeString(MmkvManager.KEY_SELECTED_SERVER).isNullOrEmpty()) { return } showCircle() // toast(R.string.toast_services_start) - if (!Utils.startVService(this, AngConfigManager.configs.index)) { - hideCircle() - } + V2RayServiceManager.startV2Ray(this) + hideCircle() } public override fun onResume() { super.onResume() - adapter.notifyDataSetChanged() + mainViewModel.reloadServerList() } public override fun onPause() { @@ -437,9 +440,8 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList .subscribe { if (it) { try { - contentResolver.openInputStream(uri).use { - val configText = it?.bufferedReader()?.readText() - importCustomizeConfig(configText) + contentResolver.openInputStream(uri).use { input -> + importCustomizeConfig(input?.bufferedReader()?.readText()) } } catch (e: Exception) { e.printStackTrace() @@ -453,19 +455,21 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList * import customize config */ fun importCustomizeConfig(server: String?) { - if (server == null) { - return - } - if (!V2rayConfigUtil.isValidConfig(server)) { - toast(R.string.toast_config_file_invalid) - return - } - val resId = AngConfigManager.importCustomizeConfig(server) - if (resId > 0) { - toast(resId) - } else { + try { + if (server == null || TextUtils.isEmpty(server)) { + toast(R.string.toast_none_data) + return + } + if (!V2rayConfigUtil.isValidConfig(server)) { + toast(R.string.toast_config_file_invalid) + return + } + mainViewModel.appendCustomConfigServer(server) toast(R.string.toast_success) - adapter.notifyDataSetChanged() + adapter.notifyItemInserted(mainViewModel.serverList.lastIndex) + } catch (e: Exception) { + e.printStackTrace() + return } } diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainRecyclerAdapter.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainRecyclerAdapter.kt index 55f3cb3a..8b780206 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainRecyclerAdapter.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainRecyclerAdapter.kt @@ -15,6 +15,7 @@ import com.v2ray.ang.dto.EConfigType import com.v2ray.ang.extension.toast import com.v2ray.ang.helper.ItemTouchHelperAdapter import com.v2ray.ang.helper.ItemTouchHelperViewHolder +import com.v2ray.ang.service.V2RayServiceManager import com.v2ray.ang.util.AngConfigManager import com.v2ray.ang.util.MmkvManager import com.v2ray.ang.util.Utils @@ -36,20 +37,13 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter by lazy { mActivity.resources.getStringArray(R.array.share_method) } - - var changeable: Boolean = true - set(value) { - if (field == value) - return - field = value - notifyDataSetChanged() - } + var isRunning = false override fun getItemCount() = mActivity.mainViewModel.serverList.size override fun onBindViewHolder(holder: BaseViewHolder, position: Int) { if (holder is MainViewHolder) { - val guid = mActivity.mainViewModel.getGuid(position) ?: return + val guid = mActivity.mainViewModel.serverList.getOrNull(position) ?: return val config = MmkvManager.decodeServerConfig(guid) ?: return val outbound = config.getProxyOutbound() ?: return val aff = MmkvManager.decodeServerAffiliationInfo(guid) @@ -59,7 +53,7 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter { if (config.configType == EConfigType.CUSTOM) { - shareFullContent(position) + shareFullContent(guid) } else { val iv = LayoutInflater.from(mActivity).inflate(R.layout.item_qrcode, null) iv.iv_qcode.setImageBitmap(AngConfigManager.share2QRCode(guid)) @@ -97,7 +91,7 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter shareFullContent(position) + 2 -> shareFullContent(guid) else -> mActivity.toast("else") } } catch (e: Exception) { @@ -108,6 +102,7 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter if (headerType == HTTP) { - val tcpSetting = V2rayConfig.OutboundBean.StreamSettingsBean.TcpSettingsBean() - tcpSetting.header.type = headerType - if (!TextUtils.isEmpty(requestHost) || !TextUtils.isEmpty(path)) { - val requestObj = V2rayConfig.OutboundBean.StreamSettingsBean.TcpSettingsBean.HeaderBean.RequestBean() - requestObj.headers.Host = requestHost.split(",").map { it.trim() } - requestObj.path = path.split(",").map { it.trim() } - tcpSetting.header.request = requestObj - sni = requestObj.headers.Host.getOrNull(0) ?: sni - } - streamSetting.tcpSettings = tcpSetting - } - "kcp" -> { - val kcpsetting = V2rayConfig.OutboundBean.StreamSettingsBean.KcpSettingsBean() - kcpsetting.header.type = headerType - if (TextUtils.isEmpty(path)) { - kcpsetting.seed = null - } else { - kcpsetting.seed = path - } - streamSetting.kcpSettings = kcpsetting - } - "ws" -> { - val wssetting = V2rayConfig.OutboundBean.StreamSettingsBean.WsSettingsBean() - if (!TextUtils.isEmpty(requestHost)) { - wssetting.headers.Host = requestHost - sni = requestHost - } - if (!TextUtils.isEmpty(path)) { - wssetting.path = path - } - streamSetting.wsSettings = wssetting - } - "h2" -> { - val h2Setting = V2rayConfig.OutboundBean.StreamSettingsBean.HttpSettingsBean() - if (!TextUtils.isEmpty(requestHost)) { - h2Setting.host = requestHost.split(",").map { it.trim() } - sni = h2Setting.host.getOrNull(0) ?: sni - } - h2Setting.path = path - streamSetting.httpSettings = h2Setting - } - "quic" -> { - val quicsetting = V2rayConfig.OutboundBean.StreamSettingsBean.QuicSettingBean() - quicsetting.security = requestHost - quicsetting.key = path - quicsetting.header.type = headerType - streamSetting.quicSettings = quicsetting - } - } - - streamSetting.security = if (sp_stream_security != null) streamSecuritys[sp_stream_security.selectedItemPosition] else TLS - val tlsSetting = V2rayConfig.OutboundBean.StreamSettingsBean.TlsSettingsBean( - allowInsecure = if (sp_allow_insecure != null) allowinsecures[sp_allow_insecure.selectedItemPosition].toBoolean() else false, - serverName = sni + var sni = streamSetting.populateTransportSettings( + if (sp_network != null) networks[sp_network.selectedItemPosition] else DEFAULT_NETWORK, + if (sp_header_type != null) headertypes[sp_header_type.selectedItemPosition] else "", + requestHost, + path, + path, + requestHost, + path ) - if (streamSetting.security == TLS) { - streamSetting.tlsSettings = tlsSetting - } else if (streamSetting.security == XTLS) { - streamSetting.xtlsSettings = tlsSetting + if (sni.isEmpty()) { + sni = requestHost } + streamSetting.populateTlsSettings( + if (sp_stream_security != null) streamSecuritys[sp_stream_security.selectedItemPosition] else TLS, + if (sp_allow_insecure != null) allowinsecures[sp_allow_insecure.selectedItemPosition].toBoolean() else false, + sni + ) } /** diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ServerCustomConfigActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ServerCustomConfigActivity.kt index a5c604c5..7205bedd 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ServerCustomConfigActivity.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ServerCustomConfigActivity.kt @@ -21,7 +21,11 @@ class ServerCustomConfigActivity : BaseActivity() { private val mainStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_MAIN, MMKV.MULTI_PROCESS_MODE) } private val editGuid by lazy { intent.getStringExtra("guid").orEmpty() } - private val isRunning by lazy { editGuid.isNotEmpty() && editGuid == mainStorage?.decodeString(MmkvManager.KEY_SELECTED_SERVER) } + private val isRunning by lazy { + intent.getBooleanExtra("isRunning", false) + && editGuid.isNotEmpty() + && editGuid == mainStorage?.decodeString(MmkvManager.KEY_SELECTED_SERVER) + } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) 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 39dd03a8..c374b3b8 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 @@ -8,7 +8,7 @@ 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.service.V2RayServiceManager import com.v2ray.ang.util.Utils import com.v2ray.ang.viewmodel.SettingsViewModel @@ -56,7 +56,7 @@ class SettingsActivity : BaseActivity() { private fun restartProxy() { Utils.stopVService(requireContext()) - Utils.startVService(requireContext(), AngConfigManager.configs.index) + V2RayServiceManager.startV2Ray(requireContext()) } private fun isRunning(): Boolean { diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/TaskerActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/TaskerActivity.kt index fac81876..b27f047b 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/TaskerActivity.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/TaskerActivity.kt @@ -7,21 +7,23 @@ import android.widget.ArrayAdapter import android.widget.ListView import java.util.ArrayList import com.v2ray.ang.R -import com.v2ray.ang.util.AngConfigManager import android.content.Intent import android.text.TextUtils import android.view.Menu import android.view.MenuItem import com.google.zxing.WriterException +import com.tencent.mmkv.MMKV import com.v2ray.ang.AppConfig +import com.v2ray.ang.util.MmkvManager import kotlinx.android.synthetic.main.activity_tasker.* - class TaskerActivity : BaseActivity() { private var listview: ListView? = null private var lstData: ArrayList = ArrayList() private var lstGuid: ArrayList = ArrayList() + private val serverStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SERVER_CONFIG, MMKV.MULTI_PROCESS_MODE) } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_tasker) @@ -30,9 +32,11 @@ class TaskerActivity : BaseActivity() { lstData.add("Default") lstGuid.add(AppConfig.TASKER_DEFAULT_GUID) - AngConfigManager.configs.vmess.forEach { - lstData.add(it.remarks) - lstGuid.add(it.guid) + serverStorage?.allKeys()?.forEach { key -> + MmkvManager.decodeServerConfig(key)?.let { config -> + lstData.add(config.remarks) + lstGuid.add(key) + } } val adapter = ArrayAdapter(this, android.R.layout.simple_list_item_single_choice, lstData) @@ -108,4 +112,3 @@ class TaskerActivity : BaseActivity() { } } - diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/AngConfigManager.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/AngConfigManager.kt index b4818f81..4e46a307 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/AngConfigManager.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/AngConfigManager.kt @@ -3,13 +3,16 @@ package com.v2ray.ang.util import android.graphics.Bitmap import android.text.TextUtils import com.google.gson.Gson +import com.tencent.mmkv.MMKV import com.v2ray.ang.AngApplication import com.v2ray.ang.AppConfig.ANG_CONFIG import com.v2ray.ang.AppConfig.HTTPS_PROTOCOL import com.v2ray.ang.AppConfig.HTTP_PROTOCOL import com.v2ray.ang.R import com.v2ray.ang.dto.* +import com.v2ray.ang.dto.V2rayConfig.Companion.DEFAULT_SECURITY import com.v2ray.ang.dto.V2rayConfig.Companion.TLS +import com.v2ray.ang.util.MmkvManager.KEY_SELECTED_SERVER import java.net.URI import java.net.URLDecoder import java.util.* @@ -18,18 +21,17 @@ object AngConfigManager { private lateinit var app: AngApplication private lateinit var angConfig: AngConfig val configs: AngConfig get() = angConfig + private val mainStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_MAIN, MMKV.MULTI_PROCESS_MODE) } fun inject(app: AngApplication) { this.app = app - if (app.firstRun) { - } loadConfig() } /** * loading config */ - fun loadConfig() { + private fun loadConfig() { try { val context = app.defaultDPreference.getPrefString(ANG_CONFIG, "") if (!TextUtils.isEmpty(context)) { @@ -54,115 +56,6 @@ object AngConfigManager { } } - /** - * add or edit server - */ - fun addServer(vmess: AngConfig.VmessBean, index: Int): Int { - try { - vmess.configVersion = 2 - vmess.configType = EConfigType.VMESS.value - - vmess.address = vmess.address.trim() - vmess.id = vmess.id.trim() - vmess.security = vmess.security.trim() - vmess.network = vmess.network.trim() - vmess.headerType = vmess.headerType.trim() - vmess.requestHost = vmess.requestHost.trim() - vmess.path = vmess.path.trim() - vmess.streamSecurity = vmess.streamSecurity.trim() - - if (index >= 0) { - //edit - angConfig.vmess[index] = vmess - } else { - //add - vmess.guid = Utils.getUuid() - angConfig.vmess.add(vmess) - if (angConfig.vmess.count() == 1) { - angConfig.index = 0 - } - } - - storeConfigFile() - } catch (e: Exception) { - e.printStackTrace() - return -1 - } - return 0 - } - - /** - * 移除服务器 - */ - fun removeServer(index: Int): Int { - try { - if (index < 0 || index > angConfig.vmess.count() - 1) { - return -1 - } - - //删除 - angConfig.vmess.removeAt(index) - - //移除的是活动的 - adjustIndexForRemovalAt(index) - - storeConfigFile() - } catch (e: Exception) { - e.printStackTrace() - return -1 - } - return 0 - } - - private fun adjustIndexForRemovalAt(index: Int) { - if (angConfig.index == index) { - if (angConfig.vmess.count() > 0) { - angConfig.index = 0 - } else { - angConfig.index = -1 - } - } else if (index < angConfig.index)//移除活动之前的 - { - angConfig.index-- - } - } - - fun swapServer(fromPosition: Int, toPosition: Int): Int { - try { - Collections.swap(angConfig.vmess, fromPosition, toPosition) - - val index = angConfig.index - if (index == fromPosition) { - angConfig.index = toPosition - } else if (index == toPosition) { - angConfig.index = fromPosition - } - //storeConfigFile() - } catch (e: Exception) { - e.printStackTrace() - return -1 - } - return 0 - } - - /** - * set active server - */ - fun setActiveServer(index: Int): Int { - try { - if (index < 0 || index > angConfig.vmess.count() - 1) { - return -1 - } - angConfig.index = index - - storeConfigFile() - } catch (e: Exception) { - e.printStackTrace() - return -1 - } - return 0 - } - /** * store config to file */ @@ -178,73 +71,65 @@ object AngConfigManager { /** * import config form qrcode or... */ - fun importConfig(server: String?, subid: String, removedSelectedServer: AngConfig.VmessBean?): Int { + private fun importConfig(str: String?, subid: String, removedSelectedServer: ServerConfig?): Int { try { - if (server == null || TextUtils.isEmpty(server)) { + if (str == null || TextUtils.isEmpty(str)) { return R.string.toast_none_data } //maybe sub - if (server.startsWith(HTTP_PROTOCOL) || server.startsWith(HTTPS_PROTOCOL)) { - addSubItem(server) + if (str.startsWith(HTTP_PROTOCOL) || str.startsWith(HTTPS_PROTOCOL)) { + addSubItem(str) return 0 } - var vmess = AngConfig.VmessBean() + var config: ServerConfig? = null + val allowInsecure = false//app.defaultDPreference.getPrefBoolean(AppConfig.PREF_ALLOW_INSECURE, false) + if (str.startsWith(EConfigType.VMESS.protocolScheme)) { + config = ServerConfig.create(EConfigType.VMESS) + val streamSetting = config.outboundBean?.streamSettings ?: return -1 - if (server.startsWith(EConfigType.VMESS.protocolScheme)) { + if (!tryParseNewVmess(str, config, allowInsecure)) { + if (str.indexOf("?") > 0) { + if (!tryResolveVmess4Kitsunebi(str, config)) { + return R.string.toast_incorrect_protocol + } + } else { + var result = str.replace(EConfigType.VMESS.protocolScheme, "") + result = Utils.decode(result) + if (TextUtils.isEmpty(result)) { + return R.string.toast_decoding_failed + } + val vmessQRCode = Gson().fromJson(result, VmessQRCode::class.java) + if (TextUtils.isEmpty(vmessQRCode.add) + || TextUtils.isEmpty(vmessQRCode.port) + || TextUtils.isEmpty(vmessQRCode.id) + || TextUtils.isEmpty(vmessQRCode.aid) + || TextUtils.isEmpty(vmessQRCode.net) + ) { + return R.string.toast_incorrect_protocol + } - val indexSplit = server.indexOf("?") - val newVmess = tryParseNewVmess(server) - if (newVmess != null) { - vmess = newVmess - vmess.subid = subid - } else if (indexSplit > 0) { - vmess = ResolveVmess4Kitsunebi(server) - } else { - - var result = server.replace(EConfigType.VMESS.protocolScheme, "") - result = Utils.decode(result) - if (TextUtils.isEmpty(result)) { - return R.string.toast_decoding_failed + config.remarks = vmessQRCode.ps + config.outboundBean?.settings?.vnext?.get(0)?.let { vnext -> + vnext.address = vmessQRCode.add + vnext.port = Utils.parseInt(vmessQRCode.port) + vnext.users[0].id = vmessQRCode.id + vnext.users[0].encryption = DEFAULT_SECURITY + vnext.users[0].alterId = Utils.parseInt(vmessQRCode.aid) + } + val sni = streamSetting.populateTransportSettings(vmessQRCode.net, vmessQRCode.type, vmessQRCode.host, + vmessQRCode.path, vmessQRCode.path, vmessQRCode.host, vmessQRCode.path) + streamSetting.populateTlsSettings(vmessQRCode.tls, allowInsecure, sni) } - val vmessQRCode = Gson().fromJson(result, VmessQRCode::class.java) - if (TextUtils.isEmpty(vmessQRCode.add) - || TextUtils.isEmpty(vmessQRCode.port) - || TextUtils.isEmpty(vmessQRCode.id) - || TextUtils.isEmpty(vmessQRCode.aid) - || TextUtils.isEmpty(vmessQRCode.net) - ) { - return R.string.toast_incorrect_protocol - } - - vmess.configType = EConfigType.VMESS.value - vmess.security = "auto" - vmess.network = "tcp" - vmess.headerType = "none" - - vmess.configVersion = Utils.parseInt(vmessQRCode.v) - vmess.remarks = vmessQRCode.ps - vmess.address = vmessQRCode.add - vmess.port = Utils.parseInt(vmessQRCode.port) - vmess.id = vmessQRCode.id - vmess.alterId = Utils.parseInt(vmessQRCode.aid) - vmess.network = vmessQRCode.net - vmess.headerType = vmessQRCode.type - vmess.requestHost = vmessQRCode.host - vmess.path = vmessQRCode.path - vmess.streamSecurity = vmessQRCode.tls - vmess.subid = subid } - upgradeServerVersion(vmess) - addServer(vmess, -1) - - } else if (server.startsWith(EConfigType.SHADOWSOCKS.protocolScheme)) { - var result = server.replace(EConfigType.SHADOWSOCKS.protocolScheme, "") + } else if (str.startsWith(EConfigType.SHADOWSOCKS.protocolScheme)) { + var result = str.replace(EConfigType.SHADOWSOCKS.protocolScheme, "") val indexSplit = result.indexOf("#") + config = ServerConfig.create(EConfigType.SHADOWSOCKS) if (indexSplit > 0) { try { - vmess.remarks = Utils.urlDecode(result.substring(indexSplit + 1, result.length)) + config.remarks = Utils.urlDecode(result.substring(indexSplit + 1, result.length)) } catch (e: Exception) { e.printStackTrace() } @@ -254,32 +139,28 @@ object AngConfigManager { //part decode val indexS = result.indexOf("@") - if (indexS > 0) { - result = Utils.decode(result.substring(0, indexS)) + result.substring(indexS, result.length) + result = if (indexS > 0) { + Utils.decode(result.substring(0, indexS)) + result.substring(indexS, result.length) } else { - result = Utils.decode(result) + Utils.decode(result) } val legacyPattern = "^(.+?):(.*)@(.+?):(\\d+?)/?$".toRegex() - val match = legacyPattern.matchEntire(result) - if (match == null) { - return R.string.toast_incorrect_protocol - } - vmess.security = match.groupValues[1].toLowerCase() - vmess.id = match.groupValues[2] - vmess.address = match.groupValues[3] - if (vmess.address.firstOrNull() == '[' && vmess.address.lastOrNull() == ']') - vmess.address = vmess.address.substring(1, vmess.address.length - 1) - vmess.port = match.groupValues[4].toInt() - vmess.subid = subid + val match = legacyPattern.matchEntire(result) ?: return R.string.toast_incorrect_protocol - addShadowsocksServer(vmess, -1) - } else if (server.startsWith(EConfigType.SOCKS.protocolScheme)) { - var result = server.replace(EConfigType.SOCKS.protocolScheme, "") + config.outboundBean?.settings?.servers?.get(0)?.let { server -> + server.address = match.groupValues[3].removeSurrounding("[", "]") + server.port = match.groupValues[4].toInt() + server.password = match.groupValues[2] + server.method = match.groupValues[1].toLowerCase() + } + } else if (str.startsWith(EConfigType.SOCKS.protocolScheme)) { + var result = str.replace(EConfigType.SOCKS.protocolScheme, "") val indexSplit = result.indexOf("#") + config = ServerConfig.create(EConfigType.SOCKS) if (indexSplit > 0) { try { - vmess.remarks = Utils.urlDecode(result.substring(indexSplit + 1, result.length)) + config.remarks = Utils.urlDecode(result.substring(indexSplit + 1, result.length)) } catch (e: Exception) { e.printStackTrace() } @@ -296,88 +177,58 @@ object AngConfigManager { } val legacyPattern = "^(.*):(.*)@(.+?):(\\d+?)$".toRegex() - val match = legacyPattern.matchEntire(result) - if (match == null) { - return R.string.toast_incorrect_protocol + val match = legacyPattern.matchEntire(result) ?: return R.string.toast_incorrect_protocol + + config.outboundBean?.settings?.servers?.get(0)?.let { server -> + server.address = match.groupValues[3].removeSurrounding("[", "]") + server.port = match.groupValues[4].toInt() + val socksUsersBean = V2rayConfig.OutboundBean.OutSettingsBean.ServersBean.SocksUsersBean() + socksUsersBean.user = match.groupValues[1].toLowerCase() + socksUsersBean.pass = match.groupValues[2] + server.users = listOf(socksUsersBean) } - vmess.security = match.groupValues[1].toLowerCase() - vmess.id = match.groupValues[2] - vmess.address = match.groupValues[3] - if (vmess.address.firstOrNull() == '[' && vmess.address.lastOrNull() == ']') - vmess.address = vmess.address.substring(1, vmess.address.length - 1) - vmess.port = match.groupValues[4].toInt() - vmess.subid = subid - - addSocksServer(vmess, -1) - - } else if (server.startsWith(EConfigType.TROJAN.protocolScheme)) { - val uri = URI(server) - - vmess.address = uri.host - vmess.port = uri.port - vmess.id = uri.userInfo - vmess.remarks = uri.fragment ?: "" - vmess.subid = subid - if (!TextUtils.isEmpty((uri.rawQuery))) { - val queryParam = uri.rawQuery.split("&") - .map { it.split("=").let { (k, v) -> k to URLDecoder.decode(v, "utf-8")!! } } - .toMap() - vmess.requestHost = queryParam["sni"] ?: "" + } else if (str.startsWith(EConfigType.TROJAN.protocolScheme)) { + val uri = URI(str) + config = ServerConfig.create(EConfigType.TROJAN) + config.remarks = uri.fragment ?: "" + config.outboundBean?.settings?.servers?.get(0)?.let { server -> + server.address = uri.host + server.port = uri.port + server.password = uri.userInfo } - - addTrojanServer(vmess, -1) - - } else if (server.startsWith(EConfigType.VLESS.protocolScheme)) { - val uri = URI(server) - - vmess.address = uri.host - vmess.port = uri.port - vmess.id = uri.userInfo - vmess.remarks = uri.fragment ?: "" - vmess.subid = subid - val queryParam = uri.rawQuery.split("&") .map { it.split("=").let { (k, v) -> k to URLDecoder.decode(v, "utf-8")!! } } .toMap() - - //vmess.flow = queryParam["flow"] ?: "" - vmess.security = queryParam["encryption"] ?: "none" - vmess.streamSecurity = queryParam["security"] ?: "" - vmess.network = queryParam["type"] ?: "tcp" - when (vmess.network) { - "tcp" -> { - vmess.headerType = queryParam["headerType"] ?: "none" - vmess.requestHost = queryParam["host"] ?: "" - } - "h2", - "http" -> { - vmess.network = "h2" - vmess.requestHost = queryParam["host"] ?: "" - vmess.path = queryParam["path"] ?: "/" - } - "ws" -> { - vmess.requestHost = queryParam["host"] ?: "" - vmess.path = queryParam["path"] ?: "/" - } - "kcp" -> { - vmess.headerType = queryParam["headerType"] ?: "none" - vmess.path = queryParam["seed"] ?: "" - } - "quic" -> { - vmess.headerType = queryParam["headerType"] ?: "none" - vmess.requestHost = queryParam["quicSecurity"] ?: "none" - vmess.path = queryParam["key"] ?: "" - } + config.outboundBean?.streamSettings?.populateTlsSettings(TLS, allowInsecure, queryParam["sni"] ?: "") + } else if (str.startsWith(EConfigType.VLESS.protocolScheme)) { + val uri = URI(str) + val queryParam = uri.rawQuery.split("&") + .map { it.split("=").let { (k, v) -> k to URLDecoder.decode(v, "utf-8")!! } } + .toMap() + config = ServerConfig.create(EConfigType.VLESS) + val streamSetting = config.outboundBean?.streamSettings ?: return -1 + config.remarks = uri.fragment ?: "" + config.outboundBean?.settings?.vnext?.get(0)?.let { vnext -> + vnext.address = uri.host + vnext.port = uri.port + vnext.users[0].id = uri.userInfo + vnext.users[0].encryption = queryParam["encryption"] ?: "none" + vnext.users[0].flow =queryParam["flow"] ?: "" } - addVlessServer(vmess, -1) - } else { + + val sni = streamSetting.populateTransportSettings(queryParam["type"] ?: "tcp", queryParam["headerType"], + queryParam["host"], queryParam["path"], queryParam["seed"], queryParam["quicSecurity"], queryParam["key"]) + streamSetting.populateTlsSettings(queryParam["security"] ?: "", allowInsecure, sni) + } + if (config == null){ return R.string.toast_incorrect_protocol } + config.subscriptionId = subid + val guid = MmkvManager.encodeServerConfig("", config) if (removedSelectedServer != null && - vmess.subid.equals(removedSelectedServer.subid) && - vmess.address.equals(removedSelectedServer.address) && - vmess.port.equals(removedSelectedServer.port)) { - setActiveServer(configs.vmess.count() - 1) + config.getProxyOutbound()?.getServerAddress() == removedSelectedServer.getProxyOutbound()?.getServerAddress() && + config.getProxyOutbound()?.getServerPort() == removedSelectedServer.getProxyOutbound()?.getServerPort()) { + mainStorage?.encode(KEY_SELECTED_SERVER, guid) } } catch (e: Exception) { e.printStackTrace() @@ -386,9 +237,9 @@ object AngConfigManager { return 0 } - fun tryParseNewVmess(uri: String): AngConfig.VmessBean? { + private fun tryParseNewVmess(uriString: String, config: ServerConfig, allowInsecure: Boolean): Boolean { return runCatching { - val uri = URI(uri) + val uri = URI(uriString) check(uri.scheme == "vmess") val (_, protocol, tlsStr, uuid, alterId) = Regex("(tcp|http|ws|kcp|quic)(\\+tls)?:([0-9a-z]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12})-([0-9]+)") @@ -398,52 +249,27 @@ object AngConfigManager { val queryParam = uri.rawQuery.split("&") .map { it.split("=").let { (k, v) -> k to URLDecoder.decode(v, "utf-8")!! } } .toMap() - val vmess = AngConfig.VmessBean() - vmess.address = uri.host - vmess.port = uri.port - vmess.id = uuid - vmess.alterId = alterId.toInt() - vmess.streamSecurity = if (tls) TLS else "" - vmess.remarks = uri.fragment - vmess.security = "auto" - // TODO: allowInsecure not supported - - when (protocol) { - "tcp" -> { - vmess.network = "tcp" - vmess.headerType = queryParam["type"] ?: "none" - vmess.requestHost = queryParam["host"] ?: "" - } - "http" -> { - vmess.network = "h2" - vmess.path = queryParam["path"]?.takeIf { it.trim() != "/" } ?: "" - vmess.requestHost = queryParam["host"]?.split("|")?.get(0) ?: "" - } - "ws" -> { - vmess.network = "ws" - vmess.path = queryParam["path"]?.takeIf { it.trim() != "/" } ?: "" - vmess.requestHost = queryParam["host"]?.split("|")?.get(0) ?: "" - } - "kcp" -> { - vmess.network = "kcp" - vmess.headerType = queryParam["type"] ?: "none" - vmess.path = queryParam["seed"] ?: "" - } - "quic" -> { - vmess.network = "quic" - vmess.requestHost = queryParam["security"] ?: "none" - vmess.headerType = queryParam["type"] ?: "none" - vmess.path = queryParam["key"] ?: "" - } + val streamSetting = config.outboundBean?.streamSettings ?: return false + config.remarks = uri.fragment + config.outboundBean.settings?.vnext?.get(0)?.let { vnext -> + vnext.address = uri.host + vnext.port = uri.port + vnext.users[0].id = uuid + vnext.users[0].encryption = DEFAULT_SECURITY + vnext.users[0].alterId = alterId.toInt() } - vmess - }.getOrNull() + + val sni = streamSetting.populateTransportSettings(protocol, queryParam["type"], + queryParam["host"]?.split("|")?.get(0) ?: "", + queryParam["path"]?.takeIf { it.trim() != "/" } ?: "", + queryParam["seed"], queryParam["security"], queryParam["key"]) + streamSetting.populateTlsSettings(if (tls) TLS else "", allowInsecure, sni) + true + }.getOrElse { false } } - private fun ResolveVmess4Kitsunebi(server: String): AngConfig.VmessBean { - - val vmess = AngConfig.VmessBean() + private fun tryResolveVmess4Kitsunebi(server: String, config: ServerConfig): Boolean { var result = server.replace(EConfigType.VMESS.protocolScheme, "") val indexSplit = result.indexOf("?") @@ -454,32 +280,29 @@ object AngConfigManager { val arr1 = result.split('@') if (arr1.count() != 2) { - return vmess + return false } val arr21 = arr1[0].split(':') val arr22 = arr1[1].split(':') - if (arr21.count() != 2 || arr21.count() != 2) { - return vmess + if (arr21.count() != 2) { + return false } - vmess.address = arr22[0] - vmess.port = Utils.parseInt(arr22[1]) - vmess.security = arr21[0] - vmess.id = arr21[1] - - vmess.security = "chacha20-poly1305" - vmess.network = "tcp" - vmess.headerType = "none" - vmess.remarks = "Alien" - vmess.alterId = 0 - - return vmess + config.remarks = "Alien" + config.outboundBean?.settings?.vnext?.get(0)?.let { vnext -> + vnext.address = arr22[0] + vnext.port = Utils.parseInt(arr22[1]) + vnext.users[0].id = arr21[1] + vnext.users[0].encryption = arr21[0] + vnext.users[0].alterId = 0 + } + return true } /** * share config */ - fun shareConfig(guid: String): String { + private fun shareConfig(guid: String): String { try { val config = MmkvManager.decodeServerConfig(guid) ?: return "" val outbound = config.getProxyOutbound() ?: return "" @@ -632,7 +455,7 @@ object AngConfigManager { continue } sb.append(url) - sb.appendln() + sb.appendLine() } if (sb.count() > 0) { Utils.setClipboard(app.applicationContext, sb.toString()) @@ -653,8 +476,7 @@ object AngConfigManager { if (TextUtils.isEmpty(conf)) { return null } - val bitmap = Utils.createQRCode(conf) - return bitmap + return Utils.createQRCode(conf) } catch (e: Exception) { e.printStackTrace() @@ -681,87 +503,30 @@ object AngConfigManager { return 0 } - /** - * import customize config - */ - fun importCustomizeConfig(server: String?): Int { - try { - if (server == null || TextUtils.isEmpty(server)) { - return R.string.toast_none_data - } - val config = ServerConfig.create(EConfigType.CUSTOM) - config.remarks = System.currentTimeMillis().toString() - config.fullConfig = Gson().fromJson(server, V2rayConfig::class.java) - MmkvManager.encodeServerConfig("", config) - } catch (e: Exception) { - e.printStackTrace() - return -1 - } - return 0 - } - - /** - * getIndexViaGuid - */ - fun getIndexViaGuid(guid: String): Int { - try { - if (TextUtils.isEmpty(guid)) { - return -1 - } - for (i in angConfig.vmess.indices) { - if (angConfig.vmess[i].guid == guid) { - return i - } - } - return -1 - } catch (e: Exception) { - e.printStackTrace() - return -1 - } - } - /** * upgrade */ - fun upgradeServerVersion(vmess: AngConfig.VmessBean): Int { + private fun upgradeServerVersion(vmess: AngConfig.VmessBean): Int { try { if (vmess.configVersion == 2) { return 0 } when (vmess.network) { - "kcp" -> { - } - "ws" -> { + "ws", "h2" -> { var path = "" var host = "" val lstParameter = vmess.requestHost.split(";") - if (lstParameter.size > 0) { - path = lstParameter.get(0).trim() + if (lstParameter.isNotEmpty()) { + path = lstParameter[0].trim() } if (lstParameter.size > 1) { - path = lstParameter.get(0).trim() - host = lstParameter.get(1).trim() + path = lstParameter[0].trim() + host = lstParameter[1].trim() } vmess.path = path vmess.requestHost = host } - "h2" -> { - var path = "" - var host = "" - val lstParameter = vmess.requestHost.split(";") - if (lstParameter.size > 0) { - path = lstParameter.get(0).trim() - } - if (lstParameter.size > 1) { - path = lstParameter.get(0).trim() - host = lstParameter.get(1).trim() - } - vmess.path = path - vmess.requestHost = host - } - else -> { - } } vmess.configVersion = 2 return 0 @@ -771,62 +536,6 @@ object AngConfigManager { } } - fun addShadowsocksServer(vmess: AngConfig.VmessBean, index: Int): Int { - try { - vmess.configVersion = 2 - vmess.configType = EConfigType.SHADOWSOCKS.value - - vmess.address = vmess.address.trim() - vmess.id = vmess.id.trim() - vmess.security = vmess.security.trim() - - if (index >= 0) { - //edit - angConfig.vmess[index] = vmess - } else { - //add - vmess.guid = System.currentTimeMillis().toString() - angConfig.vmess.add(vmess) - if (angConfig.vmess.count() == 1) { - angConfig.index = 0 - } - } - - storeConfigFile() - } catch (e: Exception) { - e.printStackTrace() - return -1 - } - return 0 - } - - fun addSocksServer(vmess: AngConfig.VmessBean, index: Int): Int { - try { - vmess.configVersion = 2 - vmess.configType = EConfigType.SOCKS.value - - vmess.address = vmess.address.trim() - - if (index >= 0) { - //edit - angConfig.vmess[index] = vmess - } else { - //add - vmess.guid = System.currentTimeMillis().toString() - angConfig.vmess.add(vmess) - if (angConfig.vmess.count() == 1) { - angConfig.index = 0 - } - } - - storeConfigFile() - } catch (e: Exception) { - e.printStackTrace() - return -1 - } - return 0 - } - fun importBatchConfig(servers: String?, subid: String): Int { try { if (servers == null) { @@ -834,8 +543,8 @@ object AngConfigManager { } val removedSelectedServer = if (!TextUtils.isEmpty(subid)) { - configs.vmess.getOrNull(configs.index)?.let { - if (it.subid == subid) { + MmkvManager.decodeServerConfig(mainStorage?.decodeString(KEY_SELECTED_SERVER) ?: "")?.let { + if (it.subscriptionId == subid) { return@let it } return@let null @@ -843,7 +552,7 @@ object AngConfigManager { } else { null } - removeServerViaSubid(subid) + MmkvManager.removeServerViaSubid(subid) // var servers = server // if (server.indexOf("vmess") >= 0 && server.indexOf("vmess") == server.lastIndexOf("vmess")) { @@ -866,7 +575,7 @@ object AngConfigManager { } - fun saveSubItem(subItem: ArrayList): Int { + private fun saveSubItem(subItem: ArrayList): Int { try { if (subItem.count() <= 0) { return -1 @@ -886,22 +595,6 @@ object AngConfigManager { return 0 } - fun removeServerViaSubid(subid: String): Int { - if (TextUtils.isEmpty(subid) || configs.vmess.count() <= 0) { - return -1 - } - - for (k in configs.vmess.count() - 1 downTo 0) { - if (configs.vmess[k].subid.equals(subid)) { - angConfig.vmess.removeAt(k) - adjustIndexForRemovalAt(k) - } - } - - storeConfigFile() - return 0 - } - fun addSubItem(subItem: AngConfig.SubItemBean, index: Int): Int { try { if (index >= 0) { @@ -942,7 +635,7 @@ object AngConfigManager { return -1 } val subid = angConfig.subItem[index].id - removeServerViaSubid(subid) + MmkvManager.removeServerViaSubid(subid) angConfig.subItem.removeAt(index) @@ -953,74 +646,4 @@ object AngConfigManager { } return 0 } - - /** - * add or edit server - */ - fun addVlessServer(vmess: AngConfig.VmessBean, index: Int): Int { - try { - vmess.configVersion = 2 - vmess.configType = EConfigType.VLESS.value - - vmess.address = vmess.address.trim() - vmess.id = vmess.id.trim() - vmess.alterId = 0 - //vmess.flow = vmess.flow.trim() - vmess.security = vmess.security.trim() - vmess.network = vmess.network.trim() - vmess.headerType = vmess.headerType.trim() - vmess.requestHost = vmess.requestHost.trim() - vmess.path = vmess.path.trim() - vmess.streamSecurity = vmess.streamSecurity.trim() - - if (index >= 0) { - //edit - angConfig.vmess[index] = vmess - } else { - //add - vmess.guid = Utils.getUuid() - angConfig.vmess.add(vmess) - if (angConfig.vmess.count() == 1) { - angConfig.index = 0 - } - } - - storeConfigFile() - } catch (e: Exception) { - e.printStackTrace() - return -1 - } - return 0 - } - - fun addTrojanServer(vmess: AngConfig.VmessBean, index: Int): Int { - try { - vmess.configVersion = 2 - vmess.configType = EConfigType.TROJAN.value - - vmess.address = vmess.address.trim() - vmess.id = vmess.id.trim() - - vmess.streamSecurity = TLS; - - if (index >= 0) { - //edit - angConfig.vmess[index] = vmess - } else { - //add - vmess.guid = System.currentTimeMillis().toString() - angConfig.vmess.add(vmess) - if (angConfig.vmess.count() == 1) { - angConfig.index = 0 - } - } - - storeConfigFile() - } catch (e: Exception) { - e.printStackTrace() - return -1 - } - return 0 - } - } diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/MmkvManager.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/MmkvManager.kt index 45f5ed63..087c346a 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/MmkvManager.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/MmkvManager.kt @@ -14,6 +14,7 @@ object MmkvManager { const val KEY_SELECTED_SERVER = "SELECTED_SERVER" const val KEY_ANG_CONFIGS = "ANG_CONFIGS" + private val mainStorage by lazy { MMKV.mmkvWithID(ID_MAIN, MMKV.MULTI_PROCESS_MODE) } private val serverStorage by lazy { MMKV.mmkvWithID(ID_SERVER_CONFIG, MMKV.MULTI_PROCESS_MODE) } private val serverAffStorage by lazy { MMKV.mmkvWithID(ID_SERVER_AFF, MMKV.MULTI_PROCESS_MODE) } @@ -28,20 +29,49 @@ object MmkvManager { return Gson().fromJson(json, ServerConfig::class.java) } - fun encodeServerConfig(guid: String, config: ServerConfig) { + fun encodeServerConfig(guid: String, config: ServerConfig): String { val key = if (guid.isBlank()) { Utils.getUuid() } else { guid } serverStorage?.encode(key, Gson().toJson(config)) + val serverList= Gson().fromJson(mainStorage?.decodeString(KEY_ANG_CONFIGS), Array::class.java).toMutableList() + serverList.add(guid) + mainStorage?.encode(KEY_ANG_CONFIGS, Gson().toJson(serverList)) + if (mainStorage?.decodeString(KEY_SELECTED_SERVER).isNullOrBlank()) { + mainStorage?.encode(KEY_SELECTED_SERVER, guid) + } + return key } fun removeServer(guid: String) { + if (guid.isBlank()) { + return + } + if (mainStorage?.decodeString(KEY_SELECTED_SERVER) == guid) { + mainStorage?.remove(KEY_SELECTED_SERVER) + } + val serverList= Gson().fromJson(mainStorage?.decodeString(KEY_ANG_CONFIGS), Array::class.java).toMutableList() + serverList.remove(guid) + mainStorage?.encode(KEY_ANG_CONFIGS, Gson().toJson(serverList)) serverStorage?.remove(guid) serverAffStorage?.remove(guid) } + fun removeServerViaSubid(subid: String) { + if (subid.isBlank()) { + return + } + serverStorage?.allKeys()?.forEach { key -> + decodeServerConfig(key)?.let { config -> + if (config.subscriptionId == subid) { + removeServer(key) + } + } + } + } + fun decodeServerAffiliationInfo(guid: String): ServerAffiliationInfo? { if (guid.isBlank()) { return null 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 ee751ad5..05aefe3e 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 @@ -19,6 +19,7 @@ import android.text.TextUtils import android.util.Log import android.util.Patterns import android.webkit.URLUtil +import com.tencent.mmkv.MMKV import com.v2ray.ang.AngApplication import com.v2ray.ang.AppConfig import com.v2ray.ang.R @@ -35,6 +36,7 @@ import kotlin.coroutines.coroutineContext object Utils { + private val mainStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_MAIN, MMKV.MULTI_PROCESS_MODE) } private val tcpTestingSockets = ArrayList() /** @@ -270,8 +272,7 @@ object Utils { } fun startVServiceFromToggle(context: Context): Boolean { - val result = context.defaultDPreference.getPrefString(AppConfig.PREF_CURR_CONFIG, "") - if (result.isBlank()) { + if (mainStorage?.decodeString(MmkvManager.KEY_SELECTED_SERVER).isNullOrEmpty()) { context.toast(R.string.app_tile_first_use) return false } @@ -279,26 +280,6 @@ object Utils { return true } - /** - * startVService - */ - fun startVService(context: Context, guid: String): Boolean { - val index = AngConfigManager.getIndexViaGuid(guid) - context.v2RayApplication.curIndex=index - return startVService(context, index) - } - - /** - * startVService - */ - fun startVService(context: Context, index: Int): Boolean { - if (AngConfigManager.setActiveServer(index) < 0) { - return false - } - V2RayServiceManager.startV2Ray(context) - return true - } - /** * stopVService */ @@ -398,8 +379,8 @@ object Utils { /** * readTextFromAssets */ - fun readTextFromAssets(app: AngApplication, fileName: String): String { - val content = app.assets.open(fileName).bufferedReader().use { + fun readTextFromAssets(context: Context, fileName: String): String { + val content = context.assets.open(fileName).bufferedReader().use { it.readText() } return content diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/V2rayConfigUtil.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/V2rayConfigUtil.kt index cb4cd815..610c7476 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/V2rayConfigUtil.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/V2rayConfigUtil.kt @@ -1,14 +1,15 @@ package com.v2ray.ang.util +import android.content.Context import android.text.TextUtils import android.util.Log import com.google.gson.* -import com.v2ray.ang.AngApplication import com.v2ray.ang.AppConfig import com.v2ray.ang.dto.V2rayConfig import org.json.JSONException import org.json.JSONObject import com.v2ray.ang.dto.EConfigType +import com.v2ray.ang.extension.defaultDPreference object V2rayConfigUtil { // private val requestObj: JsonObject by lazy { @@ -23,7 +24,7 @@ object V2rayConfigUtil { /** * 生成v2ray的客户端配置文件 */ - fun getV2rayConfig(app: AngApplication, guid: String): Result { + fun getV2rayConfig(context: Context, guid: String): Result { try { val config = MmkvManager.decodeServerConfig(guid) ?: return Result(false, "") if (config.configType == EConfigType.CUSTOM) { @@ -32,7 +33,7 @@ object V2rayConfigUtil { return Result(true, customConfig) } val outbound = config.getProxyOutbound() ?: return Result(false, "") - val result = getV2rayNonCustomConfig(app, outbound) + val result = getV2rayNonCustomConfig(context, outbound) Log.d("V2rayConfigUtilGoLog", result.content) return result } catch (e: Exception) { @@ -44,10 +45,10 @@ object V2rayConfigUtil { /** * 生成v2ray的客户端配置文件 */ - private fun getV2rayNonCustomConfig(app: AngApplication, outbound: V2rayConfig.OutboundBean): Result { + private fun getV2rayNonCustomConfig(context: Context, outbound: V2rayConfig.OutboundBean): Result { val result = Result(false, "") //取得默认配置 - val assets = Utils.readTextFromAssets(app, "v2ray_config.json") + val assets = Utils.readTextFromAssets(context, "v2ray_config.json") if (TextUtils.isEmpty(assets)) { return result } @@ -55,18 +56,18 @@ object V2rayConfigUtil { //转成Json val v2rayConfig = Gson().fromJson(assets, V2rayConfig::class.java) ?: return result - //v2rayConfig.log.loglevel = app.defaultDPreference.getPrefString(AppConfig.PREF_LOGLEVEL, "warning") + //v2rayConfig.log.loglevel = context.defaultDPreference.getPrefString(AppConfig.PREF_LOGLEVEL, "warning") - inbounds(v2rayConfig, app) + inbounds(v2rayConfig, context) v2rayConfig.outbounds[0] = outbound - routing(v2rayConfig, app) + routing(v2rayConfig, context) - if (app.defaultDPreference.getPrefBoolean(AppConfig.PREF_LOCAL_DNS_ENABLED, false)) { - customLocalDns(v2rayConfig, app) + if (context.defaultDPreference.getPrefBoolean(AppConfig.PREF_LOCAL_DNS_ENABLED, false)) { + customLocalDns(v2rayConfig, context) } else { - customRemoteDns(v2rayConfig, app) + customRemoteDns(v2rayConfig, context) } val finalConfig = GsonBuilder().setPrettyPrinting().create().toJson(v2rayConfig) @@ -79,19 +80,19 @@ object V2rayConfigUtil { /** * */ - private fun inbounds(v2rayConfig: V2rayConfig, app: AngApplication): Boolean { + private fun inbounds(v2rayConfig: V2rayConfig, context: Context): Boolean { try { - //val socksPort = Utils.parseInt(app.defaultDPreference.getPrefStringNonEmpty(AppConfig.PREF_SOCKS_PORT, AppConfig.PORT_SOCKS)) - //val httpPort = Utils.parseInt(app.defaultDPreference.getPrefStringNonEmpty(AppConfig.PREF_HTTP_PORT, AppConfig.PORT_HTTP)) + //val socksPort = Utils.parseInt(context.defaultDPreference.getPrefStringNonEmpty(AppConfig.PREF_SOCKS_PORT, AppConfig .PORT_SOCKS)) + //val httpPort = Utils.parseInt(context.defaultDPreference.getPrefStringNonEmpty(AppConfig.PREF_HTTP_PORT, AppConfig.PORT_HTTP)) v2rayConfig.inbounds.forEach { curInbound -> - if (!app.defaultDPreference.getPrefBoolean(AppConfig.PREF_PROXY_SHARING, false)) { + if (!context.defaultDPreference.getPrefBoolean(AppConfig.PREF_PROXY_SHARING, false)) { //bind all inbounds to localhost if the user requests curInbound.listen = "127.0.0.1" } } v2rayConfig.inbounds[0].port = 10808 - v2rayConfig.inbounds[0].sniffing?.enabled = app.defaultDPreference.getPrefBoolean(AppConfig.PREF_SNIFFING_ENABLED, true) + v2rayConfig.inbounds[0].sniffing?.enabled = context.defaultDPreference.getPrefBoolean(AppConfig.PREF_SNIFFING_ENABLED, true) //v2rayConfig.inbounds[1].port = httpPort @@ -111,14 +112,14 @@ object V2rayConfigUtil { /** * routing */ - private fun routing(v2rayConfig: V2rayConfig, app: AngApplication): Boolean { + private fun routing(v2rayConfig: V2rayConfig, context: Context): Boolean { try { - routingUserRule(app.defaultDPreference.getPrefString(AppConfig.PREF_V2RAY_ROUTING_AGENT, ""), AppConfig.TAG_AGENT, v2rayConfig) - routingUserRule(app.defaultDPreference.getPrefString(AppConfig.PREF_V2RAY_ROUTING_DIRECT, ""), AppConfig.TAG_DIRECT, v2rayConfig) - routingUserRule(app.defaultDPreference.getPrefString(AppConfig.PREF_V2RAY_ROUTING_BLOCKED, ""), AppConfig.TAG_BLOCKED, v2rayConfig) + routingUserRule(context.defaultDPreference.getPrefString(AppConfig.PREF_V2RAY_ROUTING_AGENT, ""), AppConfig.TAG_AGENT, v2rayConfig) + routingUserRule(context.defaultDPreference.getPrefString(AppConfig.PREF_V2RAY_ROUTING_DIRECT, ""), AppConfig.TAG_DIRECT, v2rayConfig) + routingUserRule(context.defaultDPreference.getPrefString(AppConfig.PREF_V2RAY_ROUTING_BLOCKED, ""), AppConfig.TAG_BLOCKED, v2rayConfig) - v2rayConfig.routing.domainStrategy = app.defaultDPreference.getPrefString(AppConfig.PREF_ROUTING_DOMAIN_STRATEGY, "IPIfNonMatch") - val routingMode = app.defaultDPreference.getPrefString(AppConfig.PREF_ROUTING_MODE, "0") + v2rayConfig.routing.domainStrategy = context.defaultDPreference.getPrefString(AppConfig.PREF_ROUTING_DOMAIN_STRATEGY, "IPIfNonMatch") + val routingMode = context.defaultDPreference.getPrefString(AppConfig.PREF_ROUTING_MODE, "0") // Hardcode googleapis.cn val googleapisRoute = V2rayConfig.RoutingBean.RulesBean( @@ -229,36 +230,36 @@ object V2rayConfigUtil { /** * Custom Dns */ - private fun customLocalDns(v2rayConfig: V2rayConfig, app: AngApplication): Boolean { + private fun customLocalDns(v2rayConfig: V2rayConfig, context: Context): Boolean { try { val hosts = mutableMapOf() val servers = ArrayList() - val remoteDns = Utils.getRemoteDnsServers(app.defaultDPreference) + val remoteDns = Utils.getRemoteDnsServers(context.defaultDPreference) remoteDns.forEach { servers.add(it) } - val domesticDns = Utils.getDomesticDnsServers(app.defaultDPreference) + val domesticDns = Utils.getDomesticDnsServers(context.defaultDPreference) val geositeCn = arrayListOf("geosite:cn") val geoipCn = arrayListOf("geoip:cn") - val agDomain = userRule2Domian(app.defaultDPreference.getPrefString(AppConfig.PREF_V2RAY_ROUTING_AGENT, "")) + val agDomain = userRule2Domian(context.defaultDPreference.getPrefString(AppConfig.PREF_V2RAY_ROUTING_AGENT, "")) if (agDomain.size > 0) { servers.add(V2rayConfig.DnsBean.ServersBean(remoteDns.first(), 53, agDomain, null)) } - val dirDomain = userRule2Domian(app.defaultDPreference.getPrefString(AppConfig.PREF_V2RAY_ROUTING_DIRECT, "")) + val dirDomain = userRule2Domian(context.defaultDPreference.getPrefString(AppConfig.PREF_V2RAY_ROUTING_DIRECT, "")) if (dirDomain.size > 0) { servers.add(V2rayConfig.DnsBean.ServersBean(domesticDns.first(), 53, dirDomain, geoipCn)) } - val routingMode = app.defaultDPreference.getPrefString(AppConfig.PREF_ROUTING_MODE, "0") + val routingMode = context.defaultDPreference.getPrefString(AppConfig.PREF_ROUTING_MODE, "0") if (routingMode == "2" || routingMode == "3") { servers.add(V2rayConfig.DnsBean.ServersBean(domesticDns.first(), 53, geositeCn, geoipCn)) } - val blkDomain = userRule2Domian(app.defaultDPreference.getPrefString(AppConfig.PREF_V2RAY_ROUTING_BLOCKED, "")) + val blkDomain = userRule2Domian(context.defaultDPreference.getPrefString(AppConfig.PREF_V2RAY_ROUTING_BLOCKED, "")) if (blkDomain.size > 0) { hosts.putAll(blkDomain.map { it to "127.0.0.1" }) } @@ -278,7 +279,7 @@ object V2rayConfigUtil { port = 53, network = "tcp,udp") - //val localDnsPort = Utils.parseInt(app.defaultDPreference.getPrefStringNonEmpty(AppConfig.PREF_LOCAL_DNS_PORT, AppConfig.PORT_LOCAL_DNS)) + //val localDnsPort = Utils.parseInt(context.defaultDPreference.getPrefStringNonEmpty(AppConfig.PREF_LOCAL_DNS_PORT, AppConfig.PORT_LOCAL_DNS)) v2rayConfig.inbounds.add( V2rayConfig.InboundBean( tag = "dns-in", @@ -338,12 +339,12 @@ object V2rayConfigUtil { /** * Custom Remote Dns */ - private fun customRemoteDns(v2rayConfig: V2rayConfig, app: AngApplication): Boolean { + private fun customRemoteDns(v2rayConfig: V2rayConfig, context: Context): Boolean { try { val servers = ArrayList() val hosts = mutableMapOf() - Utils.getRemoteDnsServers(app.defaultDPreference).forEach { + Utils.getRemoteDnsServers(context.defaultDPreference).forEach { servers.add(it) } // hardcode googleapi rule to fix play store problems diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/viewmodel/MainViewModel.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/viewmodel/MainViewModel.kt index 605cc15c..c510d8d0 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/viewmodel/MainViewModel.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/viewmodel/MainViewModel.kt @@ -13,16 +13,21 @@ import com.tencent.mmkv.MMKV 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.dto.ServerConfig +import com.v2ray.ang.dto.V2rayConfig import com.v2ray.ang.extension.toast import com.v2ray.ang.util.* import com.v2ray.ang.util.MmkvManager.KEY_ANG_CONFIGS import kotlinx.coroutines.* +import java.util.* class MainViewModel(application: Application) : AndroidViewModel(application) { private val mainStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_MAIN, MMKV.MULTI_PROCESS_MODE) } private val serverAffStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SERVER_AFF, MMKV.MULTI_PROCESS_MODE) } - val serverList by lazy { Gson().fromJson(mainStorage?.decodeString(KEY_ANG_CONFIGS), Array::class.java).toList() } + var serverList= Gson().fromJson(mainStorage?.decodeString(KEY_ANG_CONFIGS), Array::class.java).toMutableList() + private set val isRunning by lazy { MutableLiveData() } val updateListAction by lazy { MutableLiveData() } val updateTestResultAction by lazy { MutableLiveData() } @@ -43,8 +48,26 @@ class MainViewModel(application: Application) : AndroidViewModel(application) { super.onCleared() } - fun getGuid(index: Int): String? { - return serverList.getOrNull(index) + fun reloadServerList() { + serverList = Gson().fromJson(mainStorage?.decodeString(KEY_ANG_CONFIGS), Array::class.java).toMutableList() + updateListAction.value = -1 + } + + fun removeServer(guid: String) { + serverList.remove(guid) + MmkvManager.removeServer(guid) + } + + fun appendCustomConfigServer(server: String) { + val config = ServerConfig.create(EConfigType.CUSTOM) + config.remarks = System.currentTimeMillis().toString() + config.fullConfig = Gson().fromJson(server, V2rayConfig::class.java) + serverList.add(MmkvManager.encodeServerConfig("", config)) + } + + fun swapServer(fromPosition: Int, toPosition: Int) { + Collections.swap(serverList, fromPosition, toPosition) + mainStorage?.encode(KEY_ANG_CONFIGS, Gson().toJson(serverList)) } fun testAllTcping() {