diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index ed13ca2..496ac92 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -140,6 +140,10 @@
android:process=":background"
android:screenOrientation="portrait"
/>
+
()
+
suspend fun setSub2(url: String) {
profileBinder?.let { pm ->
pm.queryAll().forEach { itemPro ->
@@ -191,8 +201,51 @@ class MainActivity : BaseActivity() {
startLog()
firstEntry()
mainAtyVM.getExpensesRecord(mPageInfo)
+
+
+ val intentFilter = IntentFilter(Intents.ACTION_NODE_LIST)
+ mainBroadcastReceiver = NodeListBR()
+ if (Build.VERSION.SDK_INT >= 34 && applicationInfo.targetSdkVersion >= 34) {
+ registerReceiver(
+ mainBroadcastReceiver,
+ intentFilter,
+ RECEIVER_EXPORTED
+ )
+ } else {
+ registerReceiver(
+ mainBroadcastReceiver,
+ intentFilter
+ )
+ }
}
+ 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")
+ val userTraffic=serStore.userTraffic
+ Log.d("userTraffic::::${userTraffic}")
+ userTrafficMethod(userTraffic)
+ if (TextUtils.isEmpty(userTraffic)) {
+ return
+ }
+ }
+ else -> {
+
+ }
+ }
+ }
+ }
+ fun userTrafficMethod(userTraffic: String) {
+ val jo = JsonParser.parseString(userTraffic).asJsonObject
+ val t = jo.get("total").asLong
+ val d = jo.get("download").asLong
+ val u = jo.get("upload").asLong
+ val trafficUsedInGB = String.format("%.2f", (d + u) / 1024.0 / 1024.0 / 1024.0)
+ val trafficTolTelInGB = String.format("%.2f", t / 1024.0 / 1024.0 / 1024.0)
+ trafficStatisticsData.value = "流量统计:${trafficUsedInGB}GB / ${trafficTolTelInGB}GB"
+ }
private fun initData() {
clashBinder = MainApplication.getApp()?.let { ClashManager(it).wrap() as IClashManager }
profileBinder =
@@ -268,24 +321,24 @@ class MainActivity : BaseActivity() {
if (data.code != "1000") {
RxToast.error(resources.getString(R.string.NodeAcquisitionFailed) + "${data.msg}")
}
- data.data.let { url ->
- android.util.Log.e("Data", "机场URL:=${url}")
- if (url.isNotEmpty()) {
- //todo 判断订阅地址是否修改过,未修改就用接口返回的地址反之使用本地修改的地址
- var pro_sub_url =
+ data.data.let { netSubUrl ->
+ if (netSubUrl.isNotEmpty()) {
+ val proSubUrl =
MMKV.defaultMMKV().decodeString(BZYConstants.PROFILE_SUB_URL, "")
-// Log.d("pro_sub_url==>${pro_sub_url}")
- if (!TextUtils.isEmpty(pro_sub_url)) {
- if (pro_sub_url == url) {
- subUrl = url
+ subUrl = if (!TextUtils.isEmpty(proSubUrl)) {
+ if (proSubUrl == netSubUrl) {
+ netSubUrl
} else {
- subUrl = pro_sub_url
+ proSubUrl
}
} else {
- subUrl = url
+ netSubUrl
}
- launch(Dispatchers.IO) {
- subUrl?.let { setSub2(it) }
+ lifecycleScope.launch(Dispatchers.IO) {
+ subUrl?.let {
+ Log.i("netSubUrl:${netSubUrl}::保存机场URL:=${proSubUrl}::subUrl:${subUrl}")
+ setSub2(it)
+ }
}
}
}
diff --git a/app/src/main/java/com/yingmao/clash/act/NodeGroupActivity.kt b/app/src/main/java/com/yingmao/clash/act/NodeGroupActivity.kt
new file mode 100644
index 0000000..1f6ea19
--- /dev/null
+++ b/app/src/main/java/com/yingmao/clash/act/NodeGroupActivity.kt
@@ -0,0 +1,395 @@
+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.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 com.cncat.vpn.common.constants.Intents
+import com.cncat.vpn.common.log.Log
+import com.cncat.vpn.service.model.Profile
+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.profileBinder
+import com.yingmao.clash.act.MainActivity.Companion.setSub2
+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.NodeGroupItem
+import com.yingmao.clash.fragment.home.HomeFragment
+import com.yingmao.clash.fragment.home.HomeFragment.Companion.CHECK_NODE_REQUEST_CODE
+import com.yingmao.clash.remote.Resource
+import com.yingmao.clash.view.RxToast
+import com.yingmao.clash.view.adapter.NodeGroupAdapter
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
+import java.util.UUID
+import kotlin.collections.forEach
+import kotlin.collections.withIndex
+import kotlin.jvm.java
+import kotlin.let
+import kotlin.ranges.until
+import kotlin.text.isNotEmpty
+import kotlin.text.replace
+
+class NodeGroupActivity : BaseActivity(){
+ private var currentNode: MutableList = 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 lateinit var userTrafficTv: TextView
+ private var uuid: UUID? = null
+ var nodeListAdapter: NodeGroupAdapter = NodeGroupAdapter()
+ private var timerJob: Job? = null
+
+ val remote = Resource()
+ var getNodeTimeOutNum = 5
+ var isRunning = false
+ var nowSubUrl: String = ""
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_change_node)
+ changeNodeVM = ViewModelProvider(this)[ChangeNodeVM::class.java]
+ initView()
+ initData()
+ }
+
+ override fun onBackPressed() {
+ intent.putExtra(BZYConstants.EXTRA_KEY_IS_RUNING, isRunning)
+ setResult(CHECK_NODE_REQUEST_CODE, intent)
+ super.onBackPressed()
+ }
+
+ private fun initData() {
+ subUrl?.let {
+ nowSubUrl = it
+ }
+ 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,
+ RECEIVER_EXPORTED
+ )
+ } else {
+ registerReceiver(
+ broadcastReceiver,
+ intentFilter
+ )
+ }
+ doProfile()
+ }
+
+ @SuppressLint("SetTextI18n", "NotifyDataSetChanged")
+ private fun initView() {
+ aiBack = findViewById(R.id.aiBack)
+ swipeLayout = findViewById(R.id.swipeLayout)
+ nodeListRv = findViewById(R.id.rv_node_list)
+ userTrafficTv= findViewById(R.id.node_user_traffic)
+ aiBack.setOnClickListener {
+ onBackPressed()
+ }
+ swipeLayout.setColorSchemeColors(Color.rgb(47, 223, 189))
+ swipeLayout.setOnRefreshListener {
+ swipeLayout.isRefreshing = true
+ updateProfile(false)
+ swipeLayout.isRefreshing = false
+ }
+
+ nodeListRv.layoutManager = LinearLayoutManager(this)
+ nodeListRv.adapter=nodeListAdapter
+
+ nodeListAdapter.setListener(object : NodeGroupAdapter.ItemClickListener{
+ override fun onItemClick(nodeStr: String, isPosition: Int) {
+ MMKV.defaultMMKV().encode(BZYConstants.NODE_SELECTED_NAME_KEY, nodeStr)
+ val intent = Intent()
+ intent.putExtra(BZYConstants.MMKV_KEY_SELECT_NODE_NAME, nodeStr)
+ intent.putExtra(BZYConstants.EXTRA_KEY_IS_RUNING, isRunning)
+ setResult(CHECK_NODE_REQUEST_CODE, intent)
+ finish()
+ }
+
+ })
+
+// if (!TextUtils.isEmpty(serStore.userTraffic)) {
+// userTrafficTv.visibility=View.VISIBLE
+// var jo = JsonParser.parseString(serStore.userTraffic).asJsonObject
+// var t = jo.get("total")
+// var d = jo.get("download").asLong
+// var u = jo.get("upload").asLong
+// val trafficUsedInGB = String.format("%.2f", (d + u) / 1024.0 / 1024.0 / 1024.0)
+// userTrafficTv.text="已用流量:${trafficUsedInGB}GB"
+// }
+ }
+
+ private fun getNodesInfo(json: String): MutableList {
+ val mList: MutableList = mutableListOf()
+ if (TextUtils.isEmpty(json)) {
+ return mList
+ }
+ val gson = Gson()
+ val jsonArray = JsonParser.parseString(json).asJsonArray
+ for (i in 0 until jsonArray.size()) {
+ val bean = gson.fromJson(jsonArray.get(i).asString, NodeGroupItem::class.java)
+ 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()
+ val json = serStore.nodes
+ val trsss=serStore.userTraffic
+ Log.d("AAAAAAAAAAAAAAAAAAAA${trsss}")
+ if (TextUtils.isEmpty(json)) {
+ return
+ }
+ MMKV.defaultMMKV().encode(BZYConstants.PROFILE_SUB_URL, nowSubUrl)
+ //缓存节点
+ MMKV.defaultMMKV().encode(BZYConstants.NODE_All_ITEM_NAME_KEY, json)
+ currentNode.clear()
+ currentNode = getNodesInfo(json)
+ setNewData(currentNode)
+ swipeLayout.isRefreshing = false
+ }
+
+ else -> {
+
+ }
+ }
+ }
+ }
+
+ override fun onDestroy() {
+ super.onDestroy()
+ broadcastReceiver?.let {
+ unregisterReceiver(it)
+ }
+ }
+
+ override fun onResume() {
+ super.onResume()
+ isRunning = intent.getBooleanExtra(BZYConstants.EXTRA_KEY_IS_RUNING, false)
+ }
+ private fun updateProfile(isDelete: Boolean) {
+ try {
+ launch(Dispatchers.IO) {
+ profileBinder?.let { profileManager ->
+ var mProfile: Profile? = null
+ Log.d("queryAll().size ==>${profileManager.queryAll().size}")
+ profileManager.queryAll().forEach { it_pf ->
+ val confName =
+ Conf.getClashConfName(this@NodeGroupActivity.applicationContext)
+ Log.d(" it_pf.name ==>${it_pf.name}, confName==>${confName}")
+ if (confName == it_pf.name) {
+ mProfile = it_pf
+ }
+ }
+
+ if (mProfile == null) {
+ //为空的情况下选择第一个
+ profileManager.queryAll().forEach { profile ->
+ mProfile = profile
+ return@forEach
+ }
+ }
+
+ mProfile?.let { profile ->
+ uuid = profile.uuid
+ if (isDelete) {
+ MMKV.defaultMMKV()
+ .removeValueForKey(BZYConstants.NODE_All_ITEM_NAME_KEY)
+
+ } else {
+ profileManager.setActive(profile)
+ if (!TextUtils.isEmpty(profile.source)) {
+
+ if (profile.source != nowSubUrl) {
+ profileManager.patch(
+ profile.uuid,
+ profile.name,
+ nowSubUrl,
+ 1800000L
+ )
+ profileManager.commit(profile.uuid) { status ->
+ Log.d("updateProfile updateStatus: " + Gson().toJson(status.args) + ", pro==>" + status.progress)
+ }
+ }
+ } else {
+ profileManager.patch(
+ profile.uuid,
+ profile.name,
+ nowSubUrl,
+ 1800000L
+ )
+ profileManager.commit(profile.uuid) { status ->
+ Log.d("updateProfile new updateStatus: " + Gson().toJson(status.args) + ", pro==>" + status.progress)
+ }
+ }
+ profileManager.update(profile.uuid)
+ }
+ }
+ }
+ }
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+ }
+ fun doProfile() {
+ showLoading("正在获取...", this@NodeGroupActivity)
+ startTimer()
+ launch(Dispatchers.IO) {
+ setSub2(nowSubUrl)
+ }
+
+ }
+
+ private fun changeSubUrl(originalUrl: String, newUrl: String) {
+ if (originalUrl.isNotEmpty() && newUrl.isNotEmpty()) {
+ nowSubUrl = originalUrl.replace(Regex("https://[^/]+/"), "$newUrl/")
+ subUrl = nowSubUrl
+ MMKV.defaultMMKV().encode(BZYConstants.PROFILE_SUB_URL, nowSubUrl)
+ }
+ }
+
+
+
+ private fun setNewData(nodes: MutableList?) {
+ runOnUiThread {
+ if (nodes != null) {
+ val proxies = nodes[0].proxies
+ nodeListAdapter.setDataMethod(proxies,this@NodeGroupActivity)
+ val selectedName =
+ MMKV.defaultMMKV().decodeString(BZYConstants.NODE_SELECTED_NAME_KEY, "")
+ for ((index, node) in proxies.withIndex()) {
+ if (node== selectedName) {
+ nodeListAdapter.selectedPosition = index
+ }
+ }
+ nodeListAdapter.notifyDataSetChanged()
+ }
+ }
+ }
+ private fun startTimer() {
+ getNodeTimeOutNum = 15
+ timerJob = launch {
+ repeat(16) {
+ Log.d("GetNodeTimeOutNum==>$getNodeTimeOutNum")
+ if (getNodeTimeOutNum == 0) {
+ hideLoading()
+ RxToast.error("获取节点失败...正在切换其他地址")
+ if (setNewData()) {
+ timerJob?.cancel()
+ MMKV.defaultMMKV()
+ .removeValueForKey(BZYConstants.NODE_All_ITEM_NAME_KEY)
+ saveSubUrl()
+ MainApplication.restartApp(this@NodeGroupActivity, object :
+ MainApplication.OnRestartButtonListener {
+
+ override fun enterButton() {
+
+ }
+
+ override fun cancelButton() {
+ doProfile()
+ }
+
+ })
+ } else {
+ timerJob?.cancel()
+ RxToast.error("获取全部线路失败...请切换网络重试")
+ }
+ }
+ getNodeTimeOutNum--
+ delay(1000)
+ }
+ }
+ }
+ 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,
+ kotlin.collections.HashSet()
+ )
+ val switchFinSet = mmkv.decodeStringSet(BZYConstants.MMKV_KEY_Switch_Finsh_Set,
+ kotlin.collections.HashSet()
+ )
+ routerHost?.let {
+ if (it.isEmpty()) {
+ android.util.Log.d(Conf.TAG, "routerHost isEmpty")
+ return false
+ }
+ Log.d("switchFinshSet.size==>${switchFinSet?.size!!} , routerHost.size==>${routerHost.size}")
+ //次数已满
+ if (switchFinSet.size >= routerHost.size) {
+ return false
+ }
+ for (url in it) {
+ if (url == originalUrl) {
+ switchFinSet.add(url)
+ mmkv.encode(BZYConstants.MMKV_KEY_Switch_Finsh_Set, switchFinSet)
+ continue
+ }
+
+ if (switchFinSet.contains(url)) {
+ continue
+ } else {
+ isChange = true
+ android.util.Log.e("Retry", "切换到新url:$url")
+ newUrlString = url
+ switchFinSet.add(newUrlString)
+ mmkv.encode(BZYConstants.MMKV_KEY_Switch_Finsh_Set, switchFinSet)
+ break
+ }
+ }
+ mmkv.encode(BZYConstants.MMKV_KEY_CURRENT_HOST, newUrlString)
+
+ }
+ return isChange
+ }
+
+ private fun saveSubUrl() {
+ val currentHost = MMKV.defaultMMKV()
+ .decodeString(BZYConstants.MMKV_KEY_CURRENT_HOST, "")
+ val oldUrl = nowSubUrl
+ currentHost?.let { changeSubUrl(oldUrl, it) }
+ }
+
+
+
+
+}
diff --git a/app/src/main/java/com/yingmao/clash/entity/ClashNodebBean.kt b/app/src/main/java/com/yingmao/clash/entity/ClashNodebBean.kt
index d48a955..487fa77 100644
--- a/app/src/main/java/com/yingmao/clash/entity/ClashNodebBean.kt
+++ b/app/src/main/java/com/yingmao/clash/entity/ClashNodebBean.kt
@@ -14,4 +14,11 @@ data class ClashNodebBean(
val password: String,
var delay: Int,
val udp: Boolean
+) : Parcelable,Serializable
+
+@Parcelize
+data class NodeGroupItem(
+ val name: String,
+ val type: String,
+ val proxies: MutableList
) : Parcelable,Serializable
\ No newline at end of file
diff --git a/app/src/main/java/com/yingmao/clash/fragment/home/HomeFragment.kt b/app/src/main/java/com/yingmao/clash/fragment/home/HomeFragment.kt
index 89d06ec..90b08a1 100644
--- a/app/src/main/java/com/yingmao/clash/fragment/home/HomeFragment.kt
+++ b/app/src/main/java/com/yingmao/clash/fragment/home/HomeFragment.kt
@@ -32,6 +32,10 @@ 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.model.Profile
+import com.cncat.vpn.service.store.ServiceStore
+import com.google.gson.Gson
+import com.google.gson.JsonParser
import com.orhanobut.logger.Logger
import com.tencent.mmkv.MMKV
import com.yingmao.clash.R
@@ -43,10 +47,14 @@ import com.yingmao.clash.act.MainActivity.Companion.clashBinder
import com.yingmao.clash.act.MainActivity.Companion.isSubbed
import com.yingmao.clash.act.MainActivity.Companion.newMainActGlo
import com.yingmao.clash.act.MainActivity.Companion.newUuid
+import com.yingmao.clash.act.MainActivity.Companion.profileBinder
+import com.yingmao.clash.act.MainActivity.Companion.subUrl
+import com.yingmao.clash.act.NodeGroupActivity
import com.yingmao.clash.conf.BZYConstants
import com.yingmao.clash.conf.BZYConstants.Companion.ScreenFlag
import com.yingmao.clash.conf.Conf
import com.yingmao.clash.entity.ClashNodebBean
+import com.yingmao.clash.entity.NodeGroupItem
import com.yingmao.clash.fragment.AccelerateViewModel
import com.yingmao.clash.fragment.BaseFragment
import com.yingmao.clash.listener.AsyncCallBackListener
@@ -66,6 +74,7 @@ import com.yingmao.clash.view.dialog.RxDialogSureCancel
import com.yingmao.clash.webview.CustomerServiceWebView
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@@ -74,6 +83,7 @@ import okhttp3.Response
import java.text.SimpleDateFormat
import java.util.Locale
import java.util.concurrent.atomic.AtomicInteger
+import kotlin.collections.get
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
@@ -118,6 +128,8 @@ open class HomeFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dispa
private lateinit var tvNodeName: TextView
private lateinit var clLinks: ConstraintLayout
+ val serStore by lazy { ServiceStore(requireActivity()) }
+ private var currentNode: MutableList = mutableListOf()
private val mPageInfo = ExpensesRecordActivity.PageInfo()
private var reConnection = false
@@ -221,7 +233,7 @@ open class HomeFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dispa
RxToast.info(resources.getString(R.string.AccountExpired))
return@setOnClickListener
}
- val intent = Intent(CommonUtils.getApp(), ChangeNodeActivity::class.java)
+ val intent = Intent(CommonUtils.getApp(), NodeGroupActivity::class.java)
if (!TextUtils.isEmpty(newUuid)) {
intent.putExtra(BZYConstants.MMKV_KEY_CURRENT_NODE, newUuid)
intent.putExtra(BZYConstants.EXTRA_KEY_IS_RUNING, clashRunning)
@@ -361,7 +373,7 @@ open class HomeFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dispa
private fun startClash(isStart: Boolean) {
launch {
- MainActivity.profileBinder?.let { pm ->
+ profileBinder?.let { pm ->
val active = pm.queryActive()
if (active == null) {
if (isStart) {
@@ -390,7 +402,7 @@ open class HomeFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dispa
}
delay(500)
}
-
+ refreshNode()
val vpnRequest = context?.startClashService()
try {
startUTimer()
@@ -415,7 +427,7 @@ open class HomeFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dispa
tvClick.text=resources.getString(R.string.Accelerating)
ivAccelerate.setImageResource(R.mipmap.yjl_home_clink_select_bg)
hideLoading()
- checkGlo()
+// checkGlo()
}
}
@@ -697,53 +709,43 @@ open class HomeFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dispa
MMKV.defaultMMKV().decodeString(BZYConstants.NODE_SELECTED_NAME_KEY, "")
Log.d("getNodes selected_name==>${selectedName}")
if (TextUtils.isEmpty(selectedName)) {
- MainActivity.subUrl?.let {
- accelerateViewModel.getNodeOther(it, object : AsyncCallBackListener {
- override fun suc(obj: Any?) {
- proList = obj as MutableList
- proList?.let { proListItem ->
- val nodeName = proListItem[0].name
- if (!TextUtils.isEmpty(nodeName)) {
- launch(Dispatchers.IO) {
- clashBinder?.let { cm ->
+ subUrl?.let {
+ val json = serStore.nodes
+ Log.d("getNodes json==>${json}")
+ currentNode=getNodesInfo(json)
+ Log.d("getNodes currentNode==>${currentNode} dataname:${currentNode[0].proxies[0]}")
+ val baseNodeName=currentNode[0].proxies[0]
- 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)
- }
+ if (!TextUtils.isEmpty(baseNodeName)) {
+ GlobalScope.launch(Dispatchers.IO) {
+ clashBinder?.let { cm ->
- cm.patchSelector("GLOBAL", nodeName)
- }
- }
- }
- }
- }
+ 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)
+ }
-
- override fun faild(response: Response) {
- }
-
- override fun err(msg: String, exception: Exception?) {
- }
-
- })
+ cm.patchSelector("GLOBAL", baseNodeName)
+ }
+ }
+ }
}
} else {
clashBinder?.let { cm ->
@@ -903,6 +905,7 @@ open class HomeFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dispa
tvIntelligentMode.setTextColor("#FF006AFB".toColorInt())
tvGlobalMode.setTextColor("#FF000000".toColorInt())
sendMode(false)
+ checkGlo()
}
ProxyMode.GLOBAL -> {
@@ -913,6 +916,7 @@ open class HomeFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dispa
tvIntelligentMode.setTextColor("#FF000000".toColorInt())
tvGlobalMode.setTextColor("#FF006AFB".toColorInt())
sendMode(true)
+ checkGlo()
}
}
}
@@ -1005,5 +1009,46 @@ open class HomeFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dispa
}
}
+ fun refreshNode(){
+ try {
+ launch(Dispatchers.IO) {
+ profileBinder?.let { profileManager ->
+ var mProfile: Profile? = null
+ profileManager.queryAll().forEach { it_pf ->
+ val confName =
+ Conf.getClashConfName(requireActivity())
+ Log.d("refreshNode::==>${it_pf.name}, confName==>${confName}")
+ if (confName == it_pf.name) {
+ mProfile = it_pf
+ }
+ }
+ if (mProfile == null) {
+ profileManager.queryAll().forEach { profile ->
+ mProfile = profile
+ return@forEach
+ }
+ }
+ mProfile?.let { profile ->
+ profileManager.update(profile.uuid)
+ }
+ }
+ }
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+ }
+ private fun getNodesInfo(json: String): MutableList {
+ val mList: MutableList = mutableListOf()
+ if (TextUtils.isEmpty(json)) {
+ return mList
+ }
+ val gson = Gson()
+ val jsonArray = JsonParser.parseString(json).asJsonArray
+ for (i in 0 until jsonArray.size()) {
+ val bean = gson.fromJson(jsonArray.get(i).asString, NodeGroupItem::class.java)
+ mList.add(bean)
+ }
+ return mList
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/yingmao/clash/fragment/setting/SettingFragment.kt b/app/src/main/java/com/yingmao/clash/fragment/setting/SettingFragment.kt
index 4b2e9d9..c579821 100644
--- a/app/src/main/java/com/yingmao/clash/fragment/setting/SettingFragment.kt
+++ b/app/src/main/java/com/yingmao/clash/fragment/setting/SettingFragment.kt
@@ -45,6 +45,7 @@ import com.yingmao.clash.act.MainActivity
import com.yingmao.clash.act.MainActivity.Companion.profileBinder
import com.yingmao.clash.act.MainActivity.Companion.setSub2
import com.yingmao.clash.act.MainActivity.Companion.subUrl
+import com.yingmao.clash.act.MainActivity.Companion.trafficStatisticsData
import com.yingmao.clash.act.SettingActivity
import com.yingmao.clash.act.ShareActivity
import com.yingmao.clash.act.SplashHelper
@@ -107,6 +108,8 @@ class SettingFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dispatc
private var baseLoad: RxDialogShapeLoading? = null
private lateinit var mainAtyVM: MainAtyVM
+ private lateinit var tvUseNet: TextView
+
private var settingPageList = mutableListOf()
@RequiresApi(Build.VERSION_CODES.O)
override fun init(
@@ -386,6 +389,15 @@ class SettingFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dispatc
rvSettingTab=view.findViewById(R.id.rvSettingTab)
+ tvUseNet=view.findViewById(R.id.tvUseNet)
+
+ trafficStatisticsData.observe(requireActivity()){
+ it.let {
+ tvUseNet.text=it
+ }
+
+ }
+
clVipCard.setOnClickListener {
goNewRecharge()
}
diff --git a/app/src/main/java/com/yingmao/clash/view/adapter/NodeGroupAdapter.kt b/app/src/main/java/com/yingmao/clash/view/adapter/NodeGroupAdapter.kt
new file mode 100644
index 0000000..a2d8082
--- /dev/null
+++ b/app/src/main/java/com/yingmao/clash/view/adapter/NodeGroupAdapter.kt
@@ -0,0 +1,111 @@
+package com.yingmao.clash.view.adapter
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageView
+import android.widget.TextView
+import androidx.appcompat.widget.AppCompatImageView
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.recyclerview.widget.RecyclerView
+import com.cncat.vpn.common.log.Log
+import com.yingmao.clash.R
+import kotlin.collections.mutableListOf
+import kotlin.text.contains
+
+
+class NodeGroupAdapter : RecyclerView.Adapter() {
+
+ private var nodeGroupList = mutableListOf()
+
+ //设置回调监听
+ private var listener: ItemClickListener? = null
+ private var context: Context? = null
+ var selectedPosition: Int =0
+ //设置监听器
+ fun setListener(listener: ItemClickListener?) {
+ this.listener = listener
+ }
+ @SuppressLint("NotifyDataSetChanged")
+ fun setDataMethod(infoList: MutableList, context: Context) {
+ nodeGroupList=infoList
+ this.context=context
+ notifyDataSetChanged()
+ }
+ inner class NodeGroupAdapterViewHolder(view: View) : RecyclerView.ViewHolder(view) {
+ val nodeitemItem = itemView.findViewById(R.id.rl_node_list_item)
+ var nodeName = itemView.findViewById(R.id.node_list_item_tv_node_name)
+
+ var chekImg = itemView.findViewById(R.id.mrb_node_checked)
+
+ var countryImg = itemView.findViewById(R.id.node_list_item_iv_country)
+
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = NodeGroupAdapterViewHolder(
+ LayoutInflater.from(context)
+ .inflate(R.layout.node_list_item_layout, parent, false)
+ )
+
+ override fun getItemCount() = nodeGroupList.size
+
+ override fun onBindViewHolder(holder: NodeGroupAdapterViewHolder, position: Int) {
+ holder.nodeitemItem.setOnClickListener {
+ listener?.onItemClick(nodeGroupList[position],position)
+ }
+ holder.nodeName.text = nodeGroupList[position]
+ holder.chekImg.isSelected = holder.layoutPosition == selectedPosition
+ val nodeGroupName = nodeGroupList[position]
+
+ if(nodeGroupName.contains("中国")){
+ holder.countryImg.setImageResource(R.drawable.flag_china)
+ }else if(nodeGroupName.contains("香港")){
+ holder.countryImg.setImageResource(R.drawable.flag_china)
+ }else if(nodeGroupName.contains("日本")){
+ holder.countryImg.setImageResource(R.drawable.flag_japan)
+ }else if(nodeGroupName.contains("韩国")){
+ holder.countryImg.setImageResource(R.drawable.flag_korea)
+ }else if(nodeGroupName.contains("美国")){
+ holder.countryImg.setImageResource(R.drawable.flag_usa)
+ }else if(nodeGroupName.contains("德国")){
+ holder.countryImg.setImageResource(R.drawable.flag_germany)
+ }else if(nodeGroupName.contains("法国")){
+ holder.countryImg.setImageResource(R.drawable.flag_france)
+ }else if(nodeGroupName.contains("俄罗斯")){
+ holder.countryImg.setImageResource(R.drawable.flag_russia)
+ }else if(nodeGroupName.contains("新加坡")){
+ holder.countryImg.setImageResource(R.drawable.flag_singapore)
+ }else if(nodeGroupName.contains("英国")){
+ holder.countryImg.setImageResource(R.drawable.flag_uk)
+ }else if(nodeGroupName.contains("澳大利亚")){
+ holder.countryImg.setImageResource(R.drawable.flag_australia)
+ }else if(nodeGroupName.contains("印度")){
+ holder.countryImg.setImageResource(R.drawable.flag_india)
+ }else if(nodeGroupName.contains("加拿大")){
+ holder.countryImg.setImageResource(R.drawable.flag_canada)
+ }else if(nodeGroupName.contains("以色列")){
+ holder.countryImg.setImageResource(R.drawable.ic_s_il)
+ }else if(nodeGroupName.contains("西班牙")){
+ holder.countryImg.setImageResource(R.drawable.ic_xibanya)
+ }else if(nodeGroupName.contains("西班牙")){
+ holder.countryImg.setImageResource(R.drawable.ic_xibanya)
+ }else if(nodeGroupName.contains("荷兰")){
+ holder.countryImg.setImageResource(R.drawable.ic_henan)
+ }else if(nodeGroupName.contains("意大利")){
+ holder.countryImg.setImageResource(R.drawable.ic_yidali)
+ }else if(nodeGroupName.contains("塞尔维亚")){
+ holder.countryImg.setImageResource(R.drawable.ic_sewy)
+ }else{
+ holder.countryImg.setImageResource(R.drawable.ic_s_location)
+ }
+ }
+
+ /*
+* 内置监听回调
+* */
+ interface ItemClickListener {
+ fun onItemClick(nodeStr: String, isPosition:Int)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/libs/core.aar b/app/src/main/libs/core.aar
index 3feb490..02bbeee 100644
Binary files a/app/src/main/libs/core.aar and b/app/src/main/libs/core.aar differ
diff --git a/app/src/main/libs/service.aar b/app/src/main/libs/service.aar
index e0a1b40..4dff55d 100644
Binary files a/app/src/main/libs/service.aar and b/app/src/main/libs/service.aar differ
diff --git a/app/src/main/res/layout/setting_fragment_layout.xml b/app/src/main/res/layout/setting_fragment_layout.xml
index 0260cf0..a5d0b5a 100644
--- a/app/src/main/res/layout/setting_fragment_layout.xml
+++ b/app/src/main/res/layout/setting_fragment_layout.xml
@@ -288,6 +288,17 @@
app:layout_constraintStart_toStartOf="@+id/shareCard"
app:layout_constraintTop_toBottomOf="@+id/shareCard" />
+