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
This commit is contained in:
@@ -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<String> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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<ServiceControl>? = 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<Mai
|
||||
private val share_method: Array<out String> 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<Mai
|
||||
holder.itemView.setBackgroundColor(Color.TRANSPARENT)
|
||||
holder.test_result.text = aff?.getTestDelayString() ?: ""
|
||||
if (aff?.testDelayMillis?:0L < 0L) {
|
||||
holder.test_result.setTextColor(ContextCompat.getColor(mActivity, R.color.viewfinder_laser))
|
||||
holder.test_result.setTextColor(ContextCompat.getColor(mActivity, android.R.color.holo_red_dark))
|
||||
} else {
|
||||
holder.test_result.setTextColor(ContextCompat.getColor(mActivity, R.color.colorPing))
|
||||
}
|
||||
@@ -83,7 +77,7 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
|
||||
when (i) {
|
||||
0 -> {
|
||||
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<Mai
|
||||
mActivity.toast(R.string.toast_failure)
|
||||
}
|
||||
}
|
||||
2 -> shareFullContent(position)
|
||||
2 -> shareFullContent(guid)
|
||||
else -> mActivity.toast("else")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
@@ -108,6 +102,7 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
|
||||
|
||||
holder.layout_edit.setOnClickListener {
|
||||
val intent = Intent().putExtra("guid", guid)
|
||||
.putExtra("isRunning", isRunning)
|
||||
if (config.configType == EConfigType.CUSTOM) {
|
||||
mActivity.startActivity(intent.setClass(mActivity, ServerCustomConfigActivity::class.java))
|
||||
} else {
|
||||
@@ -115,28 +110,29 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
|
||||
}
|
||||
}
|
||||
holder.layout_remove.setOnClickListener {
|
||||
if (guid != mainStorage?.decodeString(MmkvManager.KEY_SELECTED_SERVER)
|
||||
&& AngConfigManager.removeServer(position) == 0) {
|
||||
if (guid != mainStorage?.decodeString(MmkvManager.KEY_SELECTED_SERVER)) {
|
||||
mActivity.mainViewModel.removeServer(guid)
|
||||
notifyItemRemoved(position)
|
||||
}
|
||||
}
|
||||
|
||||
holder.infoContainer.setOnClickListener {
|
||||
if (changeable) {
|
||||
AngConfigManager.setActiveServer(position)
|
||||
} else {
|
||||
mActivity.showCircle()
|
||||
Utils.stopVService(mActivity)
|
||||
Observable.timer(500, TimeUnit.MILLISECONDS)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe {
|
||||
mActivity.showCircle()
|
||||
if (!Utils.startVService(mActivity, position)) {
|
||||
val selected = mainStorage?.decodeString(MmkvManager.KEY_SELECTED_SERVER)
|
||||
if (guid != selected) {
|
||||
mainStorage?.encode(MmkvManager.KEY_SELECTED_SERVER, guid)
|
||||
notifyItemChanged(mActivity.mainViewModel.serverList.indexOf(selected))
|
||||
notifyItemChanged(mActivity.mainViewModel.serverList.indexOf(guid))
|
||||
if (isRunning) {
|
||||
mActivity.showCircle()
|
||||
Utils.stopVService(mActivity)
|
||||
Observable.timer(500, TimeUnit.MILLISECONDS)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe {
|
||||
V2RayServiceManager.startV2Ray(mActivity)
|
||||
mActivity.hideCircle()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
if (holder is FooterViewHolder) {
|
||||
@@ -151,8 +147,8 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
|
||||
}
|
||||
}
|
||||
|
||||
private fun shareFullContent(position: Int) {
|
||||
if (AngConfigManager.shareFullContent2Clipboard(mActivity.mainViewModel.getGuid(position)) == 0) {
|
||||
private fun shareFullContent(guid: String) {
|
||||
if (AngConfigManager.shareFullContent2Clipboard(guid) == 0) {
|
||||
mActivity.toast(R.string.toast_success)
|
||||
} else {
|
||||
mActivity.toast(R.string.toast_failure)
|
||||
@@ -214,12 +210,12 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
|
||||
}
|
||||
|
||||
override fun onItemDismiss(position: Int) {
|
||||
if (mActivity.mainViewModel.getGuid(position) != mainStorage?.decodeString(MmkvManager.KEY_SELECTED_SERVER)) {
|
||||
val guid = mActivity.mainViewModel.serverList.getOrNull(position) ?: return
|
||||
if (guid != mainStorage?.decodeString(MmkvManager.KEY_SELECTED_SERVER)) {
|
||||
// mActivity.alert(R.string.del_config_comfirm) {
|
||||
// positiveButton(android.R.string.ok) {
|
||||
if (AngConfigManager.removeServer(position) == 0) {
|
||||
notifyItemRemoved(position)
|
||||
}
|
||||
mActivity.mainViewModel.removeServer(guid)
|
||||
notifyItemRemoved(position)
|
||||
// }
|
||||
// show()
|
||||
// }
|
||||
@@ -227,13 +223,13 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
|
||||
}
|
||||
|
||||
override fun onItemMove(fromPosition: Int, toPosition: Int): Boolean {
|
||||
AngConfigManager.swapServer(fromPosition, toPosition)
|
||||
mActivity.mainViewModel.swapServer(fromPosition, toPosition)
|
||||
notifyItemMoved(fromPosition, toPosition)
|
||||
notifyItemRangeChanged(fromPosition, toPosition - fromPosition + 1)
|
||||
//notifyItemRangeChanged(fromPosition, toPosition - fromPosition + 1)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onItemMoveCompleted() {
|
||||
AngConfigManager.storeConfigFile()
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ import com.v2ray.ang.dto.ServerConfig
|
||||
import com.v2ray.ang.dto.V2rayConfig
|
||||
import com.v2ray.ang.dto.V2rayConfig.Companion.DEFAULT_NETWORK
|
||||
import com.v2ray.ang.dto.V2rayConfig.Companion.DEFAULT_PORT
|
||||
import com.v2ray.ang.dto.V2rayConfig.Companion.HTTP
|
||||
import com.v2ray.ang.dto.V2rayConfig.Companion.TLS
|
||||
import com.v2ray.ang.dto.V2rayConfig.Companion.XTLS
|
||||
import com.v2ray.ang.extension.toast
|
||||
@@ -37,7 +36,11 @@ class ServerActivity : BaseActivity() {
|
||||
|
||||
private val mainStorage by lazy { MMKV.mmkvWithID(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(KEY_SELECTED_SERVER) }
|
||||
private val isRunning by lazy {
|
||||
intent.getBooleanExtra("isRunning", false)
|
||||
&& editGuid.isNotEmpty()
|
||||
&& editGuid == mainStorage?.decodeString(KEY_SELECTED_SERVER)
|
||||
}
|
||||
private val createConfigType by lazy {
|
||||
EConfigType.fromInt(intent.getIntExtra("createConfigType", EConfigType.VMESS.value)) ?: EConfigType.VMESS
|
||||
}
|
||||
@@ -237,73 +240,25 @@ class ServerActivity : BaseActivity() {
|
||||
}
|
||||
|
||||
private fun saveStreamSettings(streamSetting: V2rayConfig.OutboundBean.StreamSettingsBean) {
|
||||
streamSetting.network = if (sp_network != null) networks[sp_network.selectedItemPosition] else DEFAULT_NETWORK
|
||||
val headerType = if (sp_header_type != null) headertypes[sp_header_type.selectedItemPosition] else ""
|
||||
val requestHost = et_request_host.text.toString().trim()
|
||||
val path = et_path?.text.toString().trim()
|
||||
var sni = requestHost
|
||||
when (streamSetting.network) {
|
||||
"tcp" -> 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
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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<String> = ArrayList()
|
||||
private var lstGuid: ArrayList<String> = 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() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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<AngConfig.SubItemBean>): Int {
|
||||
private fun saveSubItem(subItem: ArrayList<AngConfig.SubItemBean>): 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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<String>::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<String>::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
|
||||
|
||||
@@ -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<Socket?>()
|
||||
|
||||
/**
|
||||
@@ -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
|
||||
|
||||
@@ -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<String, String>()
|
||||
val servers = ArrayList<Any>()
|
||||
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<Any>()
|
||||
val hosts = mutableMapOf<String, String>()
|
||||
|
||||
Utils.getRemoteDnsServers(app.defaultDPreference).forEach {
|
||||
Utils.getRemoteDnsServers(context.defaultDPreference).forEach {
|
||||
servers.add(it)
|
||||
}
|
||||
// hardcode googleapi rule to fix play store problems
|
||||
|
||||
@@ -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<String>::class.java).toList() }
|
||||
var serverList= Gson().fromJson(mainStorage?.decodeString(KEY_ANG_CONFIGS), Array<String>::class.java).toMutableList()
|
||||
private set
|
||||
val isRunning by lazy { MutableLiveData<Boolean>() }
|
||||
val updateListAction by lazy { MutableLiveData<Int>() }
|
||||
val updateTestResultAction by lazy { MutableLiveData<String>() }
|
||||
@@ -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<String>::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() {
|
||||
|
||||
Reference in New Issue
Block a user