添加自动切换节点

This commit is contained in:
cyh 2025-07-03 16:30:34 +08:00
parent 7c964af900
commit 38f5963797
21 changed files with 842 additions and 365 deletions

View File

@ -29,6 +29,7 @@ import com.yingmao.clash.R
import com.yingmao.clash.base.BaseActivity import com.yingmao.clash.base.BaseActivity
import com.cncat.vpn.common.constants.Intents import com.cncat.vpn.common.constants.Intents
import com.cncat.vpn.common.log.Log import com.cncat.vpn.common.log.Log
import com.cncat.vpn.service.ProfileManager
import com.yingmao.clash.conf.BZYConstants import com.yingmao.clash.conf.BZYConstants
import com.yingmao.clash.conf.Conf import com.yingmao.clash.conf.Conf
import com.yingmao.clash.entity.ClashNodebBean import com.yingmao.clash.entity.ClashNodebBean
@ -38,8 +39,11 @@ import com.yingmao.clash.remote.Resource
import com.cncat.vpn.service.model.Profile import com.cncat.vpn.service.model.Profile
import com.cncat.vpn.service.remote.IProfileManager import com.cncat.vpn.service.remote.IProfileManager
import com.cncat.vpn.service.remote.IRemoteService import com.cncat.vpn.service.remote.IRemoteService
import com.cncat.vpn.service.remote.wrap
import com.yingmao.clash.BuildConfig
import com.yingmao.clash.app.MainApplication import com.yingmao.clash.app.MainApplication
import com.yingmao.clash.fragment.home.HomeFragment import com.yingmao.clash.fragment.home.HomeFragment
import com.yingmao.clash.util.CommonUtils
import com.yingmao.clash.util.Utils import com.yingmao.clash.util.Utils
import com.yingmao.clash.view.RxToast import com.yingmao.clash.view.RxToast
import com.yingmao.clash.view.adapter.NodeListAdapter import com.yingmao.clash.view.adapter.NodeListAdapter
@ -100,6 +104,8 @@ class ChangeNodeActivity : BaseActivity(), View.OnClickListener {
private fun initData() { private fun initData() {
// clash = MainApplication.getApp()?.let { ClashManager(it) } // clash = MainApplication.getApp()?.let { ClashManager(it) }
// profileBinder =
// MainApplication.getApp()?.let { ProfileManager(it).wrap() as IProfileManager }
cacheNodeStr = MMKV.defaultMMKV().decodeString(BZYConstants.NODE_All_ITEM_NAME_KEY, ""); cacheNodeStr = MMKV.defaultMMKV().decodeString(BZYConstants.NODE_All_ITEM_NAME_KEY, "");
val intentFilter = IntentFilter(Intents.ACTION_NODE_LIST) val intentFilter = IntentFilter(Intents.ACTION_NODE_LIST)
@ -233,14 +239,21 @@ class ChangeNodeActivity : BaseActivity(), View.OnClickListener {
var nowSubUrl: String = "" var nowSubUrl: String = ""
private fun updateProfile(isDelete: Boolean) { private fun updateProfile(isDelete: Boolean) {
// showLoading("正在获取...", this@ChangeNodeActivity) // showLoading("正在获取...", this@ChangeNodeActivity)
try { try {
launch(Dispatchers.IO) { launch(Dispatchers.IO) {
UIMainActivity.profileBinder?.let { profileManager -> UIMainActivity.profileBinder?.let { profileManager ->
var mProfile = profileManager.queryAll().find { // var mProfile = profileManager.queryAll().find {
it.name == Conf.getClashConfName(this@ChangeNodeActivity.applicationContext) // it.name == Conf.getClashConfName(this@ChangeNodeActivity.applicationContext)
// }
var mProfile: Profile? = null
Log.d("queryAll().size ==>${profileManager.queryAll().size}")
profileManager.queryAll().forEach { it_pf ->
var confName =
Conf.getClashConfName(this@ChangeNodeActivity.applicationContext)
Log.d(" it_pf.name ==>${it_pf.name}, confName==>${confName}")
if (confName == it_pf.name) {
mProfile = it_pf
}
} }
if (mProfile == null) { if (mProfile == null) {
@ -256,16 +269,16 @@ class ChangeNodeActivity : BaseActivity(), View.OnClickListener {
Log.d("ChangeNode22 uuid==>${profile.uuid}") Log.d("ChangeNode22 uuid==>${profile.uuid}")
if (isDelete) { if (isDelete) {
Log.d("gogogogogo isDelete") Log.d("gogogogogo isDelete")
profileManager.delete(profile.uuid) // profileManager.delete(profile.uuid)
MMKV.defaultMMKV() MMKV.defaultMMKV()
.removeValueForKey(BZYConstants.NODE_All_ITEM_NAME_KEY) .removeValueForKey(BZYConstants.NODE_All_ITEM_NAME_KEY)
setSub2(profileManager, nowSubUrl)
} else { } else {
Log.d("gogogogogo update") Log.d("gogogogogo update")
profileManager.setActive(profile) profileManager.setActive(profile)
delay(500) Log.d("updateProfile pro.source==>${profile.source}, url==>${nowSubUrl}")
if (!TextUtils.isEmpty(profile.source)) { if (!TextUtils.isEmpty(profile.source)) {
// Log.d("updateProfile pro.source==>${profile.source}, url==>${nowSubUrl}")
if (profile.source != nowSubUrl) { if (profile.source != nowSubUrl) {
Log.d("updateProfile patch") Log.d("updateProfile patch")
profileManager.patch( profileManager.patch(
@ -280,6 +293,17 @@ class ChangeNodeActivity : BaseActivity(), View.OnClickListener {
} }
Log.d("updateProfile patch 2") 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") Log.d("updateProfile go update")
profileManager.update(profile.uuid) profileManager.update(profile.uuid)
@ -318,29 +342,35 @@ class ChangeNodeActivity : BaseActivity(), View.OnClickListener {
UIMainActivity.subUrl?.let { UIMainActivity.subUrl?.let {
nowSubUrl = it nowSubUrl = it
} }
doProfile() doProfile(false)
} }
fun doProfile() { fun doProfile(isDelete: Boolean) {
Log.d("doProfile....")
if (TextUtils.isEmpty(cacheNodeStr)) { if (TextUtils.isEmpty(cacheNodeStr)) {
} }
showLoading("正在获取...", this@ChangeNodeActivity) showLoading("正在获取...", this@ChangeNodeActivity)
startTimer() startTimer()
updateProfile(false) // updateProfile(isDelete)
launch(Dispatchers.IO) {
UIMainActivity.setSub2(nowSubUrl)
}
} }
private fun changeSubUrl(originalUrl: String, newUrl: String) { private fun changeSubUrl(originalUrl: String, newUrl: String) {
if (!originalUrl.isNullOrEmpty() && !newUrl.isNullOrEmpty()) { if (!originalUrl.isNullOrEmpty() && !newUrl.isNullOrEmpty()) {
nowSubUrl = originalUrl.replace(Regex("https://[^/]+/"), "$newUrl/") nowSubUrl = originalUrl.replace(Regex("https://[^/]+/"), "$newUrl/")
UIMainActivity.subUrl = nowSubUrl
MMKV.defaultMMKV().encode(BZYConstants.PROFILE_SUB_URL, nowSubUrl) MMKV.defaultMMKV().encode(BZYConstants.PROFILE_SUB_URL, nowSubUrl)
} }
} }
private fun updateTestBut(str: String) { private fun updateTestBut(str: String) {
nodeItemTest.text=str nodeItemTest.text = str
} }
val reloadLock = Semaphore(10) val reloadLock = Semaphore(10)
@ -393,7 +423,6 @@ class ChangeNodeActivity : BaseActivity(), View.OnClickListener {
} }
} }
private fun setNewData(nodes: MutableList<ClashNodebBean>?) { private fun setNewData(nodes: MutableList<ClashNodebBean>?) {
runOnUiThread { runOnUiThread {
if (nodes != null) { if (nodes != null) {
@ -449,13 +478,25 @@ class ChangeNodeActivity : BaseActivity(), View.OnClickListener {
} }
private fun createProfile() { private fun createProfile() {
cleanProfile()
launch(Dispatchers.IO) { launch(Dispatchers.IO) {
UIMainActivity.profileBinder?.let { pro -> UIMainActivity.profileBinder?.let { pro ->
uuid?.let { uu -> createMyProfile(pro, nowSubUrl)
pro.delete(uu)
delay(500) }
setSub2(pro, UIMainActivity.subUrl)
}
}
private fun cleanProfile() {
launch(Dispatchers.IO) {
UIMainActivity.profileBinder?.let { pm ->
pm.queryAll().forEach { profile ->
Log.d("changeNode cleanProfile go delete name==>${profile.name}, uuid==>${profile.uuid}")
pm.delete(profile.uuid)
} }
} ?: let {
Log.d("changeNode profileBinder is null")
} }
} }
} }
@ -466,10 +507,6 @@ class ChangeNodeActivity : BaseActivity(), View.OnClickListener {
com.cncat.vpn.common.log.Log.d("setSub2 url isEmpty") com.cncat.vpn.common.log.Log.d("setSub2 url isEmpty")
return return
} }
com.cncat.vpn.common.log.Log.d("setSub2 2")
com.cncat.vpn.common.log.Log.d("setSub2 3") com.cncat.vpn.common.log.Log.d("setSub2 3")
val pro = pm.queryAll().find { val pro = pm.queryAll().find {
it.name == Conf.getClashConfName(this@ChangeNodeActivity.applicationContext) it.name == Conf.getClashConfName(this@ChangeNodeActivity.applicationContext)
@ -502,11 +539,40 @@ class ChangeNodeActivity : BaseActivity(), View.OnClickListener {
"new updateStatus: " + Gson().toJson(status.args) + ", pro==>" + status.progress "new updateStatus: " + Gson().toJson(status.args) + ", pro==>" + status.progress
) )
} }
// } // }
} }
} }
} }
suspend fun createMyProfile(pm: IProfileManager, url: String) {
var uuid: UUID = pm.create(
Profile.Type.Url, Conf.getClashConfName(this@ChangeNodeActivity.applicationContext)
)
delay(500)
pm.queryByUUID(uuid)?.let { qPro ->
Log.d("createMyProfile url==>${url}")
pm.setActive(qPro)
pm.patch(
qPro.uuid, qPro.name,
// "https://mojie0201.xn--8stx8olrwkucjq3b.com/api/v1/client/subscribe?token=169110d2674c49604b7e5b5cea341579",
// "https://www.otcopusapp.cc/lx3af288h5i8pz380/api/v1/client/subscribe?token=d6bc98c7ea53099fa5bd1a3ea415ebb6",
// "https://reg.bazhuayujiasu.cc/api/v1/client/subscribe?token=7f0f025ab341bcec4822d2c90aadf8b0",
url, 1800000L
)
// coroutineScope {
pm.commit(qPro.uuid) { status ->
android.util.Log.d(
Conf.TAG,
"new updateStatus: " + Gson().toJson(status.args) + ", pro==>" + status.progress
)
}
// UIMainActivity.profileBinder = pm
// }
}
}
var isCreate: Boolean = false var isCreate: Boolean = false
private var timerJob: Job? = null private var timerJob: Job? = null
private fun startTimer() { private fun startTimer() {
@ -514,15 +580,42 @@ class ChangeNodeActivity : BaseActivity(), View.OnClickListener {
timerJob = launch { timerJob = launch {
repeat(16) { repeat(16) {
Log.d("GetNodeTimeOutNum==>$GetNodeTimeOutNum") Log.d("GetNodeTimeOutNum==>$GetNodeTimeOutNum")
if (GetNodeTimeOutNum == 0) { if (GetNodeTimeOutNum == 0) {
hideLoading() hideLoading()
Log.d("stopTimer...") Log.d("stopTimer...")
RxToast.error("获取节点失败...请尝试切换其他地址") RxToast.error("获取节点失败...正在切换其他地址")
showDomainList() // showDomainList()
timerJob?.cancel() // var mmkv = MMKV.defaultMMKV()
// var currentHost = mmkv.decodeString(BZYConstants.MMKV_KEY_CURRENT_HOST, "")
if (setNewData()) {
timerJob?.cancel()
// cleanProfile()
MMKV.defaultMMKV()
.removeValueForKey(BZYConstants.NODE_All_ITEM_NAME_KEY)
// createProfile()
saveSubUrl()
MainApplication.restartApp(this@ChangeNodeActivity, object :
MainApplication.OnRestartButtonListener {
override fun enterButton() {
}
override fun cancelButton() {
doProfile(false)
}
})
// doProfile(false)
// isCreate=true
} else {
timerJob?.cancel()
RxToast.error("获取全部线路失败...请切换网络重试")
}
// delay(1000) // delay(1000)
// this@ChangeNodeActivity.finish() // this@ChangeNodeActivity.finish()
} }
GetNodeTimeOutNum-- GetNodeTimeOutNum--
delay(1000) delay(1000)
@ -545,7 +638,7 @@ class ChangeNodeActivity : BaseActivity(), View.OnClickListener {
DialogDoMainChoose.OnTryButtonListener { DialogDoMainChoose.OnTryButtonListener {
override fun tryButton() { override fun tryButton() {
saveSubUrl() saveSubUrl()
doProfile() doProfile(false)
dialogDoMainChoose?.dismiss() dialogDoMainChoose?.dismiss()
} }
@ -567,7 +660,7 @@ class ChangeNodeActivity : BaseActivity(), View.OnClickListener {
} }
override fun cancelButton() { override fun cancelButton() {
doProfile() doProfile(false)
dialogDoMainChoose?.dismiss() dialogDoMainChoose?.dismiss()
} }
@ -585,10 +678,90 @@ class ChangeNodeActivity : BaseActivity(), View.OnClickListener {
} }
} }
private fun setNewData(): Boolean {
var 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())
var switchFinshSet = mmkv.decodeStringSet(BZYConstants.MMKV_KEY_Switch_Finsh_Set, HashSet())
routerHost?.let {
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// if (!isNetworkAvailable(this@BaseActivity)){
// return isChange
// }
// }
if (it.isEmpty()) {
android.util.Log.d(Conf.TAG, "routerHost isEmpty")
return isChange
}
com.cncat.vpn.common.log.Log.d("switchFinshSet.size==>${switchFinshSet?.size!!} , routerHost.size==>${routerHost.size}")
//次数已满
if (switchFinshSet?.size!! >= routerHost.size) {
return isChange
}
android.util.Log.d(Conf.TAG, "routerHost not empty")
for (url in it) {
// com.cncat.vpn.common.log.Log.d("url==>$url")
if (url == originalUrl) {
switchFinshSet?.add(url)
mmkv.encode(BZYConstants.MMKV_KEY_Switch_Finsh_Set, switchFinshSet)
continue
}
if (switchFinshSet?.contains(url) == true) {
continue
} else {
isChange = true
android.util.Log.e("Retry", "切换到新url:$url")
newUrlString = url
switchFinshSet?.add(newUrlString)
mmkv.encode(BZYConstants.MMKV_KEY_Switch_Finsh_Set, switchFinshSet)
// mmkv.encode(BZYConstants.MMKV_KEY_OLD_CURRENT_HOST, newUrlString)
break
}
}
// val iterator = it.iterator()
// while (iterator.hasNext()) {
// val newUrl = iterator.next()
// android.util.Log.e(
// "Data",
// "遍历备用的url:$newUrl, contains==>${switchFinshSet?.contains(newUrl)}"
// )
// if (newUrl == originalUrl) {
// switchFinshSet?.add(newUrl)
// iterator.remove()
// continue
// }
// if (switchFinshSet?.contains(newUrl) == true) {
// iterator.remove()
// } else {
// android.util.Log.e("Retry", "切换到新url:$newUrl")
// newUrlString = newUrl
// iterator.remove()
// if (originalUrl != null) {
// switchFinshSet?.add(originalUrl)
// mmkv.encode(BZYConstants.MMKV_KEY_Switch_Finsh_Set, switchFinshSet)
// mmkv.encode(BZYConstants.MMKV_KEY_OLD_CURRENT_HOST, originalUrl)
// }
// break
// }
// }
// mmkv.encode(BZYConstants.MMKV_KEY_ROUTER_HOST, routerHost)
mmkv.encode(BZYConstants.MMKV_KEY_CURRENT_HOST, newUrlString)
}
return isChange
}
private fun saveSubUrl() { private fun saveSubUrl() {
var currentHost = MMKV.defaultMMKV() var currentHost = MMKV.defaultMMKV()
.decodeString(BZYConstants.MMKV_KEY_CURRENT_HOST, "") .decodeString(BZYConstants.MMKV_KEY_CURRENT_HOST, "")
// com.cncat.vpn.common.log.Log.d("tryButton currentHost==>${currentHost}") com.cncat.vpn.common.log.Log.d("tryButton currentHost==>${currentHost}")
var oldUrl = nowSubUrl var oldUrl = nowSubUrl
currentHost?.let { changeSubUrl(oldUrl, it) } currentHost?.let { changeSubUrl(oldUrl, it) }
} }

View File

@ -362,7 +362,19 @@ class MemberRechargeActivity : BaseActivity() {
override fun httpFailure(httpStatus: HttpStatus.Failure) { override fun httpFailure(httpStatus: HttpStatus.Failure) {
when (httpStatus.reqType) { when (httpStatus.reqType) {
HttpReqType.GET_RECHARGE_INFO -> { HttpReqType.GET_RECHARGE_INFO -> {
showDomainList() // showDomainList()
if (super.switchServer2()) {
val username =
MMKV.defaultMMKV()
.decodeString(BZYConstants.MMKV_KEY_REMEMBER_USERNAME, "")!!
val phToken =
MMKV.defaultMMKV().decodeString(BZYConstants.MMKV_KEY_PH_TOKEN, "")!!
val userId = MMKV.defaultMMKV().decodeString(BZYConstants.MMKV_KEY_USER_ID)!!
viewModel.getPhRechargeInfo(username, phToken, userId)
} else {
// super.switchFinshSet?.clear()
}
Logger.d("获取充值续费信息异常${httpStatus.msg}") Logger.d("获取充值续费信息异常${httpStatus.msg}")
swipeLayout.isRefreshing = false swipeLayout.isRefreshing = false
RxToast.error(resources.getString(R.string.DataError)) RxToast.error(resources.getString(R.string.DataError))

View File

@ -128,8 +128,8 @@ class NewMainActivity : BaseActivity() {
// private var profile: ProfileManager? = null // private var profile: ProfileManager? = null
// private var clashManager: ClashManager? = null // private var clashManager: ClashManager? = null
var clashBinder: IClashManager? = null private var clashBinder: IClashManager? = null
var profileBinder: IProfileManager? = null private var profileBinder: IProfileManager? = null
var newMainActGlo: Boolean = false var newMainActGlo: Boolean = false
var isUpload: Boolean = false var isUpload: Boolean = false
var hostsMem: ArrayList<String> = ArrayList() var hostsMem: ArrayList<String> = ArrayList()
@ -1187,6 +1187,7 @@ class NewMainActivity : BaseActivity() {
suspend fun createMyProfile(pm: IProfileManager, url: String) { suspend fun createMyProfile(pm: IProfileManager, url: String) {
Log.d("createMyProfile")
var uuid: UUID = pm.create( var uuid: UUID = pm.create(
Profile.Type.Url, Conf.getClashConfName(this@NewMainActivity.applicationContext) Profile.Type.Url, Conf.getClashConfName(this@NewMainActivity.applicationContext)
) )

View File

@ -110,7 +110,7 @@ class SettingActivity : BaseActivity() {
showLoading(resources.getString(R.string.UnderRepair), this@SettingActivity) showLoading(resources.getString(R.string.UnderRepair), this@SettingActivity)
launch(Dispatchers.IO) { launch(Dispatchers.IO) {
NewMainActivity.profileBinder?.let { pm -> UIMainActivity.profileBinder?.let { pm ->
pm.queryAll().forEach { profile -> pm.queryAll().forEach { profile ->
Log.d("setting profile delete ==>${profile.name}") Log.d("setting profile delete ==>${profile.name}")
pm.delete(profile.uuid) pm.delete(profile.uuid)

View File

@ -21,9 +21,9 @@ class SplashHelper(private val splashAtyVM: PublicViewMode) {
third: (AsyncCallback) -> Unit, third: (AsyncCallback) -> Unit,
callback: (Boolean) -> Unit callback: (Boolean) -> Unit
) { ) {
first(createCallback("getHost method success", "getHostByGithub method failed", callback) { first(createCallback("getHost method success", "getHost method failed", callback) {
second(createCallback("getHostByAWS method success", "getHostByAWS method failed", callback) { second(createCallback("getHostByAWS method success", "getHostByAWS method failed", callback) {
third(createCallback("getHost method success", "getHost methods failed", callback) { third(createCallback("getHostByGithub method success", "getHostByGithub methods failed", callback) {
callback(false) // 所有方法都失败 callback(false) // 所有方法都失败
}) })
}) })

View File

@ -239,6 +239,11 @@ class SplashActivity : BaseActivity() {
com.cncat.vpn.common.log.Log.d("si==>${si}, ss1==>${ss1}, ss2==>${ss2}") com.cncat.vpn.common.log.Log.d("si==>${si}, ss1==>${ss1}, ss2==>${ss2}")
MainApplication.KEY2 = ss1 MainApplication.KEY2 = ss1
MainApplication.IV2 = ss2 MainApplication.IV2 = ss2
// val byteArrayToHexStr =
// AESUtil.byteArrayToHexStr(AESUtil.encrypt("https://api.kuaifei.net+https://api.wfvpnpro.com+https://wf.jimao.xyz+https://api.msljsqpro.com+https://api.mashanglianjsq.com"))
// android.util.Log.i("AAAAAAAAAAAAAAAA","data::"+byteArrayToHexStr+"::")
// 太子 // 太子
// com.cncat.vpn.common.log.Log.d("tz encrypt:"+ AESUtil.byteArrayToHexStr(AESUtil.encrypt(""))) // com.cncat.vpn.common.log.Log.d("tz encrypt:"+ AESUtil.byteArrayToHexStr(AESUtil.encrypt("")))
// 八爪鱼 com.cncat.vpn.common.log.Log.d("bzy encrypt:"+ AESUtil.byteArrayToHexStr(AESUtil.encrypt(""))) // 八爪鱼 com.cncat.vpn.common.log.Log.d("bzy encrypt:"+ AESUtil.byteArrayToHexStr(AESUtil.encrypt("")))
@ -817,6 +822,7 @@ class SplashActivity : BaseActivity() {
val expired = checkLoginRsp.data?.expired val expired = checkLoginRsp.data?.expired
val isPerfect = checkLoginRsp.data?.isPerfect val isPerfect = checkLoginRsp.data?.isPerfect
val userCommand = checkLoginRsp.data?.userCommand val userCommand = checkLoginRsp.data?.userCommand
val logSwitch = checkLoginRsp.data?.logSwitch
val mmkv = MMKV.defaultMMKV() val mmkv = MMKV.defaultMMKV()
mmkv.encode(BZYConstants.MMKV_KEY_VPN_EXPIRED, expired ?: true) mmkv.encode(BZYConstants.MMKV_KEY_VPN_EXPIRED, expired ?: true)
mmkv.encode( mmkv.encode(
@ -825,11 +831,8 @@ class SplashActivity : BaseActivity() {
) )
mmkv.encode(BZYConstants.MMKV_KEY_IS_PERFECT, isPerfect ?: true) mmkv.encode(BZYConstants.MMKV_KEY_IS_PERFECT, isPerfect ?: true)
mmkv.encode(BZYConstants.MMKV_KEY_VPN_UserCommand, userCommand ?: "") mmkv.encode(BZYConstants.MMKV_KEY_VPN_UserCommand, userCommand ?: "")
mmkv.encode(BZYConstants.MMKV_KEY_VPN_LogSwitch, logSwitch ?: "")
toMainPage() toMainPage()
} else { } else {
toLoginPage() toLoginPage()
} }
@ -903,56 +906,56 @@ class SplashActivity : BaseActivity() {
rlPb.visibility = View.GONE rlPb.visibility = View.GONE
pb_web?.visibility = View.INVISIBLE pb_web?.visibility = View.INVISIBLE
com.cncat.vpn.common.log.Log.d("httpFailure HttpReqType.REGISTER") com.cncat.vpn.common.log.Log.d("httpFailure HttpReqType.REGISTER")
try { // try {
RxToast.error("检测到网络异常,请尝试切换其他地址.") // RxToast.error("检测到网络异常,请尝试切换其他地址.")
if (dialogDoMainChoose == null) { // if (dialogDoMainChoose == null) {
dialogDoMainChoose = DialogDoMainChoose(this@SplashActivity) // dialogDoMainChoose = DialogDoMainChoose(this@SplashActivity)
}
if (dialogDoMainChoose?.isShowing == false) {
dialogDoMainChoose?.setOnTryButtonListener(object :
DialogDoMainChoose.OnTryButtonListener {
override fun tryButton() {
var currentHost = MMKV.defaultMMKV()
.decodeString(BZYConstants.MMKV_KEY_CURRENT_HOST, "")
// com.cncat.vpn.common.log.Log.d("tryButton currentHost==>${currentHost}")
doRegisterByEncryption()
}
override fun cancel() {
com.cncat.vpn.common.log.Log.d("tryButton REGISTER cancel")
dialogDoMainChoose?.dismiss()
dialogDoMainChoose = null
}
})
dialogDoMainChoose?.setOnListItemListener(object :
DialogDoMainChoose.OnListItemListener {
override fun listItemClick() {
doRegisterByEncryption()
dialogDoMainChoose?.dismiss()
}
})
dialogDoMainChoose?.show()
}
} catch (e: Exception) {
e.printStackTrace()
}
// if (super.switchServer()) {//切换服务器并直接重试
//// doRegister()
// doRegisterByEncryption()
// } else {
// MaterialDialog(this@SplashActivity).show {
// message(text = initErrMsg)
// positiveButton(R.string.retry) {
//// doRegister()
// doRegisterByEncryption()
// }
// negativeButton(R.string.cancel) {
// it.dismiss()
// finish()
// }
// } // }
// if (dialogDoMainChoose?.isShowing == false) {
// dialogDoMainChoose?.setOnTryButtonListener(object :
// DialogDoMainChoose.OnTryButtonListener {
// override fun tryButton() {
// var currentHost = MMKV.defaultMMKV()
// .decodeString(BZYConstants.MMKV_KEY_CURRENT_HOST, "")
//// com.cncat.vpn.common.log.Log.d("tryButton currentHost==>${currentHost}")
// doRegisterByEncryption()
// }
//
// override fun cancel() {
// com.cncat.vpn.common.log.Log.d("tryButton REGISTER cancel")
// dialogDoMainChoose?.dismiss()
// dialogDoMainChoose = null
// }
// })
// dialogDoMainChoose?.setOnListItemListener(object :
// DialogDoMainChoose.OnListItemListener {
// override fun listItemClick() {
// doRegisterByEncryption()
// dialogDoMainChoose?.dismiss()
// }
//
// })
// dialogDoMainChoose?.show()
// }
// } catch (e: Exception) {
// e.printStackTrace()
// } // }
if (super.switchServer2()) {//切换服务器并直接重试
// doRegister()
doRegisterByEncryption()
} else {
MaterialDialog(this@SplashActivity).show {
message(text = initErrMsg)
positiveButton(R.string.retry) {
// doRegister()
doRegisterByEncryption()
}
negativeButton(R.string.cancel) {
it.dismiss()
finish()
}
}
}
} }
HttpReqType.LOGIN -> { HttpReqType.LOGIN -> {

View File

@ -92,8 +92,9 @@ class UIMainActivity : BaseActivity() {
private var timer: Timer? = null private var timer: Timer? = null
private var uTimerTask: TimerTask? = null private var uTimerTask: TimerTask? = null
private var newMainActGlo: Boolean = false private var newMainActGlo: Boolean = false
private var homeFragment:HomeFragment?=null private var homeFragment: HomeFragment? = null
private var settingFragment:SettingFragment?=null private var settingFragment: SettingFragment? = null
@SuppressLint("SimpleDateFormat") @SuppressLint("SimpleDateFormat")
private val dateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss") private val dateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
private var activityDialog: ActivityViewDialog? = null private var activityDialog: ActivityViewDialog? = null
@ -113,6 +114,69 @@ class UIMainActivity : BaseActivity() {
var hostsMem: ArrayList<String> = ArrayList() var hostsMem: ArrayList<String> = ArrayList()
var isSubed = false var isSubed = false
var newMainActGlo: Boolean = false var newMainActGlo: Boolean = false
public suspend fun setSub2(url: String) {
profileBinder?.let { pm ->
pm.queryAll().forEach { itemPro ->
Log.d("uimain setSub2 itemPro.name==${itemPro.name}")
if (itemPro.name != Conf.getClashConfName(MainApplication.getApp()?.applicationContext)) {
Log.d("setSub2 delete ==>${itemPro.name}")
pm.delete(itemPro.uuid)
}
}
var mProfile = pm.queryAll().find {
it.name == Conf.getClashConfName(MainApplication.getApp()?.applicationContext)
}
mProfile?.let {
var actname = it.name
Log.d("new main actname==>" + actname)
Log.d("setSub2 pro 已存在 ==>")
pm.setActive(it)
delay(500)
//判断配置文件source
if (!TextUtils.isEmpty(mProfile.source)) {
Log.d("setSub2 pro.source==>${mProfile.source}, url==>${url}")
//订阅链接不同就更换
if (mProfile.source != url) {
pm.patch(
mProfile.uuid, mProfile.name,
url, 1800000L
)
pm.commit(mProfile.uuid) { status ->
android.util.Log.d(
Conf.TAG,
"new updateStatus: " + Gson().toJson(status.args) + ", pro==>" + status.progress
)
}
}
}
pm.update(it.uuid)
newUuid = mProfile.uuid.toString()
isSubbed = true
} ?: run {
Log.d("setSub2 go create")
val uuid: UUID = pm.create(
Profile.Type.Url, Conf.getClashConfName(MainApplication.getApp()?.applicationContext)
)
delay(500)
pm.queryByUUID(uuid)?.let { qPro ->
pm.setActive(qPro)
pm.patch(
qPro.uuid, qPro.name,
url, 1800000L
)
pm.commit(qPro.uuid) { status ->
android.util.Log.d(
Conf.TAG,
"new updateStatus: " + Gson().toJson(status.args) + ", pro==>" + status.progress
)
}
newUuid = qPro.uuid.toString()
isSubbed = true
}
}
}
}
} }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@ -148,6 +212,7 @@ class UIMainActivity : BaseActivity() {
if (homeFragment == null) homeFragment = HomeFragment() if (homeFragment == null) homeFragment = HomeFragment()
showFragment(homeFragment!!) showFragment(homeFragment!!)
} }
R.id.navigation_setting -> { R.id.navigation_setting -> {
if (settingFragment == null) settingFragment = SettingFragment() if (settingFragment == null) settingFragment = SettingFragment()
showFragment(settingFragment!!) showFragment(settingFragment!!)
@ -163,6 +228,7 @@ class UIMainActivity : BaseActivity() {
transaction.replace(R.id.fragmentContainer, fragment) transaction.replace(R.id.fragmentContainer, fragment)
transaction.commit() transaction.commit()
} }
private fun showFragment(fragment: Fragment) { private fun showFragment(fragment: Fragment) {
val fragmentManager = supportFragmentManager val fragmentManager = supportFragmentManager
val transaction = fragmentManager.beginTransaction() val transaction = fragmentManager.beginTransaction()
@ -179,6 +245,7 @@ class UIMainActivity : BaseActivity() {
} }
transaction.commit() transaction.commit()
} }
override fun httpLoading(httpStatus: HttpStatus.Loading) { override fun httpLoading(httpStatus: HttpStatus.Loading) {
if (httpStatus.reqType == HttpReqType.HEARTBEAT) { if (httpStatus.reqType == HttpReqType.HEARTBEAT) {
Logger.d("心跳中") Logger.d("心跳中")
@ -196,25 +263,26 @@ class UIMainActivity : BaseActivity() {
} }
data.data.let { url -> data.data.let { url ->
android.util.Log.e("Data", "机场URL=${url}") android.util.Log.e("Data", "机场URL=${url}")
var url2="https://api.ymvpnpro11.cc:8700/7air2vr7ikiqzgvxz/api/v1/client/subscribe?token=22f3d75079ec6fa546c1862c4354e448"
// if (url.isNotEmpty()) { // if (url.isNotEmpty()) {
// subUrl = url // subUrl = url
// lifecycleScope.launch(Dispatchers.IO) { // lifecycleScope.launch(Dispatchers.IO) {
// setSub2(url) // setSub2(url)
// } // }
// } // }
if (url.isNotEmpty()) { if (url2.isNotEmpty()) {
//todo 判断订阅地址是否修改过,未修改就用接口返回的地址反之使用本地修改的地址 //todo 判断订阅地址是否修改过,未修改就用接口返回的地址反之使用本地修改的地址
var pro_sub_url = var pro_sub_url =
MMKV.defaultMMKV().decodeString(BZYConstants.PROFILE_SUB_URL, "") MMKV.defaultMMKV().decodeString(BZYConstants.PROFILE_SUB_URL, "")
// Log.d("pro_sub_url==>${pro_sub_url}") // Log.d("pro_sub_url==>${pro_sub_url}")
if (!TextUtils.isEmpty(pro_sub_url)){ if (!TextUtils.isEmpty(pro_sub_url)) {
if (pro_sub_url == url) { if (pro_sub_url == url2) {
subUrl = url subUrl = url2
} else { } else {
subUrl = pro_sub_url subUrl = pro_sub_url
} }
}else{ } else {
subUrl = url subUrl = url2
} }
lifecycleScope.launch(Dispatchers.IO) { lifecycleScope.launch(Dispatchers.IO) {
// setSub(url) // setSub(url)
@ -230,7 +298,7 @@ class UIMainActivity : BaseActivity() {
if (data.code == 200) { if (data.code == 200) {
if (data.data.data.list.isNotEmpty()) { if (data.data.data.list.isNotEmpty()) {
data.data.data.list[0].redirectLink.let { data.data.data.list[0].redirectLink.let {
if(activityDialog==null){ if (activityDialog == null) {
activityDialog = activityDialog =
ActivityViewDialog(this, data.data.data.list[0].redirectLink) ActivityViewDialog(this, data.data.data.list[0].redirectLink)
activityDialog?.setOnDialogClickListener(object : activityDialog?.setOnDialogClickListener(object :
@ -263,8 +331,10 @@ class UIMainActivity : BaseActivity() {
val expired = checkLoginRsp.data.expired val expired = checkLoginRsp.data.expired
mmkv.encode(BZYConstants.MMKV_KEY_VPN_EXPIRED, expired ?: true) mmkv.encode(BZYConstants.MMKV_KEY_VPN_EXPIRED, expired ?: true)
mmkv.encode(BZYConstants.MMKV_KEY_VPN_VALID_PERIOD, nowExpireTime) mmkv.encode(BZYConstants.MMKV_KEY_VPN_VALID_PERIOD, nowExpireTime)
val hf = supportFragmentManager.findFragmentById(R.id.fragmentContainer) as? HomeFragment val hf =
val sf = supportFragmentManager.findFragmentById(R.id.fragmentContainer) as? SettingFragment supportFragmentManager.findFragmentById(R.id.fragmentContainer) as? HomeFragment
val sf =
supportFragmentManager.findFragmentById(R.id.fragmentContainer) as? SettingFragment
hf?.setExpireDate() hf?.setExpireDate()
sf?.setExpireDate() sf?.setExpireDate()
checkPayResultDia?.dismiss() checkPayResultDia?.dismiss()
@ -343,6 +413,7 @@ class UIMainActivity : BaseActivity() {
} }
} }
} }
private fun showLoadingDialog(msg: String) { private fun showLoadingDialog(msg: String) {
payResultLoading = RxDialogShapeLoading(this) payResultLoading = RxDialogShapeLoading(this)
payResultLoading!!.setMessage(msg) payResultLoading!!.setMessage(msg)
@ -356,7 +427,11 @@ class UIMainActivity : BaseActivity() {
MMKV.defaultMMKV().removeValueForKey(BZYConstants.NODE_SELECTED_NAME_KEY) MMKV.defaultMMKV().removeValueForKey(BZYConstants.NODE_SELECTED_NAME_KEY)
MMKV.defaultMMKV().removeValueForKey(BZYConstants.MMKV_KEY_VPN_UserCommand) MMKV.defaultMMKV().removeValueForKey(BZYConstants.MMKV_KEY_VPN_UserCommand)
MMKV.defaultMMKV().removeValueForKey(BZYConstants.NODE_All_ITEM_NAME_KEY) MMKV.defaultMMKV().removeValueForKey(BZYConstants.NODE_All_ITEM_NAME_KEY)
MMKV.defaultMMKV().removeValueForKey(BZYConstants.PROFILE_SUB_URL)
MMKV.defaultMMKV().removeValueForKey(BZYConstants.MMKV_KEY_Switch_Finsh_Set)
Log.d("cleanUserInfo clashRunning==>${clashRunning}") Log.d("cleanUserInfo clashRunning==>${clashRunning}")
//清除源切换记录
if (clashRunning) { if (clashRunning) {
fragments.filterIsInstance<HomeFragment>().forEach { it.stopClash() } fragments.filterIsInstance<HomeFragment>().forEach { it.stopClash() }
} }
@ -401,61 +476,7 @@ class UIMainActivity : BaseActivity() {
} }
} }
private suspend fun setSub2(url: String) {
profileBinder?.let { pm ->
pm.queryAll().forEach { itemPro ->
if (itemPro.name != Conf.getClashConfName(this@UIMainActivity.applicationContext)) {
pm.delete(itemPro.uuid)
}
}
val pro = pm.queryActive()
pro?.let {
pm.setActive(it)
delay(500)
//判断配置文件source
if (!TextUtils.isEmpty(pro.source)) {
Log.d("setSub2 pro.source==>${pro.source}, url==>${url}")
//订阅链接不同就更换
if (pro.source != url) {
pm.patch(
pro.uuid, pro.name,
url, 1800000L
)
pm.commit(pro.uuid) { status ->
android.util.Log.d(
Conf.TAG,
"new updateStatus: " + Gson().toJson(status.args) + ", pro==>" + status.progress
)
}
}
}
pm.update(it.uuid)
newUuid = pro.uuid.toString()
isSubbed = true
} ?: run {
val uuid: UUID = pm.create(
Profile.Type.Url, Conf.getClashConfName(this@UIMainActivity.applicationContext)
)
delay(500)
pm.queryByUUID(uuid)?.let { qPro ->
pm.setActive(qPro)
pm.patch(
qPro.uuid, qPro.name,
url, 1800000L
)
pm.commit(qPro.uuid) { status ->
android.util.Log.d(
Conf.TAG,
"new updateStatus: " + Gson().toJson(status.args) + ", pro==>" + status.progress
)
}
newUuid = qPro.uuid.toString()
isSubbed = true
}
}
}
}
private fun startLog() { private fun startLog() {
@ -559,7 +580,7 @@ class UIMainActivity : BaseActivity() {
override fun onDestroy() { override fun onDestroy() {
if (activityDialog != null) { if (activityDialog != null) {
if(activityDialog!!.isShowing){ if (activityDialog!!.isShowing) {
activityDialog!!.dismiss() activityDialog!!.dismiss()
} }

View File

@ -1,6 +1,5 @@
package com.yingmao.clash.app package com.yingmao.clash.app
import android.annotation.SuppressLint
import android.app.Activity import android.app.Activity
import android.app.Application import android.app.Application
import android.app.NotificationChannel import android.app.NotificationChannel
@ -10,7 +9,6 @@ import android.content.Intent
import android.content.res.Configuration import android.content.res.Configuration
import android.os.Build import android.os.Build
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.core.content.ContextCompat.getString
import androidx.core.content.getSystemService import androidx.core.content.getSystemService
import com.cncat.vpn.common.Global import com.cncat.vpn.common.Global
import com.cncat.vpn.common.compat.currentProcessName import com.cncat.vpn.common.compat.currentProcessName
@ -18,7 +16,6 @@ import com.cncat.vpn.common.log.Log
import com.github.gzuliyujiang.oaid.DeviceID import com.github.gzuliyujiang.oaid.DeviceID
import com.github.gzuliyujiang.oaid.DeviceIdentifier import com.github.gzuliyujiang.oaid.DeviceIdentifier
import com.github.gzuliyujiang.oaid.IGetter import com.github.gzuliyujiang.oaid.IGetter
import com.jakewharton.processphoenix.ProcessPhoenix import com.jakewharton.processphoenix.ProcessPhoenix
import com.orhanobut.logger.Logger import com.orhanobut.logger.Logger
import com.stripe.android.PaymentConfiguration import com.stripe.android.PaymentConfiguration
@ -29,14 +26,16 @@ import com.yingmao.clash.conf.BZYConstants
import com.yingmao.clash.entity.UserData import com.yingmao.clash.entity.UserData
import com.yingmao.clash.util.CrashCatchHandler import com.yingmao.clash.util.CrashCatchHandler
import com.yingmao.clash.util.clashDir import com.yingmao.clash.util.clashDir
import com.yingmao.clash.view.RxToast
import com.yingmao.clash.view.dialog.DialogDoMainChoose
import com.yingmao.clash.view.dialog.DialogSureRestartChoose import com.yingmao.clash.view.dialog.DialogSureRestartChoose
import kotlinx.coroutines.MainScope import kotlinx.coroutines.MainScope
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.io.File import java.io.File
import java.io.FileOutputStream import java.io.FileOutputStream
import java.io.PrintWriter
import java.io.StringWriter
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale import java.util.Locale
@ -154,10 +153,27 @@ class MainApplication : Application() {
// if (privacyPolicyAgreed) { // if (privacyPolicyAgreed) {
// } // }
// Thread.setDefaultUncaughtExceptionHandler { thread, throwable ->
// logException(throwable)
// // 可选择上报到服务器或写入本地文件
// }
} }
fun logException(e: Throwable) {
val log = StringWriter().also { e.printStackTrace(PrintWriter(it)) }.toString()
Log.d("MainApplication logException==>${log}")
// val directory: File? = applicationContext.getExternalFilesDir("CrashLog")
// if (directory != null) {
// if (!directory.isDirectory) {
// directory.delete()
// }
// if (!directory.exists()) {
// directory.mkdirs()
// }
// }
// File(directory, "crash_${System.currentTimeMillis()}.log").writeText(log)
}
fun updateActivity(language: String, locale: Locale) { fun updateActivity(language: String, locale: Locale) {
MMKV.defaultMMKV().encode(BZYConstants.languageInfo, language) MMKV.defaultMMKV().encode(BZYConstants.languageInfo, language)
val resources = this.resources val resources = this.resources

View File

@ -6,6 +6,9 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.ActivityInfo import android.content.pm.ActivityInfo
import android.content.res.Configuration import android.content.res.Configuration
import android.net.ConnectivityManager
import android.net.NetworkCapabilities
import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.text.TextUtils import android.text.TextUtils
import android.util.Log import android.util.Log
@ -13,7 +16,9 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
import android.widget.* import android.widget.*
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.collection.arraySetOf
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import com.orhanobut.logger.Logger import com.orhanobut.logger.Logger
import com.tencent.mmkv.MMKV import com.tencent.mmkv.MMKV
@ -55,6 +60,10 @@ open class BaseActivity : AppCompatActivity(), CoroutineScope by MainScope(), Br
var baseLoading: RxDialogShapeLoading? = null var baseLoading: RxDialogShapeLoading? = null
val serStore by lazy { ServiceStore(this) } val serStore by lazy { ServiceStore(this) }
companion object {
// var switchFinshSet: MutableSet<String>? = null
}
sealed class RequestTunnel { sealed class RequestTunnel {
object ReloadAll : RequestTunnel() object ReloadAll : RequestTunnel()
object ReLaunch : RequestTunnel() object ReLaunch : RequestTunnel()
@ -261,10 +270,11 @@ open class BaseActivity : AppCompatActivity(), CoroutineScope by MainScope(), Br
} }
} }
fun selectServer(){ fun selectServer() {
val mmkv = MMKV.defaultMMKV() val mmkv = MMKV.defaultMMKV()
val routerHost = mmkv.decodeStringSet(BZYConstants.MMKV_KEY_ROUTER_HOST, HashSet()) val routerHost = mmkv.decodeStringSet(BZYConstants.MMKV_KEY_ROUTER_HOST, HashSet())
} }
@Synchronized @Synchronized
fun switchServer(): Boolean { fun switchServer(): Boolean {
var isChange = false var isChange = false
@ -303,4 +313,95 @@ open class BaseActivity : AppCompatActivity(), CoroutineScope by MainScope(), Br
return isChange return isChange
} }
@RequiresApi(Build.VERSION_CODES.M)
fun isNetworkAvailable(context: Context): Boolean {
val connectivityManager =
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val network = connectivityManager.activeNetwork ?: return false
val capabilities = connectivityManager.getNetworkCapabilities(network) ?: return false
return capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
}
@Synchronized
fun switchServer2(): Boolean {
var isChange = false
val mmkv = MMKV.defaultMMKV()
val originalUrl =
mmkv.decodeString(BZYConstants.MMKV_KEY_CURRENT_HOST, BuildConfig.service_url)
var newUrlString = originalUrl
val routerHost = mmkv.decodeStringSet(BZYConstants.MMKV_KEY_ROUTER_HOST, HashSet())
var switchFinshSet = mmkv.decodeStringSet(BZYConstants.MMKV_KEY_Switch_Finsh_Set, HashSet())
routerHost?.let {
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// if (!isNetworkAvailable(this@BaseActivity)){
// return isChange
// }
// }
if (it.isEmpty()) {
Log.d(Conf.TAG, "routerHost isEmpty")
return isChange
}
com.cncat.vpn.common.log.Log.d("switchFinshSet.size==>${switchFinshSet?.size!!} , routerHost.size==>${routerHost.size}")
//次数已满
if (switchFinshSet?.size!! >= routerHost.size) {
return isChange
}
Log.d(Conf.TAG, "routerHost not empty")
// val iterator = it.iterator()
// while (iterator.hasNext()) {
// val newUrl = iterator.next()
// Log.e(
// "Data",
// "遍历备用的url:$newUrl, contains==>${switchFinshSet?.contains(newUrl)}"
// )
// if (newUrl == originalUrl) {
// switchFinshSet?.add(newUrl)
// iterator.remove()
// continue
// }
// if (switchFinshSet?.contains(newUrl) == true) {
// iterator.remove()
// } else {
// Log.e("Retry", "切换到新url:$newUrl")
// isChange = true
// newUrlString = newUrl
// iterator.remove()
// if (originalUrl != null) {
// switchFinshSet?.add(originalUrl)
// mmkv.encode(BZYConstants.MMKV_KEY_OLD_CURRENT_HOST, originalUrl)
// }
// break
// }
// }
for (url in it) {
if (url == originalUrl) {
switchFinshSet?.add(url)
mmkv.encode(BZYConstants.MMKV_KEY_Switch_Finsh_Set, switchFinshSet)
continue
}
if (switchFinshSet?.contains(url) == true) {
continue
} else {
isChange = true
android.util.Log.e("Retry", "切换到新url:$url")
newUrlString = url
switchFinshSet?.add(newUrlString)
mmkv.encode(BZYConstants.MMKV_KEY_Switch_Finsh_Set, switchFinshSet)
// mmkv.encode(BZYConstants.MMKV_KEY_OLD_CURRENT_HOST, newUrlString)
break
}
}
// mmkv.encode(BZYConstants.MMKV_KEY_ROUTER_HOST, routerHost)
mmkv.encode(BZYConstants.MMKV_KEY_CURRENT_HOST, newUrlString)
}
return isChange
}
} }

View File

@ -1,20 +1,37 @@
package com.yingmao.clash.base package com.yingmao.clash.base
import android.os.Build
import android.text.TextUtils import android.text.TextUtils
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.google.gson.Gson
import com.cncat.vpn.common.log.Log import com.cncat.vpn.common.log.Log
import com.google.gson.Gson
import com.tencent.mmkv.MMKV
import com.yingmao.clash.BuildConfig
import com.yingmao.clash.app.MainApplication
import com.yingmao.clash.conf.BZYConstants
import com.yingmao.clash.net.http.HttpApi
import com.yingmao.clash.net.http.HttpReqType import com.yingmao.clash.net.http.HttpReqType
import com.yingmao.clash.net.http.HttpStatus import com.yingmao.clash.net.http.HttpStatus
import com.yingmao.clash.net.http.HttpStatusDownLoad import com.yingmao.clash.net.http.HttpStatusDownLoad
import com.yingmao.clash.net.http.NetService
import com.yingmao.clash.net.http.entity.BaseRsp import com.yingmao.clash.net.http.entity.BaseRsp
import com.yingmao.clash.util.AESUtil import com.yingmao.clash.util.AESUtil
import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.RequestBody
import okhttp3.RequestBody.Companion.toRequestBody
import org.json.JSONArray
import org.json.JSONObject
import java.io.File
import java.io.PrintWriter
import java.io.StringWriter
import java.text.SimpleDateFormat
import java.util.Locale
open class BaseViewMode : ViewModel() { open class BaseViewMode : ViewModel() {
@ -24,9 +41,13 @@ open class BaseViewMode : ViewModel() {
fun doAsync(reqType: HttpReqType, doInAsync: suspend () -> Any) { fun doAsync(reqType: HttpReqType, doInAsync: suspend () -> Any) {
viewModelScope.launch( viewModelScope.launch(
CoroutineExceptionHandler { _, CoroutineExceptionHandler {
exception -> _,
Log.e("BaseViewMode_doAsync:${exception.toString()}", exception) exception,
->
Log.e("BaseViewMode_doAsync:${exception.localizedMessage}", exception)
logException(exception)
statusData.value = statusData.value =
if (exception.message.isNullOrBlank()) HttpStatus.Failure( if (exception.message.isNullOrBlank()) HttpStatus.Failure(
reqType, reqType,
@ -46,16 +67,86 @@ open class BaseViewMode : ViewModel() {
Log.i("请求结果:$rsp") Log.i("请求结果:$rsp")
statusData.value = HttpStatus.Success(reqType, rsp) statusData.value = HttpStatus.Success(reqType, rsp)
} catch (e: Exception) { } catch (e: Exception) {
Log.e( "网络请求异常",e) Log.e("网络请求异常", e)
statusData.value = HttpStatus.Failure(reqType, e.toString()) statusData.value = HttpStatus.Failure(reqType, e.toString())
} }
} }
} }
fun doAsync2(reqType: HttpReqType, doInAsync: suspend () -> Any) { fun logException(e: Throwable) {
var logSwitch = MMKV.defaultMMKV().decodeString(BZYConstants.MMKV_KEY_VPN_LogSwitch, "")
if ("on" == logSwitch) {
val log = StringWriter().also { e.printStackTrace(PrintWriter(it)) }.toString()
Log.d("logException==>${log}")
val username =
MMKV.defaultMMKV().decodeString(BZYConstants.MMKV_KEY_REMEMBER_USERNAME, "")!!
var json = JSONObject()
val androidVersion = Build.VERSION.RELEASE // 系统版本号,例如 "13"
val sdkInt = Build.VERSION.SDK_INT // SDK 级别,例如 33
val manufacturer = Build.MANUFACTURER // 厂商,例如 "Xiaomi"
val model = Build.MODEL // 机型,例如 "MI 11"
val brand = Build.BRAND // 品牌,例如 "Xiaomi"
val device = Build.DEVICE // 设备代号,例如 "venus"
val hardware = Build.HARDWARE // 硬件名称,例如 "qcom"
// val nowTime = SimpleDateFormat("yyyy年MM月dd日HH时mm分ss秒E", Locale.getDefault())
var ja_streams = JSONArray()
var jo_streams_item = JSONObject()
var jo_stream = JSONObject()
var ja_values = JSONArray()
jo_stream.put("phoneNumber", username)
jo_stream.put("appVersion", BuildConfig.VERSION_NAME)
jo_stream.put("androidVersion", androidVersion)
jo_stream.put("sdkInt", sdkInt)
jo_stream.put("manufacturer", manufacturer)
jo_stream.put("model", model)
jo_stream.put("brand", brand)
jo_stream.put("device", device)
jo_stream.put("hardware", hardware)
ja_values.put(0, System.currentTimeMillis().toString() + "000000")
//错误信息
ja_values.put(1, log)
jo_streams_item.put("stream", jo_stream)
var ja_values_item = JSONArray()
ja_values_item.put(0, ja_values)
jo_streams_item.put("values", ja_values_item)
ja_streams.put(0, jo_streams_item)
json.put("streams", ja_streams)
Log.d("json==>${json}")
val httpApi = NetService.instances.createHttps(HttpApi::class.java)
val requestBody: RequestBody = json.toString().toRequestBody("application/json; charset=utf-8".toMediaType())
doAsync2(HttpReqType.APPSHAREINFO) {
httpApi.postErrLog(requestBody)
}
// val directory: File? = MainApplication.getApp()?.applicationContext?.getExternalFilesDir("CrashLog")
// if (directory != null) {
// if (!directory.isDirectory) {
// directory.delete()
// }
// if (!directory.exists()) {
// directory.mkdirs()
// }
// }
// File(directory, "crash_${System.currentTimeMillis()}.log").writeText(json.toString())
}
}
fun postErrLog(phone: String) {
val phToken = MMKV.defaultMMKV().decodeString(BZYConstants.MMKV_KEY_PH_TOKEN,"")!!
val params = mutableMapOf(
"phoneNumber" to phone,
"phToken" to phToken
)
}
fun doAsync2(reqType: HttpReqType, doInAsync: suspend () -> Any?) {
viewModelScope.launch( viewModelScope.launch(
CoroutineExceptionHandler { _, CoroutineExceptionHandler {
exception -> _,
exception,
->
Log.e("BaseViewMode_doAsync:${exception.toString()}", exception) Log.e("BaseViewMode_doAsync:${exception.toString()}", exception)
statusData.value = statusData.value =
if (exception.message.isNullOrBlank()) HttpStatus.Failure( if (exception.message.isNullOrBlank()) HttpStatus.Failure(
@ -76,7 +167,7 @@ open class BaseViewMode : ViewModel() {
Log.i("请求结果:$rsp") Log.i("请求结果:$rsp")
statusData.value = HttpStatus.Success(reqType, rsp) statusData.value = HttpStatus.Success(reqType, rsp)
} catch (e: Exception) { } catch (e: Exception) {
Log.e( "网络请求异常",e) Log.e("网络请求异常", e)
statusData.value = HttpStatus.Failure(reqType, e.toString()) statusData.value = HttpStatus.Failure(reqType, e.toString())
} }
} }
@ -114,11 +205,13 @@ open class BaseViewMode : ViewModel() {
fun doAsyncDelay( fun doAsyncDelay(
reqType: HttpReqType, reqType: HttpReqType,
delayTimeMillis: Long, delayTimeMillis: Long,
doInAsync: suspend () -> BaseRsp doInAsync: suspend () -> BaseRsp,
) { ) {
viewModelScope.launch( viewModelScope.launch(
CoroutineExceptionHandler { _, CoroutineExceptionHandler {
exception -> _,
exception,
->
Log.e("BaseViewMode_doAsyncDelay:${exception.toString()}", exception) Log.e("BaseViewMode_doAsyncDelay:${exception.toString()}", exception)
statusData.value = statusData.value =
if (exception.message.isNullOrBlank()) HttpStatus.Failure( if (exception.message.isNullOrBlank()) HttpStatus.Failure(
@ -141,7 +234,7 @@ open class BaseViewMode : ViewModel() {
Log.i("请求结果:$rsp") Log.i("请求结果:$rsp")
statusData.value = HttpStatus.Success(reqType, rsp) statusData.value = HttpStatus.Success(reqType, rsp)
} catch (e: Exception) { } catch (e: Exception) {
Log.e( "网络请求异常",e) Log.e("网络请求异常", e)
// statusData.value = HttpStatus.Failure(reqType,e.toString()) // statusData.value = HttpStatus.Failure(reqType,e.toString())
} }
} }
@ -149,8 +242,10 @@ open class BaseViewMode : ViewModel() {
fun doAsyncStrResult(reqType: HttpReqType, doInAsync: suspend () -> String) { fun doAsyncStrResult(reqType: HttpReqType, doInAsync: suspend () -> String) {
viewModelScope.launch( viewModelScope.launch(
CoroutineExceptionHandler { _, CoroutineExceptionHandler {
exception -> _,
exception,
->
Log.e("BaseViewMode_doAsyncStrResult:${exception.toString()}", exception) Log.e("BaseViewMode_doAsyncStrResult:${exception.toString()}", exception)
@ -162,7 +257,7 @@ open class BaseViewMode : ViewModel() {
val rsp = result.await() val rsp = result.await()
Log.i("请求结果:$rsp") Log.i("请求结果:$rsp")
} catch (e: Exception) { } catch (e: Exception) {
Log.e( "网络请求异常",e) Log.e("网络请求异常", e)
} }
} }
} }
@ -170,11 +265,13 @@ open class BaseViewMode : ViewModel() {
fun doAsyncEncodeStr( fun doAsyncEncodeStr(
reqType: HttpReqType, reqType: HttpReqType,
classOfT: Class<out BaseRsp>, classOfT: Class<out BaseRsp>,
doInAsync: suspend () -> String doInAsync: suspend () -> String,
) { ) {
viewModelScope.launch( viewModelScope.launch(
CoroutineExceptionHandler() { _, CoroutineExceptionHandler() {
exception -> _,
exception,
->
Log.e("BaseViewMode_doAsyncEncodeStr:${exception.toString()}", exception) Log.e("BaseViewMode_doAsyncEncodeStr:${exception.toString()}", exception)
statusData.value = statusData.value =
if (exception.message.isNullOrBlank()) HttpStatus.Failure( if (exception.message.isNullOrBlank()) HttpStatus.Failure(
@ -191,8 +288,6 @@ open class BaseViewMode : ViewModel() {
val result = async { doInAsync() } val result = async { doInAsync() }
val rspStr = result.await() val rspStr = result.await()
Log.i("doAsyncEncodeStr 加密请求结果:$rspStr") Log.i("doAsyncEncodeStr 加密请求结果:$rspStr")
if (!TextUtils.isEmpty(rspStr)) { if (!TextUtils.isEmpty(rspStr)) {
var decodeRspStr = AESUtil.decrypt(rspStr) var decodeRspStr = AESUtil.decrypt(rspStr)
Log.d("doAsyncEncodeStr 请求结果:$decodeRspStr") Log.d("doAsyncEncodeStr 请求结果:$decodeRspStr")
@ -204,20 +299,22 @@ open class BaseViewMode : ViewModel() {
statusData.value = HttpStatus.Success(reqType, rsp) statusData.value = HttpStatus.Success(reqType, rsp)
} }
} catch (e: Exception) { } catch (e: Exception) {
Log.e("网络请求异常",e) Log.e("网络请求异常", e)
throw e throw e
statusData.value = HttpStatus.Failure(reqType,e.toString()) statusData.value = HttpStatus.Failure(reqType, e.toString())
} }
} }
} }
suspend fun doAsyncEncodeStrBg( suspend fun doAsyncEncodeStrBg(
classOfT: Class<out BaseRsp>, classOfT: Class<out BaseRsp>,
doInAsync: suspend () -> String doInAsync: suspend () -> String,
): BaseRsp? { ): BaseRsp? {
return viewModelScope.async( return viewModelScope.async(
CoroutineExceptionHandler() { _, CoroutineExceptionHandler() {
exception -> _,
exception,
->
Log.e("BaseViewMode_doAsyncEncodeStrBg:${exception.toString()}", exception) Log.e("BaseViewMode_doAsyncEncodeStrBg:${exception.toString()}", exception)
}) })
{ {
@ -230,7 +327,7 @@ open class BaseViewMode : ViewModel() {
val rsp = Gson().fromJson(decodeRspStr, classOfT) val rsp = Gson().fromJson(decodeRspStr, classOfT)
return@async rsp return@async rsp
} catch (e: Exception) { } catch (e: Exception) {
Log.e("网络请求异常",e) Log.e("网络请求异常", e)
} }
return@async null return@async null
}.await() }.await()
@ -245,7 +342,7 @@ open class BaseViewMode : ViewModel() {
} }
result.await() result.await()
} catch (e: Exception) { } catch (e: Exception) {
Log.e("网络请求异常",e) Log.e("网络请求异常", e)
} }
} }
} }

View File

@ -33,11 +33,11 @@ open class PublicViewMode : BaseViewMode() {
try { //TODO bug 会引起崩溃 try { //TODO bug 会引起崩溃
val host = when { val host = when {
BuildConfig.is_ym -> { BuildConfig.is_ym -> {
rt.getHostGithub() rt.getHost()
} }
BuildConfig.is_tz -> { BuildConfig.is_tz -> {
rt.getTzHostGithub() rt.getTzHostTZ()
} }
BuildConfig.is_yunti -> { BuildConfig.is_yunti -> {
@ -45,7 +45,7 @@ open class PublicViewMode : BaseViewMode() {
} }
else -> { else -> {
rt.getBzyHostGithub() rt.getBzyHostDef()
} }
} }
// Log.e("data", "2:$host") // Log.e("data", "2:$host")

View File

@ -28,6 +28,7 @@ class BZYConstants {
const val MMKV_KEY_OLD_CURRENT_HOST = "old_current_host" const val MMKV_KEY_OLD_CURRENT_HOST = "old_current_host"
//备用地址 //备用地址
const val MMKV_KEY_ROUTER_HOST ="router_host" const val MMKV_KEY_ROUTER_HOST ="router_host"
const val MMKV_KEY_Switch_Finsh_Set ="MMKV_KEY_Switch_Finsh_Set"
const val MMKV_KEY_FIRST_HOST ="first_host" const val MMKV_KEY_FIRST_HOST ="first_host"
const val MMKV_KEY_Airport_Base64 ="airport_base64" const val MMKV_KEY_Airport_Base64 ="airport_base64"
//注册地址 //注册地址
@ -47,6 +48,7 @@ class BZYConstants {
const val MMKV_KEY_VPN_EXPIRED = "expired" const val MMKV_KEY_VPN_EXPIRED = "expired"
const val MMKV_KEY_VPN_UserCommand = "userCommand" const val MMKV_KEY_VPN_UserCommand = "userCommand"
const val MMKV_KEY_VPN_LogSwitch = "logSwitch"
//vpn过期时间 //vpn过期时间
const val MMKV_KEY_VPN_VALID_PERIOD = "vpn_valid_period" const val MMKV_KEY_VPN_VALID_PERIOD = "vpn_valid_period"

View File

@ -79,6 +79,9 @@ data class UserData(
@field:SerializedName("userCommand") @field:SerializedName("userCommand")
val userCommand: String? = null, val userCommand: String? = null,
@field:SerializedName("logSwitch")
val logSwitch: String? = null,
@field:SerializedName("intervalTime") @field:SerializedName("intervalTime")
val intervalTime: Int? = null, val intervalTime: Int? = null,

View File

@ -1,6 +1,7 @@
package com.yingmao.clash.fragment package com.yingmao.clash.fragment
import android.os.Bundle import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@ -9,9 +10,14 @@ import android.widget.ImageView
import android.widget.RelativeLayout import android.widget.RelativeLayout
import android.widget.TextView import android.widget.TextView
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import com.tencent.mmkv.MMKV
import com.yingmao.clash.BuildConfig
import com.yingmao.clash.R import com.yingmao.clash.R
import com.yingmao.clash.base.BaseViewMode import com.yingmao.clash.base.BaseViewMode
import com.yingmao.clash.conf.BZYConstants
import com.yingmao.clash.conf.Conf
import com.yingmao.clash.net.http.HttpStatus import com.yingmao.clash.net.http.HttpStatus
import java.util.HashSet
abstract class BaseFragment :Fragment() { abstract class BaseFragment :Fragment() {
@ -58,4 +64,44 @@ abstract class BaseFragment :Fragment() {
open fun httpSuccess(httpStatus: HttpStatus.Success){} open fun httpSuccess(httpStatus: HttpStatus.Success){}
open fun httpFailure(httpStatus: HttpStatus.Failure){} open fun httpFailure(httpStatus: HttpStatus.Failure){}
@Synchronized
fun switchServer(): Boolean {
var isChange = false
val mmkv = MMKV.defaultMMKV()
val originalUrl =
mmkv.decodeString(BZYConstants.MMKV_KEY_CURRENT_HOST, BuildConfig.service_url)
var newUrlString = originalUrl
val routerHost = mmkv.decodeStringSet(BZYConstants.MMKV_KEY_ROUTER_HOST, HashSet())
routerHost?.let {
// it.forEach { url ->
// com.cncat.vpn.common.log.Log.d("url==>$url")
// }
if (it.isEmpty()) {
Log.d(Conf.TAG, "routerHost isEmpty")
return isChange
}
Log.d(Conf.TAG, "routerHost not empty")
val iterator = it.iterator()
while (iterator.hasNext()) {
val newUrl = iterator.next()
Log.e("Data", "遍历备用的url:$newUrl")
if (newUrl == originalUrl) {
iterator.remove()
} else {
Log.e("Retry", "切换到新url:$newUrl")
mmkv.encode(BZYConstants.MMKV_KEY_OLD_CURRENT_HOST, originalUrl)
isChange = true
newUrlString = newUrl
iterator.remove()
break
}
}
// mmkv.encode(BZYConstants.MMKV_KEY_ROUTER_HOST, routerHost)
mmkv.encode(BZYConstants.MMKV_KEY_CURRENT_HOST, newUrlString)
}
return isChange
}
} }

View File

@ -33,6 +33,7 @@ import com.yingmao.clash.act.NewMainActivity
import com.yingmao.clash.act.NewMainActivity.Companion.isSubed import com.yingmao.clash.act.NewMainActivity.Companion.isSubed
import com.yingmao.clash.act.NewMainActivity.Companion.newUuid import com.yingmao.clash.act.NewMainActivity.Companion.newUuid
import com.yingmao.clash.act.ProxyModeActivity import com.yingmao.clash.act.ProxyModeActivity
import com.yingmao.clash.act.UIMainActivity
import com.yingmao.clash.conf.BZYConstants import com.yingmao.clash.conf.BZYConstants
import com.yingmao.clash.conf.Conf import com.yingmao.clash.conf.Conf
import com.yingmao.clash.entity.ClashNodebBean import com.yingmao.clash.entity.ClashNodebBean
@ -294,7 +295,7 @@ class NewAccFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dispatch
// return // return
// } // }
launch { launch {
NewMainActivity.profileBinder?.let { pm -> UIMainActivity.profileBinder?.let { pm ->
val active = pm.queryActive() val active = pm.queryActive()
if (active == null) { if (active == null) {
com.cncat.vpn.common.log.Log.d("startClash: 未选择") com.cncat.vpn.common.log.Log.d("startClash: 未选择")
@ -707,7 +708,7 @@ class NewAccFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dispatch
} else { } else {
if (clashRunning) { if (clashRunning) {
delay(1000) delay(1000)
NewMainActivity.clashBinder?.let { cm -> UIMainActivity.clashBinder?.let { cm ->
val names = cm.queryProxyGroupNames(false) val names = cm.queryProxyGroupNames(false)
Log.i("checkGlo names==>${names}") Log.i("checkGlo names==>${names}")
if (names.isNotEmpty()) { if (names.isNotEmpty()) {
@ -771,7 +772,7 @@ class NewAccFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dispatch
var nodeName = proListItem[0].name var nodeName = proListItem[0].name
if (!TextUtils.isEmpty(nodeName)) { if (!TextUtils.isEmpty(nodeName)) {
launch(Dispatchers.IO) { launch(Dispatchers.IO) {
NewMainActivity.clashBinder?.let { cm -> UIMainActivity.clashBinder?.let { cm ->
val o = cm.queryOverride(Clash.OverrideSlot.Session) val o = cm.queryOverride(Clash.OverrideSlot.Session)
o.mode = TunnelState.Mode.Global o.mode = TunnelState.Mode.Global
@ -812,7 +813,7 @@ class NewAccFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dispatch
}) })
} }
} else { } else {
NewMainActivity.clashBinder?.let { cm -> UIMainActivity.clashBinder?.let { cm ->
val o = cm.queryOverride(Clash.OverrideSlot.Session) val o = cm.queryOverride(Clash.OverrideSlot.Session)
o.mode = TunnelState.Mode.Global o.mode = TunnelState.Mode.Global
cm.patchOverride(Clash.OverrideSlot.Session, o) cm.patchOverride(Clash.OverrideSlot.Session, o)

View File

@ -628,104 +628,7 @@ class RechargeFragment : BaseFragment() {
noVerticalPadding = true, noVerticalPadding = true,
horizontalPadding = false horizontalPadding = false
) )
// for (index in data.payType!!) {
// if (index.payModel == 10) {
// findViewById<LinearLayout>(R.id.llAggregatePayment).visibility =
// View.VISIBLE
// val JUhePay = findViewById<TextView>(R.id.JUhePay)
// JUhePay.visibility = View.VISIBLE
// JUhePay.text = index.payChannel
// if (index.payType == 1) {
// val ivAlipay = findViewById<AppCompatImageView>(R.id.ivAlipay)
// ivAlipay.visibility = View.VISIBLE
// ivAlipay.setOnClickListener {
// val payModel = index.payModel
// val payType = index.payType
// val payParam = index.payParam
// if (payModel != null && payType != null && payParam != null) {
// viewModel.orderPayment(data, payModel, payType, payParam)
// } else {
// viewModel.orderPayment(data, 10, 1, "ip@影猫充值")
// }
// }
// } else if (index.payType == 2) {
// val ivWechatPay = findViewById<AppCompatImageView>(R.id.ivWechatPay)
// ivWechatPay.visibility = View.VISIBLE
// ivWechatPay.setOnClickListener {
// val payModel = index.payModel
// val payType = index.payType
// val payParam = index.payParam
// if (payModel != null && payType != null && payParam != null) {
// viewModel.orderPayment(data, payModel, payType, payParam)
// } else {
// viewModel.orderPayment(data, 10, 2, "ip@影猫充值")
// }
//
// }
// }
// } else if (index.payModel == 11) {
// findViewById<LinearLayout>(R.id.llQuickPayment).visibility =
// View.VISIBLE
// val KJPay = findViewById<TextView>(R.id.KJPay)
// KJPay.visibility = View.VISIBLE
// KJPay.text = index.payChannel
// if (index.payType == 1) {
// val ivQuickPayment =
// findViewById<AppCompatImageView>(R.id.ivQuickPayment)
// ivQuickPayment.visibility = View.VISIBLE
// ivQuickPayment.setOnClickListener {
// val payModel = index.payModel
// val payType = index.payType
// val payParam = index.payParam
// if (payModel != null && payType != null && payParam != null) {
// viewModel.orderPayment(data, payModel, payType, payParam)
// } else {
// viewModel.orderPayment(data, 11, 1, "ip@影猫充值")
// }
// }
// } else if (index.payType == 2) {
// val ivQPWechatPay =
// findViewById<AppCompatImageView>(R.id.ivQPWechatPay)
// ivQPWechatPay.visibility = View.VISIBLE
// ivQPWechatPay.setOnClickListener {
// val payModel = index.payModel
// val payType = index.payType
// val payParam = index.payParam
// if (payModel != null && payType != null && payParam != null) {
// viewModel.orderPayment(data, payModel, payType, payParam)
// } else {
// viewModel.orderPayment(data, 11, 1, "ip@影喵充值")
// }
// }
// }
// } else if (index.payModel == 7) {
// findViewById<LinearLayout>(R.id.llStripe).visibility = View.VISIBLE
// findViewById<LinearLayout>(R.id.llStripePay).visibility = View.VISIBLE
// var payinfo = findViewById<TextView>(R.id.tvStripePay)
// payinfo.visibility = View.VISIBLE
//
// payinfo.text = index.payChannel
// findViewById<AppCompatImageView>(R.id.ivStripeAlipay).visibility =
// View.VISIBLE
// findViewById<AppCompatImageView>(R.id.ivStripeAlipay).setOnClickListener {
// if (selectedGradeID != 0) {
// if (!PayUtils.isAlipayInstalled(requireActivity())) {
// RxToast.error(resources.getString(R.string.AlipayNotInstalled))
// return@setOnClickListener
// }
// dismiss()
// showLoading(
// resources.getString(R.string.PaymentIn),
// requireActivity()
// )
// Log.i("PayGradeID:", "PayGradeID:${selectedGradeID}")
// viewModel.getPayClientSecret(selectedGradeID)
// } else {
// RxToast.info(getString(R.string.NoPaymentReceipt))
// }
// }
// }
// }
getCustomView().findViewById<AppCompatImageView>(R.id.ivBack) getCustomView().findViewById<AppCompatImageView>(R.id.ivBack)
.setOnClickListener { .setOnClickListener {
dismiss() dismiss()
@ -735,76 +638,7 @@ class RechargeFragment : BaseFragment() {
} }
} }
// private fun goWebViewStartPayClient(rsp: BaseRsp, isPayWeb: Boolean) {
// (rsp as BiLaiNormalRsp).let {
// if (it.code != "1000") {
// RxToast.error(if (it.message.isNullOrBlank()) "获取支付信息失败(${it.code}" else "${it.code}:${it.message}")
// } else {
// if (isPayWeb) {
// activity?.let {
// if (rsp.data != null) {
//// goWebPay(it, rsp.data!!)
//// goWebPay(it, newUrl)
// } else {
// RxToast.error("未能获取到支付信息,请联系客服")
// }
// }
// return
// }
//
// val gson = Gson().fromJson(it.data!!, BiLaiRsp::class.java)
// Logger.d("跳转支付宝支付页面:" + gson.payurl)
// var realPayUrl = ""
// var isNeedScan = false
// if (gson.payurl != null) {
// realPayUrl = gson.payurl!!
// isNeedScan = false
// } else if (gson.qrcode != null) {
// realPayUrl = gson.qrcode!!
// isNeedScan = true
// } else {
// realPayUrl = gson.urlscheme!!
// isNeedScan = true
// }
// // Log.e("data==", "html:" + realPayUrl);
//
//
// activity?.let {
// if (!isNeedScan) {
//// goWebPay(it, newUrl)
// } else {
// //二维码支付
// qrPayDialog = MaterialDialog(it).show {
// this.cancelable(false)
// cornerRadius(res = R.dimen.dp_10)
// customView(R.layout.qrpay_dialog_layout, scrollable = false, noVerticalPadding = true, horizontalPadding = false)
// getCustomView().findViewById<TextView>(R.id.tv_qr_pay_note).setText(R.string.qr_pay_note_wx)
//
// val ivQr = getCustomView().findViewById<ImageView>(R.id.iv_qr)
//
// val writer = MultiFormatWriter()
// val matrix = writer.encode(realPayUrl, BarcodeFormat.QR_CODE, 300, 300)
// val encoder = BarcodeEncoder()
// val bitmap = encoder.createBitmap(matrix)
// ivQr.setImageBitmap(bitmap)
//
// getCustomView().findViewById<TextView>(R.id.tv_pay_finish).setOnClickListener {
// showLoadingDialog("正在查询支付结果...")
// viewModel.codePayStatus(outTradeNo)
// }
//
// getCustomView().findViewById<TextView>(R.id.tv_pay_cancle).setOnClickListener {
// if (bitmap != null && !bitmap.isRecycled) {
// bitmap.recycle()
// dismiss()
// }
// }
// }
// }
// }
// }
// }
// }/
fun goWebPay(it: Activity, realPayUrl: String) { fun goWebPay(it: Activity, realPayUrl: String) {
val intent = Intent(context, BZYWebViewNormal::class.java) val intent = Intent(context, BZYWebViewNormal::class.java)

View File

@ -62,6 +62,7 @@ import com.yingmao.clash.act.UIMainActivity.Companion.newUuid
import com.yingmao.clash.act.UIMainActivity.Companion.profileBinder import com.yingmao.clash.act.UIMainActivity.Companion.profileBinder
import com.yingmao.clash.act.UIMainActivity.Companion.subUrl import com.yingmao.clash.act.UIMainActivity.Companion.subUrl
import com.yingmao.clash.app.MainApplication import com.yingmao.clash.app.MainApplication
import com.yingmao.clash.base.BaseActivity
import com.yingmao.clash.conf.BZYConstants import com.yingmao.clash.conf.BZYConstants
import com.yingmao.clash.conf.Conf import com.yingmao.clash.conf.Conf
import com.yingmao.clash.entity.CheckLoginRsp import com.yingmao.clash.entity.CheckLoginRsp
@ -636,6 +637,10 @@ class SettingFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dispatc
MMKV.defaultMMKV().removeValueForKey(BZYConstants.MMKV_KEY_VPN_UserCommand) MMKV.defaultMMKV().removeValueForKey(BZYConstants.MMKV_KEY_VPN_UserCommand)
MMKV.defaultMMKV().removeValueForKey(BZYConstants.NODE_All_ITEM_NAME_KEY) MMKV.defaultMMKV().removeValueForKey(BZYConstants.NODE_All_ITEM_NAME_KEY)
MMKV.defaultMMKV().removeValueForKey(BZYConstants.PROFILE_SUB_URL) MMKV.defaultMMKV().removeValueForKey(BZYConstants.PROFILE_SUB_URL)
MMKV.defaultMMKV().removeValueForKey(BZYConstants.MMKV_KEY_Switch_Finsh_Set)
//清除源切换记录
Log.d("cleanUserInfo clashRunning==>${clashRunning}") Log.d("cleanUserInfo clashRunning==>${clashRunning}")
if (clashRunning) { if (clashRunning) {
fragments.filterIsInstance<HomeFragment>().forEach { it.stopClash() } fragments.filterIsInstance<HomeFragment>().forEach { it.stopClash() }

View File

@ -33,6 +33,7 @@ import com.yingmao.clash.net.http.entity.UploadParamData
import com.yingmao.clash.net.http.entity.VipSignRsp import com.yingmao.clash.net.http.entity.VipSignRsp
import okhttp3.RequestBody import okhttp3.RequestBody
import retrofit2.Call import retrofit2.Call
import retrofit2.Response
import retrofit2.http.Body import retrofit2.http.Body
import retrofit2.http.GET import retrofit2.http.GET
import retrofit2.http.Header import retrofit2.http.Header
@ -75,6 +76,7 @@ interface HttpApi {
@POST("/netbarcloud/vpn/octopusRegister.do") @POST("/netbarcloud/vpn/octopusRegister.do")
suspend fun register(@QueryMap params: Map<String, String>): RegisterRsp suspend fun register(@QueryMap params: Map<String, String>): RegisterRsp
//加密注册 //加密注册
@Headers( @Headers(
"User-Agent: Octopus_Android" "User-Agent: Octopus_Android"
@ -94,7 +96,6 @@ interface HttpApi {
): RegisterRsp ): RegisterRsp
//显示赠送时长 //显示赠送时长
@Headers( @Headers(
"User-Agent: Octopus_Android" "User-Agent: Octopus_Android"
@ -218,8 +219,10 @@ interface HttpApi {
@GET("/taiziregister.txt") @GET("/taiziregister.txt")
suspend fun getTzRegister(): String suspend fun getTzRegister(): String
@GET("/taiziregister_182269.txt") @GET("/taiziregister_182269.txt")
suspend fun getTz182269Register(): String suspend fun getTz182269Register(): String
@GET("/bzyregister.txt") @GET("/bzyregister.txt")
suspend fun getBzyRegister(): String suspend fun getBzyRegister(): String
@ -631,4 +634,16 @@ interface HttpApi {
BZYConstants.MMKV_KEY_TOKEN BZYConstants.MMKV_KEY_TOKEN
).toString(), ).toString(),
): AppStopStatus ): AppStopStatus
@Headers(
"User-Agent: Octopus_Android",
"Authorization: Basic Z20yMXdvcGc6eDVjZXlzbTY="
)
@POST("/netbarcloud/api/v1/push")
suspend fun postErrLog(
@Body requestBody: RequestBody,
@Header("token") string: String = MMKV.defaultMMKV().decodeString(
BZYConstants.MMKV_KEY_TOKEN
).toString(),
): Response<NormalRsp>
} }

View File

@ -16,6 +16,7 @@ import okhttp3.Callback
import okhttp3.Cookie import okhttp3.Cookie
import okhttp3.CookieJar import okhttp3.CookieJar
import okhttp3.HttpUrl import okhttp3.HttpUrl
import okhttp3.Interceptor
import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
@ -72,6 +73,37 @@ class NetService private constructor() : INetService {
var httpClient: OkHttpClient? = null; var httpClient: OkHttpClient? = null;
var downHttpClient: OkHttpClient? = null; var downHttpClient: OkHttpClient? = null;
class ExceptionLoggingInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
return try {
val response = chain.proceed(request)
// 如果需要也可记录非 2xx 的响应
if (!response.isSuccessful) {
logHttpError(request, response.code, null)
}
response
} catch (e: Exception) {
logHttpError(request, null, e)
throw e // 抛出异常让上层处理
}
}
private fun logHttpError(request: Request, code: Int?, e: Exception?) {
val log = buildString {
append("URL: ${request.url}\n")
append("Method: ${request.method}\n")
append("Headers: ${request.headers}\n")
append("Body: ${request.body}\n")
code?.let { append("Response Code: $code\n") }
e?.let { append("Exception: ${e.message}\n${e.stackTraceToString()}") }
}
// 这里可以上传到服务端,或者保存到本地文件
println("❌ 网络请求异常:\n$log")
// FirebaseCrashlytics.getInstance().recordException(e) // 如果用了 Firebase
}
}
override fun httpsClient(connTO:Long,readTO:Long,writeTO:Long): OkHttpClient { override fun httpsClient(connTO:Long,readTO:Long,writeTO:Long): OkHttpClient {
var loggingInterceptor = HttpLoggingInterceptor( var loggingInterceptor = HttpLoggingInterceptor(
@ -79,8 +111,10 @@ class NetService private constructor() : INetService {
override fun log(message: String) { override fun log(message: String) {
Log.d("loggingInterceptor==>$message") Log.d("loggingInterceptor==>$message")
} }
} }
) )
loggingInterceptor.level = HttpLoggingInterceptor.Level.BODY loggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
var builder = OkHttpClient.Builder() var builder = OkHttpClient.Builder()
.retryOnConnectionFailure(true) .retryOnConnectionFailure(true)
@ -95,6 +129,7 @@ class NetService private constructor() : INetService {
.hostnameVerifier(getHostnameVerifier()!!) .hostnameVerifier(getHostnameVerifier()!!)
if (BuildConfig.DEBUG) { if (BuildConfig.DEBUG) {
builder.addInterceptor(loggingInterceptor) builder.addInterceptor(loggingInterceptor)
// builder.addInterceptor(ExceptionLoggingInterceptor())
} }
// builder.addInterceptor(loggingInterceptor) // builder.addInterceptor(loggingInterceptor)
return builder.build() return builder.build()
@ -161,6 +196,7 @@ class NetService private constructor() : INetService {
.addConverterFactory(ScalarsConverterFactory.create())//json解析器Gson .addConverterFactory(ScalarsConverterFactory.create())//json解析器Gson
.baseUrl(baseUrl) //设置BaseURL需以'/'结尾 .baseUrl(baseUrl) //设置BaseURL需以'/'结尾
.build() //创建Retrofit对象 .build() //创建Retrofit对象
return retrofit.create(HttpApi::class.java) return retrofit.create(HttpApi::class.java)
} }
fun createFreeBaseUrl2(): HttpApi { fun createFreeBaseUrl2(): HttpApi {

View File

@ -0,0 +1,112 @@
package com.yingmao.clash.view.dialog
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Context
import android.content.DialogInterface
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.tencent.mmkv.MMKV
import com.yingmao.clash.BuildConfig
import com.yingmao.clash.R
import com.yingmao.clash.conf.BZYConstants
/**
* @author tamsiree
* @date 2016/7/19
* 确认 取消 Dialog
*/
open class DialogAutoDoMainChoose : RxDialog {
lateinit var titleView: TextView
private set
lateinit var but_try: TextView
private set
lateinit var but_cancel: TextView
private set
lateinit var domainRecyclerView: RecyclerView
private set
lateinit var onTryListener: OnTryButtonListener
lateinit var onDomainListItemListener: OnListItemListener
var domainFininshSet = mutableSetOf<String>();
constructor(context: Context?, themeResId: Int) : super(context!!, themeResId) {
initView()
}
constructor(
context: Context?,
cancelable: Boolean,
cancelListener: DialogInterface.OnCancelListener?,
) : super(context!!, cancelable, cancelListener) {
initView()
}
constructor(context: Context?) : super(context!!) {
initView()
}
constructor(context: Activity?) : super(context!!) {
initView()
}
constructor(context: Context?, alpha: Float, gravity: Int) : super(context, alpha, gravity) {
initView()
}
private fun initView() {
val dialogView = LayoutInflater.from(context).inflate(R.layout.dialog_domain_choose, null)
titleView = dialogView.findViewById(R.id.tv_title)
but_try = dialogView.findViewById(R.id.but_try)
but_cancel = dialogView.findViewById(R.id.but_cancel)
domainRecyclerView = dialogView.findViewById(R.id.domian_list)
but_try.setOnClickListener {
onTryListener.tryButton()
}
but_cancel.setOnClickListener { onTryListener.cancel() }
setContentView(dialogView)
setCancelable(false)
window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
setNewData()
}
@SuppressLint("NotifyDataSetChanged")
private fun setNewData() {
var mmkv = MMKV.defaultMMKV()
val originalUrl =
mmkv.decodeString(BZYConstants.MMKV_KEY_CURRENT_HOST, BuildConfig.service_url)
var newUrlString = originalUrl
val routerHost = mmkv.decodeStringSet(BZYConstants.MMKV_KEY_ROUTER_HOST, HashSet())
}
fun setOnTryButtonListener(listener: OnTryButtonListener) {
this.onTryListener = listener
}
private fun changeSubUrl(originalUrl: String, newUrl: String): String {
return originalUrl.replace(Regex("https://[^/]+/"), "$newUrl/")
}
interface OnTryButtonListener {
fun tryButton()
fun cancel()
}
interface OnListItemListener {
fun listItemClick()
}
}

View File

@ -106,7 +106,6 @@ open class DialogDoMainChoose : RxDialog {
} }
domainListAdapter.notifyDataSetChanged() domainListAdapter.notifyDataSetChanged()
} }
} }
fun setOnTryButtonListener(listener: OnTryButtonListener) { fun setOnTryButtonListener(listener: OnTryButtonListener) {