Merge branch 'master' into yijianlian

# Conflicts:
#	app/src/main/java/com/yingmao/clash/act/ChangeNodeActivity.kt
#	app/src/main/java/com/yingmao/clash/act/LoginActivity.kt
#	app/src/main/java/com/yingmao/clash/act/MainActivity.kt
#	app/src/main/java/com/yingmao/clash/act/MemberRechargeActivity.kt
#	app/src/main/java/com/yingmao/clash/act/NewMainActivity.kt
#	app/src/main/java/com/yingmao/clash/act/SettingActivity.kt
#	app/src/main/java/com/yingmao/clash/act/SplashActivity.kt
#	app/src/main/java/com/yingmao/clash/app/MainApplication.kt
#	app/src/main/java/com/yingmao/clash/base/BaseActivity.kt
#	app/src/main/java/com/yingmao/clash/base/BaseViewMode.kt
#	app/src/main/java/com/yingmao/clash/fragment/NewAccFragment.kt
#	app/src/main/java/com/yingmao/clash/fragment/home/HomeFragment.kt
#	app/src/main/java/com/yingmao/clash/fragment/recharge/RechargeFragment.kt
#	app/src/main/java/com/yingmao/clash/fragment/setting/SettingFragment.kt
#	app/src/main/java/com/yingmao/clash/net/http/HttpApi.kt
#	app/src/main/java/com/yingmao/clash/service/ConnectionService.java
#	app/src/main/java/com/yingmao/clash/vm/MainAtyVM.kt
#	app/src/main/res/values-en/strings.xml
#	app/src/main/res/values-zh-rHK/strings.xml
#	app/src/main/res/values-zh-rTW/strings.xml
#	app/src/main/res/values/strings.xml
#	build.gradle.kts
#	gradle/wrapper/gradle-wrapper.properties
This commit is contained in:
level 2025-07-22 14:52:09 +08:00
commit d9d32a31a6
43 changed files with 1033 additions and 3536 deletions

View File

@ -101,10 +101,7 @@
android:name=".act.RegisterActivity"
android:process=":background"
android:screenOrientation="portrait" />
<activity
android:name=".act.NewMainActivity"
android:process=":background"
android:screenOrientation="portrait" />
<activity
android:name=".act.LoginActivity"
android:process=":background"
@ -113,9 +110,7 @@
android:name=".webview.BZYWebViewActivity"
android:process=":background"
android:screenOrientation="portrait" />
<activity
android:name=".act.MyPointCardActivity"
android:screenOrientation="portrait" />
<activity
android:name=".act.ShareActivity"
android:process=":background"
@ -123,9 +118,7 @@
<activity
android:name=".act.BandPhoneActivity"
android:screenOrientation="portrait" />
<activity
android:name=".act.ZFBTXActivity"
android:screenOrientation="portrait" />
<activity
android:name=".webview.BZYWebViewOther"
android:process=":background"

View File

