parent
3d9c1b4585
commit
2feb4d7ead
|
|
@ -140,6 +140,10 @@
|
|||
android:process=":background"
|
||||
android:screenOrientation="portrait"
|
||||
/>
|
||||
<activity
|
||||
android:name=".act.NodeGroupActivity"
|
||||
android:process=":background"
|
||||
android:screenOrientation="portrait" />
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="${applicationId}.fileprovider"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
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.os.Build
|
||||
import android.os.Bundle
|
||||
import android.text.TextUtils
|
||||
|
|
@ -9,12 +12,15 @@ import android.widget.TextView
|
|||
import androidx.annotation.RequiresApi
|
||||
import androidx.appcompat.widget.AppCompatImageView
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
import com.afollestad.materialdialogs.customview.customView
|
||||
import com.afollestad.materialdialogs.customview.getCustomView
|
||||
import com.baidu.mobads.action.ActionType
|
||||
import com.baidu.mobads.action.BaiduAction
|
||||
import com.cncat.vpn.common.constants.Intents
|
||||
import com.cncat.vpn.common.log.Log
|
||||
import com.cncat.vpn.core.model.LogMessage
|
||||
import com.cncat.vpn.service.ClashManager
|
||||
|
|
@ -27,6 +33,7 @@ import com.cncat.vpn.service.remote.wrap
|
|||
import com.google.gson.Gson
|
||||
import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonObject
|
||||
import com.google.gson.JsonParser
|
||||
import com.orhanobut.logger.Logger
|
||||
import com.tencent.mmkv.MMKV
|
||||
import com.yingmao.clash.BuildConfig
|
||||
|
|
@ -117,6 +124,9 @@ class MainActivity : BaseActivity() {
|
|||
var newMainActGlo: Boolean = false
|
||||
var newUuid: String? = null
|
||||
var clashBinder: IClashManager? = null
|
||||
private var mainBroadcastReceiver: BroadcastReceiver? = null
|
||||
val trafficStatisticsData = MutableLiveData<String>()
|
||||
|
||||
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
|
||||
}
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
subUrl?.let {
|
||||
Log.i("netSubUrl:${netSubUrl}::保存机场URL:=${proSubUrl}::subUrl:${subUrl}")
|
||||
setSub2(it)
|
||||
}
|
||||
launch(Dispatchers.IO) {
|
||||
subUrl?.let { setSub2(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<NodeGroupItem> = 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<IRemoteService>()
|
||||
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<NodeGroupItem> {
|
||||
val mList: MutableList<NodeGroupItem> = 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<NodeGroupItem>?) {
|
||||
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) }
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -15,3 +15,10 @@ data class ClashNodebBean(
|
|||
var delay: Int,
|
||||
val udp: Boolean
|
||||
) : Parcelable,Serializable
|
||||
|
||||
@Parcelize
|
||||
data class NodeGroupItem(
|
||||
val name: String,
|
||||
val type: String,
|
||||
val proxies: MutableList<String>
|
||||
) : Parcelable,Serializable
|
||||
|
|
@ -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<NodeGroupItem> = 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,14 +709,15 @@ 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<ClashNodebBean>
|
||||
proList?.let { proListItem ->
|
||||
val nodeName = proListItem[0].name
|
||||
if (!TextUtils.isEmpty(nodeName)) {
|
||||
launch(Dispatchers.IO) {
|
||||
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]
|
||||
|
||||
if (!TextUtils.isEmpty(baseNodeName)) {
|
||||
GlobalScope.launch(Dispatchers.IO) {
|
||||
clashBinder?.let { cm ->
|
||||
|
||||
val o = cm.queryOverride(Clash.OverrideSlot.Session)
|
||||
|
|
@ -729,22 +742,11 @@ open class HomeFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dispa
|
|||
delay(1000)
|
||||
}
|
||||
|
||||
cm.patchSelector("GLOBAL", nodeName)
|
||||
cm.patchSelector("GLOBAL", baseNodeName)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun faild(response: Response) {
|
||||
}
|
||||
|
||||
override fun err(msg: String, exception: Exception?) {
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
} else {
|
||||
clashBinder?.let { cm ->
|
||||
val o = cm.queryOverride(Clash.OverrideSlot.Session)
|
||||
|
|
@ -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<NodeGroupItem> {
|
||||
val mList: MutableList<NodeGroupItem> = 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
|
||||
}
|
||||
}
|
||||
|
|
@ -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<SettingPageBean>()
|
||||
@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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<NodeGroupAdapter.NodeGroupAdapterViewHolder>() {
|
||||
|
||||
private var nodeGroupList = mutableListOf<String>()
|
||||
|
||||
//设置回调监听
|
||||
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<String>, context: Context) {
|
||||
nodeGroupList=infoList
|
||||
this.context=context
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
inner class NodeGroupAdapterViewHolder(view: View) : RecyclerView.ViewHolder(view) {
|
||||
val nodeitemItem = itemView.findViewById<ConstraintLayout>(R.id.rl_node_list_item)
|
||||
var nodeName = itemView.findViewById<TextView>(R.id.node_list_item_tv_node_name)
|
||||
|
||||
var chekImg = itemView.findViewById<AppCompatImageView>(R.id.mrb_node_checked)
|
||||
|
||||
var countryImg = itemView.findViewById<AppCompatImageView>(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)
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
|
|
@ -288,6 +288,17 @@
|
|||
app:layout_constraintStart_toStartOf="@+id/shareCard"
|
||||
app:layout_constraintTop_toBottomOf="@+id/shareCard" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvUseNet"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text=""
|
||||
android:textColor="#FF000000"
|
||||
android:layout_marginTop="10dp"
|
||||
android:textSize="13sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintEnd_toEndOf="@+id/shareCard"
|
||||
app:layout_constraintTop_toBottomOf="@+id/shareCard" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rvSettingTab"
|
||||
|
|
|
|||
|
|
@ -64,8 +64,8 @@ subprojects {
|
|||
}
|
||||
minSdk = 21
|
||||
targetSdk = 34
|
||||
versionName = "1.0.4.8"
|
||||
versionCode = 1048
|
||||
versionName = "1.0.5.2"
|
||||
versionCode = 1052
|
||||
resValue("string", "release_name", "v$versionName")
|
||||
resValue("integer", "release_code", "$versionCode")
|
||||
externalNativeBuild {
|
||||
|
|
@ -93,22 +93,6 @@ subprojects {
|
|||
}
|
||||
productFlavors {
|
||||
flavorDimensions("feature")
|
||||
create("2210") {
|
||||
isDefault = true
|
||||
dimension = flavorDimensionList[0]
|
||||
buildConfigField("boolean", "PREMIUM", "Boolean.parseBoolean(\"false\")")
|
||||
buildConfigField("boolean", "is_free", "false")
|
||||
buildConfigField("boolean", "is_back_cn", "false")
|
||||
buildConfigField("String", "applicationId", "\"com.jsq.wufu\"")
|
||||
buildConfigField("String", "service_url", "\"https://api.yijianlianjsq.com/\"")
|
||||
buildConfigField("String", "productId", "\"2210\"")
|
||||
manifestPlaceholders(
|
||||
mapOf(
|
||||
"APP_NAME" to "@string/wf",
|
||||
"APP_ICON" to "@mipmap/oneclickconnection"
|
||||
)
|
||||
)
|
||||
}
|
||||
create("2211") {
|
||||
isDefault = true
|
||||
dimension = flavorDimensionList[0]
|
||||
|
|
@ -125,57 +109,9 @@ subprojects {
|
|||
)
|
||||
)
|
||||
}
|
||||
create("2212") {
|
||||
isDefault = true
|
||||
dimension = flavorDimensionList[0]
|
||||
buildConfigField("boolean", "PREMIUM", "Boolean.parseBoolean(\"false\")")
|
||||
buildConfigField("boolean", "is_free", "false")
|
||||
buildConfigField("boolean", "is_back_cn", "false")
|
||||
buildConfigField("String", "applicationId", "\"com.jsq.wufu\"")
|
||||
buildConfigField("String", "service_url", "\"https://api.yijianlianjsq.com/\"")
|
||||
buildConfigField("String", "productId", "\"2212\"")
|
||||
manifestPlaceholders(
|
||||
mapOf(
|
||||
"APP_NAME" to "@string/wf",
|
||||
"APP_ICON" to "@mipmap/oneclickconnection"
|
||||
)
|
||||
)
|
||||
}
|
||||
create("2213") {
|
||||
isDefault = true
|
||||
dimension = flavorDimensionList[0]
|
||||
buildConfigField("boolean", "PREMIUM", "Boolean.parseBoolean(\"false\")")
|
||||
buildConfigField("boolean", "is_free", "false")
|
||||
buildConfigField("boolean", "is_back_cn", "false")
|
||||
buildConfigField("String", "applicationId", "\"com.jsq.wufu\"")
|
||||
buildConfigField("String", "service_url", "\"https://api.yijianlianjsq.com/\"")
|
||||
buildConfigField("String", "productId", "\"2213\"")
|
||||
manifestPlaceholders(
|
||||
mapOf(
|
||||
"APP_NAME" to "@string/wf",
|
||||
"APP_ICON" to "@mipmap/oneclickconnection"
|
||||
)
|
||||
)
|
||||
}
|
||||
create("2214") {
|
||||
isDefault = true
|
||||
dimension = flavorDimensionList[0]
|
||||
buildConfigField("boolean", "PREMIUM", "Boolean.parseBoolean(\"false\")")
|
||||
buildConfigField("boolean", "is_free", "false")
|
||||
buildConfigField("boolean", "is_back_cn", "false")
|
||||
buildConfigField("String", "applicationId", "\"com.jsq.wufu\"")
|
||||
buildConfigField("String", "service_url", "\"https://api.yijianlianjsq.com/\"")
|
||||
buildConfigField("String", "productId", "\"2214\"")
|
||||
manifestPlaceholders(
|
||||
mapOf(
|
||||
"APP_NAME" to "@string/wf",
|
||||
"APP_ICON" to "@mipmap/oneclickconnection"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
sourceSets {
|
||||
getByName("2210") {
|
||||
getByName("2211") {
|
||||
java.srcDirs("src/foss/java")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue