703 lines
26 KiB
Kotlin
703 lines
26 KiB
Kotlin
package com.yingmao.clash.act
|
|
|
|
|
|
import android.annotation.SuppressLint
|
|
import android.content.BroadcastReceiver
|
|
import android.content.Context
|
|
import android.content.Intent
|
|
import android.content.IntentFilter
|
|
import android.graphics.Color
|
|
import android.os.Build
|
|
import android.os.Bundle
|
|
import android.text.TextUtils
|
|
import android.view.View
|
|
import android.widget.ImageView
|
|
import android.widget.TextView
|
|
import androidx.appcompat.widget.AppCompatImageView
|
|
import androidx.lifecycle.ViewModelProvider
|
|
import androidx.recyclerview.widget.LinearLayoutManager
|
|
import androidx.recyclerview.widget.RecyclerView
|
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener
|
|
import com.chad.library.adapter.base.BaseQuickAdapter
|
|
import com.cncat.vpn.common.constants.Intents
|
|
import com.cncat.vpn.common.log.Log
|
|
import com.cncat.vpn.core.Clash
|
|
import com.cncat.vpn.core.model.TunnelState
|
|
import com.cncat.vpn.service.remote.IRemoteService
|
|
import com.google.gson.Gson
|
|
import com.google.gson.JsonParser
|
|
import com.tencent.mmkv.MMKV
|
|
import com.yingmao.clash.BuildConfig
|
|
import com.yingmao.clash.R
|
|
import com.yingmao.clash.act.MainActivity.Companion.clashBinder
|
|
import com.yingmao.clash.act.MainActivity.Companion.profileBinder
|
|
import com.yingmao.clash.act.MainActivity.Companion.subUrl
|
|
import com.yingmao.clash.app.MainApplication
|
|
import com.yingmao.clash.base.BaseActivity
|
|
import com.yingmao.clash.conf.BZYConstants
|
|
import com.yingmao.clash.conf.Conf
|
|
import com.yingmao.clash.entity.ClashNodebBean
|
|
import com.yingmao.clash.fragment.home.HomeFragment.Companion.CHECK_NODE_REQUEST_CODE
|
|
import com.yingmao.clash.fragment.home.HomeFragment.Companion.clashRunning
|
|
import com.yingmao.clash.fragment.home.HomeFragment.Companion.isGlo
|
|
import com.yingmao.clash.listener.AsyncCallBackListener
|
|
import com.yingmao.clash.remote.Resource
|
|
import com.yingmao.clash.util.Utils
|
|
import com.yingmao.clash.view.RxToast
|
|
import com.yingmao.clash.view.adapter.NodeListAdapter
|
|
import com.yingmao.clash.view.dialog.DialogDoMainChoose
|
|
import kotlinx.coroutines.Dispatchers
|
|
import kotlinx.coroutines.Job
|
|
import kotlinx.coroutines.coroutineScope
|
|
import kotlinx.coroutines.delay
|
|
import kotlinx.coroutines.launch
|
|
import kotlinx.coroutines.sync.Semaphore
|
|
import kotlinx.coroutines.sync.withPermit
|
|
import okhttp3.Response
|
|
import java.util.UUID
|
|
|
|
class ChangeNodeActivity : BaseActivity(), View.OnClickListener {
|
|
companion object {
|
|
private var currentNode: MutableList<ClashNodebBean> = mutableListOf()
|
|
|
|
}
|
|
|
|
private lateinit var swipeLayout: SwipeRefreshLayout
|
|
private lateinit var changeNodeVM: ChangeNodeVM
|
|
private lateinit var nodeListRv: RecyclerView
|
|
private lateinit var aiBack: AppCompatImageView
|
|
private var broadcastReceiver: BroadcastReceiver? = null
|
|
private var cacheNodeStr: String? = ""
|
|
private var uuid: UUID? = null
|
|
private var testRunDing = false
|
|
private lateinit var tvProA: TextView
|
|
private lateinit var tvProj: TextView
|
|
private var timerJob: Job? = null
|
|
val remote = Resource<IRemoteService>()
|
|
var getNodeTimeOutNum = 5
|
|
var isRunDing = false
|
|
var dialogDoMainChoose: DialogDoMainChoose? = null
|
|
var proList: MutableList<ClashNodebBean>? = null
|
|
var nowSubUrl: String = ""
|
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
super.onCreate(savedInstanceState)
|
|
setContentView(R.layout.activity_change_node)
|
|
changeNodeVM = ViewModelProvider(this)[ChangeNodeVM::class.java]
|
|
observe(this, changeNodeVM)
|
|
initData()
|
|
initViews()
|
|
}
|
|
|
|
@Deprecated("Deprecated in Java")
|
|
override fun onBackPressed() {
|
|
intent.putExtra(BZYConstants.EXTRA_KEY_IS_RUNING, isRunDing)
|
|
setResult(CHECK_NODE_REQUEST_CODE, intent)
|
|
super.onBackPressed()
|
|
}
|
|
|
|
private fun initData() {
|
|
cacheNodeStr = MMKV.defaultMMKV().decodeString(BZYConstants.NODE_All_ITEM_NAME_KEY, "")
|
|
val intentFilter = IntentFilter(Intents.ACTION_NODE_LIST)
|
|
broadcastReceiver = NodeListBR()
|
|
if (Build.VERSION.SDK_INT >= 34 && applicationInfo.targetSdkVersion >= 34) {
|
|
registerReceiver(
|
|
broadcastReceiver,
|
|
intentFilter,
|
|
Context.RECEIVER_EXPORTED
|
|
)
|
|
} else {
|
|
registerReceiver(
|
|
broadcastReceiver,
|
|
intentFilter
|
|
)
|
|
}
|
|
|
|
}
|
|
|
|
@SuppressLint("SetTextI18n")
|
|
private fun initViews() {
|
|
aiBack = findViewById(R.id.aiBack)
|
|
tvProA = findViewById(R.id.tvProA)
|
|
tvProj = findViewById(R.id.tvProj)
|
|
|
|
val isGlobal = MMKV.defaultMMKV().decodeBool(BZYConstants.MMKV_KEY_GLOBAL_MODEL, false)
|
|
var px: ProxyMode
|
|
if (isGlobal) {
|
|
px = ProxyMode.GLOBAL
|
|
} else {
|
|
px = ProxyMode.PAC
|
|
}
|
|
setBackground(px)
|
|
|
|
|
|
tvProA.setOnClickListener {
|
|
if(!clashRunning){
|
|
setBackground(ProxyMode.PAC)
|
|
MMKV.defaultMMKV().encode(BZYConstants.MMKV_KEY_GLOBAL_MODEL, false)
|
|
checkGlo()
|
|
}else{
|
|
RxToast.info(resources.getString(R.string.closeRunningChoose))
|
|
}
|
|
if(clashRunning){
|
|
RxToast.info(resources.getString(R.string.SwitchingSourceModel))
|
|
return@setOnClickListener
|
|
}
|
|
|
|
}
|
|
tvProj.setOnClickListener {
|
|
if(!clashRunning){
|
|
MMKV.defaultMMKV().encode(BZYConstants.MMKV_KEY_GLOBAL_MODEL, true)
|
|
setBackground(ProxyMode.GLOBAL)
|
|
}else{
|
|
RxToast.info(resources.getString(R.string.closeRunningChoose))
|
|
}
|
|
|
|
}
|
|
|
|
aiBack.setOnClickListener {
|
|
onBackPressed()
|
|
}
|
|
swipeLayout = findViewById(R.id.swipeLayout)
|
|
initRefreshLayout()
|
|
nodeListRv = findViewById(R.id.rv_node_list)
|
|
findViewById<ImageView>(R.id.iv_back).setOnClickListener {
|
|
onBackPressed()
|
|
}
|
|
initAdapter()
|
|
if (!TextUtils.isEmpty(cacheNodeStr)) {
|
|
currentNode = getNodesInfo(cacheNodeStr!!)
|
|
setNewData(currentNode)
|
|
}
|
|
}
|
|
|
|
private fun getNodesInfo(json: String): MutableList<ClashNodebBean> {
|
|
val mList: MutableList<ClashNodebBean> = mutableListOf()
|
|
if (TextUtils.isEmpty(json)) {
|
|
return mList
|
|
}
|
|
val gson = Gson()
|
|
val ja = JsonParser.parseString(json).asJsonArray
|
|
|
|
val cnb = ClashNodebBean("自动选择", "", "", 1, "", "", 0, true);
|
|
mList.add(cnb)
|
|
for (i in 0 until ja.size()) {
|
|
val bean = gson.fromJson(ja.get(i).asString, ClashNodebBean::class.java)
|
|
bean.delay = Utils.generateRandomTwoDigitNumber(10, 60)
|
|
mList.add(bean)
|
|
}
|
|
return mList
|
|
}
|
|
|
|
inner class NodeListBR : BroadcastReceiver() {
|
|
override fun onReceive(context: Context?, intent: Intent?) {
|
|
when (intent?.action) {
|
|
Intents.ACTION_NODE_LIST -> {
|
|
Log.d("ACTION_NODE_LIST ACTION_NODE_LIST")
|
|
hideLoading()
|
|
timerJob?.cancel()
|
|
dialogDoMainChoose?.dismiss()
|
|
val json = serStore.nodes
|
|
if (TextUtils.isEmpty(json)) {
|
|
return
|
|
}
|
|
//缓存订阅地址
|
|
subUrl = nowSubUrl
|
|
MMKV.defaultMMKV().encode(BZYConstants.PROFILE_SUB_URL, nowSubUrl)
|
|
//缓存节点
|
|
MMKV.defaultMMKV().encode(BZYConstants.NODE_All_ITEM_NAME_KEY, json)
|
|
currentNode = getNodesInfo(json)
|
|
setNewData(currentNode)
|
|
swipeLayout.isRefreshing = false
|
|
}
|
|
|
|
else -> {
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
override fun onDestroy() {
|
|
super.onDestroy()
|
|
broadcastReceiver?.let {
|
|
unregisterReceiver(it)
|
|
}
|
|
if (dialogDoMainChoose != null) {
|
|
if (dialogDoMainChoose!!.isShowing) {
|
|
dialogDoMainChoose!!.dismiss()
|
|
dialogDoMainChoose = null
|
|
}
|
|
}
|
|
}
|
|
|
|
override fun onResume() {
|
|
super.onResume()
|
|
isRunDing = intent.getBooleanExtra(BZYConstants.EXTRA_KEY_IS_RUNING, false)
|
|
}
|
|
|
|
private fun updateProfile(isDelete: Boolean) {
|
|
try {
|
|
launch(Dispatchers.IO) {
|
|
profileBinder?.let { profileManager ->
|
|
var mProfile = profileManager.queryAll().find {
|
|
it.name == Conf.getClashConfName(this@ChangeNodeActivity.applicationContext)
|
|
}
|
|
|
|
if (mProfile == null) {
|
|
//为空的情况下选择第一个
|
|
profileManager.queryAll().forEach { profile ->
|
|
mProfile = profile
|
|
return@forEach
|
|
}
|
|
}
|
|
|
|
mProfile?.let { profile ->
|
|
uuid = profile.uuid
|
|
Log.d("ChangeNode22 uuid==>${profile.uuid}")
|
|
if (isDelete) {
|
|
MMKV.defaultMMKV()
|
|
.removeValueForKey(BZYConstants.NODE_All_ITEM_NAME_KEY)
|
|
|
|
} else {
|
|
profileManager.setActive(profile)
|
|
Log.d("updateProfile pro.source==>${profile.source}, url==>${nowSubUrl}")
|
|
if (!TextUtils.isEmpty(profile.source)) {
|
|
|
|
if (profile.source != nowSubUrl) {
|
|
Log.d("updateProfile patch")
|
|
profileManager.patch(
|
|
profile.uuid,
|
|
profile.name,
|
|
nowSubUrl,
|
|
1800000L
|
|
)
|
|
Log.d("updateProfile patch 1")
|
|
profileManager.commit(profile.uuid) { status ->
|
|
Log.d("updateProfile updateStatus: " + Gson().toJson(status.args) + ", pro==>" + status.progress)
|
|
}
|
|
Log.d("updateProfile patch 2")
|
|
}
|
|
} else {
|
|
profileManager.patch(
|
|
profile.uuid,
|
|
profile.name,
|
|
nowSubUrl,
|
|
1800000L
|
|
)
|
|
Log.d("updateProfile patch new 1")
|
|
profileManager.commit(profile.uuid) { status ->
|
|
Log.d("updateProfile new updateStatus: " + Gson().toJson(status.args) + ", pro==>" + status.progress)
|
|
}
|
|
}
|
|
Log.d("updateProfile go update")
|
|
profileManager.update(profile.uuid)
|
|
}
|
|
Log.d("gogogogogo end")
|
|
}
|
|
}
|
|
}
|
|
} catch (e: Exception) {
|
|
e.printStackTrace()
|
|
}
|
|
}
|
|
|
|
private fun initRefreshLayout() {
|
|
swipeLayout.setColorSchemeColors(Color.rgb(47, 223, 189))
|
|
swipeLayout.setOnRefreshListener(object : OnRefreshListener {
|
|
override fun onRefresh() {
|
|
if (testRunDing) {
|
|
RxToast.info("正在测速中.");
|
|
swipeLayout.isRefreshing = false
|
|
return
|
|
}
|
|
swipeLayout.isRefreshing = true
|
|
updateProfile(false)
|
|
swipeLayout.isRefreshing = false
|
|
changeNodeVM.getAirNodes()
|
|
}
|
|
})
|
|
}
|
|
|
|
override fun onStart() {
|
|
super.onStart()
|
|
subUrl?.let {
|
|
nowSubUrl = it
|
|
}
|
|
doProfile(false)
|
|
|
|
}
|
|
|
|
fun doProfile(isDelete: Boolean) {
|
|
showLoading("正在获取...", this@ChangeNodeActivity)
|
|
startTimer()
|
|
launch(Dispatchers.IO) {
|
|
MainActivity.setSub2(nowSubUrl)
|
|
}
|
|
|
|
}
|
|
|
|
private fun changeSubUrl(originalUrl: String, newUrl: String) {
|
|
if (!originalUrl.isNullOrEmpty() && !newUrl.isNullOrEmpty()) {
|
|
nowSubUrl = originalUrl.replace(Regex("https://[^/]+/"), "$newUrl/")
|
|
subUrl = nowSubUrl
|
|
MMKV.defaultMMKV().encode(BZYConstants.PROFILE_SUB_URL, nowSubUrl)
|
|
}
|
|
}
|
|
|
|
val reloadLock = Semaphore(10)
|
|
private fun setPing() {
|
|
if (!clashRunning) {
|
|
RxToast.info("请先开启加速后,再进行测试.")
|
|
return
|
|
}
|
|
if (testRunDing) {
|
|
RxToast.info("正在测速中.")
|
|
return
|
|
}
|
|
testRunDing = true
|
|
updateMoreBar("测速中")
|
|
launch(Dispatchers.IO) {
|
|
val group = reloadLock.withPermit {
|
|
clashBinder?.let { cb ->
|
|
val names = cb.queryProxyGroupNames(false)
|
|
if (names.isNotEmpty()) {
|
|
cb.healthCheck(names[0])
|
|
}
|
|
delay(1000)
|
|
cb.queryProxyGroup(names[0], uiStore.proxySort)
|
|
}
|
|
}
|
|
testRunDing = false
|
|
runOnUiThread { updateMoreBar("线路测速") }
|
|
val newData = mutableListOf<ClashNodebBean>()
|
|
group?.proxies?.forEach { proxy ->
|
|
Log.d("node==>${proxy}")
|
|
if (currentNode.isNotEmpty()) {
|
|
for (nodeItem: ClashNodebBean in currentNode) {
|
|
if (nodeItem.name == proxy.name) {
|
|
if (proxy.name != "自动选择") {
|
|
nodeItem.delay = proxy.delay;
|
|
}
|
|
Log.d("nodeItem==>${nodeItem}")
|
|
newData.add(nodeItem)
|
|
return@forEach
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (newData.isNotEmpty()) {
|
|
setNewData(newData)
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun setNewData(nodes: MutableList<ClashNodebBean>?) {
|
|
runOnUiThread {
|
|
if (nodes != null) {
|
|
nodeListAdapter.setNewInstance(nodes)
|
|
val selectedName =
|
|
MMKV.defaultMMKV().decodeString(BZYConstants.NODE_SELECTED_NAME_KEY, "")
|
|
for ((index, node) in nodes.withIndex()) {
|
|
if (node.name == selectedName) {
|
|
nodeListAdapter.selectedPosition = index
|
|
}
|
|
}
|
|
nodeListAdapter.notifyDataSetChanged()
|
|
}
|
|
}
|
|
}
|
|
|
|
var nodeListAdapter: NodeListAdapter = NodeListAdapter(R.layout.node_list_item_layout)
|
|
private fun initAdapter() {
|
|
nodeListRv.layoutManager = LinearLayoutManager(this)
|
|
nodeListRv.adapter = nodeListAdapter
|
|
nodeListAdapter.selectedPosition
|
|
nodeListAdapter.setOnItemClickListener { baseQuickAdapter: BaseQuickAdapter<*, *>, view: View, position: Int ->
|
|
(baseQuickAdapter.getItem(position) as ClashNodebBean).let { profile ->
|
|
MMKV.defaultMMKV().encode(BZYConstants.NODE_SELECTED_NAME_KEY, profile.name)
|
|
nodeListAdapter.notifyItemChanged(nodeListAdapter.selectedPosition)
|
|
nodeListAdapter.selectedPosition =
|
|
if (nodeListAdapter.selectedPosition == position) -1 else position
|
|
nodeListAdapter.notifyItemChanged(position)
|
|
launch(Dispatchers.IO) {
|
|
val intent = Intent()
|
|
intent.putExtra(BZYConstants.MMKV_KEY_SELECT_NODE_NAME, profile.name)
|
|
intent.putExtra(BZYConstants.EXTRA_KEY_IS_RUNING, isRunDing)
|
|
setResult(CHECK_NODE_REQUEST_CODE, intent)
|
|
finish()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
private fun startTimer() {
|
|
getNodeTimeOutNum = 15
|
|
timerJob = launch {
|
|
repeat(16) {
|
|
Log.d("GetNodeTimeOutNum==>$getNodeTimeOutNum")
|
|
|
|
if (getNodeTimeOutNum == 0) {
|
|
hideLoading()
|
|
Log.d("stopTimer...")
|
|
RxToast.error("获取节点失败...正在切换其他地址")
|
|
if (setNewData()) {
|
|
timerJob?.cancel()
|
|
MMKV.defaultMMKV()
|
|
.removeValueForKey(BZYConstants.NODE_All_ITEM_NAME_KEY)
|
|
saveSubUrl()
|
|
|
|
MainApplication.restartApp(this@ChangeNodeActivity, object :
|
|
MainApplication.OnRestartButtonListener {
|
|
|
|
override fun enterButton() {
|
|
|
|
}
|
|
|
|
override fun cancelButton() {
|
|
// doProfile(false)
|
|
}
|
|
|
|
})
|
|
} else {
|
|
timerJob?.cancel()
|
|
RxToast.error("获取全部线路失败...请切换网络重试")
|
|
}
|
|
}
|
|
getNodeTimeOutNum--
|
|
delay(1000)
|
|
}
|
|
}
|
|
}
|
|
|
|
override fun onClick(p0: View?) {
|
|
when (p0?.id) {
|
|
R.id.tv_more -> {
|
|
setPing()
|
|
}
|
|
|
|
else -> {
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun setBackground(proxyMode: ProxyMode) {
|
|
|
|
when (proxyMode) {
|
|
ProxyMode.PAC -> {
|
|
|
|
isGlo = false
|
|
tvProA.setBackgroundResource(R.drawable.yil_node_proxy_choose_bg)
|
|
tvProj.setBackgroundResource(0)
|
|
MMKV.defaultMMKV().encode(BZYConstants.MMKV_KEY_GLOBAL_MODEL, false)
|
|
sendMode(false)
|
|
}
|
|
|
|
ProxyMode.GLOBAL -> {
|
|
isGlo = true
|
|
tvProA.setBackgroundResource(0)
|
|
tvProj.setBackgroundResource(R.drawable.yil_node_proxy_choose_bg)
|
|
MMKV.defaultMMKV().encode(BZYConstants.MMKV_KEY_GLOBAL_MODEL, true)
|
|
sendMode(true)
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
fun sendMode(type: Boolean) {
|
|
val intent = Intent(Intents.ACTION_Glo_Mode)
|
|
intent.putExtra("mode", type)
|
|
sendBroadcast(
|
|
intent.setPackage(this.packageName)
|
|
)
|
|
}
|
|
|
|
private fun setNewData(): Boolean {
|
|
val mmkv = MMKV.defaultMMKV()
|
|
val originalUrl =
|
|
mmkv.decodeString(BZYConstants.MMKV_KEY_CURRENT_HOST, BuildConfig.service_url)
|
|
var newUrlString = originalUrl
|
|
var isChange = false
|
|
val routerHost = mmkv.decodeStringSet(BZYConstants.MMKV_KEY_ROUTER_HOST, HashSet())
|
|
val switchFinishSet = mmkv.decodeStringSet(BZYConstants.MMKV_KEY_Switch_Finsh_Set, HashSet())
|
|
routerHost?.let {
|
|
if (it.isEmpty()) {
|
|
android.util.Log.d(Conf.TAG, "routerHost isEmpty")
|
|
return false
|
|
}
|
|
Log.d("switchFinshSet.size==>${switchFinishSet?.size!!} , routerHost.size==>${routerHost.size}")
|
|
//次数已满
|
|
if (switchFinishSet.size >= routerHost.size) {
|
|
return false
|
|
}
|
|
android.util.Log.d(Conf.TAG, "routerHost not empty")
|
|
for (url in it) {
|
|
|
|
if (url == originalUrl) {
|
|
switchFinishSet.add(url)
|
|
mmkv.encode(BZYConstants.MMKV_KEY_Switch_Finsh_Set, switchFinishSet)
|
|
continue
|
|
}
|
|
|
|
if (switchFinishSet.contains(url)) {
|
|
continue
|
|
} else {
|
|
isChange = true
|
|
android.util.Log.e("Retry", "切换到新url:$url")
|
|
newUrlString = url
|
|
switchFinishSet.add(newUrlString)
|
|
mmkv.encode(BZYConstants.MMKV_KEY_Switch_Finsh_Set, switchFinishSet)
|
|
break
|
|
}
|
|
}
|
|
mmkv.encode(BZYConstants.MMKV_KEY_CURRENT_HOST, newUrlString)
|
|
|
|
}
|
|
return isChange
|
|
}
|
|
|
|
private fun saveSubUrl() {
|
|
val currentHost = MMKV.defaultMMKV()
|
|
.decodeString(BZYConstants.MMKV_KEY_CURRENT_HOST, "")
|
|
Log.d("tryButton currentHost==>${currentHost}")
|
|
val oldUrl = nowSubUrl
|
|
currentHost?.let { changeSubUrl(oldUrl, it) }
|
|
}
|
|
|
|
enum class ProxyMode {
|
|
PAC, GLOBAL
|
|
}
|
|
private fun checkGlo() {
|
|
val isGlobal = isGlo
|
|
val isGlobal2 = MMKV.defaultMMKV().decodeBool(BZYConstants.MMKV_KEY_GLOBAL_MODEL, false)
|
|
Log.i("checkGlo isGlobal==>${isGlobal}, isGlobal2==>${isGlobal2}")
|
|
val px: ProxyMode = if (isGlobal2) {
|
|
ProxyMode.GLOBAL
|
|
} else {
|
|
ProxyMode.PAC
|
|
}
|
|
setBackground(px)
|
|
|
|
launch(Dispatchers.IO) {
|
|
if (isGlobal) {
|
|
if (clashRunning) {
|
|
getNodes()
|
|
}
|
|
} else {
|
|
if (clashRunning) {
|
|
delay(1000)
|
|
clashBinder?.let { cm ->
|
|
val names = cm.queryProxyGroupNames(false)
|
|
Log.i("checkGlo names==>${names}")
|
|
if (names.isNotEmpty()) {
|
|
val selectedName =
|
|
MMKV.defaultMMKV()
|
|
.decodeString(BZYConstants.NODE_SELECTED_NAME_KEY, "");
|
|
Log.i("selected_name ==>${selectedName}")
|
|
if (!TextUtils.isEmpty(selectedName)) {
|
|
val o = cm.queryOverride(Clash.OverrideSlot.Session)
|
|
o.mode = TunnelState.Mode.Rule
|
|
cm.patchOverride(Clash.OverrideSlot.Session, o)
|
|
delay(500)
|
|
var gName = names[0]
|
|
//全局模式下 names[0]会自动添加一个 "GLOBAL" 组
|
|
if (names.size >= 2) {
|
|
if ("GLOBAL" == gName) {
|
|
//names[0] 为 "GLOBAL" //names[1]才是规则模式下的组名,所以需要手动赋值
|
|
gName = names[1]
|
|
}
|
|
}
|
|
cm.patchSelector(gName, selectedName!!)
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
private suspend fun getNodes() {
|
|
coroutineScope {
|
|
val selectedName =
|
|
MMKV.defaultMMKV().decodeString(BZYConstants.NODE_SELECTED_NAME_KEY, "")
|
|
Log.d("getNodes selected_name==>${selectedName}")
|
|
if (TextUtils.isEmpty(selectedName)) {
|
|
subUrl?.let {
|
|
changeNodeVM.getNodeOther(it, object : AsyncCallBackListener {
|
|
override fun suc(obj: Any?) {
|
|
proList = obj as MutableList<ClashNodebBean>
|
|
proList?.let { proListItem ->
|
|
val nodeName = proListItem[0].name
|
|
if (!TextUtils.isEmpty(nodeName)) {
|
|
launch(Dispatchers.IO) {
|
|
clashBinder?.let { cm ->
|
|
|
|
val o = cm.queryOverride(Clash.OverrideSlot.Session)
|
|
o.mode = TunnelState.Mode.Global
|
|
cm.patchOverride(Clash.OverrideSlot.Session, o)
|
|
delay(3000)
|
|
//等待全局模式生效
|
|
var maxIndex = 0
|
|
while (true) {
|
|
if (maxIndex > 3) {
|
|
break
|
|
}
|
|
val names = cm.queryProxyGroupNames(false)
|
|
if (names.isNotEmpty()) {
|
|
if (names[0] == "GLOBAL") {
|
|
Log.d("repeat end")
|
|
break
|
|
}
|
|
}
|
|
Log.d("repeat agent")
|
|
maxIndex++
|
|
delay(1000)
|
|
}
|
|
|
|
cm.patchSelector("GLOBAL", nodeName)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
override fun faild(response: Response) {
|
|
}
|
|
|
|
override fun err(msg: String, exception: Exception?) {
|
|
}
|
|
|
|
})
|
|
}
|
|
} else {
|
|
clashBinder?.let { cm ->
|
|
val o = cm.queryOverride(Clash.OverrideSlot.Session)
|
|
o.mode = TunnelState.Mode.Global
|
|
cm.patchOverride(Clash.OverrideSlot.Session, o)
|
|
delay(3000)
|
|
//等待全局
|
|
var maxIndex = 0
|
|
while (true) {
|
|
if (maxIndex > 3) {
|
|
break
|
|
}
|
|
val names = cm.queryProxyGroupNames(false)
|
|
if (names.isNotEmpty()) {
|
|
if (names[0] == "GLOBAL") {
|
|
Log.d("repeat end")
|
|
break
|
|
}
|
|
}
|
|
Log.d("repeat agent")
|
|
maxIndex++
|
|
delay(1000)
|
|
}
|
|
|
|
cm.patchSelector("GLOBAL", selectedName!!)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|