@ -250,15 +250,16 @@ class ChangeNodeActivity : BaseActivity(), View.OnClickListener {
Log.d("ChangeNode22 uuid==>${profile.uuid}")
if (isDelete) {
Log.d("gogogogogo isDelete")
profileManager.delete(profile.uuid)
// profileManager.delete(profile.uuid)
MMKV.defaultMMKV()
.removeValueForKey(BZYConstants.NODE_All_ITEM_NAME_KEY)
setSub2(profileManager, nowSubUrl)
} else {
Log.d("gogogogogo update")
profileManager.setActive(profile)
delay(500)
Log.d("updateProfile pro.source==>${profile.source}, url==>${nowSubUrl}")
if (!TextUtils.isEmpty(profile.source)) {
if (profile.source != nowSubUrl) {
Log.d("updateProfile patch")
profileManager.patch(
@ -273,6 +274,17 @@ class ChangeNodeActivity : BaseActivity(), View.OnClickListener {
}
Log.d("updateProfile patch 2")
}
} else {
profileManager.patch(
profile.uuid,
profile.name,
nowSubUrl,
1800000L
)
Log.d("updateProfile patch new 1")
profileManager.commit(profile.uuid) { status ->
Log.d("updateProfile new updateStatus: " + Gson().toJson(status.args) + ", pro==>" + status.progress)
}
}
Log.d("updateProfile go update")
profileManager.update(profile.uuid)
@ -375,7 +387,6 @@ class ChangeNodeActivity : BaseActivity(), View.OnClickListener {
}
}
private fun setNewData(nodes: MutableList<ClashNodebBean>?) {
runOnUiThread {
if (nodes != null) {

View File

@ -31,13 +31,12 @@ import com.cncat.vpn.common.log.Log
import com.yingmao.clash.conf.BZYConstants
import com.yingmao.clash.conf.Conf
import com.yingmao.clash.entity.ClashNodebBean
import com.yingmao.clash.fragment.NewAccFragment
import com.yingmao.clash.fragment.NewAccFragment.Companion.CHECK_NODE_RESULT_CODE
import com.yingmao.clash.remote.Resource
import com.cncat.vpn.service.model.Profile
import com.cncat.vpn.service.remote.IProfileManager
import com.cncat.vpn.service.remote.IRemoteService
import com.yingmao.clash.app.MainApplication
import com.yingmao.clash.fragment.home.HomeFragment
import com.yingmao.clash.util.Utils
import com.yingmao.clash.view.RxToast
import com.yingmao.clash.view.adapter.NodeListAdapter
@ -92,7 +91,7 @@ class ChangeNodeTzActivity : BaseActivity(), View.OnClickListener {
override fun onBackPressed() {
intent.putExtra(BZYConstants.EXTRA_KEY_IS_RUNING, is_runing)
setResult(CHECK_NODE_RESULT_CODE, intent)
setResult(HomeFragment.CHECK_NODE_RESULT_CODE, intent)
super.onBackPressed()
}
@ -337,7 +336,7 @@ class ChangeNodeTzActivity : BaseActivity(), View.OnClickListener {
val reloadLock = Semaphore(10)
private fun setPing() {
if (!NewAccFragment.clashRunning) {
if (!HomeFragment.clashRunning) {
RxToast.info("请先开启加速后,再进行测试.");
return
}
@ -430,7 +429,7 @@ class ChangeNodeTzActivity : BaseActivity(), View.OnClickListener {
val intent = Intent()
intent.putExtra(BZYConstants.MMKV_KEY_SELECT_NODE_NAME, "自动选择")
intent.putExtra(BZYConstants.EXTRA_KEY_IS_RUNING, is_runing)
setResult(CHECK_NODE_RESULT_CODE, intent)
setResult(HomeFragment.CHECK_NODE_RESULT_CODE, intent)
finish()
}
}

View File

@ -1,147 +0,0 @@
package com.yingmao.clash.act
import android.os.Bundle
import android.text.Html
import android.widget.EditText
import android.widget.TextView
import androidx.lifecycle.ViewModelProvider
import com.yingmao.clash.view.dialog.RxDialogShapeLoading
import com.orhanobut.logger.Logger
import com.yingmao.clash.R
import com.yingmao.clash.base.BaseActivity
import com.yingmao.clash.net.http.HttpReqType
import com.yingmao.clash.net.http.HttpStatus
import com.yingmao.clash.net.http.entity.NormalMessageRsp
import com.yingmao.clash.net.http.entity.NormalRsp
import com.yingmao.clash.view.RxToast
class MyPointCardActivity : BaseActivity() {
private lateinit var pointCardIdEt:EditText
private lateinit var pointCardRechargeTv:TextView
private lateinit var pointCardBalanceTv:TextView
private lateinit var pointCardVM: MyPointCardVM
private lateinit var rxDialogShapeLoading: RxDialogShapeLoading
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.layout_activity_pointcard)
showTitleBar("我的点卡")
pointCardVM = ViewModelProvider(this).get(MyPointCardVM::class.java)
observe(this, pointCardVM)
initView()
pointCardVM.getPointCardSurplus()
}
private fun initView() {
pointCardIdEt = findViewById(R.id.et_point_card_id)
pointCardRechargeTv = findViewById(R.id.tv_point_card_recharge)
pointCardBalanceTv = findViewById(R.id.tv_point_card_balance)
pointCardRechargeTv.setOnClickListener {
rechargePointCard()
}
}
//点卡充值
private fun rechargePointCard() {
if(::pointCardIdEt.isInitialized) {
val pointCardId = pointCardIdEt.text.toString().trim()
if(pointCardId.isEmpty()){
RxToast.warning("请输入点卡卡号")
return
}
val regex = "^[a-z0-9A-Z]{16}$"
val matches = pointCardId.matches(Regex(regex))
if(!matches){
RxToast.warning("您输入点卡卡号有误,请查证后输入!")
return
}
pointCardVM.rechargePointCard(pointCardId)
}
}
override fun httpLoading(httpStatus: HttpStatus.Loading) {
if(httpStatus.reqType == HttpReqType.POINT_CARD_RECHARGE){
if(!::rxDialogShapeLoading.isInitialized) {
rxDialogShapeLoading = RxDialogShapeLoading(this)
}
rxDialogShapeLoading.setMessage("正在充值,请稍后...")
if(!rxDialogShapeLoading.isShowing){
rxDialogShapeLoading.show()
}
}else if(httpStatus.reqType == HttpReqType.GET_POINTCARD_SURPLUS){
if(!::rxDialogShapeLoading.isInitialized) {
rxDialogShapeLoading = RxDialogShapeLoading(this)
}
rxDialogShapeLoading.setMessage("正在查询点卡余额,请稍后...")
if(!rxDialogShapeLoading.isShowing){
rxDialogShapeLoading.show()
}
}
}
override fun httpSuccess(httpStatus: HttpStatus.Success) {
if(httpStatus.reqType == HttpReqType.POINT_CARD_RECHARGE){
if(::rxDialogShapeLoading.isInitialized) {
rxDialogShapeLoading.cancel()
}
if(httpStatus.rsp is NormalMessageRsp){
(httpStatus.rsp as NormalMessageRsp).let {
if(it.code == "1000"){
//TODO 充值成功
RxToast.info("点卡充值成功!")
pointCardIdEt.text.clear()
pointCardVM.getPointCardSurplus()
}else{
RxToast.error("${it.code}${it.message}")
return@httpSuccess
}
}
}
}else if(httpStatus.reqType == HttpReqType.GET_POINTCARD_SURPLUS){
if(::rxDialogShapeLoading.isInitialized) {
rxDialogShapeLoading.cancel()
}
if(httpStatus.rsp is NormalRsp){
(httpStatus.rsp as NormalRsp).let {
if(it.code == "1000"){
//TODO 查询余额成功,展示余额
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
pointCardBalanceTv.text = Html.fromHtml(
"剩余点数:${it.data}",
Html.FROM_HTML_MODE_LEGACY
)
}else{
pointCardBalanceTv.text = Html.fromHtml(
"剩余点数:${it.data}"
)
}
}else{
RxToast.error("${it.code}${it.message}")
return@httpSuccess
}
}
}
}
}
override fun httpFailure(httpStatus: HttpStatus.Failure) {
if(httpStatus.reqType == HttpReqType.POINT_CARD_RECHARGE) {
if (::rxDialogShapeLoading.isInitialized) {
rxDialogShapeLoading.cancel()
}
Logger.d("点卡充值异常:${httpStatus.msg}")
RxToast.error("点卡充值异常")
}else if(httpStatus.reqType == HttpReqType.GET_POINTCARD_SURPLUS) {
if (::rxDialogShapeLoading.isInitialized) {
rxDialogShapeLoading.cancel()
}
Logger.d("查询点卡余额异常:${httpStatus.msg}")
RxToast.error("查询点卡余额异常")
}
}
}

View File

@ -1,36 +0,0 @@
package com.yingmao.clash.act
import com.orhanobut.logger.Logger
import com.tencent.mmkv.MMKV
import com.yingmao.clash.base.BaseViewMode
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.NetService
class MyPointCardVM: BaseViewMode() {
fun rechargePointCard(cardCode:String){
val httpApi = NetService.instances.createHttps(HttpApi::class.java)
val username = MMKV.defaultMMKV().decodeString(BZYConstants.MMKV_KEY_REMEMBER_USERNAME,"")!!
val phToken = MMKV.defaultMMKV().decodeString(BZYConstants.MMKV_KEY_PH_TOKEN,"")!!
val params = hashMapOf(
"phoneNumber" to username,
"cardCode" to cardCode,
"phToken" to phToken
)
doAsync(HttpReqType.POINT_CARD_RECHARGE){httpApi.pointCardSurplusRecharge(params)}
}
fun getPointCardSurplus() {
val username = MMKV.defaultMMKV().decodeString(BZYConstants.MMKV_KEY_REMEMBER_USERNAME,"")!!
val phToken = MMKV.defaultMMKV().decodeString(BZYConstants.MMKV_KEY_PH_TOKEN,"")!!
Logger.d("获取点卡信息")
val params =hashMapOf(
"phoneNumber" to username,
"phToken" to phToken
)
val httpApi = NetService.instances.createHttps(HttpApi::class.java)
doAsync(HttpReqType.GET_POINTCARD_SURPLUS) { httpApi.getPointCardSurplus(params) }
}
}

View File

@ -496,34 +496,23 @@ class NewMainActivity : BaseActivity() {
val lastNotice =
MMKV.defaultMMKV().decodeString(BZYConstants.MMKV_KEY_NOTICE_READ, "")
val data = it.data
// if (data?.notice != null) {
// initMainPageHorseRaceLamp(data.notice)
// }
if (data?.notice != null && lastNotice != data.notice) {
val notice = data.notice
MaterialDialog(this).show {
this.cancelable(false)
cornerRadius(res = R.dimen.dp_10)
customView(
R.layout.dialog_notice_layout,
scrollable = false,
noVerticalPadding = true,
horizontalPadding = false
)
val noticeContent = getCustomView().findViewById<TextView>(R.id.tv_notice_content)
noticeContent.text=notice
noticeContent.movementMethod = LinkMovementMethod.getInstance()
val noticeUrl = getCustomView().findViewById<TextView>(R.id.tv_notice_url)
noticeUrl.text= context.resources.getString(R.string.ClientDownload)+"\n${data.downloadUrl}"
getCustomView().findViewById<TextView>(R.id.tv_sure).setOnClickListener {
val notice = data.notice!!
val dialog = DialogNotice(this@NewMainActivity, notice, data.downloadUrl)
dialog.setOnReadClickListener(object : DialogNotice.OnReadClickListener {
override fun onReadClick() {
MMKV.defaultMMKV()
.encode(BZYConstants.MMKV_KEY_NOTICE_READ, data.notice)
.encode(BZYConstants.MMKV_KEY_NOTICE_READ, data.notice!!)
MMKV.defaultMMKV().encode(
BZYConstants.MMKV_KEY_NOTICE_DOWNLOAD_READ, data.downloadUrl
)
dismiss()
dialog.dismiss()
}
}
})
dialog.show()
}
}
}
@ -548,21 +537,14 @@ class NewMainActivity : BaseActivity() {
mmkv.encode(BZYConstants.MMKV_KEY_VPN_EXPIRED, expired ?: true)
mmkv.encode(BZYConstants.MMKV_KEY_VPN_VALID_PERIOD, nowExpireTime)
setExpireDate()
fragments.filterIsInstance<NewAccFragment>().forEach {
it.setExpireDate()
it.changeUserState()
}
fragments.filterIsInstance<NewAccFragment>().forEach { it.setExpireDate() }
checkPayResultDia?.dismiss()
mainAtyVM.getAirNodes()
RxToast.info(resources.getString(R.string.SuccessfullyUpdatedUserInformation))
mainAtyVM.getExpensesRecord(mPageInfo)
} else {
// RxToast.info(resources.getString(R.string.PaymentResultsStr))
}
} else if(checkLoginRsp.code == -2){
RxToast.info(resources.getString(R.string.LoginOut))
cleanUserInfoToLogout()
}else{
} else {
RxToast.info(resources.getString(R.string.QueryFailed))
}
payResultLoading?.dismiss()
@ -590,25 +572,26 @@ class NewMainActivity : BaseActivity() {
} else {
RxToast.error(resources.getString(R.string.NodeAcquisitionFailed) + "${data.msg}")
}
data.data.let { url ->
data.data?.let { url ->
android.util.Log.e("Data", "机场URL=${url}")
// var url2="https://api.ymvpnpro11.cc:8700/7air2vr7ikiqzgvxz/api/v1/client/subscribe?token=5af82930d222840cbc2dec572b21d6c6"
if (url.isNotEmpty()) {
//todo 判断订阅地址是否修改过,未修改就用接口返回的地址反之使用本地修改的地址
val proSubUrl =
var pro_sub_url =
MMKV.defaultMMKV().decodeString(BZYConstants.PROFILE_SUB_URL, "")
if (!TextUtils.isEmpty(proSubUrl)){
if (proSubUrl == url) {
if (!TextUtils.isEmpty(pro_sub_url)) {
if (pro_sub_url == url) {
subUrl = url
} else {
subUrl = proSubUrl
subUrl = pro_sub_url
}
}else{
} else {
subUrl = url
}
lifecycleScope.launch(Dispatchers.IO) {
// setSub(url)
subUrl?.let { setSub2(it) }
}
}
@ -631,149 +614,31 @@ class NewMainActivity : BaseActivity() {
} else if (httpStatus.reqType == HttpReqType.GetActivity) {
val data = httpStatus.rsp as GetActivityRsp
if (data.code == 200) {
if (data.data.data.list.isNotEmpty()) {
if(data.data.data.list.size>1){
val bannerImgBeans=mutableListOf<BannerImgBean>()
data.data.data.list.forEach {item->
bannerImgBeans.add(BannerImgBean(item.title,item.content,item.redirectLink,0))
}
homeActivityDialog = HomeActivityDialog(this,bannerImgBeans)
homeActivityDialog!!.show()
homeActivityDialog?.setOnDialogClickListener(object : HomeActivityDialog.OnHADialogClickListener {
override fun onRechargeClickListener() {
//跳转到支付界面
goBackupRechargeWeb()
}
if (data.data != null) {
if (data.data.data != null) {
if (data.data.data.list.isNotEmpty()) {
data.data.data.list[0].redirectLink.let {
if(activityDialog==null){
activityDialog =
ActivityViewDialog(this, data.data.data.list[0].redirectLink)
activityDialog?.setOnDialogClickListener(object :
OnDialogClickListener {
override fun onRechargeClickListener() {
//跳转到支付界面
goBackupRechargeWeb()
}
override fun onCancelClickListener() {
homeActivityDialog?.dismiss()
}
override fun onClickImgType(position: Int) {
android.util.Log.i("Postion", "position::$position")
if(bannerImgBeans.get(position).redirectLink.contains("https:")){
val uri = Uri.parse(bannerImgBeans.get(position).redirectLink)
val intent = Intent(Intent.ACTION_VIEW)
intent.setData(uri)
startActivity(intent)
}else if(bannerImgBeans.get(position).redirectLink.contains("firstCharge")){
goBackupRechargeWeb()
}else if(bannerImgBeans.get(position).redirectLink.contains("BindAccount")){
var useraccount =
MMKV.defaultMMKV().decodeString(BZYConstants.MMKV_KEY_REMEMBER_USERNAME, "")!!
if (Utils.isEmail(useraccount) || Utils.isPhoneNumber(useraccount)) {
RxToast.info(resources.getString(R.string.bindingAgi))
return
}
val dialog = DialogBandChoose(this@NewMainActivity)
dialog.setOnBandClickListener(object : DialogBandChoose.OnBandClickListener {
override fun onBandClick(isPhone: Boolean) {
val intent = Intent(CommonUtils.getApp(), BandPhoneActivity::class.java)
intent.putExtra("isPhone", isPhone)
startActivity(intent)
override fun onCancelClickListener() {
activityDialog?.dismiss()
}
})
dialog.show()
}else if(bannerImgBeans.get(position).redirectLink.contains("SignIn")){
mainAtyVM.vipSign()
activityDialog?.show()
}
}
})
}else{
data.data.data.list[0].redirectLink.let {
var bannerImgBeans=mutableListOf<BannerImgBean>()
bannerImgBeans.add(BannerImgBean(data.data.data.list[0].title,data.data.data.list[0].content,data.data.data.list[0].redirectLink,0))
homeActivityDialog =
HomeActivityDialog(this,bannerImgBeans)
homeActivityDialog!!.show()
homeActivityDialog?.setOnDialogClickListener(object :
HomeActivityDialog.OnHADialogClickListener {
override fun onRechargeClickListener() {
//跳转到支付界面
goBackupRechargeWeb()
}
override fun onCancelClickListener() {
homeActivityDialog?.dismiss()
}
override fun onClickImgType(position: Int) {
android.util.Log.i("Postion", "position::$position")
if(bannerImgBeans.get(position).redirectLink.contains("https:")){
var uri = Uri.parse(bannerImgBeans.get(position).redirectLink)
val intent = Intent(Intent.ACTION_VIEW)
intent.setData(uri)
startActivity(intent)
}else if(bannerImgBeans.get(position).redirectLink.contains("firstCharge")){
goBackupRechargeWeb()
}else if(bannerImgBeans.get(position).redirectLink.contains("BindAccount")){
var useraccount =
MMKV.defaultMMKV().decodeString(BZYConstants.MMKV_KEY_REMEMBER_USERNAME, "")!!
if (Utils.isEmail(useraccount) || Utils.isPhoneNumber(useraccount)) {
RxToast.info(resources.getString(R.string.bindingAgi))
return
}
val dialog = DialogBandChoose(this@NewMainActivity)
dialog.setOnBandClickListener(object : DialogBandChoose.OnBandClickListener {
override fun onBandClick(isPhone: Boolean) {
val intent = Intent(CommonUtils.getApp(), BandPhoneActivity::class.java)
intent.putExtra("isPhone", isPhone)
startActivity(intent)
}
})
dialog.show()
}else if(bannerImgBeans.get(position).redirectLink.contains("SignIn")){
mainAtyVM.vipSign()
}
}
})
}
}
}
}
}else if(httpStatus.reqType == HttpReqType.SelectNode){
(httpStatus.rsp as ShowNode).let {
if (it.code == 200) {
val countryFlag = it.data?.countryFlag
if(countryFlag == true){
findViewById<ConstraintLayout>(R.id.clLocation).visibility=View.VISIBLE
}
}
}
}else if(httpStatus.reqType == HttpReqType.EXPENSES_RECORD){
if(httpStatus.rsp is ExpensesRecordRsp){
httpStatus.rsp.let { expensesRecordRsp ->
if(expensesRecordRsp.code == "1000"){
if(expensesRecordRsp.data != null) {
val expensesRecordList = expensesRecordRsp.data.expensesRecords
if(expensesRecordList!=null){
if(expensesRecordList.size==1){
val orderID = expensesRecordList[0].id
val report = expensesRecordList[0].reportFlag
android.util.Log.i("UPLoad:","data:::"+report+"orderId:"+orderID)
if(report == null){
//调用上报日志 记录数据
if (orderID != null) {
mainAtyVM.getReport(orderID)
}
}
}
}
}
}
}
}
}else if(httpStatus.reqType == HttpReqType.Report){
(httpStatus.rsp as AppVersionCheckRsp).let {
if(it.code==200){
//上报百度支付功能
android.util.Log.i("reqType:","data:::"+it.code)
BaiduAction.logAction(ActionType.PURCHASE)
}
}
}
}
@ -812,34 +677,33 @@ class NewMainActivity : BaseActivity() {
// fragments.add(RechargeFragment())
containerVP.adapter = MainVPAdapter(fragments, supportFragmentManager, lifecycle)
containerVP.isUserInputEnabled = false
clHomeRecharge=findViewById(R.id.clHomeRecharge)
clDeepSeek=findViewById(R.id.clDeepSeek)
floating=findViewById(R.id.floating)
tv_exit=findViewById(R.id.tv_exit)
clSing=findViewById(R.id.clSing)
clSing.setOnClickListener {
mainAtyVM.vipSign()
var aniAct = findViewById<LottieAnimationView>(R.id.lottie_activity)
aniAct.setAnimation(R.raw.activity)
aniAct.playAnimation()
aniAct.setOnClickListener {
startActivity(Intent(CommonUtils.getApp(), ActivityActivity::class.java))
}
clHomeRecharge.setOnClickListener {
goBackupRechargeWeb()
}
tv_exit.setOnClickListener {
cleanUserInfo()
finish()
}
clDeepSeek.setOnClickListener {
startActivity(Intent(this, CustomerServiceWebView::class.java))
drawer.close()
}
floating.setOnClickListener {
startActivity(Intent(this, CustomerServiceWebView::class.java))
findViewById<LinearLayout>(R.id.ll_activity).setOnClickListener {
startActivity(Intent(CommonUtils.getApp(), ActivityActivity::class.java))
}
setSometion()
}
private fun initMainPageHorseRaceLamp(noticeData: String) {
val mainPageHorseRaceLamp = findViewById<TextView>(R.id.mainPageHorseRaceLamp)
if (noticeData.isNotEmpty()) {
mainPageHorseRaceLamp.text = noticeData
mainPageHorseRaceLamp.requestFocus()
mainPageHorseRaceLamp.marqueeRepeatLimit = Integer.MAX_VALUE
mainPageHorseRaceLamp.isFocusable = true
mainPageHorseRaceLamp.ellipsize = TextUtils.TruncateAt.MARQUEE
mainPageHorseRaceLamp.setSingleLine()
mainPageHorseRaceLamp.isFocusableInTouchMode = true
mainPageHorseRaceLamp.setHorizontallyScrolling(true)
} else {
val llNoticeBackground = findViewById<LinearLayout>(R.id.llNoticeBackground)
llNoticeBackground.visibility = View.GONE
}
}
private fun cleanUserInfo() {
@ -849,9 +713,6 @@ class NewMainActivity : BaseActivity() {
MMKV.defaultMMKV().removeValueForKey(BZYConstants.NODE_SELECTED_NAME_KEY)
MMKV.defaultMMKV().removeValueForKey(BZYConstants.MMKV_KEY_VPN_UserCommand)
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)
isSubbed =false
Log.d("cleanUserInfo clashRunning==>${clashRunning}")
if (clashRunning) {
fragments.filterIsInstance<NewAccFragment>().forEach { it.stopClash() }
@ -862,28 +723,6 @@ class NewMainActivity : BaseActivity() {
startActivity(Intent(CommonUtils.getApp(), LoginActivity::class.java))
finish()
}
private fun cleanUserInfoToLogout() {
MMKV.defaultMMKV().removeValueForKey(BZYConstants.MMKV_KEY_PH_TOKEN)
MMKV.defaultMMKV().removeValueForKey(BZYConstants.MMKV_KEY_TOKEN)
MMKV.defaultMMKV().removeValueForKey(BZYConstants.MMKV_KEY_CURRENT_NODE)
MMKV.defaultMMKV().removeValueForKey(BZYConstants.NODE_SELECTED_NAME_KEY)
MMKV.defaultMMKV().removeValueForKey(BZYConstants.MMKV_KEY_VPN_UserCommand)
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)
isSubbed =false
Log.d("cleanUserInfo clashRunning==>${clashRunning}")
if (clashRunning) {
fragments.filterIsInstance<NewAccFragment>().forEach { it.stopClash() }
}
stopUploadTimer()
mainAtyVM.stopHeartBeat()
cleanProfile()
val intent=Intent(CommonUtils.getApp(), LoginActivity::class.java)
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(intent)
finish()
}
private fun cleanProfile() {
@ -906,11 +745,25 @@ class NewMainActivity : BaseActivity() {
tv_username = findViewById(R.id.tv_username)
iv_slider = findViewById(R.id.iv_slider)
ll_slider = findViewById(R.id.ll_slider)
tv_exit = findViewById(R.id.tv_exit)
tv_band = findViewById(R.id.tv_band)
// containerVP.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
// override fun onPageSelected(position: Int) {
// super.onPageSelected(position)
// if (position == 1 || position == 2) {
// tv_rechar.visibility = View.GONE
// } else {
// tv_rechar.visibility = View.VISIBLE
// }
// }
// })
findViewById<TextView>(R.id.tv_version).text =
resources.getString(R.string.edition) + "${BuildConfig.VERSION_NAME}." + BuildConfig.productId
if (MMKV.defaultMMKV().decodeBool(BZYConstants.MMKV_KEY_IS_PERFECT)) {
// 临时用户显示默认密码
// findViewById<TextView>(R.id.tv_default_psw).visibility = View.VISIBLE
tv_band.setOnClickListener {//绑定
showBingDialogChoose()
drawer.close()
@ -924,37 +777,35 @@ class NewMainActivity : BaseActivity() {
MMKV.defaultMMKV().decodeString(BZYConstants.MMKV_KEY_REMEMBER_USERNAME)
}"
}
findViewById<ConstraintLayout>(R.id.clRecharge).setOnClickListener {//充值
tv_exit.setOnClickListener {//退出
cleanUserInfo()
finish()
}
findViewById<TextView>(R.id.tv_viprecharge).setOnClickListener {//充值
// tv_rechar.visibility = View.GONE
// iv_slider.setImageResource(R.drawable.ic_s_back)
drawer.close()
goBackupRechargeWeb()
}
findViewById<TextView>(R.id.tv_vip_recharge).setOnClickListener {
drawer.close()
goBackupRechargeWeb()
}
findViewById<ConstraintLayout>(R.id.clNotice).setOnClickListener {//通知
findViewById<TextView>(R.id.tv_notice).setOnClickListener {//通知
val notice =
MMKV.defaultMMKV().decodeString(BZYConstants.MMKV_KEY_NOTICE_READ, "暂无新通知")!!
val downloadUrl =
val download_url =
MMKV.defaultMMKV().decodeString(BZYConstants.MMKV_KEY_NOTICE_DOWNLOAD_READ, "")!!
MaterialDialog(this).show {
this.cancelable(false)
cornerRadius(res = R.dimen.dp_10)
customView(
R.layout.dialog_notice_layout,
scrollable = false,
noVerticalPadding = true,
horizontalPadding = false
)
val noticeContent = getCustomView().findViewById<TextView>(R.id.tv_notice_content)
noticeContent.text=notice
noticeContent.movementMethod = LinkMovementMethod.getInstance()
val noticeUrl = getCustomView().findViewById<TextView>(R.id.tv_notice_url)
noticeUrl.text= context.resources.getString(R.string.ClientDownload)+"\n${downloadUrl}"
getCustomView().findViewById<TextView>(R.id.tv_sure).setOnClickListener {
dismiss()
val dialog = DialogNotice(this@NewMainActivity, notice, download_url)
dialog.setOnReadClickListener(object : DialogNotice.OnReadClickListener {
override fun onReadClick() {
dialog.dismiss()
}
}
})
dialog.show()
drawer.close()
}
@ -965,43 +816,68 @@ class NewMainActivity : BaseActivity() {
clipboardManager.setPrimaryClip(clipData)
RxToast.info(resources.getString(R.string.CopyToClipboard))
}
findViewById<ConstraintLayout>(R.id.clShare).setOnClickListener {//分享
// tv_rechar.setOnClickListener {
// goRechargeWeb()
// }
findViewById<TextView>(R.id.tv_share).setOnClickListener {//分享
startActivity(Intent(this@NewMainActivity, ShareActivity::class.java))
drawer.close()
}
findViewById<ConstraintLayout>(R.id.clSetting).setOnClickListener {//设置
findViewById<TextView>(R.id.tv_setting).setOnClickListener {//设置
startActivity(Intent(this, SettingActivity::class.java))
drawer.close()
}
findViewById<TextView>(R.id.tv_dianka).setOnClickListener {//点卡
startActivity(Intent(this, MyPointCardActivity::class.java))
drawer.close()
}
findViewById<TextView>(R.id.tv_vip_sign).setOnClickListener {//会员签到
mainAtyVM.vipSign()
// drawer.close()
}
findViewById<TextView>(R.id.tv_tixian).setOnClickListener {//提现
startActivity(Intent(this, ZFBTXActivity::class.java))
drawer.close()
}
val telegramBtn = findViewById<TextView>(R.id.tv_dianbao)
findViewById<ConstraintLayout>(R.id.clLocation).setOnClickListener {//变更
if (checkExpire()) {
RxToast.info(resources.getString(R.string.AccountExpired))
return@setOnClickListener
telegramBtn.setOnClickListener {//电报
var uri = Uri.parse(BZYConstants.DefTGUrl)
val intent = Intent(Intent.ACTION_VIEW)
when {
BuildConfig.is_ym -> {
uri = Uri.parse(BZYConstants.YMTGUrl)
}
BuildConfig.is_tz -> {
uri = Uri.parse(BZYConstants.TZTGUrl)
}
BuildConfig.is_yunti -> {
uri = Uri.parse(BZYConstants.YunTiTGUrl)
}
else -> {
uri = Uri.parse(BZYConstants.DefTGUrl)
}
}
val intent = Intent(CommonUtils.getApp(), ChangeNodeActivity::class.java)
if (!TextUtils.isEmpty(newUuid)) {
intent.putExtra(BZYConstants.MMKV_KEY_CURRENT_NODE, newUuid)
intent.putExtra(BZYConstants.EXTRA_KEY_IS_RUNING, clashRunning)
}
startActivityForResult(intent, CHECK_NODE_REQUEST_CODE)
intent.setData(uri)
startActivity(intent)
drawer.close()
}
findViewById<ConstraintLayout>(R.id.clActivity).setOnClickListener {//活动
startActivity(Intent(this, ActivityActivity::class.java))
drawer.close()
}
findViewById<ConstraintLayout>(R.id.clAccount).setOnClickListener {
startActivity(Intent(this, MineInfoActivity::class.java))
drawer.close()
}
findViewById<TextView>(R.id.tv_kefu).setOnClickListener {//客服
startActivity(Intent(this, BZYWebViewKeFu::class.java))
findViewById<ConstraintLayout>(R.id.clKf).setOnClickListener {//客服
startActivity(Intent(this, CustomerServiceWebView::class.java))
drawer.close()
}
var fds_view = findViewById<TextView>(R.id.tv_fds);
fds_view.setOnClickListener {//防丢失
startActivity(Intent(this, LossPreventionActivity::class.java))
drawer.close()
}
drawer = findViewById(R.id.drawer)
iv_slider.setOnClickListener {
if (containerVP.currentItem != 0) {
@ -1011,6 +887,10 @@ class NewMainActivity : BaseActivity() {
drawer.openDrawer(GravityCompat.START)
}
}
if (BuildConfig.is_tz && BuildConfig.productId == "182269") {
telegramBtn.visibility = View.INVISIBLE
fds_view.visibility = View.INVISIBLE
}
}
var checkPayResultDia: MaterialDialog? = null
@ -1032,7 +912,7 @@ class NewMainActivity : BaseActivity() {
public fun goBackupRechargeWeb() {
startActivityForResult(
Intent(this@NewMainActivity, MemberRechargeActivity::class.java),
Intent(this@NewMainActivity, BackupRechargeActivity::class.java),
REQUEST_CODE_PAY_WEB_VIEW_ACTIVITY
)
}
@ -1072,6 +952,7 @@ class NewMainActivity : BaseActivity() {
checkBindAccountDia = MaterialDialog(this).show {
this.cancelable(false)
cornerRadius(res = R.dimen.dp_10)
customView(
R.layout.bind_account_dialog_layout,
scrollable = false,
@ -1101,6 +982,9 @@ class NewMainActivity : BaseActivity() {
dialog.show()
}
var payResultLoading: RxDialogShapeLoading? = null
private var timerJob: Job? = null
private var loadingTimeoutNum = 3
private fun showLoadingDialog(msg: String) {
payResultLoading = RxDialogShapeLoading(this)
payResultLoading!!.setMessage(msg)
@ -1313,42 +1197,97 @@ class NewMainActivity : BaseActivity() {
}
startActivity(intent)
}
public suspend fun setSub2(url: String) {
Log.d("setSub2 2")
profileBinder?.let { pm ->
Log.d("setSub2 3")
pm.queryAll().forEach { itemPro ->
Log.d("setSub2 itemPro ==>${itemPro}")
if (itemPro.name != Conf.getClashConfName(this@NewMainActivity.applicationContext)) {
Log.d("setSub2 delete ==>${itemPro.name}")
pm.delete(itemPro.uuid)
}
}
val pro = pm.queryActive()
Log.d("setSub2 4")
pro?.let {
var actname = it.name
Log.d("new main actname==>" + actname)
Log.d("setSub2 pro 已存在 ==>")
pm.setActive(it)
delay(500)
if (!TextUtils.isEmpty(pro.source)) {
// Log.d("setSub2 pro.source==>${pro.source}, url==>${url}")
if (pro.source != url) {
Log.d("setSub2 patch")
pm.patch(pro.uuid, pro.name, url, 1800000L)
pm.commit(pro.uuid) { status ->
Log.d("setSub2 updateStatus: " + Gson().toJson(status.args) + ", pro==>" + status.progress)
}
}
}
pm.update(it.uuid)
newUuid = pro.uuid.toString();
isSubed = true
Log.d("setSub2 setActive end")
} ?: run {
createMyProfile(pm, url)
}
}
}
suspend fun createMyProfile(pm: IProfileManager, url: String) {
var uuid: UUID = pm.create(
Profile.Type.Url, Conf.getClashConfName(this@NewMainActivity.applicationContext)
)
delay(500)
pm.queryByUUID(uuid)?.let { qPro ->
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
)
}
newUuid = qPro.uuid.toString();
isSubed = true
// }
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_CODE_PAY_WEB_VIEW_ACTIVITY) {
if (activityDialog != null) {
activityDialog?.dismiss()
}
goCheckLogin()
showBindAccount()
}else if(requestCode == CHECK_NODE_REQUEST_CODE){
fragments.filterIsInstance<NewAccFragment>().forEach { it.nodeChooseBase(data) }
}
}
override fun onDestroy() {
super.onDestroy()
stopUploadTimer()
mainAtyVM.stopHeartBeat()
if(dialogDoMainChoose!=null){
if(dialogDoMainChoose!!.isShowing){
dialogDoMainChoose!!.dismiss()
dialogDoMainChoose=null
if (activityDialog != null) {
if(activityDialog!!.isShowing){
activityDialog!!.dismiss()
}
}
super.onDestroy()
stopUploadTimer()
mainAtyVM.stopHeartBeat()
}
class PageInfo {
var page = 1
fun nextPage() {
page++
}
fun reset() {
page = 1
}
val isFirstPage: Boolean
get() = page == 1
}
}

View File

@ -14,8 +14,8 @@ import com.yingmao.clash.R
import com.yingmao.clash.base.BaseActivity
import com.cncat.vpn.common.log.Log
import com.yingmao.clash.conf.BZYConstants
import com.yingmao.clash.fragment.NewAccFragment
import com.cncat.vpn.service.model.AccessControlMode
import com.yingmao.clash.fragment.home.HomeFragment
import com.yingmao.clash.view.RxToast
import com.yingmao.clash.view.popwindow.CommonPopupWindow
import okio.ByteString.Companion.encode
@ -120,7 +120,7 @@ class ProxyModeActivity : BaseActivity() {
}
}
llApplicationMode.setOnClickListener {
if (NewAccFragment.clashRunning) {
if (HomeFragment.clashRunning) {
RxToast.info("请关闭加速后再选择.");
return@setOnClickListener
}
@ -207,7 +207,7 @@ class ProxyModeActivity : BaseActivity() {
when (proxyMode) {
ProxyMode.PAC -> {
NewAccFragment.isGlo = false
HomeFragment.isGlo = false
radio_but_global.isChecked = false
radio_but_pac.isChecked = true
radioApplicationMode.isChecked = false
@ -218,7 +218,7 @@ class ProxyModeActivity : BaseActivity() {
}
ProxyMode.GLOBAL -> {
NewAccFragment.isGlo = true
HomeFragment.isGlo = true
radio_but_global.isChecked = true
radio_but_pac.isChecked = false
radioApplicationMode.isChecked = false

View File

@ -21,9 +21,9 @@ class SplashHelper(private val splashAtyVM: PublicViewMode) {
third: (AsyncCallback) -> 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) {
third(createCallback("getHost method success", "getHost methods failed", callback) {
third(createCallback("getHostByGithub method success", "getHostByGithub methods failed", callback) {
callback(false) // 所有方法都失败
})
})

View File

@ -54,7 +54,7 @@ import com.yingmao.clash.R
import com.yingmao.clash.app.MainApplication
import com.yingmao.clash.app.MainApplication.Companion.OAID
import com.yingmao.clash.base.BaseActivity
import com.yingmao.clash.captcha.Captcha
import com.yingmao.clash.conf.BZYConstants
import com.yingmao.clash.conf.BZYConstants.Companion.MMKV_KEY_IS_REGISTER
import com.yingmao.clash.conf.BZYConstants.Companion.MMKV_KEY_NOTE_DEFAULT_PSW
@ -79,9 +79,7 @@ import com.yingmao.clash.view.dialog.DialogDoMainChoose
import com.yingmao.clash.view.dialog.RxDialogSureCancel
import com.yingmao.clash.view.dialog.SplashDialog
import com.yingmao.clash.vm.SplashAtyVM
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.async
import kotlinx.coroutines.delay
@ -434,56 +432,7 @@ class SplashActivity : BaseActivity() {
super.onStarted()
}
private fun graphicVerificationDialog() {
//开启人机验证
MaterialDialog(this).show {
this.cancelable(false)
cornerRadius(res = R.dimen.dp_10)
customView(
R.layout.graphic_verification_dialog_layout,
scrollable = false,
noVerticalPadding = true,
horizontalPadding = false
)
val captchaSimple = findViewById<Captcha>(R.id.captCha)
captchaSimple.setCaptchaListener(object : Captcha.CaptchaListener {
override fun onAccess(time: Long): String {
Toast.makeText(
this@SplashActivity,
resources.getString(R.string.VerificationSuccessful),
Toast.LENGTH_SHORT
).show()
dismiss()
val intent = Intent(this@SplashActivity, LoginActivity::class.java)
startActivity(intent)
finish()
return resources.getString(R.string.VerificationSuccessful)
.replace("%d", "$time")
}
override fun onFailed(failedCount: Int): String {
Toast.makeText(
this@SplashActivity,
resources.getString(R.string.VerificationFailed),
Toast.LENGTH_SHORT
).show()
return resources.getString(R.string.VerificationSuccessful)
.replace("%d", "$failedCount")
}
override fun onMaxFailed(): String {
Toast.makeText(
this@SplashActivity,
resources.getString(R.string.VerifiedOnce),
Toast.LENGTH_SHORT
)
.show()
finish()
return resources.getString(R.string.VerifiedOnce)
}
})
}
}
private fun isNeedRegister(isNeedRegister: Boolean) {
if (!BuildConfig.is_free && BuildConfig.productId.isNotEmpty()) { //渠道包都自动注册登录广州22222也是非自动注册
@ -677,6 +626,7 @@ class SplashActivity : BaseActivity() {
val expired = checkLoginRsp.data?.expired
val isPerfect = checkLoginRsp.data?.isPerfect
val userCommand = checkLoginRsp.data?.userCommand
val logSwitch = checkLoginRsp.data?.logSwitch
val mmkv = MMKV.defaultMMKV()
mmkv.encode(BZYConstants.MMKV_KEY_VPN_EXPIRED, expired ?: true)
mmkv.encode(
@ -685,11 +635,8 @@ class SplashActivity : BaseActivity() {
)
mmkv.encode(BZYConstants.MMKV_KEY_IS_PERFECT, isPerfect ?: true)
mmkv.encode(BZYConstants.MMKV_KEY_VPN_UserCommand, userCommand ?: "")
mmkv.encode(BZYConstants.MMKV_KEY_VPN_LogSwitch, logSwitch ?: "")
toMainPage()
} else {
toLoginPage()
}

View File

@ -47,6 +47,7 @@ import com.yingmao.clash.net.http.HttpReqType
import com.yingmao.clash.net.http.HttpStatus
import com.yingmao.clash.net.http.NetService
import com.yingmao.clash.net.http.entity.AppVersionCheckRsp
import com.yingmao.clash.net.http.entity.ExpensesRecordRsp
import com.yingmao.clash.net.http.entity.GetActivityRsp
import com.yingmao.clash.net.http.entity.PhDownloadUrlRsp
import com.yingmao.clash.net.http.entity.SubscriptionRsp
@ -83,7 +84,7 @@ import java.util.UUID
class UIMainActivity : BaseActivity() {
private lateinit var bottomNavigationView: BottomNavigationView
private val fragments = arrayListOf<Fragment>()
val fragments = arrayListOf<Fragment>()
private lateinit var mainAtyVM: MainAtyVM
private var payResultLoading: RxDialogShapeLoading? = null
private val clashRunning: Boolean
@ -92,19 +93,20 @@ class UIMainActivity : BaseActivity() {
private var timer: Timer? = null
private var uTimerTask: TimerTask? = null
private var newMainActGlo: Boolean = false
private var homeFragment:HomeFragment?=null
private var settingFragment:SettingFragment?=null
private var homeFragment: HomeFragment? = null
private var settingFragment: SettingFragment? = null
@SuppressLint("SimpleDateFormat")
private val dateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
private var activityDialog: ActivityViewDialog? = null
private var checkPayResultDia: MaterialDialog? = null
private var checkBindAccountDia: MaterialDialog? = null
private val mPageInfo = PageInfo()
companion object {
//记录心跳失败次数
var HEART_BEAT_FAILURE_FREQUENCY = 0
const val REQUEST_CODE_PAY_WEB_VIEW_ACTIVITY = 1000
var isSubbed = false
var isSubed = false
var newUuid: String? = null
var subUrl: String? = null
var clashBinder: IClashManager? = null
@ -112,6 +114,71 @@ class UIMainActivity : BaseActivity() {
var isUpload: Boolean = false
var hostsMem: ArrayList<String> = ArrayList()
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()
isSubed = 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()
isSubed = true
}
}
}
}
var isVip:Boolean=false
}
override fun onCreate(savedInstanceState: Bundle?) {
@ -130,6 +197,7 @@ class UIMainActivity : BaseActivity() {
newMainActGlo = isGlobal
startLog()
firstEntry()
mainAtyVM.getExpensesRecord(mPageInfo)
}
private fun initView(savedInstanceState: Bundle?) {
@ -147,6 +215,7 @@ class UIMainActivity : BaseActivity() {
if (homeFragment == null) homeFragment = HomeFragment()
showFragment(homeFragment!!)
}
R.id.navigation_setting -> {
if (settingFragment == null) settingFragment = SettingFragment()
showFragment(settingFragment!!)
@ -162,6 +231,7 @@ class UIMainActivity : BaseActivity() {
transaction.replace(R.id.fragmentContainer, fragment)
transaction.commit()
}
private fun showFragment(fragment: Fragment) {
val fragmentManager = supportFragmentManager
val transaction = fragmentManager.beginTransaction()
@ -178,6 +248,7 @@ class UIMainActivity : BaseActivity() {
}
transaction.commit()
}
override fun httpLoading(httpStatus: HttpStatus.Loading) {
if (httpStatus.reqType == HttpReqType.HEARTBEAT) {
Logger.d("心跳中")
@ -195,6 +266,7 @@ class UIMainActivity : BaseActivity() {
}
data.data.let { 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()) {
// subUrl = url
// lifecycleScope.launch(Dispatchers.IO) {
@ -206,13 +278,13 @@ class UIMainActivity : BaseActivity() {
var pro_sub_url =
MMKV.defaultMMKV().decodeString(BZYConstants.PROFILE_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) {
subUrl = url
} else {
subUrl = pro_sub_url
}
}else{
} else {
subUrl = url
}
lifecycleScope.launch(Dispatchers.IO) {
@ -229,7 +301,7 @@ class UIMainActivity : BaseActivity() {
if (data.code == 200) {
if (data.data.data.list.isNotEmpty()) {
data.data.data.list[0].redirectLink.let {
if(activityDialog==null){
if (activityDialog == null) {
activityDialog =
ActivityViewDialog(this, data.data.data.list[0].redirectLink)
activityDialog?.setOnDialogClickListener(object :
@ -262,8 +334,10 @@ class UIMainActivity : BaseActivity() {
val expired = checkLoginRsp.data.expired
mmkv.encode(BZYConstants.MMKV_KEY_VPN_EXPIRED, expired ?: true)
mmkv.encode(BZYConstants.MMKV_KEY_VPN_VALID_PERIOD, nowExpireTime)
val hf = supportFragmentManager.findFragmentById(R.id.fragmentContainer) as? HomeFragment
val sf = supportFragmentManager.findFragmentById(R.id.fragmentContainer) as? SettingFragment
val hf =
supportFragmentManager.findFragmentById(R.id.fragmentContainer) as? HomeFragment
val sf =
supportFragmentManager.findFragmentById(R.id.fragmentContainer) as? SettingFragment
hf?.setExpireDate()
sf?.setExpireDate()
checkPayResultDia?.dismiss()
@ -272,7 +346,10 @@ class UIMainActivity : BaseActivity() {
} else {
// RxToast.info(resources.getString(R.string.PaymentResultsStr))
}
} else {
}else if(checkLoginRsp.code == -2){
RxToast.info(resources.getString(R.string.LoginOut))
cleanUserInfoToLogout()
}else {
RxToast.info(resources.getString(R.string.QueryFailed))
}
payResultLoading?.dismiss()
@ -304,7 +381,30 @@ class UIMainActivity : BaseActivity() {
}
}
}
HttpReqType.EXPENSES_RECORD ->{
if(httpStatus.rsp is ExpensesRecordRsp){
httpStatus.rsp.let { expensesRecordRsp ->
if(expensesRecordRsp.code == "1000"){
if(expensesRecordRsp.data != null) {
val expensesRecordList = expensesRecordRsp.data.expensesRecords
if(expensesRecordList!=null){
if(expensesRecordList.size>0){
isVip=true
}else{
isVip=false
}
}else{
isVip=false
}
}else{
isVip=false
}
}else{
isVip=false
}
}
}
}
else -> {
}
@ -342,6 +442,7 @@ class UIMainActivity : BaseActivity() {
}
}
}
private fun showLoadingDialog(msg: String) {
payResultLoading = RxDialogShapeLoading(this)
payResultLoading!!.setMessage(msg)
@ -355,10 +456,15 @@ class UIMainActivity : BaseActivity() {
MMKV.defaultMMKV().removeValueForKey(BZYConstants.NODE_SELECTED_NAME_KEY)
MMKV.defaultMMKV().removeValueForKey(BZYConstants.MMKV_KEY_VPN_UserCommand)
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}")
//清除源切换记录
if (clashRunning) {
fragments.filterIsInstance<HomeFragment>().forEach { it.stopClash() }
}
isSubed=false
stopUploadTimer()
mainAtyVM.stopHeartBeat()
cleanProfile()
@ -367,6 +473,7 @@ class UIMainActivity : BaseActivity() {
}
fun startUploadTimer() {
stopUploadTimer()
if (timer == null) {
timer = Timer()
uTimerTask = object : TimerTask() {
@ -400,61 +507,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() {
@ -558,7 +611,7 @@ class UIMainActivity : BaseActivity() {
override fun onDestroy() {
if (activityDialog != null) {
if(activityDialog!!.isShowing){
if (activityDialog!!.isShowing) {
activityDialog!!.dismiss()
}
@ -681,4 +734,38 @@ class UIMainActivity : BaseActivity() {
REQUEST_CODE_PAY_WEB_VIEW_ACTIVITY
)
}
private fun cleanUserInfoToLogout() {
MMKV.defaultMMKV().removeValueForKey(BZYConstants.MMKV_KEY_PH_TOKEN)
MMKV.defaultMMKV().removeValueForKey(BZYConstants.MMKV_KEY_TOKEN)
MMKV.defaultMMKV().removeValueForKey(BZYConstants.MMKV_KEY_CURRENT_NODE)
MMKV.defaultMMKV().removeValueForKey(BZYConstants.NODE_SELECTED_NAME_KEY)
MMKV.defaultMMKV().removeValueForKey(BZYConstants.MMKV_KEY_VPN_UserCommand)
MMKV.defaultMMKV().removeValueForKey(BZYConstants.NODE_All_ITEM_NAME_KEY)
MMKV.defaultMMKV().removeValueForKey(BZYConstants.PROFILE_SUB_URL)
Log.d("cleanUserInfo clashRunning==>${clashRunning}")
if (clashRunning) {
fragments.filterIsInstance<HomeFragment>().forEach { it.stopClash() }
}
isSubed=false
stopUploadTimer()
mainAtyVM.stopHeartBeat()
cleanProfile()
val intent=Intent(CommonUtils.getApp(), LoginActivity::class.java)
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(intent)
finish()
}
class PageInfo {
var page = 1
fun nextPage() {
page++
}
fun reset() {
page = 1
}
val isFirstPage: Boolean
get() = page == 1
}
}

View File

@ -1,143 +0,0 @@
package com.yingmao.clash.act
import android.os.Bundle
import android.view.View
import android.widget.EditText
import android.widget.TextView
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.observe
import com.yingmao.clash.view.dialog.RxDialogShapeLoading
import com.tencent.mmkv.MMKV
import com.yingmao.clash.R
import com.yingmao.clash.base.BaseActivity
import com.yingmao.clash.conf.BZYConstants
import com.yingmao.clash.net.http.HttpReqType
import com.yingmao.clash.net.http.HttpStatus
import com.yingmao.clash.net.http.entity.AppDataInfoRsp
import com.yingmao.clash.net.http.entity.GetSMSCodeRsp
import com.yingmao.clash.view.RxToast
class ZFBTXActivity : BaseActivity() {
private lateinit var et_zfb_account: EditText
private lateinit var et_tixian_amount: EditText
private lateinit var tv_can_reward_amount: TextView
private var rewardAmount: String = ""
private lateinit var txAmount: String
private var isTiXia = false
private var zfbAccount = ""
private lateinit var rxDialogShapeLoading: RxDialogShapeLoading
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.layout_activity_zfbtx)
showTitleBar("现金奖励提现")
initView()
}
private fun initView() {
val viewModel = ViewModelProvider(this).get(ZFBTXAtyVM::class.java)
viewModel.statusData.observe(this) {
when (it) {
is HttpStatus.Loading -> {
rxDialogShapeLoading = RxDialogShapeLoading(this)
if (it.reqType == HttpReqType.APPDATAINFO) {
rxDialogShapeLoading.setMessage("正在获取可提现余额...")
} else {
rxDialogShapeLoading.setMessage("正在提现...")
}
rxDialogShapeLoading.show()
}
is HttpStatus.Success -> {
rxDialogShapeLoading.cancel()
if (it.reqType == HttpReqType.APPDATAINFO) {
(it.rsp as AppDataInfoRsp).let { appDataRsp ->
if (appDataRsp.code == 1000) {
rewardAmount = appDataRsp.data?.rewardTotalAmount!!
tv_can_reward_amount.text = "${rewardAmount}"
tv_can_reward_amount.isClickable = false
if (isTiXia) {
val phone = MMKV.defaultMMKV().decodeString(BZYConstants.MMKV_KEY_REMEMBER_USERNAME,"")!!
viewModel.appRewardAmount(phone, txAmount, zfbAccount)
}
} else {
RxToast.warning("获取可提现金额失败,请重试")
isTiXia = false
tv_can_reward_amount.text = "重试"
tv_can_reward_amount.setOnClickListener {
MMKV.defaultMMKV().decodeString(BZYConstants.MMKV_KEY_REMEMBER_USERNAME)
?.let { it1 -> viewModel.appDataInfo(it1) }
}
}
}
} else {
(it.rsp as GetSMSCodeRsp).let { rsp ->
if (rsp.code == 1000) {
RxToast.success("提现发起成功,请等待到账")
setResult(101)
finish()
// viewModel.appDataInfo(MMKV.defaultMMKV().decodeString(BZYConstants.MMKV_KEY_REMEMBER_USERNAME))
} else {
RxToast.warning("提现失败:${rsp.message}")
}
}
}
}
is HttpStatus.Failure -> {
rxDialogShapeLoading.cancel()
if (it.reqType == HttpReqType.APPDATAINFO) {
RxToast.warning("获取可提现金额失败,请检查网络")
tv_can_reward_amount.setOnClickListener {
MMKV.defaultMMKV().decodeString(BZYConstants.MMKV_KEY_REMEMBER_USERNAME)
?.let { it1 -> viewModel.appDataInfo(it1) }
}
} else {
RxToast.warning("提现失败,请检查网络")
}
}
}
}
et_zfb_account = findViewById(R.id.et_zfb_account)
tv_can_reward_amount = findViewById(R.id.tv_can_reward_amount)
et_tixian_amount = findViewById(R.id.et_tixian_amount)
MMKV.defaultMMKV().decodeString(BZYConstants.MMKV_KEY_REMEMBER_USERNAME)
?.let { viewModel.appDataInfo(it) }
findViewById<View>(R.id.btn_tixian).setOnClickListener {
zfbAccount = et_zfb_account.text.trim().toString()
txAmount = et_tixian_amount.text.trim().toString()
if (zfbAccount.isEmpty() || zfbAccount.length < 3) {
RxToast.warning("请输入有效的支付宝账号")
} else {
if (!isNumber(txAmount)) {
RxToast.warning("请输入有效的金额")
return@setOnClickListener
}
if (rewardAmount.isEmpty()) {
RxToast.warning("未能获取到可提现金额,请重试")
return@setOnClickListener
}
if (txAmount.toFloat() > rewardAmount.toFloat()) {
RxToast.warning("已超过可提现金额")
return@setOnClickListener
}
isTiXia = true
MMKV.defaultMMKV().decodeString(BZYConstants.MMKV_KEY_REMEMBER_USERNAME)
?.let { it1 -> viewModel.appDataInfo(it1) }
}
}
}
fun isNumber(str: String): Boolean {
val regex = Regex("^[1-9]\\d*(\\.[0-9]+)?$")
return regex.matches(str)
}
}

View File

@ -1,33 +0,0 @@
package com.yingmao.clash.act
import com.tencent.mmkv.MMKV
import com.yingmao.clash.base.PublicViewMode
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.NetService
class ZFBTXAtyVM : PublicViewMode() {
fun appRewardAmount(phone: String, rewardAmount: String, payAccount: String) {
val httpApi = NetService.instances.createHttps(HttpApi::class.java)
val phToken = MMKV.defaultMMKV().decodeString(BZYConstants.MMKV_KEY_PH_TOKEN,"")!!
val params = mutableMapOf(
"phoneNumber" to phone,
"rewardAmount" to rewardAmount,
"payAccount" to payAccount,
"phToken" to phToken,
)
doAsync(HttpReqType.APPREWARDAMOUNT) { httpApi.appRewardAmount(params) }
}
fun appDataInfo(phone: String) {
val phToken = MMKV.defaultMMKV().decodeString(BZYConstants.MMKV_KEY_PH_TOKEN,"")!!
val params = mutableMapOf(
"phoneNumber" to phone,
"phToken" to phToken
)
val httpApi = NetService.instances.createHttps(HttpApi::class.java)
doAsync(HttpReqType.APPDATAINFO) { httpApi.appDataInfo(params) }
}
}

View File

@ -170,7 +170,21 @@ class MainApplication : Application() {
BaiduAction.setActivateInterval(this, 7)
}
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) {
MMKV.defaultMMKV().encode(BZYConstants.languageInfo, language)
val resources = this.resources

View File

@ -6,6 +6,9 @@ import android.content.Context
import android.content.Intent
import android.content.pm.ActivityInfo
import android.content.res.Configuration
import android.net.ConnectivityManager
import android.net.NetworkCapabilities
import android.os.Build
import android.os.Bundle
import android.text.TextUtils
import android.util.Log
@ -13,13 +16,14 @@ import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager
import android.widget.*
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity
import androidx.collection.arraySetOf
import androidx.lifecycle.ViewModelProvider
import com.orhanobut.logger.Logger
import com.tencent.mmkv.MMKV
import com.yingmao.clash.BuildConfig
import com.yingmao.clash.R
import com.yingmao.clash.act.MainActivity
import com.yingmao.clash.conf.BZYConstants
import com.yingmao.clash.conf.Conf
import com.cncat.vpn.core.bridge.ClashException
@ -55,6 +59,10 @@ open class BaseActivity : AppCompatActivity(), CoroutineScope by MainScope(), Br
var baseLoading: RxDialogShapeLoading? = null
val serStore by lazy { ServiceStore(this) }
companion object {
// var switchFinshSet: MutableSet<String>? = null
}
sealed class RequestTunnel {
object ReloadAll : RequestTunnel()
object ReLaunch : RequestTunnel()
@ -263,10 +271,11 @@ open class BaseActivity : AppCompatActivity(), CoroutineScope by MainScope(), Br
}
}
fun selectServer(){
fun selectServer() {
val mmkv = MMKV.defaultMMKV()
val routerHost = mmkv.decodeStringSet(BZYConstants.MMKV_KEY_ROUTER_HOST, HashSet())
}
@Synchronized
fun switchServer(): Boolean {
var isChange = false
@ -305,6 +314,16 @@ open class BaseActivity : AppCompatActivity(), CoroutineScope by MainScope(), Br
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

View File

@ -15,7 +15,6 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.ViewModelProvider
import com.orhanobut.logger.Logger
import com.yingmao.clash.R
import com.yingmao.clash.act.MainActivity
import com.cncat.vpn.common.log.Log
//import com.yingmao.clash.design.model.DarkMode
//import com.yingmao.clash.design.store.UiStore

View File

@ -26,8 +26,11 @@ 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() {
@ -68,11 +71,21 @@ open class BaseViewMode : ViewModel() {
}
}
}
fun postErrLog(phone: String) {
fun doAsync2(reqType: HttpReqType, doInAsync: suspend () -> Any) {
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(
CoroutineExceptionHandler { _,
exception ->
CoroutineExceptionHandler {
_,
exception,
->
Log.e("BaseViewMode_doAsync:${exception.toString()}", exception)
statusData.value =
if (exception.message.isNullOrBlank()) HttpStatus.Failure(
@ -93,7 +106,7 @@ open class BaseViewMode : ViewModel() {
Log.i("请求结果:$rsp")
statusData.value = HttpStatus.Success(reqType, rsp)
} catch (e: Exception) {
Log.e( "网络请求异常",e)
Log.e("网络请求异常", e)
statusData.value = HttpStatus.Failure(reqType, e.toString())
}
}
@ -131,11 +144,13 @@ open class BaseViewMode : ViewModel() {
fun doAsyncDelay(
reqType: HttpReqType,
delayTimeMillis: Long,
doInAsync: suspend () -> BaseRsp
doInAsync: suspend () -> BaseRsp,
) {
viewModelScope.launch(
CoroutineExceptionHandler { _,
exception ->
CoroutineExceptionHandler {
_,
exception,
->
Log.e("BaseViewMode_doAsyncDelay:${exception.toString()}", exception)
statusData.value =
if (exception.message.isNullOrBlank()) HttpStatus.Failure(
@ -158,7 +173,7 @@ open class BaseViewMode : ViewModel() {
Log.i("请求结果:$rsp")
statusData.value = HttpStatus.Success(reqType, rsp)
} catch (e: Exception) {
Log.e( "网络请求异常",e)
Log.e("网络请求异常", e)
// statusData.value = HttpStatus.Failure(reqType,e.toString())
}
}
@ -166,8 +181,10 @@ open class BaseViewMode : ViewModel() {
fun doAsyncStrResult(reqType: HttpReqType, doInAsync: suspend () -> String) {
viewModelScope.launch(
CoroutineExceptionHandler { _,
exception ->
CoroutineExceptionHandler {
_,
exception,
->
Log.e("BaseViewMode_doAsyncStrResult:${exception.toString()}", exception)
@ -179,7 +196,7 @@ open class BaseViewMode : ViewModel() {
val rsp = result.await()
Log.i("请求结果:$rsp")
} catch (e: Exception) {
Log.e( "网络请求异常",e)
Log.e("网络请求异常", e)
}
}
}
@ -187,11 +204,13 @@ open class BaseViewMode : ViewModel() {
fun doAsyncEncodeStr(
reqType: HttpReqType,
classOfT: Class<out BaseRsp>,
doInAsync: suspend () -> String
doInAsync: suspend () -> String,
) {
viewModelScope.launch(
CoroutineExceptionHandler() { _,
exception ->
CoroutineExceptionHandler() {
_,
exception,
->
Log.e("BaseViewMode_doAsyncEncodeStr:${exception.toString()}", exception)
statusData.value =
if (exception.message.isNullOrBlank()) HttpStatus.Failure(
@ -221,20 +240,22 @@ open class BaseViewMode : ViewModel() {
statusData.value = HttpStatus.Success(reqType, rsp)
}
} catch (e: Exception) {
Log.e("网络请求异常",e)
Log.e("网络请求异常", e)
throw e
statusData.value = HttpStatus.Failure(reqType,e.toString())
statusData.value = HttpStatus.Failure(reqType, e.toString())
}
}
}
suspend fun doAsyncEncodeStrBg(
classOfT: Class<out BaseRsp>,
doInAsync: suspend () -> String
doInAsync: suspend () -> String,
): BaseRsp? {
return viewModelScope.async(
CoroutineExceptionHandler() { _,
exception ->
CoroutineExceptionHandler() {
_,
exception,
->
Log.e("BaseViewMode_doAsyncEncodeStrBg:${exception.toString()}", exception)
})
{
@ -247,7 +268,7 @@ open class BaseViewMode : ViewModel() {
val rsp = Gson().fromJson(decodeRspStr, classOfT)
return@async rsp
} catch (e: Exception) {
Log.e("网络请求异常",e)
Log.e("网络请求异常", e)
}
return@async null
}.await()
@ -262,7 +283,7 @@ open class BaseViewMode : ViewModel() {
}
result.await()
} catch (e: Exception) {
Log.e("网络请求异常",e)
Log.e("网络请求异常", e)
}
}
}

View File

@ -30,7 +30,7 @@ open class PublicViewMode : BaseViewMode() {
}
BuildConfig.is_tz -> {
rt.getTzHostGithub()
rt.getTzHostTZ()
}
BuildConfig.is_yunti -> {
@ -38,7 +38,7 @@ open class PublicViewMode : BaseViewMode() {
}
else -> {
rt.getBzyHostGithub()
rt.getBzyHostDef()
}
}
host.takeIf { it.isNotEmpty() }?.let {

View File

@ -1,68 +0,0 @@
package com.yingmao.clash.captcha;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
/**
* 网络加载图片的Task类
*
*/
class BitmapLoaderTask extends AsyncTask<String, Integer, Bitmap> {
private Callback callback;
public BitmapLoaderTask(Callback callback) {
this.callback = callback;
}
interface Callback{
void result(Bitmap bitmap);
}
@Override
protected Bitmap doInBackground(String... params) {
Bitmap result = null;
URL url = null;
HttpURLConnection conn = null;
InputStream is = null;
try {
url = new URL(params[0]);
conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
int code = conn.getResponseCode();
if (code == 200) {
is = conn.getInputStream();
result = BitmapFactory.decodeStream(is);
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (is != null) {
is.close();
}
if (conn != null) {
conn.disconnect();
}
} catch (Exception e) {
e.printStackTrace();
}
}
return result;
}
@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
callback.result(bitmap);
}
}

View File

@ -1,352 +0,0 @@
package com.yingmao.clash.captcha;
import android.animation.Animator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.SeekBar;
import android.widget.TextView;
import androidx.annotation.AttrRes;
import androidx.annotation.DrawableRes;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.yingmao.clash.R;
/**
* Created by luozhanming on 2018/1/17.
*/
public class Captcha extends LinearLayout {
//控件成员
private PictureVertifyView vertifyView; //拼图块
private TextSeekbar seekbar; //滑动条块
private View accessSuccess, accessFailed; //验证成功/失败显示的视图
private TextView accessText, accessFailedText; //验证成功/失败显示的文字
private ImageView refreshView; //刷新按钮
//控件属性
private int drawableId = -1; //验证图片资源id
private int progressDrawableId; //滑动条背景id
private int thumbDrawableId; //滑动条滑块id
private int mMode; //控件验证模式(有滑动条/无滑动条)
private int maxFailedCount; //最大失败次数
private int failCount; //已失败次数
private int blockSize; //拼图缺块大小
//处理滑动条逻辑
private boolean isResponse;
private boolean isDown;
private CaptchaListener mListener;
private BitmapLoaderTask mTask;
/**
* 带滑动条验证模式
*/
public static final int MODE_BAR = 1;
/**
* 不带滑动条验证手触模式
*/
public static final int MODE_NONBAR = 2;
@IntDef(value = {MODE_BAR, MODE_NONBAR})
public @interface Mode {
}
public interface CaptchaListener {
/**
* Called when captcha access.
*
* @param time cost of access time
* @return text to show,show default when return null
*/
String onAccess(long time);
/**
* Called when captcha failed.
*
* @param failCount fail count
* @return text to show,show default when return null
*/
String onFailed(int failCount);
/**
* Called when captcha failed
*
* @return text to show,show default when return null
*/
String onMaxFailed();
}
public Captcha(@NonNull Context context) {
super(context);
}
public Captcha(@NonNull Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public Captcha(@NonNull final Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.Captcha);
// drawableId = typedArray.getResourceId(R.styleable.Captcha_src, R.drawable.gv_icon);
progressDrawableId = typedArray.getResourceId(R.styleable.Captcha_progressDrawable, R.drawable.po_seekbar);
thumbDrawableId = typedArray.getResourceId(R.styleable.Captcha_thumbDrawable,R.drawable.thumb);
mMode = typedArray.getInteger(R.styleable.Captcha_mode, MODE_BAR);
maxFailedCount = typedArray.getInteger(R.styleable.Captcha_max_fail_count, 3);
blockSize = typedArray.getDimensionPixelSize(R.styleable.Captcha_blockSize, Utils.dp2px(getContext(), 50));
typedArray.recycle();
init();
}
private void init() {
View parentView = LayoutInflater.from(getContext()).inflate(R.layout.container, this, true);
vertifyView = (PictureVertifyView) parentView.findViewById(R.id.vertifyView);
seekbar = (TextSeekbar) parentView.findViewById(R.id.seekbar);
accessSuccess = parentView.findViewById(R.id.accessRight);
accessFailed = parentView.findViewById(R.id.accessFailed);
accessText = (TextView) parentView.findViewById(R.id.accessText);
accessFailedText = (TextView) parentView.findViewById(R.id.accessFailedText);
refreshView = (ImageView) parentView.findViewById(R.id.refresh);
setMode(mMode);
if(drawableId!=-1){
vertifyView.setImageResource(drawableId);
}
setBlockSize(blockSize);
vertifyView.callback(new PictureVertifyView.Callback() {
@Override
public void onSuccess(long time) {
if (mListener != null) {
String s = mListener.onAccess(time);
if (s != null) {
accessText.setText(s);
} else {//默认文案
accessText.setText(String.format(getResources().getString(R.string.vertify_access), time));
}
}
accessSuccess.setVisibility(VISIBLE);
accessFailed.setVisibility(GONE);
}
@Override
public void onFailed() {
seekbar.setEnabled(false);
vertifyView.setTouchEnable(false);
failCount = failCount > maxFailedCount ? maxFailedCount : failCount + 1;
accessFailed.setVisibility(VISIBLE);
accessSuccess.setVisibility(GONE);
if (mListener != null) {
if (failCount == maxFailedCount) {
String s = mListener.onMaxFailed();
if (s != null) {
accessFailedText.setText(s);
} else {//默认文案
accessFailedText.setText(String.format(getResources().getString(R.string.vertify_failed), maxFailedCount - failCount));
}
} else {
String s = mListener.onFailed(failCount);
if (s != null) {
accessFailedText.setText(s);
} else {//默认文案
accessFailedText.setText(String.format(getResources().getString(R.string.vertify_failed), maxFailedCount - failCount));
}
}
}
}
});
setSeekBarStyle(progressDrawableId, thumbDrawableId);
//用于处理滑动条渐滑逻辑
seekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (isDown) { //手指按下
isDown = false;
if (progress > 10) { //按下位置不正确
isResponse = false;
} else {
isResponse = true;
accessFailed.setVisibility(GONE);
vertifyView.down(0);
}
}
if (isResponse) {
vertifyView.move(progress);
} else {
seekBar.setProgress(0);
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
isDown = true;
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
if (isResponse) {
vertifyView.loose();
}
}
});
refreshView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
startRefresh(v);
}
});
}
private void startRefresh(View v) {
//点击刷新按钮启动动画
v.animate().rotationBy(360).setDuration(500)
.setInterpolator(new AccelerateDecelerateInterpolator())
.setListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
reset(false);
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
}
public void setCaptchaListener(CaptchaListener listener) {
this.mListener = listener;
}
public void setCaptchaStrategy(CaptchaStrategy strategy) {
if (strategy != null) {
vertifyView.setCaptchaStrategy(strategy);
}
}
public void setSeekBarStyle(@DrawableRes int progressDrawable, @DrawableRes int thumbDrawable) {
seekbar.setProgressDrawable(getResources().getDrawable(progressDrawable));
seekbar.setThumb(getResources().getDrawable(thumbDrawable));
seekbar.setThumbOffset(0);
}
/**
* 设置滑块图片大小单位px
*/
public void setBlockSize(int blockSize) {
vertifyView.setBlockSize(blockSize);
}
/**
* 设置滑块验证模式
*/
public void setMode(@Mode int mode) {
this.mMode = mode;
vertifyView.setMode(mode);
if (mMode == MODE_NONBAR) {
seekbar.setVisibility(GONE);
vertifyView.setTouchEnable(true);
} else {
seekbar.setVisibility(VISIBLE);
seekbar.setEnabled(true);
}
hideText();
}
public int getMode() {
return this.mMode;
}
public void setMaxFailedCount(int count) {
this.maxFailedCount = count;
}
public int getMaxFailedCount() {
return this.maxFailedCount;
}
public void setBitmap(int drawableId) {
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), drawableId);
setBitmap(bitmap);
}
public void setBitmap(Bitmap bitmap) {
vertifyView.setImageBitmap(bitmap);
reset(false);
}
public void setBitmap(String url) {
mTask = new BitmapLoaderTask(new BitmapLoaderTask.Callback() {
@Override
public void result(Bitmap bitmap) {
setBitmap(bitmap);
}
});
mTask.execute(url);
}
@Override
protected void onDetachedFromWindow() {
if(mTask!=null&&mTask.getStatus().equals(AsyncTask.Status.RUNNING)){
mTask.cancel(true);
}
super.onDetachedFromWindow();
}
/**
* 复位
* @param clearFailed 是否清除失败次数
*/
public void reset(boolean clearFailed) {
hideText();
vertifyView.reset();
if (clearFailed) {
failCount = 0;
}
if (mMode == MODE_BAR) {
seekbar.setEnabled(true);
seekbar.setProgress(0);
} else {
vertifyView.setTouchEnable(true);
}
}
/**
* 隐藏成功失败文字显示
* */
public void hideText() {
accessFailed.setVisibility(GONE);
accessSuccess.setVisibility(GONE);
}
}

View File

@ -1,70 +0,0 @@
package com.yingmao.clash.captcha;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
/**
* Captcha的拼图区域策咯
*
*/
public abstract class CaptchaStrategy {
protected Context mContext;
public CaptchaStrategy(Context ctx) {
this.mContext = ctx;
}
protected Context getContext() {
return mContext;
}
/**
* 定义缺块的形状
*
* @param blockSize 单位dp注意转化为px
* @return path of the shape
*/
public abstract Path getBlockShape(int blockSize);
/**
* 定义缺块的位置信息
*
* @param width picture width unit:px
* @param height picture height unit:px
* @param blockSize
* @return position info of the block
*/
public abstract PositionInfo getBlockPostionInfo(int width, int height, int blockSize);
/**
* 定义滑块图片的位置信息(只有设置为无滑动条模式有用)
*
* @param width picture width
* @param height picture height
* @return position info of the block
*/
public PositionInfo getPositionInfoForSwipeBlock(int width, int height, int blockSize){
return getBlockPostionInfo(width,height,blockSize);
}
/**
* 获得缺块阴影的Paint
*/
public abstract Paint getBlockShadowPaint();
/**
* 获得滑块图片的Paint
*/
public abstract Paint getBlockBitmapPaint();
/**
* 装饰滑块图片在绘制图片后执行即绘制滑块前景
*/
public void decoreateSwipeBlockBitmap(Canvas canvas, Path shape) {
}
}

View File

@ -1,96 +0,0 @@
package com.yingmao.clash.captcha;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.graphics.Path;
import androidx.annotation.NonNull;
import java.util.Random;
/**
* 默认CaptchaStrategy
*
*/
public class DefaultCaptchaStrategy extends CaptchaStrategy {
public DefaultCaptchaStrategy(Context ctx) {
super(ctx);
}
@Override
public Path getBlockShape(int blockSize) {
int gap = (int) (blockSize/5f);
Path path = new Path();
path.moveTo(0, gap);
path.rLineTo(blockSize/2.5f, 0);
path.rLineTo(0, -gap);
path.rLineTo(gap, 0);
path.rLineTo(0, gap);
path.rLineTo(2 * gap, 0);
path.rLineTo(0, 4 * gap);
path.rLineTo(-5 * gap, 0);
path.rLineTo(0, -1.5f * gap);
path.rLineTo(gap, 0);
path.rLineTo(0, -gap);
path.rLineTo(-gap, 0);
path.close();
return path;
}
@Override
public @NonNull PositionInfo getBlockPostionInfo(int width, int height, int blockSize) {
Random random = new Random();
int left = random.nextInt(width - blockSize +1);
//Avoid robot frequently and quickly click the start point to access the captcha.
if (left < blockSize) {
left = blockSize;
}
int top = random.nextInt(height - blockSize +1);
if (top < 0) {
top = 0;
}
return new PositionInfo(left, top);
}
@Override
public @NonNull PositionInfo getPositionInfoForSwipeBlock(int width, int height, int blockSize) {
Random random = new Random();
int left = random.nextInt(width - blockSize+1);
int top = random.nextInt(height - blockSize+1);
if (top < 0) {
top = 0;
}
return new PositionInfo(left, top);
}
@Override
public Paint getBlockShadowPaint() {
Paint shadowPaint = new Paint();
shadowPaint.setColor(Color.parseColor("#000000"));
shadowPaint.setAlpha(165);
return shadowPaint;
}
@Override
public Paint getBlockBitmapPaint() {
Paint paint = new Paint();
return paint;
}
@Override
public void decoreateSwipeBlockBitmap(Canvas canvas, Path shape) {
Paint paint = new Paint();
paint.setColor(Color.parseColor("#FFFFFF"));
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(10);
paint.setPathEffect(new DashPathEffect(new float[]{20,20},10));
Path path = new Path(shape);
canvas.drawPath(path,paint);
}
}

View File

@ -1,304 +0,0 @@
package com.yingmao.clash.captcha;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.AppCompatImageView;
/**
* 拼图区域控件
*
*/
class PictureVertifyView extends AppCompatImageView {
//状态码
private static final int STATE_DOWN = 1;
private static final int STATE_MOVE = 2;
private static final int STATE_LOOSEN = 3;
private static final int STATE_IDEL = 4;
private static final int STATE_ACCESS = 5;
private static final int STATE_UNACCESS = 6;
private static final int TOLERANCE = 10; //验证的最大容差
private int mState = STATE_IDEL; //当前状态
private PositionInfo shadowInfo; //拼图缺块阴影的位置
private PositionInfo blockInfo; //拼图缺块的位置
private Bitmap verfityBlock; //拼图缺块Bitmap
private Path blockShape; //拼图缺块形状
private Paint bitmapPaint; //绘制拼图缺块的画笔
private Paint shadowPaint; //绘制拼图缺块阴影的画笔
private long startTouchTime; //滑动/触动开始时间
private long looseTime; //滑动/触动松开时间
private int blockSize = 50;
private boolean mTouchEnable = true; //是否可触动
private Callback callback;
private CaptchaStrategy mStrategy;
private int mMode; //Captcha验证模式
interface Callback {
void onSuccess(long time);
void onFailed();
}
public PictureVertifyView(Context context) {
this(context, null);
}
public PictureVertifyView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public PictureVertifyView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mStrategy = new DefaultCaptchaStrategy(context);
shadowPaint = mStrategy.getBlockShadowPaint();
bitmapPaint = mStrategy.getBlockBitmapPaint();
setLayerType(View.LAYER_TYPE_SOFTWARE, bitmapPaint);
}
private void initDrawElements(){
if (shadowInfo == null) {
shadowInfo = mStrategy.getBlockPostionInfo(getWidth(), getHeight(), blockSize);
if (mMode == Captcha.MODE_BAR) {
blockInfo = new PositionInfo(0, shadowInfo.top);
} else {
blockInfo = mStrategy.getPositionInfoForSwipeBlock(getWidth(), getHeight(), blockSize);
}
}
if (blockShape == null) {
blockShape = mStrategy.getBlockShape(blockSize);
blockShape.offset(shadowInfo.left, shadowInfo.top);
}
if (verfityBlock == null) {
verfityBlock = createBlockBitmap();
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
initDrawElements();
if (mState != STATE_ACCESS) {
canvas.drawPath(blockShape, shadowPaint);
}
if (mState == STATE_MOVE || mState == STATE_IDEL || mState == STATE_DOWN || mState == STATE_UNACCESS) {
canvas.drawBitmap(verfityBlock, blockInfo.left, blockInfo.top, bitmapPaint);
}
}
/**
*按下滑动条(滑动条模式)
* */
void down(int progress) {
startTouchTime = System.currentTimeMillis();
mState = STATE_DOWN;
blockInfo.left = (int) (progress / 100f * (getWidth() - blockSize));
invalidate();
}
/**
*触动拼图块(触动模式)
* */
void downByTouch(float x, float y) {
mState = STATE_DOWN;
blockInfo.left = (int) (x - blockSize / 2f);
blockInfo.top = (int) (y - blockSize / 2f);
startTouchTime = System.currentTimeMillis();
invalidate();
}
/**
* 移动拼图缺块(滑动条模式)
*/
void move(int progress) {
mState = STATE_MOVE;
blockInfo.left = (int) (progress / 100f * (getWidth() - blockSize));
invalidate();
}
/**
* 触动拼图缺块(触动模式)
*/
void moveByTouch(float offsetX, float offsetY) {
mState = STATE_MOVE;
blockInfo.left += offsetX;
blockInfo.top += offsetY;
invalidate();
}
/**
* 松开
* */
void loose() {
mState = STATE_LOOSEN;
looseTime = System.currentTimeMillis();
checkAccess();
invalidate();
}
/**
* 复位
* */
void reset() {
mState = STATE_IDEL;
verfityBlock = null;
shadowInfo = null;
blockShape = null;
invalidate();
}
void unAccess() {
mState = STATE_UNACCESS;
invalidate();
}
void access() {
mState = STATE_ACCESS;
invalidate();
}
void callback(Callback callback) {
this.callback = callback;
}
void setCaptchaStrategy(CaptchaStrategy strategy) {
this.mStrategy = strategy;
}
void setBlockSize(int size) {
this.blockSize = size;
this.blockShape = null;
this.blockInfo = null;
this.shadowInfo = null;
this.verfityBlock = null;
invalidate();
}
public void setBitmap(Bitmap bitmap) {
this.blockShape = null;
this.blockInfo = null;
this.shadowInfo = null;
this.verfityBlock.recycle();
this.verfityBlock = null;
setImageBitmap(bitmap);
}
void setMode(@Captcha.Mode int mode) {
this.mMode = mode;
this.blockShape = null;
this.blockInfo = null;
this.shadowInfo = null;
this.verfityBlock = null;
invalidate();
}
void setTouchEnable(boolean enable) {
this.mTouchEnable = enable;
}
/**
* 生成拼图缺块的Bitmap
* */
private Bitmap createBlockBitmap() {
Bitmap tempBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(tempBitmap);
getDrawable().setBounds(0, 0, getWidth(), getHeight());
canvas.clipPath(blockShape);
getDrawable().draw(canvas);
mStrategy.decoreateSwipeBlockBitmap(canvas, blockShape);
return cropBitmap(tempBitmap);
}
/**
* 保留拼图缺块大小的bitmap
* */
private Bitmap cropBitmap(Bitmap bmp) {
Bitmap result = null;
result = Bitmap.createBitmap(bmp, shadowInfo.left, shadowInfo.top, blockSize, blockSize);
bmp.recycle();
return result;
}
/**
* 检测是否通过
*/
private void checkAccess() {
if (Math.abs(blockInfo.left - shadowInfo.left) < TOLERANCE && Math.abs(blockInfo.top - shadowInfo.top) < TOLERANCE) {
access();
if (callback != null) {
long deltaTime = looseTime - startTouchTime;
callback.onSuccess(deltaTime);
}
} else {
unAccess();
if (callback != null) {
callback.onFailed();
}
}
}
private float tempX, tempY, downX, downY;
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
//触动模式下点击超出拼图缺块的区域不进行处理
if (event.getAction() == MotionEvent.ACTION_DOWN && mMode == Captcha.MODE_NONBAR) {
if (event.getX() < blockInfo.left || event.getX() > blockInfo.left + blockSize || event.getY() < blockInfo.top || event.getY() > blockInfo.top + blockSize) {
return false;
}
}
return super.dispatchTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (mMode == Captcha.MODE_NONBAR && verfityBlock != null && mTouchEnable) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
downX = x;
downY = y;
downByTouch(x, y);
break;
case MotionEvent.ACTION_UP:
loose();
break;
case MotionEvent.ACTION_MOVE:
float offsetX = x - tempX;
float offsetY = y - tempY;
moveByTouch(offsetX, offsetY);
break;
}
tempX = x;
tempY = y;
}
return true;
}
}

View File

@ -1,13 +0,0 @@
package com.yingmao.clash.captcha;
public class PositionInfo {
int left; //拼图缺块离整张图片左边距离
int top; //拼图缺块离整张图片上方距离
public PositionInfo(int left, int top) {
this.left = left;
this.top = top;
}
}

View File

@ -1,44 +0,0 @@
package com.yingmao.clash.captcha;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import androidx.appcompat.widget.AppCompatSeekBar;
import com.yingmao.clash.R;
class TextSeekbar extends AppCompatSeekBar {
private Paint textPaint;
public TextSeekbar(Context context) {
super(context);
}
public TextSeekbar(Context context, AttributeSet attrs) {
this(context, attrs, R.style.MySeekbarSytle);
}
public TextSeekbar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
textPaint = new Paint();
textPaint.setTextAlign(Paint.Align.CENTER);
int textSize = Utils.dp2px(context, 14);
textPaint.setTextSize(textSize);
textPaint.setAntiAlias(true);
textPaint.setColor(Color.parseColor("#545454"));
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();
float top = fontMetrics.top;//为基线到字体上边框的距离,即上图中的top
float bottom = fontMetrics.bottom;//为基线到字体下边框的距离,即上图中的bottom
int baseLineY = (int) (getHeight() / 2 - top / 2 - bottom / 2);//基线中间点的y轴计算公式
canvas.drawText("向右滑动滑块完成拼图", getWidth() / 2, baseLineY, textPaint);
}
}

View File

@ -1,15 +0,0 @@
package com.yingmao.clash.captcha;
import android.content.Context;
public class Utils {
/**
* dp转px
* */
public static int dp2px(Context ctx, float dip) {
float density = ctx.getResources().getDisplayMetrics().density;
return (int) (dip * density);
}
}

View File

@ -28,6 +28,7 @@ class BZYConstants {
const val MMKV_KEY_OLD_CURRENT_HOST = "old_current_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_Airport_Base64 ="airport_base64"
//注册地址
@ -47,6 +48,7 @@ class BZYConstants {
const val MMKV_KEY_VPN_EXPIRED = "expired"
const val MMKV_KEY_VPN_UserCommand = "userCommand"
const val MMKV_KEY_VPN_LogSwitch = "logSwitch"
//vpn过期时间
const val MMKV_KEY_VPN_VALID_PERIOD = "vpn_valid_period"

View File

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

View File

@ -1,6 +1,7 @@
package com.yingmao.clash.fragment
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@ -9,9 +10,14 @@ import android.widget.ImageView
import android.widget.RelativeLayout
import android.widget.TextView
import androidx.fragment.app.Fragment
import com.tencent.mmkv.MMKV
import com.yingmao.clash.BuildConfig
import com.yingmao.clash.R
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 java.util.HashSet
abstract class BaseFragment :Fragment() {
@ -58,4 +64,44 @@ abstract class BaseFragment :Fragment() {
open fun httpSuccess(httpStatus: HttpStatus.Success){}
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

@ -264,7 +264,7 @@ open class NewAccFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dis
val vpnRequest = context?.startClashService()
try {
startUTimer()
start_u_timer()
if (vpnRequest != null) {
val result = startActivityForResult(
ActivityResultContracts.StartActivityForResult(), vpnRequest
@ -273,17 +273,15 @@ open class NewAccFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dis
stateChanged(Clash_Starting, true)
context?.startClashService()
accelerateViewModel.appStartupStatus()
// clashRunning = true
}
}else{
accelerateViewModel.appStartupStatus()
}
} catch (e: Exception) {
stopUTimer()
stop_u_timer()
e.printStackTrace()
}
withContext(Dispatchers.Main){
hideLoading()
}
}
}
@ -308,7 +306,7 @@ open class NewAccFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dis
inner class ClashBP : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
Log.i("action==>" + intent?.action)
com.cncat.vpn.common.log.Log.i("action==>" + intent?.action)
when (intent?.action) {
Intents.ACTION_CLASH_STARTED -> {
if (::rxDialogShapeLoading.isInitialized) {
@ -317,6 +315,11 @@ open class NewAccFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dis
accelerateIv.isSelected = true
clashRunning = true
// MMKV.defaultMMKV().encode(
// BZYConstants.MMKV_KEY_CURRENT_NODE,
// currentNode
// )
// setNodeNameAndImg(currentNode!!.name)
if (isAdded) {
RxToast.success(resources.getString(R.string.AccelerateSuccess))
}
@ -329,11 +332,12 @@ open class NewAccFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dis
}
accelerateIv.isSelected = false
clashRunning = false
stopUTimer()
stop_u_timer()
}
Intents.ACTION_CLASH_REQUEST_STOP -> {
Log.d("ACTION_CLASH_REQUEST_STOP")
// clashRunning = false
com.cncat.vpn.common.log.Log.d("ACTION_CLASH_REQUEST_STOP")
if (::rxDialogShapeLoading.isInitialized) {
if (!rxDialogShapeLoading.isShowing) {
if (isAdded) {
@ -345,7 +349,7 @@ open class NewAccFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dis
}
Intents.ACTION_Glo_Mode -> {
val mode = intent.getBooleanExtra("mode", false)
var mode = intent.getBooleanExtra("mode", false)
Log.d("ACTION_Glo_Mode mode==>${mode}")
isGlo = mode
}
@ -353,7 +357,14 @@ open class NewAccFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dis
}
}
private fun stateChanged(state: Int, isShowToast: Boolean) {
private fun getNodesStatus() {
}
fun stateChanged(state: Int, isShowToast: Boolean) {
Logger.i("!!!!!!!!!!:${state}")
when (state) {
@ -362,11 +373,18 @@ open class NewAccFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dis
rxDialogShapeLoading.cancel()
}
accelerateIv.isSelected = true
// MMKV.defaultMMKV().encode(
// BZYConstants.MMKV_KEY_CURRENT_NODE,
// currentNode
// )
// setNodeNameAndImg(currentNode!!.name)
if (isShowToast) {
if (isAdded) {
RxToast.success(resources.getString(R.string.AccelerateSuccess))
}
}
// File("${Core.deviceStorage.noBackupFilesDir}/${BaseService.CONFIG_FILE}").delete()
accelerateViewModel.appStartupStatus()
}
Clash_Stop -> {
@ -374,6 +392,9 @@ open class NewAccFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dis
rxDialogShapeLoading.cancel()
}
accelerateIv.isSelected = false
// if (reConnection) {
// connectVPNSS(currentNode)
// }
}
Clash_Starting -> {
@ -387,37 +408,27 @@ open class NewAccFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dis
}
}
// BaseService.State.Stopping -> {
// if (::rxDialogShapeLoading.isInitialized) {
// if (!rxDialogShapeLoading.isShowing) {
// rxDialogShapeLoading.setMessage("正在断开...")
// rxDialogShapeLoading.show()
// }
// }
// }
else -> {}
}
}
fun nodeChooseBase(data: Intent?){
val node = data?.extras?.getString(BZYConstants.MMKV_KEY_SELECT_NODE_NAME)
val isRunning = data?.extras?.getBoolean(BZYConstants.EXTRA_KEY_IS_RUNING)
Log.d("onActivityResult ==>>> Node:${node}")
node?.let {
if (clashRunning) {
Log.d("clashRunning $clashRunning")
} else {
startClash(false)
}
checkGlo()
}?.run {
if (isRunning == true && !clashRunning) {
startClash(false)
}
}
}
fun changeUserState(){
tvTimeInfo.text=resources.getString(R.string.HYDq)
}
@Deprecated("Deprecated in Java")
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
com.cncat.vpn.common.log.Log.i("onActivityResult ==>>> requestCode:${requestCode} resultCode:${resultCode}")
if (requestCode == REQUEST_CONNECT) {
if (resultCode == Activity.RESULT_OK) {
if (clashRunning) {
Log.d("clashRunning $clashRunning")
} else {
startClash(false)
startClash()
}
} else {
if (::rxDialogShapeLoading.isInitialized || rxDialogShapeLoading.isShowing) {
@ -425,7 +436,25 @@ open class NewAccFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dis
}
}
} else if (requestCode == CHECK_NODE_REQUEST_CODE) {
getDelayData()
val node = data?.extras?.getString(BZYConstants.MMKV_KEY_SELECT_NODE_NAME)
val is_runing = data?.extras?.getBoolean(BZYConstants.EXTRA_KEY_IS_RUNING)
// val node = data?.extras?.getSerializable(BZYConstants.MMKV_KEY_SELECT_NODE) as Profile
Log.d("onActivityResult ==>>> Node:${node}")
node?.let {
setNodeNameAndImg(node)
if (clashRunning) {
Log.d("clashRunning $clashRunning")
} else {
startClash()
}
checkGlo()
}?.run {
if (is_runing == true && !clashRunning) {
startClash()
} else {
}
}
} else if (requestCode == Proxy_Mode_REQUEST_CODE) {
checkGlo()
}
@ -433,7 +462,8 @@ open class NewAccFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dis
@SuppressLint("SimpleDateFormat")
fun setExpireDate() {
val isExpire = checkExpire()
var isExpire = checkExpire()
// var isExpire = false
var endTimeInfo = "--"
if (isExpire) {
if (isAdded) {
@ -489,38 +519,118 @@ open class NewAccFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dis
}
}
tvEndTime.text = endTimeInfo
tv_end_time.text = endTimeInfo
}
private fun checkExpire(): Boolean {
val validPeriod = MMKV.defaultMMKV().decodeLong(BZYConstants.MMKV_KEY_VPN_VALID_PERIOD)
if (validPeriod > 0) {
val nowTime = System.currentTimeMillis();
Log.d("validPeriod==>$validPeriod, nowTime==>$nowTime")
var nowTime = System.currentTimeMillis();
com.cncat.vpn.common.log.Log.d("validPeriod==>$validPeriod, nowTime==>$nowTime")
if (validPeriod >= nowTime) {
return false
}
}
return true;
}
private fun setNodeNameAndImg(it: String) {
launch(Dispatchers.Main) {
nodeNameTv.text = it
when {
it.contains("中国", true) -> {
countryIv.setImageResource(R.drawable.flag_china)
}
it.contains("香港", true) -> {
countryIv.setImageResource(R.drawable.flag_china)
}
it.contains("日本", true) -> {
countryIv.setImageResource(R.drawable.flag_japan)
}
it.contains("韩国", true) -> {
countryIv.setImageResource(R.drawable.flag_korea)
}
it.contains("美国", true) -> {
countryIv.setImageResource(R.drawable.flag_usa)
}
it.contains("德国", true) -> {
countryIv.setImageResource(R.drawable.flag_germany)
}
it.contains("法国", true) -> {
countryIv.setImageResource(R.drawable.flag_france)
}
it.contains("俄罗斯", true) -> {
countryIv.setImageResource(R.drawable.flag_russia)
}
it.contains("新加坡", true) -> {
countryIv.setImageResource(R.drawable.flag_singapore)
}
it.contains("英国", true) -> {
countryIv.setImageResource(R.drawable.flag_uk)
}
it.contains("澳大利亚", true) -> {
countryIv.setImageResource(R.drawable.flag_australia)
}
it.contains("印度", true) -> {
countryIv.setImageResource(R.drawable.flag_india)
}
it.contains("加拿大", true) -> {
countryIv.setImageResource(R.drawable.flag_canada)
}
else -> {
countryIv.setImageResource(R.drawable.ic_s_location)
}
}
}
}
private fun goRechargeWeb() {
if (activity != null) {
var mact = activity as NewMainActivity
mact.goRechargeWeb()
}
}
private fun goNewRecharge() {
if (activity != null) {
var mact = activity as NewMainActivity
mact.goBackupRechargeWeb()
}
}
private fun updateUserInfo() {
if (activity != null) {
val mainAct = activity as NewMainActivity
mainAct.goCheckLogin()
var mact = activity as NewMainActivity
mact.goCheckLogin()
}
}
private fun startUTimer() {
private fun start_u_timer() {
if (activity != null) {
val mainAct = activity as NewMainActivity
mainAct.startUploadTimer()
var mact = activity as NewMainActivity
mact.startUploadTimer()
}
}
private fun stopUTimer() {
private fun stop_u_timer() {
if (activity != null) {
val mainAct = activity as NewMainActivity
mainAct.stopUploadTimer()
var mact = activity as NewMainActivity
mact.stopUploadTimer()
}
}
@ -529,6 +639,7 @@ open class NewAccFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dis
val isGlobal = isGlo
val isGlobal2 = MMKV.defaultMMKV().decodeBool(BZYConstants.MMKV_KEY_GLOBAL_MODEL, false)
Log.i("checkGlo isGlobal==>${isGlobal}, isGlobal2==>${isGlobal2}")
setProxyModeView(isGlobal)
launch(Dispatchers.IO) {
if (isGlobal) {
if (clashRunning) {
@ -537,15 +648,15 @@ open class NewAccFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dis
} else {
if (clashRunning) {
delay(1000)
clashBinder?.let { cm ->
NewMainActivity.clashBinder?.let { cm ->
val names = cm.queryProxyGroupNames(false)
Log.i("checkGlo names==>${names}")
if (names.isNotEmpty()) {
val selectedName =
var selected_name =
MMKV.defaultMMKV()
.decodeString(BZYConstants.NODE_SELECTED_NAME_KEY, "");
Log.i("selected_name ==>${selectedName}")
if (!TextUtils.isEmpty(selectedName)) {
Log.i("selected_name ==>${selected_name}")
if (!TextUtils.isEmpty(selected_name)) {
val o = cm.queryOverride(Clash.OverrideSlot.Session)
o.mode = TunnelState.Mode.Rule
cm.patchOverride(Clash.OverrideSlot.Session, o)
@ -558,7 +669,9 @@ open class NewAccFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dis
gName = names[1]
}
}
cm.patchSelector(gName, selectedName!!)
cm.patchSelector(gName, selected_name!!)
} else {
}
}
@ -567,86 +680,103 @@ open class NewAccFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dis
}
}
}
private suspend fun getNodes() {
coroutineScope {
val selectedName =
MMKV.defaultMMKV().decodeString(BZYConstants.NODE_SELECTED_NAME_KEY, "")
Log.d("getNodes selected_name==>${selectedName}")
if (TextUtils.isEmpty(selectedName)) {
NewMainActivity.subUrl?.let {
accelerateViewModel.get_Node_Other(it, object : AsyncCallBackListener {
override fun suc(obj: Any?) {
proList = obj as MutableList<ClashNodebBean>
proList?.let { proListItem ->
val nodeName = proListItem[0].name
if (!TextUtils.isEmpty(nodeName)) {
launch(Dispatchers.IO) {
clashBinder?.let { cm ->
val o = cm.queryOverride(Clash.OverrideSlot.Session)
o.mode = TunnelState.Mode.Global
cm.patchOverride(Clash.OverrideSlot.Session, o)
delay(3000)
//等待全局模式生效
var maxIndex = 0
while (true) {
if (maxIndex > 3) {
private fun setProxyModeView(isGlobal: Boolean) {
if (isGlobal) {
iv_proxy_mode.setImageResource(R.drawable.ic_s_global)
if (isAdded) {
tv_proxy_model.text = resources.getString(R.string.GlobalMode)
}
} else {
iv_proxy_mode.setImageResource(R.drawable.smart)
if (isAdded) {
tv_proxy_model.text = resources.getString(R.string.IntelligentMode)
}
}
}
var proList: MutableList<ClashNodebBean>? = null
private suspend fun getNodes() {
var selected_name =
MMKV.defaultMMKV().decodeString(BZYConstants.NODE_SELECTED_NAME_KEY, "");
Log.d("getNodes selected_name==>${selected_name}")
if (TextUtils.isEmpty(selected_name)) {
NewMainActivity.subUrl?.let {
accelerateViewModel?.get_Node_Other(it, object : AsyncCallBackListener {
override fun suc(obj: Any?) {
proList = obj as MutableList<ClashNodebBean>
proList?.let { proListItem ->
var nodeName = proListItem[0].name
if (!TextUtils.isEmpty(nodeName)) {
launch(Dispatchers.IO) {
NewMainActivity.clashBinder?.let { cm ->
val o = cm.queryOverride(Clash.OverrideSlot.Session)
o.mode = TunnelState.Mode.Global
cm.patchOverride(Clash.OverrideSlot.Session, o)
delay(3000)
//等待全局模式生效
var maxIndex = 0
while (true) {
if (maxIndex > 3) {
break
}
var names = cm.queryProxyGroupNames(false)
if (names.isNotEmpty()) {
if (names[0] == "GLOBAL") {
Log.d("repeat end")
break
}
val names = cm.queryProxyGroupNames(false)
if (names.isNotEmpty()) {
if (names[0] == "GLOBAL") {
Log.d("repeat end")
break
}
}
Log.d("repeat agent")
maxIndex++
delay(1000)
}
cm.patchSelector("GLOBAL", nodeName)
Log.d("repeat agent")
maxIndex++
delay(1000)
}
cm.patchSelector("GLOBAL", nodeName)
}
}
}
}
override fun faild(response: Response) {
}
override fun err(msg: String, exception: Exception?) {
}
})
}
} else {
clashBinder?.let { cm ->
val o = cm.queryOverride(Clash.OverrideSlot.Session)
o.mode = TunnelState.Mode.Global
cm.patchOverride(Clash.OverrideSlot.Session, o)
delay(3000)
//等待全局
var maxIndex = 0
while (true) {
if (maxIndex > 3) {
break
}
val names = cm.queryProxyGroupNames(false)
if (names.isNotEmpty()) {
if (names[0] == "GLOBAL") {
Log.d("repeat end")
break
}
}
Log.d("repeat agent")
maxIndex++
delay(1000)
}
cm.patchSelector("GLOBAL", selectedName!!)
override fun faild(response: Response) {
}
override fun err(msg: String, exception: Exception?) {
}
})
}
} else {
NewMainActivity.clashBinder?.let { cm ->
val o = cm.queryOverride(Clash.OverrideSlot.Session)
o.mode = TunnelState.Mode.Global
cm.patchOverride(Clash.OverrideSlot.Session, o)
delay(3000)
//等待全局
var maxIndex = 0
while (true) {
if (maxIndex > 3) {
break
}
var names = cm.queryProxyGroupNames(false)
if (names.isNotEmpty()) {
if (names[0] == "GLOBAL") {
Log.d("repeat end")
break
}
}
Log.d("repeat agent")
maxIndex++
delay(1000)
}
cm.patchSelector("GLOBAL", selected_name!!)
}
}
}
@ -671,16 +801,9 @@ open class NewAccFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dis
if(dataInfo.data!=null){
if(dataInfo.data.id!=null){
connectId= dataInfo.data.id
val serviceIntent= Intent(requireContext(), ConnectionService::class.java)
serviceIntent.putExtra("receiver",resultReceiver)
val serviceRunning =
Utils.isServiceRunning(requireContext(), ConnectionService::class.java)
android.util.Log.i("AppStartupStatus","data::"+serviceRunning)
if(!serviceRunning){
requireContext().startService(serviceIntent)
}
requireContext().startService(serviceIntent)
}
}
}
@ -689,26 +812,6 @@ open class NewAccFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dis
val serviceIntent= Intent(requireContext(), ConnectionService::class.java)
requireContext().stopService(serviceIntent)
}
HttpReqType.EXPENSES_RECORD->{
if(httpStatus.rsp is ExpensesRecordRsp){
httpStatus.rsp.let { expensesRecordRsp ->
if(expensesRecordRsp.code == "1000"){
if(expensesRecordRsp.data != null){
val expensesRecordList = expensesRecordRsp.data.expensesRecords
if(expensesRecordList!=null){
if(expensesRecordList.size>0){
tvTimeInfo.text=resources.getString(R.string.HYDq)
}else{
tvTimeInfo.text=resources.getString(R.string.YkStr)
}
}
}
}else {
RxToast.error("${expensesRecordRsp.code}${expensesRecordRsp.message}")
}
}
}
}
else->{
}
@ -727,36 +830,4 @@ open class NewAccFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dis
}
}
}
override fun onDestroy() {
super.onDestroy()
val serviceRunning =
Utils.isServiceRunning(requireContext(), ConnectionService::class.java)
if(serviceRunning){
val serviceIntent= Intent(requireContext(), ConnectionService::class.java)
requireContext().stopService(serviceIntent)
}
}
fun showLoading(str: String, context: Context) {
val message = if (TextUtils.isEmpty(str)) "进行中..." else str
if (baseLoading == null) {
baseLoading = RxDialogShapeLoading(context)
}
baseLoading?.setMessage(message)
if (baseLoading?.isShowing == false) {
baseLoading?.show()
}
}
fun hideLoading() {
if (baseLoading != null && baseLoading?.isShowing == true) {
baseLoading?.cancel()
}
}
private fun goNewRecharge() {
if (activity != null) {
val newMain = activity as NewMainActivity
newMain.goBackupRechargeWeb()
}
}
}

View File

@ -1,139 +0,0 @@
package com.yingmao.clash.ui.fragment
import android.app.Activity
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.FragmentManager
class TFragmentManager(private val mFragmentManager: FragmentManager, private val mContainerViewId: Int,
/**
* Fragment切换数组
*/
private val mFragments: MutableList<Fragment>) {
/**
* 当前选中的Tab
*/
var currentTab = 0
private set
val currentFragment: Fragment
get() = mFragments[currentTab]
/**
* 界面切换控制
*/
fun setFragments(index: Int) {
val fragment = mFragments[index]
showFragment(mFragmentManager, fragment, mContainerViewId)
currentTab = index
}
companion object {
/**
* android.app.Activity下的使用
* 动态的使用Fragment
*
*
* 在布局文件中使用 FrameLayout 标签
*
* @param activity activity
* @param fragment fragment
* @param containerViewId <FrameLayout android:id="@+id/containerViewId"></FrameLayout>
*/
fun showFragment(activity: Activity, fragment: android.app.Fragment?, containerViewId: Int) {
val fragmentManager = activity.fragmentManager
val fragmentTransaction = fragmentManager.beginTransaction()
fragmentTransaction.replace(containerViewId, fragment)
fragmentTransaction.commit()
}
/**
* androidX 包下的使用
* 动态的使用Fragment
*
*
* 在布局文件中使用 FrameLayout 标签
*
* @param fragmentLazy fragmentLazy
* @param containerViewId <FrameLayout android:id="@+id/containerViewId"></FrameLayout>
*/
fun showFragmentLazy(fragmentManager: FragmentManager, fragmentLazy: FragmentLazy, containerViewId: Int) {
val fragmentTransaction = fragmentManager.beginTransaction()
fragmentTransaction.replace(containerViewId, fragmentLazy)
fragmentTransaction.commit()
fragmentLazy.onHiddenChanged(true)
fragmentLazy.onHiddenChanged(false)
}
//----------------------------------------------------------------------------------------------Fragment的静态使用 start
//在布局文件中直接使用标签
/* <fragment
android:layout_below="@id/id_fragment_title"
android:id="@+id/id_fragment_content"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />*/
//==============================================================================================Fragment的静态使用 end
//----------------------------------------------------------------------------------------------Fragment的动态使用 start
/**
* androidX 包下的使用
* 动态的使用Fragment
*
*
* 在布局文件中使用 FrameLayout 标签
*
* @param fragment fragment
* @param containerViewId <FrameLayout android:id="@+id/containerViewId"></FrameLayout>
*/
fun showFragment(fragmentManager: FragmentManager, fragment: Fragment, containerViewId: Int) {
val fragmentTransaction = fragmentManager.beginTransaction()
fragmentTransaction.replace(containerViewId, fragment)
fragmentTransaction.commit()
fragment.onHiddenChanged(true)
fragment.onHiddenChanged(false)
}
//==============================================================================================Fragment的动态使用 end
/**
* androidX 包下的使用
* 动态的使用Fragment
*
*
* 在布局文件中使用 FrameLayout 标签
*
* @param fragmentActivity fragmentActivity
* @param fragment fragment
* @param containerViewId <FrameLayout android:id="@+id/containerViewId"></FrameLayout>
*/
fun showFragment(fragmentActivity: FragmentActivity, fragment: Fragment?, containerViewId: Int) {
val fragmentManager = fragmentActivity.supportFragmentManager
val fragmentTransaction = fragmentManager.beginTransaction()
fragmentTransaction.replace(containerViewId, fragment!!)
fragmentTransaction.commit()
}
/**
* androidX 包下的使用
* 动态的使用Fragment
*
*
* 在布局文件中使用 FrameLayout 标签
*
* @param fragmentActivity fragmentActivity
* @param fragmentLazy fragmentLazy
* @param containerViewId <FrameLayout android:id="@+id/containerViewId"></FrameLayout>
*/
fun showFragmentLazy(fragmentActivity: FragmentActivity, fragmentLazy: FragmentLazy, containerViewId: Int) {
val fragmentManager = fragmentActivity.supportFragmentManager
val fragmentTransaction = fragmentManager.beginTransaction()
fragmentTransaction.replace(containerViewId, fragmentLazy)
fragmentTransaction.commit()
fragmentLazy.onHiddenChanged(true)
fragmentLazy.onHiddenChanged(false)
}
}
init {
if (mFragments.isNotEmpty()) {
setFragments(0)
}
}
}

View File

@ -2,7 +2,6 @@ package com.yingmao.clash.fragment.home
import android.annotation.SuppressLint
import android.app.Activity
import android.app.ActivityManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
@ -45,7 +44,6 @@ import com.yingmao.clash.act.ConnectionModeActivity
import com.yingmao.clash.act.LoginActivity
import com.yingmao.clash.act.UIMainActivity
import com.yingmao.clash.act.UIMainActivity.Companion.clashBinder
import com.yingmao.clash.act.UIMainActivity.Companion.isSubbed
import com.yingmao.clash.act.UIMainActivity.Companion.newMainActGlo
import com.yingmao.clash.act.UIMainActivity.Companion.newUuid
import com.yingmao.clash.act.UIMainActivity.Companion.profileBinder
@ -53,19 +51,18 @@ import com.yingmao.clash.act.UIMainActivity.Companion.subUrl
import com.yingmao.clash.conf.BZYConstants
import com.yingmao.clash.conf.Conf
import com.yingmao.clash.entity.ClashNodebBean
import com.yingmao.clash.fragment.AccelerateViewModel
import com.yingmao.clash.fragment.BaseFragment
import com.yingmao.clash.listener.AsyncCallBackListener
import com.yingmao.clash.net.http.HttpReqType
import com.yingmao.clash.net.http.HttpStatus
import com.yingmao.clash.net.http.entity.AppStartupStatus
import com.yingmao.clash.net.http.entity.AppStopStatus
import com.yingmao.clash.net.http.entity.PhDownloadUrlRsp
import com.yingmao.clash.service.ConnectionService
import com.yingmao.clash.util.ActivityResultLifecycle
import com.yingmao.clash.util.CommonUtils
import com.yingmao.clash.util.MarqueeTextView
import com.yingmao.clash.util.PayUtils
import com.yingmao.clash.util.Utils
import com.yingmao.clash.util.startClashService
import com.yingmao.clash.util.stopClashService
import com.yingmao.clash.view.RxToast
@ -106,12 +103,12 @@ class HomeFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dispatcher
private lateinit var llLine: ConstraintLayout
private lateinit var clActivity: ConstraintLayout
private lateinit var clMember: ConstraintLayout
private val fragments = arrayListOf<Fragment>()
var proList: MutableList<ClashNodebBean>? = null
private val nextRequestKey = AtomicInteger(0)
private var reConnection = false
private var connectId = -1
private var baseLoading: RxDialogShapeLoading? = null
companion object {
const val REQUEST_CONNECT = 1
@ -186,8 +183,8 @@ class HomeFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dispatcher
llLine = view.findViewById(R.id.llLine)
clActivity = view.findViewById(R.id.clActivity)
clMember = view.findViewById(R.id.clMember)
if (BuildConfig.productId=="182269"&&BuildConfig.is_tz){
clActivity.visibility=View.INVISIBLE
if (BuildConfig.productId == "182269" && BuildConfig.is_tz) {
clActivity.visibility = View.INVISIBLE
}
llLine.setOnClickListener {
@ -212,7 +209,7 @@ class HomeFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dispatcher
return@setOnClickListener
}
var intent: Intent? = null
if (BuildConfig.is_tz&&BuildConfig.productId=="182269") {
if (BuildConfig.is_tz && BuildConfig.productId == "182269") {
intent = Intent(CommonUtils.getApp(), ChangeNodeTzActivity::class.java)
} else {
intent = Intent(CommonUtils.getApp(), ChangeNodeActivity::class.java)
@ -260,7 +257,28 @@ class HomeFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dispatcher
}
rxDialogSureCancel.show()
} else {
startClash()
if (checkExpire()) {
if (isAdded) {
hideLoading()
MaterialDialog(requireContext()).show {
this.cancelable(false)
cornerRadius(res = R.dimen.dp_10)
customView(
R.layout.expired_recharge__layout,
scrollable = false,
noVerticalPadding = true,
horizontalPadding = false
)
getCustomView().findViewById<TextView>(R.id.tv_pay_finish).setOnClickListener {
goNewRecharge()
dismiss()
}
}
}
return@setOnClickListener
}
showLoading(resources.getString(R.string.Starting), requireContext())
startClash(true)
}
}
clActivity.setOnClickListener {
@ -276,14 +294,31 @@ class HomeFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dispatcher
var selected_name =
MMKV.defaultMMKV().decodeString(BZYConstants.NODE_SELECTED_NAME_KEY, "");
MMKV.defaultMMKV().decodeString(BZYConstants.NODE_SELECTED_NAME_KEY, "")
if (!TextUtils.isEmpty(selected_name)) {
if (!BuildConfig.is_tz){
if (!BuildConfig.is_tz) {
setNodeNameAndImg(selected_name!!)
}
}
}
fun showLoading(str: String, context: Context) {
val message = if (TextUtils.isEmpty(str)) "进行中..." else str
if (baseLoading == null) {
baseLoading = RxDialogShapeLoading(context)
}
baseLoading?.setMessage(message)
if (baseLoading?.isShowing == false) {
baseLoading?.show()
}
}
fun hideLoading() {
if (baseLoading != null && baseLoading?.isShowing == true) {
baseLoading?.cancel()
}
}
private fun updateUserInfo() {
if (activity != null) {
val mact = activity as UIMainActivity
@ -384,7 +419,12 @@ class HomeFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dispatcher
MMKV.defaultMMKV().encode(BZYConstants.CONNECTID, dataInfo.data.id)
val serviceIntent = Intent(requireContext(), ConnectionService::class.java)
serviceIntent.putExtra("receiver", resultReceiver)
requireContext().startService(serviceIntent)
val serviceRunning =
Utils.isServiceRunning(requireContext(), ConnectionService::class.java)
android.util.Log.i("AppStartupStatus", "data::" + serviceRunning)
if (!serviceRunning) {
requireContext().startService(serviceIntent)
}
}
}
}
@ -457,7 +497,7 @@ class HomeFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dispatcher
if (clashRunning) {
Log.d("clashRunning $clashRunning")
} else {
startClash()
startClash(false)
}
} else {
if (::rxDialogShapeLoading.isInitialized || rxDialogShapeLoading.isShowing) {
@ -473,12 +513,12 @@ class HomeFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dispatcher
if (clashRunning) {
Log.d("clashRunning $clashRunning")
} else {
startClash()
startClash(false)
}
checkGlo()
}?.run {
if (isRunNing == true && !clashRunning) {
startClash()
startClash(false)
}
}
} else if (requestCode == Proxy_Mode_REQUEST_CODE) {
@ -486,27 +526,11 @@ class HomeFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dispatcher
}
}
private fun startClash() {
if (checkExpire()) {
if (isAdded) {
MaterialDialog(requireContext()).show {
this.cancelable(false)
cornerRadius(res = R.dimen.dp_10)
customView(
R.layout.expired_recharge__layout,
scrollable = false,
noVerticalPadding = true,
horizontalPadding = false
)
getCustomView().findViewById<TextView>(R.id.tv_pay_finish).setOnClickListener {
goNewRecharge()
dismiss()
}
}
}
return
}
private fun startClash(isStart:Boolean) {
launch {
if(isStart){
delay(2000)
}
profileBinder?.let { pm ->
val active = pm.queryActive()
if (active == null) {
@ -520,14 +544,12 @@ class HomeFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dispatcher
pm.setActive(profile)
newUuid = profile.uuid.toString()
isSubbed = true
} ?: run {
//为空的情况下选择第一个
pm.queryAll().forEach { profile ->
pm.setActive(profile)
newUuid = profile.uuid.toString()
isSubbed = true
return@forEach
}
}
@ -555,6 +577,9 @@ class HomeFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dispatcher
stopUpLoadTimer()
e.printStackTrace()
}
withContext(Dispatchers.Main) {
hideLoading()
}
}
}
@ -994,4 +1019,14 @@ class HomeFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dispatcher
})
dialog.show()
}
override fun onDestroy() {
super.onDestroy()
val serviceRunning =
Utils.isServiceRunning(requireContext(), ConnectionService::class.java)
if (serviceRunning) {
val serviceIntent = Intent(requireContext(), ConnectionService::class.java)
requireContext().stopService(serviceIntent)
}
}
}

View File

@ -1,249 +0,0 @@
package com.yingmao.clash.fragment.mine
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.RelativeLayout
import android.widget.TextView
import androidx.appcompat.widget.SwitchCompat
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.observe
import com.yingmao.clash.act.BandPhoneActivity
import com.yingmao.clash.act.ChangePwdActivity
import com.yingmao.clash.act.MyPointCardActivity
import com.yingmao.clash.act.ShareActivity
import com.yingmao.clash.act.ZFBTXActivity
import com.yingmao.clash.view.dialog.DialogBandChoose
import com.tencent.mmkv.MMKV
import com.yingmao.clash.BuildConfig
import com.yingmao.clash.R
import com.yingmao.clash.act.ExpensesRecordActivity
import com.yingmao.clash.act.LoginActivity
import com.yingmao.clash.conf.BZYConstants
import com.yingmao.clash.net.http.HttpStatus
import com.yingmao.clash.net.http.entity.AppDataInfoRsp
import com.yingmao.clash.util.CommonUtils
import com.yingmao.clash.view.RxToast
import java.text.SimpleDateFormat
import java.util.Locale
class MineFragment : Fragment(), View.OnClickListener {
private lateinit var exitTv: TextView
private lateinit var usernameTv: TextView
private lateinit var versionNameTv: TextView
private lateinit var expensesRecordTv: TextView
private lateinit var changePasswordTv: TextView
private lateinit var commonProblemTv: TextView
private lateinit var pointCardTv: TextView
private lateinit var bandGiftTv: TextView
private lateinit var tv_tixian_amount: TextView
private lateinit var expireDateTv: TextView
private lateinit var tvProxyModel: TextView
private lateinit var supportAccelerationStatusTv: TextView
private lateinit var currentVersionRl: RelativeLayout
private lateinit var scProxyModel: SwitchCompat
private lateinit var rl_band_phone: View
private var canRewardAmount: String = ""
companion object {
fun newInstance() = MineFragment()
}
private lateinit var viewModel: MineViewModel
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val view = inflater.inflate(R.layout.fragment_mine, container, false)
initViews(view)
return view
}
private fun initViews(view: View?) {
view?.let {
usernameTv = it.findViewById(R.id.tv_username)
versionNameTv = it.findViewById(R.id.tv_version_name)
versionNameTv.text = BuildConfig.VERSION_NAME
usernameTv.text = MMKV.defaultMMKV().decodeString(BZYConstants.MMKV_KEY_REMEMBER_USERNAME)
exitTv = it.findViewById(R.id.tv_exit)
exitTv.setOnClickListener(this)
expensesRecordTv = it.findViewById(R.id.tv_expenses_record)
expensesRecordTv.setOnClickListener(this)
changePasswordTv = it.findViewById(R.id.tv_change_password)
changePasswordTv.setOnClickListener(this)
commonProblemTv = it.findViewById(R.id.tv_common_problem)
commonProblemTv.setOnClickListener(this)
pointCardTv = it.findViewById(R.id.tv_point_card)
pointCardTv.setOnClickListener(this)
currentVersionRl = it.findViewById(R.id.rl_current_version)
currentVersionRl.setOnClickListener(this)
it.findViewById<View>(R.id.rl_kefu).setOnClickListener(this)
it.findViewById<View>(R.id.rl_add_freiend).setOnClickListener(this)
rl_band_phone = it.findViewById(R.id.rl_band_phone)
it.findViewById<View>(R.id.rl_tixian).setOnClickListener(this)
bandGiftTv = it.findViewById(R.id.tv_band_gift)
tv_tixian_amount = it.findViewById(R.id.tv_tixian_amount)
tvProxyModel = it.findViewById(R.id.tv_proxy_model)
scProxyModel = it.findViewById(R.id.sc_proxy_model)
scProxyModel.setOnCheckedChangeListener { _, b ->
MMKV.defaultMMKV().encode(BZYConstants.MMKV_KEY_GLOBAL_MODEL, b)
if (b) {
tvProxyModel.setText("全局模式")
} else {
tvProxyModel.setText("智能模式")
}
RxToast.success("已切换,重连接生效")
}
if (MMKV.defaultMMKV().decodeBool(BZYConstants.MMKV_KEY_GLOBAL_MODEL)) {
tvProxyModel.setText("全局模式")
}
if (MMKV.defaultMMKV().decodeBool(BZYConstants.MMKV_KEY_IS_PERFECT)) {
rl_band_phone.setOnClickListener(this)
rl_band_phone.visibility = View.VISIBLE
}
expireDateTv = it.findViewById(R.id.tv_expire_date)
val validPeriod = MMKV.defaultMMKV().decodeLong(BZYConstants.MMKV_KEY_VPN_VALID_PERIOD)
if (validPeriod > 0) {
val dateFormat = SimpleDateFormat("yyyy.MM.dd HH:mm", Locale.CHINA)
expireDateTv.text = dateFormat.format(validPeriod)
} else {
expireDateTv.text = "--"
}
supportAccelerationStatusTv = it.findViewById(R.id.tv_support_acceleration_status)
val isExpired = MMKV.defaultMMKV().decodeBool(BZYConstants.MMKV_KEY_VPN_EXPIRED)
val valid_period = MMKV.defaultMMKV().decodeLong(BZYConstants.MMKV_KEY_VPN_VALID_PERIOD)
updateAccountStatus(isExpired, valid_period)
}
viewModel = ViewModelProvider(this).get(MineViewModel::class.java)
viewModel.statusData.observe(viewLifecycleOwner) {
when (it) {
is HttpStatus.Success -> {
(it.rsp as AppDataInfoRsp).let { data ->
if (data.code == 1000) {
bandGiftTv.text = "完善成功,赠送 ${data.data?.dataGiftdays}"
// tv_tixian_amount.text = "可提现金额 ${data.data?.rewardTotalAmount} 元"
if (data.data?.rewardTotalAmount != null) {
canRewardAmount = data.data?.rewardTotalAmount!!
}
}
}
}
else -> {}
}
}
MMKV.defaultMMKV().decodeString(BZYConstants.MMKV_KEY_REMEMBER_USERNAME)
?.let { viewModel.appDataInfo(it) }
}
/*override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
}*/
override fun onClick(v: View?) {
when (v?.id) {
R.id.tv_expenses_record -> {
val intent = Intent(CommonUtils.getApp(), ExpensesRecordActivity::class.java)
startActivity(intent)
}
R.id.tv_point_card -> {
val intent = Intent(CommonUtils.getApp(), MyPointCardActivity::class.java)
startActivity(intent)
}
R.id.tv_change_password -> {
val intent = Intent(CommonUtils.getApp(), ChangePwdActivity::class.java)
startActivity(intent)
}
R.id.rl_kefu -> {
val intent = Intent(Intent.ACTION_VIEW)
val uri = Uri.parse("https://t.me/bazhuayuvpn")
intent.setData(uri)
startActivity(intent)
}
R.id.rl_current_version -> {
RxToast.success("已经是最新版本")
}
R.id.tv_exit -> {
MMKV.defaultMMKV().removeValueForKey(BZYConstants.MMKV_KEY_TOKEN)
MMKV.defaultMMKV().removeValueForKey(BZYConstants.MMKV_KEY_CURRENT_NODE)
startActivity(Intent(CommonUtils.getApp(), LoginActivity::class.java))
activity?.finish()
}
R.id.rl_add_freiend -> {
val intent = Intent(CommonUtils.getApp(), ShareActivity::class.java)
startActivity(intent)
}
R.id.rl_band_phone -> {
val dialog = DialogBandChoose(activity)
dialog.setOnBandClickListener(object : DialogBandChoose.OnBandClickListener {
override fun onBandClick(isPhone: Boolean) {
val intent = Intent(CommonUtils.getApp(), BandPhoneActivity::class.java)
intent.putExtra("isPhone", isPhone)
startActivityForResult(intent, 98)
}
})
dialog.show()
}
R.id.rl_tixian -> {
val intent = Intent(CommonUtils.getApp(), ZFBTXActivity::class.java)
startActivityForResult(intent, 90)
}
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == 98 && resultCode == 100) {
MMKV.defaultMMKV().removeValueForKey(BZYConstants.MMKV_KEY_TOKEN)
MMKV.defaultMMKV().removeValueForKey(BZYConstants.MMKV_KEY_CURRENT_NODE)
startActivity(Intent(CommonUtils.getApp(), LoginActivity::class.java))
activity?.finish()
} else if (requestCode == 90 && resultCode == 101) {
// viewModel.appDataInfo(MMKV.defaultMMKV().decodeString(BZYConstants.MMKV_KEY_REMEMBER_USERNAME))
}
}
fun updateAccountStatus(isExpired: Boolean, validityPeriod: Long) {
if (::supportAccelerationStatusTv.isInitialized) {
if (isExpired) {
supportAccelerationStatusTv.text = "不支持加速"
} else {
supportAccelerationStatusTv.text = "支持加速"
}
}
if (::expireDateTv.isInitialized) {
if (validityPeriod > 0) {
val dateFormat = SimpleDateFormat("yyyy.MM.dd HH:mm", Locale.CHINA)
expireDateTv.text = dateFormat.format(validityPeriod)
} else {
expireDateTv.text = "--"
}
}
}
}

View File

@ -1,25 +0,0 @@
package com.yingmao.clash.fragment.mine
import com.tencent.mmkv.MMKV
import com.yingmao.clash.base.PublicViewMode
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.NetService
class MineViewModel : PublicViewMode() {
fun appDataInfo(phone: String) {
val phToken = MMKV.defaultMMKV().decodeString(BZYConstants.MMKV_KEY_PH_TOKEN,"")!!
val params = mutableMapOf(
"phoneNumber" to phone,
"phToken" to phToken
)
val httpApi = NetService.instances.createHttps(HttpApi::class.java)
doAsync(HttpReqType.APPDATAINFO) { httpApi.appDataInfo(params) }
}
}

View File

@ -1,8 +1,6 @@
package com.yingmao.clash.fragment.setting
import android.annotation.SuppressLint
import android.app.AlarmManager
import android.app.PendingIntent
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
@ -11,8 +9,6 @@ import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.os.Handler
import android.os.Looper
import android.provider.Settings
import android.text.TextUtils
import android.view.View
@ -43,14 +39,12 @@ import com.google.gson.Gson
import com.hjq.permissions.OnPermissionCallback
import com.hjq.permissions.Permission
import com.hjq.permissions.XXPermissions
import com.jakewharton.processphoenix.ProcessPhoenix
import com.orhanobut.logger.Logger
import com.tencent.mmkv.MMKV
import com.yingmao.clash.BuildConfig
import com.yingmao.clash.R
import com.yingmao.clash.act.AgreementAndPrivacyActivity
import com.yingmao.clash.act.ChangePwdActivity
import com.yingmao.clash.act.InstallApplicationActivity
import com.yingmao.clash.act.LoginActivity
import com.yingmao.clash.act.LossPreventionActivity
import com.yingmao.clash.act.NewMainActivity
@ -82,7 +76,6 @@ import com.yingmao.clash.view.RxToast
import com.yingmao.clash.view.adapter.LanguageAdapter
import com.yingmao.clash.view.dialog.DialogDoMainChoose
import com.yingmao.clash.view.dialog.DialogNotice
import com.yingmao.clash.view.dialog.DialogSureRestartChoose
import com.yingmao.clash.view.dialog.RxDialogShapeLoading
import com.yingmao.clash.vm.MainAtyVM
import com.yingmao.clash.webview.BZYWebViewKeFu
@ -97,7 +90,6 @@ import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
import java.util.UUID
import kotlin.system.exitProcess
class SettingFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dispatchers.IO) {
@ -257,10 +249,10 @@ class SettingFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dispatc
}
setExpireDate()
if (checkExpire()) {
userIdentity.text = requireActivity().resources.getString(R.string.TemporaryUser)
} else {
if (isVip) {
userIdentity.text = requireActivity().resources.getString(R.string.VIPMember)
} else {
userIdentity.text = requireActivity().resources.getString(R.string.TemporaryUser)
}
userIdentityNumber.text =
MMKV.defaultMMKV().decodeString(BZYConstants.MMKV_KEY_REMEMBER_USERNAME)
@ -328,7 +320,16 @@ class SettingFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dispatc
startActivity(intent)
}
initializationRepair.setOnClickListener {
if (checkExpire()) {
RxToast.info(resources.getString(R.string.AccountExpired))
return@setOnClickListener
}
if(clashRunning){
RxToast.info("请关闭加速后在进行初始化操作")
return@setOnClickListener
}
if (TextUtils.isEmpty(subUrl)) {
RxToast.success(resources.getString(R.string.tooFast))
return@setOnClickListener
}
cleanNodeMmkv()
@ -337,6 +338,7 @@ class SettingFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dispatc
launch(Dispatchers.IO) {
profileBinder?.let { pm ->
pm.queryAll().forEach { profile ->
Log.d("setting profile delete ==>${profile.name}")
pm.delete(profile.uuid)
}
@ -636,6 +638,12 @@ class SettingFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dispatc
MMKV.defaultMMKV().removeValueForKey(BZYConstants.MMKV_KEY_VPN_UserCommand)
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)
//清除源切换记录
UIMainActivity.isSubed=false
Log.d("cleanUserInfo clashRunning==>${clashRunning}")
if (clashRunning) {
fragments.filterIsInstance<HomeFragment>().forEach { it.stopClash() }

View File

@ -228,8 +228,10 @@ interface HttpApi {
@GET("/taiziregister.txt")
suspend fun getTzRegister(): String
@GET("/taiziregister_182269.txt")
suspend fun getTz182269Register(): String
@GET("/bzyregister.txt")
suspend fun getBzyRegister(): String

View File

@ -16,6 +16,7 @@ import okhttp3.Callback
import okhttp3.Cookie
import okhttp3.CookieJar
import okhttp3.HttpUrl
import okhttp3.Interceptor
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.OkHttpClient
import okhttp3.Request
@ -72,6 +73,37 @@ class NetService private constructor() : INetService {
var httpClient: 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 {
var loggingInterceptor = HttpLoggingInterceptor(
@ -79,8 +111,10 @@ class NetService private constructor() : INetService {
override fun log(message: String) {
Log.d("loggingInterceptor==>$message")
}
}
)
loggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
var builder = OkHttpClient.Builder()
.retryOnConnectionFailure(true)
@ -95,6 +129,7 @@ class NetService private constructor() : INetService {
.hostnameVerifier(getHostnameVerifier()!!)
if (BuildConfig.DEBUG) {
builder.addInterceptor(loggingInterceptor)
// builder.addInterceptor(ExceptionLoggingInterceptor())
}
// builder.addInterceptor(loggingInterceptor)
return builder.build()
@ -161,6 +196,7 @@ class NetService private constructor() : INetService {
.addConverterFactory(ScalarsConverterFactory.create())//json解析器Gson
.baseUrl(baseUrl) //设置BaseURL需以'/'结尾
.build() //创建Retrofit对象
return retrofit.create(HttpApi::class.java)
}
fun createFreeBaseUrl2(): HttpApi {

View File

@ -13,7 +13,7 @@ import com.yingmao.clash.fragment.AccelerateViewModel;
public class ConnectionService extends Service {
private static final String TAG = "TimerService";
private static final long INTERVAL_MILLIS = 60000; // 10
private static final long INTERVAL_MILLIS = 60000; // 60
private Handler handler = new Handler();
private Intent intent;
private Runnable runnable = new Runnable() {

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

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

View File

@ -12,12 +12,12 @@ import com.yingmao.clash.act.NewMainActivity
import com.yingmao.clash.base.PublicViewMode
import com.cncat.vpn.common.log.Log
import com.google.gson.Gson
import com.yingmao.clash.act.PageInfo
import com.yingmao.clash.app.MainApplication
import com.yingmao.clash.conf.BZYConstants
import com.yingmao.clash.entity.CheckLoginRsp
import com.yingmao.clash.entity.ClashNodebBean
import com.yingmao.clash.entity.Node
import com.yingmao.clash.fragment.setting.SettingFragment
import com.yingmao.clash.http.OkHttpBase
import com.yingmao.clash.listener.AsyncCallBackListener
import com.yingmao.clash.listener.DownLoadListener

View File

@ -401,4 +401,5 @@ Service Description\n
<string name="LoginOut">It is detected that the current account is logged in on other devices. If it is not your own operation, please change the password immediately and log out of the unfamiliar device.</string>
<string name="tooFast">The application is initializing. Please wait for 1~2 seconds and try again.</string>
<string name="Starting">Starting</string>
</resources>

View File

@ -412,8 +412,8 @@
<string name="Switching">切换中,请稍后</string>
<string name="switchManually">当前节点拥堵请手动切换</string>
<string name="SwitchingSource">请关闭加速后在进行源切换</string>
<string name="LoginOut">检测到当前账号在其他设备登录,如非本人操作,请立即修改密码并退出陌生设备。</string>
<string name="tooFast">应用初始化中,请等待1~2秒后重试</string>
<string name="Starting">启动中</string>
</resources>