parent
4d7146bd7b
commit
5f55d20c83
|
@ -181,11 +181,11 @@ dependencies {
|
|||
implementation files('libs/frpclib.aar')
|
||||
|
||||
testImplementation deps.junit
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.4'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
|
||||
androidTestImplementation deps.espresso.core
|
||||
|
||||
implementation 'androidx.core:core-ktx:1.9.0'
|
||||
implementation "androidx.activity:activity-ktx:1.6.1"
|
||||
implementation 'androidx.core:core-ktx:1.8.0'
|
||||
implementation "androidx.activity:activity-ktx:1.5.1"
|
||||
implementation "androidx.fragment:fragment-ktx:1.5.4"
|
||||
implementation "androidx.cardview:cardview:1.0.0"
|
||||
implementation 'androidx.appcompat:appcompat:1.5.1'
|
||||
|
@ -202,8 +202,6 @@ dependencies {
|
|||
//WebView
|
||||
implementation 'com.github.xuexiangjys.AgentWeb:agentweb-core:1.0.0'
|
||||
implementation 'com.github.xuexiangjys.AgentWeb:agentweb-download:1.0.0'//选填
|
||||
//腾讯的键值对存储mmkv:https://github.com/Tencent/MMKV
|
||||
implementation 'com.tencent:mmkv:1.2.15'
|
||||
//屏幕适配AutoSize:https://github.com/JessYanCoding/AndroidAutoSize
|
||||
implementation 'me.jessyan:autosize:1.2.1'
|
||||
//umeng统计
|
||||
|
|
|
@ -58,6 +58,8 @@
|
|||
<application
|
||||
android:name=".App"
|
||||
android:configChanges="screenSize|keyboardHidden|orientation|keyboard"
|
||||
android:defaultToDeviceProtectedStorage="true"
|
||||
android:directBootAware="true"
|
||||
android:fullBackupContent="@xml/backup_descriptor"
|
||||
android:hardwareAccelerated="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
|
@ -215,6 +217,7 @@
|
|||
|
||||
<receiver
|
||||
android:name=".receiver.BootReceiver"
|
||||
android:defaultToDeviceProtectedStorage="true"
|
||||
android:directBootAware="true"
|
||||
android:exported="true"
|
||||
tools:ignore="IntentFilterExportedReceiver">
|
||||
|
|
|
@ -173,7 +173,7 @@ class App : Application(), CactusCallback, Configuration.Provider by Core {
|
|||
setChannelId(FRONT_CHANNEL_ID) //渠道Id
|
||||
setChannelName(FRONT_CHANNEL_NAME) //渠道名
|
||||
setTitle(getString(R.string.app_name))
|
||||
setContent(SettingUtils.notifyContent.toString())
|
||||
setContent(SettingUtils.notifyContent)
|
||||
setSmallIcon(R.drawable.ic_forwarder)
|
||||
setLargeIcon(R.mipmap.ic_launcher)
|
||||
setPendingIntent(pendingIntent)
|
||||
|
@ -214,8 +214,10 @@ class App : Application(), CactusCallback, Configuration.Provider by Core {
|
|||
*/
|
||||
private fun initLibs() {
|
||||
Core.init(this)
|
||||
// 配置文件初始化
|
||||
SharedPreference.init(applicationContext)
|
||||
// 转发历史工具类初始化
|
||||
HistoryUtils.init(this)
|
||||
HistoryUtils.init(applicationContext)
|
||||
// X系列基础库初始化
|
||||
XBasicLibInit.init(this)
|
||||
// 版本更新初始化
|
||||
|
|
|
@ -1,14 +1,11 @@
|
|||
package com.idormy.sms.forwarder.activity
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.util.Log
|
||||
import android.view.KeyEvent
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.idormy.sms.forwarder.utils.CommonUtils.Companion.showPrivacyDialog
|
||||
import com.idormy.sms.forwarder.utils.MMKVUtils
|
||||
import com.idormy.sms.forwarder.utils.SettingUtils
|
||||
import com.idormy.sms.forwarder.utils.SettingUtils.Companion.isAgreePrivacy
|
||||
import com.idormy.sms.forwarder.utils.SettingUtils.Companion.isFirstOpen
|
||||
import com.xuexiang.xui.utils.KeyboardUtils
|
||||
import com.xuexiang.xui.widget.activity.BaseSplashActivity
|
||||
import com.xuexiang.xui.widget.dialog.materialdialog.DialogAction
|
||||
|
@ -38,12 +35,6 @@ class SplashActivity : BaseSplashActivity(), CancelAdapt {
|
|||
* 启动页结束后的动作
|
||||
*/
|
||||
override fun onSplashFinished() {
|
||||
if (isFirstOpen) {
|
||||
isFirstOpen = false
|
||||
Log.d(TAG, "从SP迁移数据")
|
||||
MMKVUtils.importSharedPreferences(this)
|
||||
}
|
||||
|
||||
if (isAgreePrivacy) {
|
||||
whereToJump()
|
||||
} else {
|
||||
|
|
|
@ -1,149 +1,149 @@
|
|||
package com.idormy.sms.forwarder.fragment
|
||||
|
||||
import android.content.Intent
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.idormy.sms.forwarder.core.BaseFragment
|
||||
import com.idormy.sms.forwarder.core.webview.AgentWebActivity
|
||||
import com.idormy.sms.forwarder.databinding.FragmentAboutBinding
|
||||
import com.idormy.sms.forwarder.utils.CacheUtils
|
||||
import com.idormy.sms.forwarder.utils.CommonUtils.Companion.gotoProtocol
|
||||
import com.idormy.sms.forwarder.utils.CommonUtils.Companion.previewMarkdown
|
||||
import com.idormy.sms.forwarder.utils.CommonUtils.Companion.previewPicture
|
||||
import com.idormy.sms.forwarder.utils.HistoryUtils
|
||||
import com.idormy.sms.forwarder.utils.HttpServerUtils
|
||||
import com.idormy.sms.forwarder.utils.XToastUtils
|
||||
import com.idormy.sms.forwarder.utils.sdkinit.XUpdateInit
|
||||
import com.xuexiang.xaop.annotation.SingleClick
|
||||
import com.xuexiang.xpage.annotation.Page
|
||||
import com.xuexiang.xui.widget.actionbar.TitleBar
|
||||
import com.xuexiang.xui.widget.textview.supertextview.SuperTextView
|
||||
import com.xuexiang.xutil.app.AppUtils
|
||||
import com.xuexiang.xutil.file.FileUtils
|
||||
import frpclib.Frpclib
|
||||
import java.io.File
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
@Page(name = "关于软件")
|
||||
class AboutFragment : BaseFragment<FragmentAboutBinding?>(), SuperTextView.OnSuperTextViewClickListener {
|
||||
|
||||
override fun viewBindingInflate(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup,
|
||||
): FragmentAboutBinding {
|
||||
return FragmentAboutBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
override fun initTitle(): TitleBar? {
|
||||
val titleBar = super.initTitle()!!.setImmersive(false)
|
||||
titleBar.setTitle(R.string.menu_about)
|
||||
return titleBar
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化控件
|
||||
*/
|
||||
override fun initViews() {
|
||||
binding!!.menuVersion.setLeftString(String.format(resources.getString(R.string.about_app_version), AppUtils.getAppVersionName()))
|
||||
binding!!.menuCache.setLeftString(String.format(resources.getString(R.string.about_cache_size), CacheUtils.getTotalCacheSize(requireContext())))
|
||||
|
||||
if (FileUtils.isFileExists(context?.filesDir?.absolutePath + "/libs/libgojni.so")) {
|
||||
binding!!.menuFrpc.setLeftString(String.format(resources.getString(R.string.about_frpc_version), Frpclib.getVersion()))
|
||||
binding!!.menuFrpc.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
val dateFormat = SimpleDateFormat("yyyy", Locale.CHINA)
|
||||
val currentYear = dateFormat.format(Date())
|
||||
binding!!.copyright.text = java.lang.String.format(resources.getString(R.string.about_copyright), currentYear)
|
||||
}
|
||||
|
||||
override fun initListeners() {
|
||||
binding!!.btnUpdate.setOnClickListener {
|
||||
XUpdateInit.checkUpdate(requireContext(), true)
|
||||
}
|
||||
binding!!.btnCache.setOnClickListener {
|
||||
HistoryUtils.clear()
|
||||
CacheUtils.clearAllCache(requireContext())
|
||||
XToastUtils.success(R.string.about_cache_purged)
|
||||
binding!!.menuCache.setLeftString(String.format(resources.getString(R.string.about_cache_size), CacheUtils.getTotalCacheSize(requireContext())))
|
||||
}
|
||||
binding!!.btnFrpc.setOnClickListener {
|
||||
try {
|
||||
val soFile = File(context?.filesDir?.absolutePath + "/libs/libgojni.so")
|
||||
if (soFile.exists()) soFile.delete()
|
||||
XToastUtils.success(R.string.about_frpc_deleted)
|
||||
|
||||
val intent: Intent? = context?.packageManager?.getLaunchIntentForPackage(context?.packageName.toString())
|
||||
intent?.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||
startActivity(intent)
|
||||
android.os.Process.killProcess(android.os.Process.myPid()) //杀掉以前进程
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
XToastUtils.error(e.message.toString())
|
||||
}
|
||||
}
|
||||
binding!!.btnGithub.setOnClickListener {
|
||||
AgentWebActivity.goWeb(context, getString(R.string.url_project_github))
|
||||
}
|
||||
binding!!.btnGitee.setOnClickListener {
|
||||
AgentWebActivity.goWeb(context, getString(R.string.url_project_gitee))
|
||||
}
|
||||
binding!!.btnAddQqGroup1.setOnClickListener {
|
||||
AgentWebActivity.goWeb(context, getString(R.string.url_add_qq_group_1))
|
||||
}
|
||||
binding!!.btnAddQqGroup2.setOnClickListener {
|
||||
AgentWebActivity.goWeb(context, getString(R.string.url_add_qq_group_2))
|
||||
}
|
||||
binding!!.btnAddQqGroup3.setOnClickListener {
|
||||
AgentWebActivity.goWeb(context, getString(R.string.url_add_qq_group_3))
|
||||
}
|
||||
binding!!.btnAddQqGroup4.setOnClickListener {
|
||||
AgentWebActivity.goWeb(context, getString(R.string.url_add_qq_group_4))
|
||||
}
|
||||
binding!!.btnAddQqGroup5.setOnClickListener {
|
||||
AgentWebActivity.goWeb(context, getString(R.string.url_add_qq_group_5))
|
||||
}
|
||||
|
||||
binding!!.menuWechatMiniprogram.setOnSuperTextViewClickListener(this)
|
||||
binding!!.menuDonation.setOnSuperTextViewClickListener(this)
|
||||
binding!!.menuWecomGroup.setOnSuperTextViewClickListener(this)
|
||||
binding!!.menuDingtalkGroup.setOnSuperTextViewClickListener(this)
|
||||
binding!!.menuQqChannel.setOnSuperTextViewClickListener(this)
|
||||
binding!!.menuUserProtocol.setOnSuperTextViewClickListener(this)
|
||||
binding!!.menuPrivacyProtocol.setOnSuperTextViewClickListener(this)
|
||||
}
|
||||
|
||||
@SingleClick
|
||||
override fun onClick(v: SuperTextView) {
|
||||
when (v.id) {
|
||||
R.id.menu_donation -> {
|
||||
previewMarkdown(this, getString(R.string.about_item_donation_link), getString(R.string.url_donation_link), false)
|
||||
}
|
||||
R.id.menu_wechat_miniprogram -> {
|
||||
if (HttpServerUtils.safetyMeasures != 3) {
|
||||
XToastUtils.error("微信小程序只支持SM4加密传输!请前往主动控制·服务端修改安全措施!")
|
||||
//return
|
||||
}
|
||||
previewPicture(this, getString(R.string.url_wechat_miniprogram), null)
|
||||
}
|
||||
R.id.menu_wecom_group -> {
|
||||
previewPicture(this, getString(R.string.url_wework_group), null)
|
||||
}
|
||||
R.id.menu_dingtalk_group -> {
|
||||
previewPicture(this, getString(R.string.url_dingtalk_group), null)
|
||||
}
|
||||
R.id.menu_qq_channel -> {
|
||||
AgentWebActivity.goWeb(context, getString(R.string.url_qq_channel))
|
||||
}
|
||||
R.id.menu_user_protocol -> {
|
||||
gotoProtocol(this, isPrivacy = false, isImmersive = false)
|
||||
}
|
||||
R.id.menu_privacy_protocol -> {
|
||||
gotoProtocol(this, isPrivacy = true, isImmersive = false)
|
||||
}
|
||||
}
|
||||
}
|
||||
package com.idormy.sms.forwarder.fragment
|
||||
|
||||
import android.content.Intent
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.idormy.sms.forwarder.core.BaseFragment
|
||||
import com.idormy.sms.forwarder.core.webview.AgentWebActivity
|
||||
import com.idormy.sms.forwarder.databinding.FragmentAboutBinding
|
||||
import com.idormy.sms.forwarder.utils.CacheUtils
|
||||
import com.idormy.sms.forwarder.utils.CommonUtils.Companion.gotoProtocol
|
||||
import com.idormy.sms.forwarder.utils.CommonUtils.Companion.previewMarkdown
|
||||
import com.idormy.sms.forwarder.utils.CommonUtils.Companion.previewPicture
|
||||
import com.idormy.sms.forwarder.utils.HistoryUtils
|
||||
import com.idormy.sms.forwarder.utils.HttpServerUtils
|
||||
import com.idormy.sms.forwarder.utils.XToastUtils
|
||||
import com.idormy.sms.forwarder.utils.sdkinit.XUpdateInit
|
||||
import com.xuexiang.xaop.annotation.SingleClick
|
||||
import com.xuexiang.xpage.annotation.Page
|
||||
import com.xuexiang.xui.widget.actionbar.TitleBar
|
||||
import com.xuexiang.xui.widget.textview.supertextview.SuperTextView
|
||||
import com.xuexiang.xutil.app.AppUtils
|
||||
import com.xuexiang.xutil.file.FileUtils
|
||||
import frpclib.Frpclib
|
||||
import java.io.File
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
@Page(name = "关于软件")
|
||||
class AboutFragment : BaseFragment<FragmentAboutBinding?>(), SuperTextView.OnSuperTextViewClickListener {
|
||||
|
||||
override fun viewBindingInflate(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup,
|
||||
): FragmentAboutBinding {
|
||||
return FragmentAboutBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
override fun initTitle(): TitleBar? {
|
||||
val titleBar = super.initTitle()!!.setImmersive(false)
|
||||
titleBar.setTitle(R.string.menu_about)
|
||||
return titleBar
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化控件
|
||||
*/
|
||||
override fun initViews() {
|
||||
binding!!.menuVersion.setLeftString(String.format(resources.getString(R.string.about_app_version), AppUtils.getAppVersionName()))
|
||||
binding!!.menuCache.setLeftString(String.format(resources.getString(R.string.about_cache_size), CacheUtils.getTotalCacheSize(requireContext())))
|
||||
|
||||
if (FileUtils.isFileExists(context?.filesDir?.absolutePath + "/libs/libgojni.so")) {
|
||||
binding!!.menuFrpc.setLeftString(String.format(resources.getString(R.string.about_frpc_version), Frpclib.getVersion()))
|
||||
binding!!.menuFrpc.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
val dateFormat = SimpleDateFormat("yyyy", Locale.CHINA)
|
||||
val currentYear = dateFormat.format(Date())
|
||||
binding!!.copyright.text = java.lang.String.format(resources.getString(R.string.about_copyright), currentYear)
|
||||
}
|
||||
|
||||
override fun initListeners() {
|
||||
binding!!.btnUpdate.setOnClickListener {
|
||||
XUpdateInit.checkUpdate(requireContext(), true)
|
||||
}
|
||||
binding!!.btnCache.setOnClickListener {
|
||||
HistoryUtils.clearPreference()
|
||||
CacheUtils.clearAllCache(requireContext())
|
||||
XToastUtils.success(R.string.about_cache_purged)
|
||||
binding!!.menuCache.setLeftString(String.format(resources.getString(R.string.about_cache_size), CacheUtils.getTotalCacheSize(requireContext())))
|
||||
}
|
||||
binding!!.btnFrpc.setOnClickListener {
|
||||
try {
|
||||
val soFile = File(context?.filesDir?.absolutePath + "/libs/libgojni.so")
|
||||
if (soFile.exists()) soFile.delete()
|
||||
XToastUtils.success(R.string.about_frpc_deleted)
|
||||
|
||||
val intent: Intent? = context?.packageManager?.getLaunchIntentForPackage(context?.packageName.toString())
|
||||
intent?.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||
startActivity(intent)
|
||||
android.os.Process.killProcess(android.os.Process.myPid()) //杀掉以前进程
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
XToastUtils.error(e.message.toString())
|
||||
}
|
||||
}
|
||||
binding!!.btnGithub.setOnClickListener {
|
||||
AgentWebActivity.goWeb(context, getString(R.string.url_project_github))
|
||||
}
|
||||
binding!!.btnGitee.setOnClickListener {
|
||||
AgentWebActivity.goWeb(context, getString(R.string.url_project_gitee))
|
||||
}
|
||||
binding!!.btnAddQqGroup1.setOnClickListener {
|
||||
AgentWebActivity.goWeb(context, getString(R.string.url_add_qq_group_1))
|
||||
}
|
||||
binding!!.btnAddQqGroup2.setOnClickListener {
|
||||
AgentWebActivity.goWeb(context, getString(R.string.url_add_qq_group_2))
|
||||
}
|
||||
binding!!.btnAddQqGroup3.setOnClickListener {
|
||||
AgentWebActivity.goWeb(context, getString(R.string.url_add_qq_group_3))
|
||||
}
|
||||
binding!!.btnAddQqGroup4.setOnClickListener {
|
||||
AgentWebActivity.goWeb(context, getString(R.string.url_add_qq_group_4))
|
||||
}
|
||||
binding!!.btnAddQqGroup5.setOnClickListener {
|
||||
AgentWebActivity.goWeb(context, getString(R.string.url_add_qq_group_5))
|
||||
}
|
||||
|
||||
binding!!.menuWechatMiniprogram.setOnSuperTextViewClickListener(this)
|
||||
binding!!.menuDonation.setOnSuperTextViewClickListener(this)
|
||||
binding!!.menuWecomGroup.setOnSuperTextViewClickListener(this)
|
||||
binding!!.menuDingtalkGroup.setOnSuperTextViewClickListener(this)
|
||||
binding!!.menuQqChannel.setOnSuperTextViewClickListener(this)
|
||||
binding!!.menuUserProtocol.setOnSuperTextViewClickListener(this)
|
||||
binding!!.menuPrivacyProtocol.setOnSuperTextViewClickListener(this)
|
||||
}
|
||||
|
||||
@SingleClick
|
||||
override fun onClick(v: SuperTextView) {
|
||||
when (v.id) {
|
||||
R.id.menu_donation -> {
|
||||
previewMarkdown(this, getString(R.string.about_item_donation_link), getString(R.string.url_donation_link), false)
|
||||
}
|
||||
R.id.menu_wechat_miniprogram -> {
|
||||
if (HttpServerUtils.safetyMeasures != 3) {
|
||||
XToastUtils.error("微信小程序只支持SM4加密传输!请前往主动控制·服务端修改安全措施!")
|
||||
//return
|
||||
}
|
||||
previewPicture(this, getString(R.string.url_wechat_miniprogram), null)
|
||||
}
|
||||
R.id.menu_wecom_group -> {
|
||||
previewPicture(this, getString(R.string.url_wework_group), null)
|
||||
}
|
||||
R.id.menu_dingtalk_group -> {
|
||||
previewPicture(this, getString(R.string.url_dingtalk_group), null)
|
||||
}
|
||||
R.id.menu_qq_channel -> {
|
||||
AgentWebActivity.goWeb(context, getString(R.string.url_qq_channel))
|
||||
}
|
||||
R.id.menu_user_protocol -> {
|
||||
gotoProtocol(this, isPrivacy = false, isImmersive = false)
|
||||
}
|
||||
R.id.menu_privacy_protocol -> {
|
||||
gotoProtocol(this, isPrivacy = true, isImmersive = false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,387 +1,387 @@
|
|||
package com.idormy.sms.forwarder.fragment
|
||||
|
||||
import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.RadioGroup
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.idormy.sms.forwarder.App
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.idormy.sms.forwarder.adapter.WidgetItemAdapter
|
||||
import com.idormy.sms.forwarder.core.BaseFragment
|
||||
import com.idormy.sms.forwarder.databinding.FragmentClientBinding
|
||||
import com.idormy.sms.forwarder.server.model.BaseResponse
|
||||
import com.idormy.sms.forwarder.server.model.ConfigData
|
||||
import com.idormy.sms.forwarder.utils.*
|
||||
import com.xuexiang.xaop.annotation.SingleClick
|
||||
import com.xuexiang.xhttp2.XHttp
|
||||
import com.xuexiang.xhttp2.cache.model.CacheMode
|
||||
import com.xuexiang.xhttp2.callback.SimpleCallBack
|
||||
import com.xuexiang.xhttp2.exception.ApiException
|
||||
import com.xuexiang.xpage.annotation.Page
|
||||
import com.xuexiang.xpage.base.XPageFragment
|
||||
import com.xuexiang.xpage.core.PageOption
|
||||
import com.xuexiang.xpage.model.PageInfo
|
||||
import com.xuexiang.xrouter.utils.TextUtils
|
||||
import com.xuexiang.xui.adapter.recyclerview.RecyclerViewHolder
|
||||
import com.xuexiang.xui.utils.CountDownButtonHelper
|
||||
import com.xuexiang.xui.utils.DensityUtils
|
||||
import com.xuexiang.xui.utils.ResUtils
|
||||
import com.xuexiang.xui.utils.WidgetUtils
|
||||
import com.xuexiang.xui.widget.actionbar.TitleBar
|
||||
import com.xuexiang.xui.widget.dialog.materialdialog.DialogAction
|
||||
import com.xuexiang.xui.widget.dialog.materialdialog.MaterialDialog
|
||||
import com.xuexiang.xutil.XUtil
|
||||
import com.xuexiang.xutil.data.ConvertTools
|
||||
|
||||
|
||||
@Suppress("PrivatePropertyName", "PropertyName")
|
||||
@Page(name = "主动控制·客户端")
|
||||
class ClientFragment : BaseFragment<FragmentClientBinding?>(), View.OnClickListener, RecyclerViewHolder.OnItemClickListener<PageInfo> {
|
||||
|
||||
val TAG: String = ClientFragment::class.java.simpleName
|
||||
private var appContext: App? = null
|
||||
private var serverConfig: ConfigData? = null
|
||||
private var serverHistory: MutableMap<String, String> = mutableMapOf()
|
||||
private var mCountDownHelper: CountDownButtonHelper? = null
|
||||
|
||||
override fun initViews() {
|
||||
appContext = requireActivity().application as App
|
||||
|
||||
//测试按钮增加倒计时,避免重复点击
|
||||
mCountDownHelper = CountDownButtonHelper(binding!!.btnServerTest, 3)
|
||||
mCountDownHelper!!.setOnCountDownListener(object : CountDownButtonHelper.OnCountDownListener {
|
||||
override fun onCountDown(time: Int) {
|
||||
binding!!.btnServerTest.text = String.format(getString(R.string.seconds_n), time)
|
||||
}
|
||||
|
||||
override fun onFinished() {
|
||||
binding!!.btnServerTest.text = getString(R.string.server_test)
|
||||
}
|
||||
})
|
||||
|
||||
WidgetUtils.initGridRecyclerView(binding!!.recyclerView, 3, DensityUtils.dp2px(1f))
|
||||
val widgetItemAdapter = WidgetItemAdapter(CLIENT_FRAGMENT_LIST)
|
||||
widgetItemAdapter.setOnItemClickListener(this)
|
||||
binding!!.recyclerView.adapter = widgetItemAdapter
|
||||
|
||||
//取出历史记录
|
||||
val history = HttpServerUtils.serverHistory
|
||||
if (!TextUtils.isEmpty(history)) {
|
||||
serverHistory = Gson().fromJson(history, object : TypeToken<MutableMap<String, String>>() {}.type)
|
||||
}
|
||||
|
||||
if (CommonUtils.checkUrl(HttpServerUtils.serverAddress)) queryConfig(false)
|
||||
}
|
||||
|
||||
override fun initTitle(): TitleBar? {
|
||||
val titleBar = super.initTitle()!!.setImmersive(false)
|
||||
//纯客户端模式
|
||||
if (SettingUtils.enablePureClientMode) {
|
||||
titleBar.setTitle(R.string.app_name).setSubTitle(getString(R.string.menu_client)).disableLeftView()
|
||||
titleBar.addAction(object : TitleBar.ImageAction(R.drawable.ic_logout) {
|
||||
@SingleClick
|
||||
override fun performAction(view: View) {
|
||||
XToastUtils.success(getString(R.string.exit_pure_client_mode))
|
||||
SettingUtils.enablePureClientMode = false
|
||||
XUtil.exitApp()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
titleBar.setTitle(R.string.menu_client)
|
||||
}
|
||||
return titleBar
|
||||
}
|
||||
|
||||
override fun viewBindingInflate(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup,
|
||||
): FragmentClientBinding {
|
||||
return FragmentClientBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
override fun initListeners() {
|
||||
binding!!.etServerAddress.setText(HttpServerUtils.serverAddress)
|
||||
binding!!.etServerAddress.addTextChangedListener(object : TextWatcher {
|
||||
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
|
||||
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
|
||||
override fun afterTextChanged(s: Editable) {
|
||||
HttpServerUtils.serverAddress = binding!!.etServerAddress.text.toString().trim().trimEnd('/')
|
||||
}
|
||||
})
|
||||
|
||||
//安全措施
|
||||
var safetyMeasuresId = R.id.rb_safety_measures_none
|
||||
when (HttpServerUtils.clientSafetyMeasures) {
|
||||
1 -> {
|
||||
safetyMeasuresId = R.id.rb_safety_measures_sign
|
||||
binding!!.tvSignKey.text = getString(R.string.sign_key)
|
||||
}
|
||||
2 -> {
|
||||
safetyMeasuresId = R.id.rb_safety_measures_rsa
|
||||
binding!!.tvSignKey.text = getString(R.string.public_key)
|
||||
}
|
||||
3 -> {
|
||||
safetyMeasuresId = R.id.rb_safety_measures_sm4
|
||||
binding!!.tvSignKey.text = getString(R.string.sm4_key)
|
||||
}
|
||||
else -> {
|
||||
binding!!.layoutSignKey.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
binding!!.rgSafetyMeasures.check(safetyMeasuresId)
|
||||
binding!!.rgSafetyMeasures.setOnCheckedChangeListener { _: RadioGroup?, checkedId: Int ->
|
||||
var safetyMeasures = 0
|
||||
binding!!.layoutSignKey.visibility = View.VISIBLE
|
||||
when (checkedId) {
|
||||
R.id.rb_safety_measures_sign -> {
|
||||
safetyMeasures = 1
|
||||
binding!!.tvSignKey.text = getString(R.string.sign_key)
|
||||
}
|
||||
R.id.rb_safety_measures_rsa -> {
|
||||
safetyMeasures = 2
|
||||
binding!!.tvSignKey.text = getString(R.string.public_key)
|
||||
}
|
||||
R.id.rb_safety_measures_sm4 -> {
|
||||
safetyMeasures = 3
|
||||
binding!!.tvSignKey.text = getString(R.string.sm4_key)
|
||||
}
|
||||
else -> {
|
||||
binding!!.layoutSignKey.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
HttpServerUtils.clientSafetyMeasures = safetyMeasures
|
||||
}
|
||||
|
||||
binding!!.etSignKey.setText(HttpServerUtils.clientSignKey)
|
||||
binding!!.etSignKey.addTextChangedListener(object : TextWatcher {
|
||||
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
|
||||
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
|
||||
override fun afterTextChanged(s: Editable) {
|
||||
HttpServerUtils.clientSignKey = binding!!.etSignKey.text.toString().trim()
|
||||
}
|
||||
})
|
||||
|
||||
binding!!.btnWechatMiniprogram.setOnClickListener(this)
|
||||
binding!!.btnServerHistory.setOnClickListener(this)
|
||||
binding!!.btnServerTest.setOnClickListener(this)
|
||||
}
|
||||
|
||||
@SingleClick
|
||||
override fun onClick(v: View) {
|
||||
when (v.id) {
|
||||
R.id.btn_wechat_miniprogram -> {
|
||||
if (HttpServerUtils.safetyMeasures != 3) {
|
||||
XToastUtils.error("微信小程序只支持SM4加密传输!请前往主动控制·服务端修改安全措施!")
|
||||
return
|
||||
}
|
||||
CommonUtils.previewPicture(this, getString(R.string.url_wechat_miniprogram), null)
|
||||
}
|
||||
R.id.btn_server_history -> {
|
||||
if (serverHistory.isEmpty()) {
|
||||
XToastUtils.warning(getString(R.string.no_server_history))
|
||||
return
|
||||
}
|
||||
Log.d(TAG, "serverHistory = $serverHistory")
|
||||
|
||||
MaterialDialog.Builder(requireContext()).title(R.string.server_history).items(serverHistory.keys).itemsCallbackSingleChoice(0) { _: MaterialDialog?, _: View?, _: Int, text: CharSequence ->
|
||||
//XToastUtils.info("$which: $text")
|
||||
val matches = Regex("【(.*)】(.*)", RegexOption.IGNORE_CASE).findAll(text).toList().flatMap(MatchResult::groupValues)
|
||||
Log.i(TAG, "matches = $matches")
|
||||
if (matches.isNotEmpty()) {
|
||||
binding!!.etServerAddress.setText(matches[2])
|
||||
} else {
|
||||
binding!!.etServerAddress.setText(text)
|
||||
}
|
||||
val signKey = serverHistory[text].toString()
|
||||
if (!TextUtils.isEmpty(signKey)) {
|
||||
val keyMatches = Regex("(.*)##(.*)", RegexOption.IGNORE_CASE).findAll(signKey).toList().flatMap(MatchResult::groupValues)
|
||||
Log.i(TAG, "keyMatches = $keyMatches")
|
||||
if (keyMatches.isNotEmpty()) {
|
||||
binding!!.etSignKey.setText(keyMatches[1])
|
||||
var safetyMeasuresId = R.id.rb_safety_measures_none
|
||||
when (keyMatches[2]) {
|
||||
"1" -> {
|
||||
safetyMeasuresId = R.id.rb_safety_measures_sign
|
||||
binding!!.tvSignKey.text = getString(R.string.sign_key)
|
||||
}
|
||||
"2" -> {
|
||||
safetyMeasuresId = R.id.rb_safety_measures_rsa
|
||||
binding!!.tvSignKey.text = getString(R.string.public_key)
|
||||
}
|
||||
"3" -> {
|
||||
safetyMeasuresId = R.id.rb_safety_measures_sm4
|
||||
binding!!.tvSignKey.text = getString(R.string.sm4_key)
|
||||
}
|
||||
else -> {
|
||||
binding!!.tvSignKey.visibility = View.GONE
|
||||
binding!!.etSignKey.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
binding!!.rgSafetyMeasures.check(safetyMeasuresId)
|
||||
} else {
|
||||
binding!!.etSignKey.setText(serverHistory[text])
|
||||
}
|
||||
}
|
||||
true // allow selection
|
||||
}.positiveText(R.string.select).negativeText(R.string.cancel).neutralText(R.string.clear_history).neutralColor(ResUtils.getColors(R.color.red)).onNeutral { _: MaterialDialog?, _: DialogAction? ->
|
||||
serverHistory.clear()
|
||||
HttpServerUtils.serverHistory = ""
|
||||
}.show()
|
||||
}
|
||||
R.id.btn_server_test -> {
|
||||
if (!CommonUtils.checkUrl(HttpServerUtils.serverAddress)) {
|
||||
XToastUtils.error(getString(R.string.invalid_service_address))
|
||||
return
|
||||
}
|
||||
queryConfig(true)
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onItemClick(itemView: View, item: PageInfo, position: Int) {
|
||||
try {
|
||||
if (item.name != ResUtils.getString(R.string.api_clone) && !CommonUtils.checkUrl(HttpServerUtils.serverAddress)) {
|
||||
XToastUtils.error(getString(R.string.invalid_service_address))
|
||||
serverConfig = null
|
||||
return
|
||||
}
|
||||
if (serverConfig == null && item.name != ResUtils.getString(R.string.api_clone)) {
|
||||
XToastUtils.error(getString(R.string.click_test_button_first))
|
||||
return
|
||||
}
|
||||
if (serverConfig != null && ((item.name == ResUtils.getString(R.string.api_sms_send) && !serverConfig!!.enableApiSmsSend) || (item.name == ResUtils.getString(R.string.api_sms_query) && !serverConfig!!.enableApiSmsQuery) || (item.name == ResUtils.getString(R.string.api_call_query) && !serverConfig!!.enableApiCallQuery) || (item.name == ResUtils.getString(R.string.api_contact_query) && !serverConfig!!.enableApiContactQuery) || (item.name == ResUtils.getString(R.string.api_battery_query) && !serverConfig!!.enableApiBatteryQuery) || (item.name == ResUtils.getString(R.string.api_wol) && !serverConfig!!.enableApiWol))) {
|
||||
XToastUtils.error(getString(R.string.disabled_on_the_server))
|
||||
return
|
||||
}
|
||||
@Suppress("UNCHECKED_CAST") PageOption.to(Class.forName(item.classPath) as Class<XPageFragment>) //跳转的fragment
|
||||
.setNewActivity(true).open(this)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
XToastUtils.error(e.message.toString())
|
||||
}
|
||||
}
|
||||
|
||||
private fun queryConfig(needToast: Boolean) {
|
||||
val requestUrl: String = HttpServerUtils.serverAddress + "/config/query"
|
||||
Log.i(TAG, "requestUrl:$requestUrl")
|
||||
|
||||
val msgMap: MutableMap<String, Any> = mutableMapOf()
|
||||
val timestamp = System.currentTimeMillis()
|
||||
msgMap["timestamp"] = timestamp
|
||||
|
||||
val clientSignKey = HttpServerUtils.clientSignKey.toString()
|
||||
if (HttpServerUtils.clientSafetyMeasures != 0 && TextUtils.isEmpty(clientSignKey)) {
|
||||
if (needToast) XToastUtils.error("请输入签名密钥/RSA公钥/SM4密钥")
|
||||
return
|
||||
}
|
||||
|
||||
if (HttpServerUtils.clientSafetyMeasures == 1 && !TextUtils.isEmpty(clientSignKey)) {
|
||||
msgMap["sign"] = HttpServerUtils.calcSign(timestamp.toString(), clientSignKey)
|
||||
}
|
||||
val dataMap: MutableMap<String, Any> = mutableMapOf()
|
||||
msgMap["data"] = dataMap
|
||||
|
||||
var requestMsg: String = Gson().toJson(msgMap)
|
||||
Log.i(TAG, "requestMsg:$requestMsg")
|
||||
|
||||
val postRequest = XHttp.post(requestUrl)
|
||||
.keepJson(true)
|
||||
.timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
|
||||
.cacheMode(CacheMode.NO_CACHE).timeStamp(true)
|
||||
|
||||
when (HttpServerUtils.clientSafetyMeasures) {
|
||||
2 -> {
|
||||
try {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey.toString())
|
||||
requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
requestMsg = RSACrypt.encryptByPublicKey(requestMsg, publicKey)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
if (needToast) XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
3 -> {
|
||||
try {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey.toString())
|
||||
//requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
val encryptCBC = SM4Crypt.encrypt(requestMsg.toByteArray(), sm4Key)
|
||||
requestMsg = ConvertTools.bytes2HexString(encryptCBC)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
if (needToast) XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
else -> {
|
||||
postRequest.upJson(requestMsg)
|
||||
}
|
||||
}
|
||||
|
||||
if (needToast) mCountDownHelper?.start()
|
||||
postRequest.execute(object : SimpleCallBack<String>() {
|
||||
override fun onError(e: ApiException) {
|
||||
XToastUtils.error(e.displayMessage)
|
||||
if (needToast) mCountDownHelper?.finish()
|
||||
}
|
||||
|
||||
override fun onSuccess(response: String) {
|
||||
Log.i(TAG, response)
|
||||
try {
|
||||
var json = response
|
||||
if (HttpServerUtils.clientSafetyMeasures == 2) {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey.toString())
|
||||
json = RSACrypt.decryptByPublicKey(json, publicKey)
|
||||
json = String(Base64.decode(json))
|
||||
} else if (HttpServerUtils.clientSafetyMeasures == 3) {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey.toString())
|
||||
val encryptCBC = ConvertTools.hexStringToByteArray(json)
|
||||
val decryptCBC = SM4Crypt.decrypt(encryptCBC, sm4Key)
|
||||
json = String(decryptCBC)
|
||||
}
|
||||
val resp: BaseResponse<ConfigData> = Gson().fromJson(json, object : TypeToken<BaseResponse<ConfigData>>() {}.type)
|
||||
if (resp.code == 200) {
|
||||
serverConfig = resp.data!!
|
||||
if (needToast) XToastUtils.success(ResUtils.getString(R.string.request_succeeded))
|
||||
//删除3.0.8之前保存的记录
|
||||
serverHistory.remove(HttpServerUtils.serverAddress.toString())
|
||||
//添加到历史记录
|
||||
val key = "【${serverConfig?.extraDeviceMark}】${HttpServerUtils.serverAddress.toString()}"
|
||||
if (TextUtils.isEmpty(HttpServerUtils.clientSignKey)) {
|
||||
serverHistory[key] = "SMSFORWARDER##" + HttpServerUtils.clientSafetyMeasures.toString()
|
||||
} else {
|
||||
serverHistory[key] = HttpServerUtils.clientSignKey + "##" + HttpServerUtils.clientSafetyMeasures.toString()
|
||||
}
|
||||
HttpServerUtils.serverHistory = Gson().toJson(serverHistory)
|
||||
HttpServerUtils.serverConfig = Gson().toJson(serverConfig)
|
||||
} else {
|
||||
if (needToast) XToastUtils.error(ResUtils.getString(R.string.request_failed) + resp.msg)
|
||||
}
|
||||
if (needToast) mCountDownHelper?.finish()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
if (needToast) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + response)
|
||||
mCountDownHelper?.finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
if (mCountDownHelper != null) mCountDownHelper!!.recycle()
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
package com.idormy.sms.forwarder.fragment
|
||||
|
||||
import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.RadioGroup
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.idormy.sms.forwarder.App
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.idormy.sms.forwarder.adapter.WidgetItemAdapter
|
||||
import com.idormy.sms.forwarder.core.BaseFragment
|
||||
import com.idormy.sms.forwarder.databinding.FragmentClientBinding
|
||||
import com.idormy.sms.forwarder.server.model.BaseResponse
|
||||
import com.idormy.sms.forwarder.server.model.ConfigData
|
||||
import com.idormy.sms.forwarder.utils.*
|
||||
import com.xuexiang.xaop.annotation.SingleClick
|
||||
import com.xuexiang.xhttp2.XHttp
|
||||
import com.xuexiang.xhttp2.cache.model.CacheMode
|
||||
import com.xuexiang.xhttp2.callback.SimpleCallBack
|
||||
import com.xuexiang.xhttp2.exception.ApiException
|
||||
import com.xuexiang.xpage.annotation.Page
|
||||
import com.xuexiang.xpage.base.XPageFragment
|
||||
import com.xuexiang.xpage.core.PageOption
|
||||
import com.xuexiang.xpage.model.PageInfo
|
||||
import com.xuexiang.xrouter.utils.TextUtils
|
||||
import com.xuexiang.xui.adapter.recyclerview.RecyclerViewHolder
|
||||
import com.xuexiang.xui.utils.CountDownButtonHelper
|
||||
import com.xuexiang.xui.utils.DensityUtils
|
||||
import com.xuexiang.xui.utils.ResUtils
|
||||
import com.xuexiang.xui.utils.WidgetUtils
|
||||
import com.xuexiang.xui.widget.actionbar.TitleBar
|
||||
import com.xuexiang.xui.widget.dialog.materialdialog.DialogAction
|
||||
import com.xuexiang.xui.widget.dialog.materialdialog.MaterialDialog
|
||||
import com.xuexiang.xutil.XUtil
|
||||
import com.xuexiang.xutil.data.ConvertTools
|
||||
|
||||
|
||||
@Suppress("PrivatePropertyName", "PropertyName")
|
||||
@Page(name = "主动控制·客户端")
|
||||
class ClientFragment : BaseFragment<FragmentClientBinding?>(), View.OnClickListener, RecyclerViewHolder.OnItemClickListener<PageInfo> {
|
||||
|
||||
val TAG: String = ClientFragment::class.java.simpleName
|
||||
private var appContext: App? = null
|
||||
private var serverConfig: ConfigData? = null
|
||||
private var serverHistory: MutableMap<String, String> = mutableMapOf()
|
||||
private var mCountDownHelper: CountDownButtonHelper? = null
|
||||
|
||||
override fun initViews() {
|
||||
appContext = requireActivity().application as App
|
||||
|
||||
//测试按钮增加倒计时,避免重复点击
|
||||
mCountDownHelper = CountDownButtonHelper(binding!!.btnServerTest, 3)
|
||||
mCountDownHelper!!.setOnCountDownListener(object : CountDownButtonHelper.OnCountDownListener {
|
||||
override fun onCountDown(time: Int) {
|
||||
binding!!.btnServerTest.text = String.format(getString(R.string.seconds_n), time)
|
||||
}
|
||||
|
||||
override fun onFinished() {
|
||||
binding!!.btnServerTest.text = getString(R.string.server_test)
|
||||
}
|
||||
})
|
||||
|
||||
WidgetUtils.initGridRecyclerView(binding!!.recyclerView, 3, DensityUtils.dp2px(1f))
|
||||
val widgetItemAdapter = WidgetItemAdapter(CLIENT_FRAGMENT_LIST)
|
||||
widgetItemAdapter.setOnItemClickListener(this)
|
||||
binding!!.recyclerView.adapter = widgetItemAdapter
|
||||
|
||||
//取出历史记录
|
||||
val history = HttpServerUtils.serverHistory
|
||||
if (!TextUtils.isEmpty(history)) {
|
||||
serverHistory = Gson().fromJson(history, object : TypeToken<MutableMap<String, String>>() {}.type)
|
||||
}
|
||||
|
||||
if (CommonUtils.checkUrl(HttpServerUtils.serverAddress)) queryConfig(false)
|
||||
}
|
||||
|
||||
override fun initTitle(): TitleBar? {
|
||||
val titleBar = super.initTitle()!!.setImmersive(false)
|
||||
//纯客户端模式
|
||||
if (SettingUtils.enablePureClientMode) {
|
||||
titleBar.setTitle(R.string.app_name).setSubTitle(getString(R.string.menu_client)).disableLeftView()
|
||||
titleBar.addAction(object : TitleBar.ImageAction(R.drawable.ic_logout) {
|
||||
@SingleClick
|
||||
override fun performAction(view: View) {
|
||||
XToastUtils.success(getString(R.string.exit_pure_client_mode))
|
||||
SettingUtils.enablePureClientMode = false
|
||||
XUtil.exitApp()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
titleBar.setTitle(R.string.menu_client)
|
||||
}
|
||||
return titleBar
|
||||
}
|
||||
|
||||
override fun viewBindingInflate(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup,
|
||||
): FragmentClientBinding {
|
||||
return FragmentClientBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
override fun initListeners() {
|
||||
binding!!.etServerAddress.setText(HttpServerUtils.serverAddress)
|
||||
binding!!.etServerAddress.addTextChangedListener(object : TextWatcher {
|
||||
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
|
||||
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
|
||||
override fun afterTextChanged(s: Editable) {
|
||||
HttpServerUtils.serverAddress = binding!!.etServerAddress.text.toString().trim().trimEnd('/')
|
||||
}
|
||||
})
|
||||
|
||||
//安全措施
|
||||
var safetyMeasuresId = R.id.rb_safety_measures_none
|
||||
when (HttpServerUtils.clientSafetyMeasures) {
|
||||
1 -> {
|
||||
safetyMeasuresId = R.id.rb_safety_measures_sign
|
||||
binding!!.tvSignKey.text = getString(R.string.sign_key)
|
||||
}
|
||||
2 -> {
|
||||
safetyMeasuresId = R.id.rb_safety_measures_rsa
|
||||
binding!!.tvSignKey.text = getString(R.string.public_key)
|
||||
}
|
||||
3 -> {
|
||||
safetyMeasuresId = R.id.rb_safety_measures_sm4
|
||||
binding!!.tvSignKey.text = getString(R.string.sm4_key)
|
||||
}
|
||||
else -> {
|
||||
binding!!.layoutSignKey.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
binding!!.rgSafetyMeasures.check(safetyMeasuresId)
|
||||
binding!!.rgSafetyMeasures.setOnCheckedChangeListener { _: RadioGroup?, checkedId: Int ->
|
||||
var safetyMeasures = 0
|
||||
binding!!.layoutSignKey.visibility = View.VISIBLE
|
||||
when (checkedId) {
|
||||
R.id.rb_safety_measures_sign -> {
|
||||
safetyMeasures = 1
|
||||
binding!!.tvSignKey.text = getString(R.string.sign_key)
|
||||
}
|
||||
R.id.rb_safety_measures_rsa -> {
|
||||
safetyMeasures = 2
|
||||
binding!!.tvSignKey.text = getString(R.string.public_key)
|
||||
}
|
||||
R.id.rb_safety_measures_sm4 -> {
|
||||
safetyMeasures = 3
|
||||
binding!!.tvSignKey.text = getString(R.string.sm4_key)
|
||||
}
|
||||
else -> {
|
||||
binding!!.layoutSignKey.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
HttpServerUtils.clientSafetyMeasures = safetyMeasures
|
||||
}
|
||||
|
||||
binding!!.etSignKey.setText(HttpServerUtils.clientSignKey)
|
||||
binding!!.etSignKey.addTextChangedListener(object : TextWatcher {
|
||||
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
|
||||
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
|
||||
override fun afterTextChanged(s: Editable) {
|
||||
HttpServerUtils.clientSignKey = binding!!.etSignKey.text.toString().trim()
|
||||
}
|
||||
})
|
||||
|
||||
binding!!.btnWechatMiniprogram.setOnClickListener(this)
|
||||
binding!!.btnServerHistory.setOnClickListener(this)
|
||||
binding!!.btnServerTest.setOnClickListener(this)
|
||||
}
|
||||
|
||||
@SingleClick
|
||||
override fun onClick(v: View) {
|
||||
when (v.id) {
|
||||
R.id.btn_wechat_miniprogram -> {
|
||||
if (HttpServerUtils.safetyMeasures != 3) {
|
||||
XToastUtils.error("微信小程序只支持SM4加密传输!请前往主动控制·服务端修改安全措施!")
|
||||
return
|
||||
}
|
||||
CommonUtils.previewPicture(this, getString(R.string.url_wechat_miniprogram), null)
|
||||
}
|
||||
R.id.btn_server_history -> {
|
||||
if (serverHistory.isEmpty()) {
|
||||
XToastUtils.warning(getString(R.string.no_server_history))
|
||||
return
|
||||
}
|
||||
Log.d(TAG, "serverHistory = $serverHistory")
|
||||
|
||||
MaterialDialog.Builder(requireContext()).title(R.string.server_history).items(serverHistory.keys).itemsCallbackSingleChoice(0) { _: MaterialDialog?, _: View?, _: Int, text: CharSequence ->
|
||||
//XToastUtils.info("$which: $text")
|
||||
val matches = Regex("【(.*)】(.*)", RegexOption.IGNORE_CASE).findAll(text).toList().flatMap(MatchResult::groupValues)
|
||||
Log.i(TAG, "matches = $matches")
|
||||
if (matches.isNotEmpty()) {
|
||||
binding!!.etServerAddress.setText(matches[2])
|
||||
} else {
|
||||
binding!!.etServerAddress.setText(text)
|
||||
}
|
||||
val signKey = serverHistory[text].toString()
|
||||
if (!TextUtils.isEmpty(signKey)) {
|
||||
val keyMatches = Regex("(.*)##(.*)", RegexOption.IGNORE_CASE).findAll(signKey).toList().flatMap(MatchResult::groupValues)
|
||||
Log.i(TAG, "keyMatches = $keyMatches")
|
||||
if (keyMatches.isNotEmpty()) {
|
||||
binding!!.etSignKey.setText(keyMatches[1])
|
||||
var safetyMeasuresId = R.id.rb_safety_measures_none
|
||||
when (keyMatches[2]) {
|
||||
"1" -> {
|
||||
safetyMeasuresId = R.id.rb_safety_measures_sign
|
||||
binding!!.tvSignKey.text = getString(R.string.sign_key)
|
||||
}
|
||||
"2" -> {
|
||||
safetyMeasuresId = R.id.rb_safety_measures_rsa
|
||||
binding!!.tvSignKey.text = getString(R.string.public_key)
|
||||
}
|
||||
"3" -> {
|
||||
safetyMeasuresId = R.id.rb_safety_measures_sm4
|
||||
binding!!.tvSignKey.text = getString(R.string.sm4_key)
|
||||
}
|
||||
else -> {
|
||||
binding!!.tvSignKey.visibility = View.GONE
|
||||
binding!!.etSignKey.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
binding!!.rgSafetyMeasures.check(safetyMeasuresId)
|
||||
} else {
|
||||
binding!!.etSignKey.setText(serverHistory[text])
|
||||
}
|
||||
}
|
||||
true // allow selection
|
||||
}.positiveText(R.string.select).negativeText(R.string.cancel).neutralText(R.string.clear_history).neutralColor(ResUtils.getColors(R.color.red)).onNeutral { _: MaterialDialog?, _: DialogAction? ->
|
||||
serverHistory.clear()
|
||||
HttpServerUtils.serverHistory = ""
|
||||
}.show()
|
||||
}
|
||||
R.id.btn_server_test -> {
|
||||
if (!CommonUtils.checkUrl(HttpServerUtils.serverAddress)) {
|
||||
XToastUtils.error(getString(R.string.invalid_service_address))
|
||||
return
|
||||
}
|
||||
queryConfig(true)
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onItemClick(itemView: View, item: PageInfo, position: Int) {
|
||||
try {
|
||||
if (item.name != ResUtils.getString(R.string.api_clone) && !CommonUtils.checkUrl(HttpServerUtils.serverAddress)) {
|
||||
XToastUtils.error(getString(R.string.invalid_service_address))
|
||||
serverConfig = null
|
||||
return
|
||||
}
|
||||
if (serverConfig == null && item.name != ResUtils.getString(R.string.api_clone)) {
|
||||
XToastUtils.error(getString(R.string.click_test_button_first))
|
||||
return
|
||||
}
|
||||
if (serverConfig != null && ((item.name == ResUtils.getString(R.string.api_sms_send) && !serverConfig!!.enableApiSmsSend) || (item.name == ResUtils.getString(R.string.api_sms_query) && !serverConfig!!.enableApiSmsQuery) || (item.name == ResUtils.getString(R.string.api_call_query) && !serverConfig!!.enableApiCallQuery) || (item.name == ResUtils.getString(R.string.api_contact_query) && !serverConfig!!.enableApiContactQuery) || (item.name == ResUtils.getString(R.string.api_battery_query) && !serverConfig!!.enableApiBatteryQuery) || (item.name == ResUtils.getString(R.string.api_wol) && !serverConfig!!.enableApiWol))) {
|
||||
XToastUtils.error(getString(R.string.disabled_on_the_server))
|
||||
return
|
||||
}
|
||||
@Suppress("UNCHECKED_CAST") PageOption.to(Class.forName(item.classPath) as Class<XPageFragment>) //跳转的fragment
|
||||
.setNewActivity(true).open(this)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
XToastUtils.error(e.message.toString())
|
||||
}
|
||||
}
|
||||
|
||||
private fun queryConfig(needToast: Boolean) {
|
||||
val requestUrl: String = HttpServerUtils.serverAddress + "/config/query"
|
||||
Log.i(TAG, "requestUrl:$requestUrl")
|
||||
|
||||
val msgMap: MutableMap<String, Any> = mutableMapOf()
|
||||
val timestamp = System.currentTimeMillis()
|
||||
msgMap["timestamp"] = timestamp
|
||||
|
||||
val clientSignKey = HttpServerUtils.clientSignKey
|
||||
if (HttpServerUtils.clientSafetyMeasures != 0 && TextUtils.isEmpty(clientSignKey)) {
|
||||
if (needToast) XToastUtils.error("请输入签名密钥/RSA公钥/SM4密钥")
|
||||
return
|
||||
}
|
||||
|
||||
if (HttpServerUtils.clientSafetyMeasures == 1 && !TextUtils.isEmpty(clientSignKey)) {
|
||||
msgMap["sign"] = HttpServerUtils.calcSign(timestamp.toString(), clientSignKey)
|
||||
}
|
||||
val dataMap: MutableMap<String, Any> = mutableMapOf()
|
||||
msgMap["data"] = dataMap
|
||||
|
||||
var requestMsg: String = Gson().toJson(msgMap)
|
||||
Log.i(TAG, "requestMsg:$requestMsg")
|
||||
|
||||
val postRequest = XHttp.post(requestUrl)
|
||||
.keepJson(true)
|
||||
.timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
|
||||
.cacheMode(CacheMode.NO_CACHE).timeStamp(true)
|
||||
|
||||
when (HttpServerUtils.clientSafetyMeasures) {
|
||||
2 -> {
|
||||
try {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey)
|
||||
requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
requestMsg = RSACrypt.encryptByPublicKey(requestMsg, publicKey)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
if (needToast) XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
3 -> {
|
||||
try {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey)
|
||||
//requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
val encryptCBC = SM4Crypt.encrypt(requestMsg.toByteArray(), sm4Key)
|
||||
requestMsg = ConvertTools.bytes2HexString(encryptCBC)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
if (needToast) XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
else -> {
|
||||
postRequest.upJson(requestMsg)
|
||||
}
|
||||
}
|
||||
|
||||
if (needToast) mCountDownHelper?.start()
|
||||
postRequest.execute(object : SimpleCallBack<String>() {
|
||||
override fun onError(e: ApiException) {
|
||||
XToastUtils.error(e.displayMessage)
|
||||
if (needToast) mCountDownHelper?.finish()
|
||||
}
|
||||
|
||||
override fun onSuccess(response: String) {
|
||||
Log.i(TAG, response)
|
||||
try {
|
||||
var json = response
|
||||
if (HttpServerUtils.clientSafetyMeasures == 2) {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey)
|
||||
json = RSACrypt.decryptByPublicKey(json, publicKey)
|
||||
json = String(Base64.decode(json))
|
||||
} else if (HttpServerUtils.clientSafetyMeasures == 3) {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey)
|
||||
val encryptCBC = ConvertTools.hexStringToByteArray(json)
|
||||
val decryptCBC = SM4Crypt.decrypt(encryptCBC, sm4Key)
|
||||
json = String(decryptCBC)
|
||||
}
|
||||
val resp: BaseResponse<ConfigData> = Gson().fromJson(json, object : TypeToken<BaseResponse<ConfigData>>() {}.type)
|
||||
if (resp.code == 200) {
|
||||
serverConfig = resp.data!!
|
||||
if (needToast) XToastUtils.success(ResUtils.getString(R.string.request_succeeded))
|
||||
//删除3.0.8之前保存的记录
|
||||
serverHistory.remove(HttpServerUtils.serverAddress)
|
||||
//添加到历史记录
|
||||
val key = "【${serverConfig?.extraDeviceMark}】${HttpServerUtils.serverAddress}"
|
||||
if (TextUtils.isEmpty(HttpServerUtils.clientSignKey)) {
|
||||
serverHistory[key] = "SMSFORWARDER##" + HttpServerUtils.clientSafetyMeasures.toString()
|
||||
} else {
|
||||
serverHistory[key] = HttpServerUtils.clientSignKey + "##" + HttpServerUtils.clientSafetyMeasures.toString()
|
||||
}
|
||||
HttpServerUtils.serverHistory = Gson().toJson(serverHistory)
|
||||
HttpServerUtils.serverConfig = Gson().toJson(serverConfig)
|
||||
} else {
|
||||
if (needToast) XToastUtils.error(ResUtils.getString(R.string.request_failed) + resp.msg)
|
||||
}
|
||||
if (needToast) mCountDownHelper?.finish()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
if (needToast) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + response)
|
||||
mCountDownHelper?.finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
if (mCountDownHelper != null) mCountDownHelper!!.recycle()
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
}
|
|
@ -79,23 +79,15 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding?>(), View.OnClickL
|
|||
switchEnableSms(binding!!.sbEnableSms)
|
||||
//转发通话记录
|
||||
switchEnablePhone(
|
||||
binding!!.sbEnablePhone,
|
||||
binding!!.scbCallType1,
|
||||
binding!!.scbCallType2,
|
||||
binding!!.scbCallType3,
|
||||
binding!!.scbCallType4
|
||||
binding!!.sbEnablePhone, binding!!.scbCallType1, binding!!.scbCallType2, binding!!.scbCallType3, binding!!.scbCallType4
|
||||
)
|
||||
//转发应用通知
|
||||
switchEnableAppNotify(
|
||||
binding!!.sbEnableAppNotify,
|
||||
binding!!.scbCancelAppNotify,
|
||||
binding!!.scbNotUserPresent
|
||||
binding!!.sbEnableAppNotify, binding!!.scbCancelAppNotify, binding!!.scbNotUserPresent
|
||||
)
|
||||
//启动时异步获取已安装App信息
|
||||
switchEnableLoadAppList(
|
||||
binding!!.sbEnableLoadAppList,
|
||||
binding!!.scbLoadUserApp,
|
||||
binding!!.scbLoadSystemApp
|
||||
binding!!.sbEnableLoadAppList, binding!!.scbLoadUserApp, binding!!.scbLoadSystemApp
|
||||
)
|
||||
//过滤多久内重复消息
|
||||
binding!!.xsbDuplicateMessagesLimits.setDefaultValue(SettingUtils.duplicateMessagesLimits)
|
||||
|
@ -103,8 +95,7 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding?>(), View.OnClickL
|
|||
SettingUtils.duplicateMessagesLimits = newValue
|
||||
}
|
||||
//免打扰(禁用转发)时间段
|
||||
binding!!.tvSilentPeriod.text =
|
||||
mTimeOption[SettingUtils.silentPeriodStart] + " ~ " + mTimeOption[SettingUtils.silentPeriodEnd]
|
||||
binding!!.tvSilentPeriod.text = mTimeOption[SettingUtils.silentPeriodStart] + " ~ " + mTimeOption[SettingUtils.silentPeriodEnd]
|
||||
//自动删除N天前的转发记录
|
||||
binding!!.xsbAutoCleanLogs.setDefaultValue(SettingUtils.autoCleanLogsDays)
|
||||
binding!!.xsbAutoCleanLogs.setOnSeekBarListener { _: XSeekBar?, newValue: Int ->
|
||||
|
@ -129,9 +120,7 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding?>(), View.OnClickL
|
|||
|
||||
//Cactus增强保活措施
|
||||
switchEnableCactus(
|
||||
binding!!.sbEnableCactus,
|
||||
binding!!.scbPlaySilenceMusic,
|
||||
binding!!.scbOnePixelActivity
|
||||
binding!!.sbEnableCactus, binding!!.scbPlaySilenceMusic, binding!!.scbOnePixelActivity
|
||||
)
|
||||
|
||||
//接口请求失败重试时间间隔
|
||||
|
@ -176,21 +165,17 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding?>(), View.OnClickL
|
|||
val etSmsTemplate: EditText = binding!!.etSmsTemplate
|
||||
when (v.id) {
|
||||
R.id.btn_silent_period -> {
|
||||
OptionsPickerBuilder(
|
||||
context,
|
||||
OnOptionsSelectListener { _: View?, options1: Int, options2: Int, _: Int ->
|
||||
SettingUtils.silentPeriodStart = options1
|
||||
SettingUtils.silentPeriodEnd = options2
|
||||
val txt = mTimeOption[options1] + " ~ " + mTimeOption[options2]
|
||||
binding!!.tvSilentPeriod.text = txt
|
||||
XToastUtils.toast(txt)
|
||||
return@OnOptionsSelectListener false
|
||||
}).setTitleText(getString(R.string.select_time_period))
|
||||
.setSelectOptions(SettingUtils.silentPeriodStart, SettingUtils.silentPeriodEnd)
|
||||
.build<Any>().also {
|
||||
it.setNPicker(mTimeOption, mTimeOption)
|
||||
it.show()
|
||||
}
|
||||
OptionsPickerBuilder(context, OnOptionsSelectListener { _: View?, options1: Int, options2: Int, _: Int ->
|
||||
SettingUtils.silentPeriodStart = options1
|
||||
SettingUtils.silentPeriodEnd = options2
|
||||
val txt = mTimeOption[options1] + " ~ " + mTimeOption[options2]
|
||||
binding!!.tvSilentPeriod.text = txt
|
||||
XToastUtils.toast(txt)
|
||||
return@OnOptionsSelectListener false
|
||||
}).setTitleText(getString(R.string.select_time_period)).setSelectOptions(SettingUtils.silentPeriodStart, SettingUtils.silentPeriodEnd).build<Any>().also {
|
||||
it.setNPicker(mTimeOption, mTimeOption)
|
||||
it.show()
|
||||
}
|
||||
}
|
||||
R.id.btn_extra_device_mark -> {
|
||||
binding!!.etExtraDeviceMark.setText(PhoneUtils.getDeviceName())
|
||||
|
@ -201,8 +186,7 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding?>(), View.OnClickL
|
|||
if (App.SimInfoList.isEmpty()) {
|
||||
XToastUtils.error(R.string.tip_can_not_get_sim_infos)
|
||||
XXPermissions.startPermissionActivity(
|
||||
requireContext(),
|
||||
"android.permission.READ_PHONE_STATE"
|
||||
requireContext(), "android.permission.READ_PHONE_STATE"
|
||||
)
|
||||
return
|
||||
}
|
||||
|
@ -210,8 +194,7 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding?>(), View.OnClickL
|
|||
if (!App.SimInfoList.containsKey(0)) {
|
||||
XToastUtils.error(
|
||||
String.format(
|
||||
getString(R.string.tip_can_not_get_sim_info),
|
||||
1
|
||||
getString(R.string.tip_can_not_get_sim_info), 1
|
||||
)
|
||||
)
|
||||
return
|
||||
|
@ -225,8 +208,7 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding?>(), View.OnClickL
|
|||
if (App.SimInfoList.isEmpty()) {
|
||||
XToastUtils.error(R.string.tip_can_not_get_sim_infos)
|
||||
XXPermissions.startPermissionActivity(
|
||||
requireContext(),
|
||||
"android.permission.READ_PHONE_STATE"
|
||||
requireContext(), "android.permission.READ_PHONE_STATE"
|
||||
)
|
||||
return
|
||||
}
|
||||
|
@ -234,8 +216,7 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding?>(), View.OnClickL
|
|||
if (!App.SimInfoList.containsKey(1)) {
|
||||
XToastUtils.error(
|
||||
String.format(
|
||||
getString(R.string.tip_can_not_get_sim_info),
|
||||
2
|
||||
getString(R.string.tip_can_not_get_sim_info), 2
|
||||
)
|
||||
)
|
||||
return
|
||||
|
@ -254,22 +235,19 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding?>(), View.OnClickL
|
|||
}
|
||||
R.id.bt_insert_extra -> {
|
||||
CommonUtils.insertOrReplaceText2Cursor(
|
||||
etSmsTemplate,
|
||||
getString(R.string.tag_card_slot)
|
||||
etSmsTemplate, getString(R.string.tag_card_slot)
|
||||
)
|
||||
return
|
||||
}
|
||||
R.id.bt_insert_time -> {
|
||||
CommonUtils.insertOrReplaceText2Cursor(
|
||||
etSmsTemplate,
|
||||
getString(R.string.tag_receive_time)
|
||||
etSmsTemplate, getString(R.string.tag_receive_time)
|
||||
)
|
||||
return
|
||||
}
|
||||
R.id.bt_insert_device_name -> {
|
||||
CommonUtils.insertOrReplaceText2Cursor(
|
||||
etSmsTemplate,
|
||||
getString(R.string.tag_device_name)
|
||||
etSmsTemplate, getString(R.string.tag_device_name)
|
||||
)
|
||||
return
|
||||
}
|
||||
|
@ -291,8 +269,7 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding?>(), View.OnClickL
|
|||
// 发送短信
|
||||
//.permission(Permission.SEND_SMS)
|
||||
// 读取短信
|
||||
.permission(Permission.READ_SMS)
|
||||
.request(object : OnPermissionCallback {
|
||||
.permission(Permission.READ_SMS).request(object : OnPermissionCallback {
|
||||
override fun onGranted(permissions: List<String>, all: Boolean) {
|
||||
if (all) {
|
||||
XToastUtils.info(R.string.toast_granted_all)
|
||||
|
@ -320,11 +297,7 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding?>(), View.OnClickL
|
|||
//转发通话
|
||||
@SuppressLint("UseSwitchCompatOrMaterialCode")
|
||||
fun switchEnablePhone(
|
||||
sbEnablePhone: SwitchButton,
|
||||
scbCallType1: SmoothCheckBox,
|
||||
scbCallType2: SmoothCheckBox,
|
||||
scbCallType3: SmoothCheckBox,
|
||||
scbCallType4: SmoothCheckBox
|
||||
sbEnablePhone: SwitchButton, scbCallType1: SmoothCheckBox, scbCallType2: SmoothCheckBox, scbCallType3: SmoothCheckBox, scbCallType4: SmoothCheckBox
|
||||
) {
|
||||
sbEnablePhone.isChecked = SettingUtils.enablePhone
|
||||
scbCallType1.isChecked = SettingUtils.enableCallType1
|
||||
|
@ -349,8 +322,7 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding?>(), View.OnClickL
|
|||
// 读取通话记录
|
||||
.permission(Permission.READ_CALL_LOG)
|
||||
// 读取联系人
|
||||
.permission(Permission.READ_CONTACTS)
|
||||
.request(object : OnPermissionCallback {
|
||||
.permission(Permission.READ_CONTACTS).request(object : OnPermissionCallback {
|
||||
override fun onGranted(permissions: List<String>, all: Boolean) {
|
||||
if (all) {
|
||||
XToastUtils.info(R.string.toast_granted_all)
|
||||
|
@ -410,9 +382,7 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding?>(), View.OnClickL
|
|||
//转发应用通知
|
||||
@SuppressLint("UseSwitchCompatOrMaterialCode")
|
||||
fun switchEnableAppNotify(
|
||||
sbEnableAppNotify: SwitchButton,
|
||||
scbCancelAppNotify: SmoothCheckBox,
|
||||
scbNotUserPresent: SmoothCheckBox
|
||||
sbEnableAppNotify: SwitchButton, scbCancelAppNotify: SmoothCheckBox, scbNotUserPresent: SmoothCheckBox
|
||||
) {
|
||||
val layoutOptionalAction: LinearLayout = binding!!.layoutOptionalAction
|
||||
val isEnable: Boolean = SettingUtils.enableAppNotify
|
||||
|
@ -428,8 +398,7 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding?>(), View.OnClickL
|
|||
// 通知栏监听权限
|
||||
.permission(Permission.BIND_NOTIFICATION_LISTENER_SERVICE)
|
||||
// 通知栏权限
|
||||
.permission(Permission.NOTIFICATION_SERVICE)
|
||||
.request(object : OnPermissionCallback {
|
||||
.permission(Permission.NOTIFICATION_SERVICE).request(object : OnPermissionCallback {
|
||||
override fun onGranted(permissions: List<String>, all: Boolean) {
|
||||
SettingUtils.enableAppNotify = true
|
||||
sbEnableAppNotify.isChecked = true
|
||||
|
@ -441,17 +410,11 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding?>(), View.OnClickL
|
|||
sbEnableAppNotify.isChecked = false
|
||||
XToastUtils.error(R.string.tips_notification_listener)
|
||||
// 如果是被永久拒绝就跳转到应用权限系统设置页面
|
||||
MaterialDialog.Builder(context!!)
|
||||
.content(R.string.toast_denied_never)
|
||||
.positiveText(R.string.lab_yes)
|
||||
.negativeText(R.string.lab_no)
|
||||
.onPositive { _: MaterialDialog?, _: DialogAction? ->
|
||||
XXPermissions.startPermissionActivity(
|
||||
requireContext(),
|
||||
permissions
|
||||
)
|
||||
}
|
||||
.show()
|
||||
MaterialDialog.Builder(context!!).content(R.string.toast_denied_never).positiveText(R.string.lab_yes).negativeText(R.string.lab_no).onPositive { _: MaterialDialog?, _: DialogAction? ->
|
||||
XXPermissions.startPermissionActivity(
|
||||
requireContext(), permissions
|
||||
)
|
||||
}.show()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -469,9 +432,7 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding?>(), View.OnClickL
|
|||
//启动时异步获取已安装App信息 (binding!!.sbEnableLoadAppList, binding!!.scbLoadUserApp, binding!!.scbLoadSystemApp)
|
||||
@SuppressLint("UseSwitchCompatOrMaterialCode")
|
||||
fun switchEnableLoadAppList(
|
||||
sbEnableLoadAppList: SwitchButton,
|
||||
scbLoadUserApp: SmoothCheckBox,
|
||||
scbLoadSystemApp: SmoothCheckBox
|
||||
sbEnableLoadAppList: SwitchButton, scbLoadUserApp: SmoothCheckBox, scbLoadSystemApp: SmoothCheckBox
|
||||
) {
|
||||
val isEnable: Boolean = SettingUtils.enableLoadAppList
|
||||
sbEnableLoadAppList.isChecked = isEnable
|
||||
|
@ -516,12 +477,10 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding?>(), View.OnClickL
|
|||
|
||||
//设置低电量报警
|
||||
private fun editBatteryLevelAlarm(
|
||||
xrsBatteryLevelAlarm: XRangeSlider,
|
||||
scbBatteryLevelAlarmOnce: SmoothCheckBox
|
||||
xrsBatteryLevelAlarm: XRangeSlider, scbBatteryLevelAlarmOnce: SmoothCheckBox
|
||||
) {
|
||||
xrsBatteryLevelAlarm.setStartingMinMax(
|
||||
SettingUtils.batteryLevelMin,
|
||||
SettingUtils.batteryLevelMax
|
||||
SettingUtils.batteryLevelMin, SettingUtils.batteryLevelMax
|
||||
)
|
||||
xrsBatteryLevelAlarm.setOnRangeSliderListener(object : OnRangeSliderListener {
|
||||
override fun onMaxChanged(slider: XRangeSlider, maxValue: Int) {
|
||||
|
@ -548,8 +507,7 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding?>(), View.OnClickL
|
|||
@SuppressLint("UseSwitchCompatOrMaterialCode")
|
||||
fun switchBatteryCron(sbBatteryCron: SwitchButton) {
|
||||
sbBatteryCron.isChecked = SettingUtils.enableBatteryCron
|
||||
binding!!.layoutBatteryCron.visibility =
|
||||
if (SettingUtils.enableBatteryCron) View.VISIBLE else View.GONE
|
||||
binding!!.layoutBatteryCron.visibility = if (SettingUtils.enableBatteryCron) View.VISIBLE else View.GONE
|
||||
sbBatteryCron.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
|
||||
binding!!.layoutBatteryCron.visibility = if (isChecked) View.VISIBLE else View.GONE
|
||||
SettingUtils.enableBatteryCron = isChecked
|
||||
|
@ -560,8 +518,7 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding?>(), View.OnClickL
|
|||
|
||||
//设置推送电池状态时机
|
||||
private fun editBatteryCronTiming(
|
||||
etBatteryCronStartTime: EditText,
|
||||
etBatteryCronInterval: EditText
|
||||
etBatteryCronStartTime: EditText, etBatteryCronInterval: EditText
|
||||
) {
|
||||
etBatteryCronStartTime.setText(SettingUtils.batteryCronStartTime)
|
||||
etBatteryCronStartTime.setOnClickListener {
|
||||
|
@ -573,12 +530,7 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding?>(), View.OnClickL
|
|||
//BatteryReportCronTask.getSingleton().updateTimer()
|
||||
}
|
||||
//.setTimeSelectChangeListener { date: Date? -> etBatteryCronStartTime.setText(DateUtils.date2String(date, DateUtils.HHmm.get())) }
|
||||
.setType(false, false, false, true, true, false)
|
||||
.setTitleText(getString(R.string.time_picker))
|
||||
.setSubmitText(getString(R.string.ok))
|
||||
.setCancelText(getString(R.string.cancel))
|
||||
.setDate(calendar)
|
||||
.build()
|
||||
.setType(false, false, false, true, true, false).setTitleText(getString(R.string.time_picker)).setSubmitText(getString(R.string.ok)).setCancelText(getString(R.string.cancel)).setDate(calendar).build()
|
||||
mTimePicker.show()
|
||||
}
|
||||
|
||||
|
@ -601,8 +553,7 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding?>(), View.OnClickL
|
|||
|
||||
//开机启动
|
||||
private fun checkWithReboot(
|
||||
@SuppressLint("UseSwitchCompatOrMaterialCode") sbWithReboot: SwitchButton,
|
||||
tvAutoStartup: TextView
|
||||
@SuppressLint("UseSwitchCompatOrMaterialCode") sbWithReboot: SwitchButton, tvAutoStartup: TextView
|
||||
) {
|
||||
tvAutoStartup.text = getAutoStartTips()
|
||||
|
||||
|
@ -610,12 +561,10 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding?>(), View.OnClickL
|
|||
val cm = ComponentName(getAppPackageName(), BootReceiver::class.java.name)
|
||||
val pm: PackageManager = getPackageManager()
|
||||
val state = pm.getComponentEnabledSetting(cm)
|
||||
sbWithReboot.isChecked =
|
||||
(state != PackageManager.COMPONENT_ENABLED_STATE_DISABLED && state != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER)
|
||||
sbWithReboot.isChecked = !(state == PackageManager.COMPONENT_ENABLED_STATE_DISABLED || state == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER)
|
||||
sbWithReboot.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
|
||||
try {
|
||||
val newState =
|
||||
if (isChecked) PackageManager.COMPONENT_ENABLED_STATE_ENABLED else PackageManager.COMPONENT_ENABLED_STATE_DISABLED
|
||||
val newState = if (isChecked) PackageManager.COMPONENT_ENABLED_STATE_ENABLED else PackageManager.COMPONENT_ENABLED_STATE_DISABLED
|
||||
pm.setComponentEnabledSetting(cm, newState, PackageManager.DONT_KILL_APP)
|
||||
if (isChecked) startToAutoStartSetting(requireContext())
|
||||
} catch (e: Exception) {
|
||||
|
@ -635,8 +584,7 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding?>(), View.OnClickL
|
|||
}
|
||||
|
||||
try {
|
||||
val isIgnoreBatteryOptimization: Boolean =
|
||||
KeepAliveUtils.isIgnoreBatteryOptimization(requireActivity())
|
||||
val isIgnoreBatteryOptimization: Boolean = KeepAliveUtils.isIgnoreBatteryOptimization(requireActivity())
|
||||
sbBatterySetting.isChecked = isIgnoreBatteryOptimization
|
||||
sbBatterySetting.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
|
||||
if (isChecked && !isIgnoreBatteryOptimization) {
|
||||
|
@ -657,8 +605,7 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding?>(), View.OnClickL
|
|||
//不在最近任务列表中显示
|
||||
@SuppressLint("ObsoleteSdkInt,UseSwitchCompatOrMaterialCode")
|
||||
fun switchExcludeFromRecents(
|
||||
layoutExcludeFromRecents: LinearLayout,
|
||||
sbExcludeFromRecents: SwitchButton
|
||||
layoutExcludeFromRecents: LinearLayout, sbExcludeFromRecents: SwitchButton
|
||||
) {
|
||||
//安卓6.0以下没有不在最近任务列表中显示
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
||||
|
@ -683,9 +630,7 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding?>(), View.OnClickL
|
|||
//转发应用通知
|
||||
@SuppressLint("UseSwitchCompatOrMaterialCode")
|
||||
fun switchEnableCactus(
|
||||
sbEnableCactus: SwitchButton,
|
||||
scbPlaySilenceMusic: SmoothCheckBox,
|
||||
scbOnePixelActivity: SmoothCheckBox
|
||||
sbEnableCactus: SwitchButton, scbPlaySilenceMusic: SmoothCheckBox, scbOnePixelActivity: SmoothCheckBox
|
||||
) {
|
||||
val layoutCactusOptional: LinearLayout = binding!!.layoutCactusOptional
|
||||
val isEnable: Boolean = SettingUtils.enableCactus
|
||||
|
@ -716,9 +661,7 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding?>(), View.OnClickL
|
|||
|
||||
//接口请求失败重试时间间隔
|
||||
private fun editRetryDelayTime(
|
||||
etRetryTimes: EditText,
|
||||
etDelayTime: EditText,
|
||||
etTimeout: EditText
|
||||
etRetryTimes: EditText, etDelayTime: EditText, etTimeout: EditText
|
||||
) {
|
||||
etRetryTimes.setText(java.lang.String.valueOf(SettingUtils.requestRetryTimes))
|
||||
etRetryTimes.addTextChangedListener(object : TextWatcher {
|
||||
|
@ -818,8 +761,7 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding?>(), View.OnClickL
|
|||
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
|
||||
override fun afterTextChanged(s: Editable) {
|
||||
SettingUtils.notifyContent = etNotifyContent.text.toString().trim()
|
||||
LiveEventBus.get(EVENT_UPDATE_NOTIFY, String::class.java)
|
||||
.post(SettingUtils.notifyContent.toString())
|
||||
LiveEventBus.get(EVENT_UPDATE_NOTIFY, String::class.java).post(SettingUtils.notifyContent)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -875,14 +817,9 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding?>(), View.OnClickL
|
|||
switchDirectlyToClient.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
|
||||
SettingUtils.enablePureClientMode = isChecked
|
||||
if (isChecked) {
|
||||
MaterialDialog.Builder(requireContext())
|
||||
.content(getString(R.string.enabling_pure_client_mode))
|
||||
.positiveText(R.string.lab_yes)
|
||||
.onPositive { _: MaterialDialog?, _: DialogAction? ->
|
||||
XUtil.exitApp()
|
||||
}
|
||||
.negativeText(R.string.lab_no)
|
||||
.show()
|
||||
MaterialDialog.Builder(requireContext()).content(getString(R.string.enabling_pure_client_mode)).positiveText(R.string.lab_yes).onPositive { _: MaterialDialog?, _: DialogAction? ->
|
||||
XUtil.exitApp()
|
||||
}.negativeText(R.string.lab_no).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -914,37 +851,19 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding?>(), View.OnClickL
|
|||
)
|
||||
put(
|
||||
"samsung", listOf(
|
||||
"com.samsung.android.sm_cn/com.samsung.android.sm.ui.ram.AutoRunActivity",
|
||||
"com.samsung.android.sm_cn/com.samsung.android.sm.ui.appmanagement.AppManagementActivity",
|
||||
"com.samsung.android.sm_cn/com.samsung.android.sm.ui.cstyleboard.SmartManagerDashBoardActivity",
|
||||
"com.samsung.android.sm_cn/.ui.ram.RamActivity",
|
||||
"com.samsung.android.sm_cn/.app.dashboard.SmartManagerDashBoardActivity",
|
||||
"com.samsung.android.sm/com.samsung.android.sm.ui.ram.AutoRunActivity",
|
||||
"com.samsung.android.sm/com.samsung.android.sm.ui.appmanagement.AppManagementActivity",
|
||||
"com.samsung.android.sm/com.samsung.android.sm.ui.cstyleboard.SmartManagerDashBoardActivity",
|
||||
"com.samsung.android.sm/.ui.ram.RamActivity",
|
||||
"com.samsung.android.sm/.app.dashboard.SmartManagerDashBoardActivity",
|
||||
"com.samsung.android.lool/com.samsung.android.sm.ui.battery.BatteryActivity",
|
||||
"com.samsung.android.sm_cn",
|
||||
"com.samsung.android.sm"
|
||||
"com.samsung.android.sm_cn/com.samsung.android.sm.ui.ram.AutoRunActivity", "com.samsung.android.sm_cn/com.samsung.android.sm.ui.appmanagement.AppManagementActivity", "com.samsung.android.sm_cn/com.samsung.android.sm.ui.cstyleboard.SmartManagerDashBoardActivity", "com.samsung.android.sm_cn/.ui.ram.RamActivity", "com.samsung.android.sm_cn/.app.dashboard.SmartManagerDashBoardActivity", "com.samsung.android.sm/com.samsung.android.sm.ui.ram.AutoRunActivity", "com.samsung.android.sm/com.samsung.android.sm.ui.appmanagement.AppManagementActivity", "com.samsung.android.sm/com.samsung.android.sm.ui.cstyleboard.SmartManagerDashBoardActivity", "com.samsung.android.sm/.ui.ram.RamActivity", "com.samsung.android.sm/.app.dashboard.SmartManagerDashBoardActivity", "com.samsung.android.lool/com.samsung.android.sm.ui.battery.BatteryActivity", "com.samsung.android.sm_cn", "com.samsung.android.sm"
|
||||
)
|
||||
)
|
||||
put(
|
||||
"HUAWEI", listOf(
|
||||
"com.huawei.systemmanager/.startupmgr.ui.StartupNormalAppListActivity", //EMUI9.1.0(方舟,9.0)
|
||||
"com.huawei.systemmanager/.appcontrol.activity.StartupAppControlActivity",
|
||||
"com.huawei.systemmanager/.optimize.process.ProtectActivity",
|
||||
"com.huawei.systemmanager/.optimize.bootstart.BootStartActivity",
|
||||
"com.huawei.systemmanager" //最后一行可以写包名, 这样如果签名的类路径在某些新版本的ROM中没找到 就直接跳转到对应的安全中心/手机管家 首页.
|
||||
"com.huawei.systemmanager/.appcontrol.activity.StartupAppControlActivity", "com.huawei.systemmanager/.optimize.process.ProtectActivity", "com.huawei.systemmanager/.optimize.bootstart.BootStartActivity", "com.huawei.systemmanager" //最后一行可以写包名, 这样如果签名的类路径在某些新版本的ROM中没找到 就直接跳转到对应的安全中心/手机管家 首页.
|
||||
)
|
||||
)
|
||||
put(
|
||||
"vivo", listOf(
|
||||
"com.iqoo.secure/.ui.phoneoptimize.BgStartUpManager",
|
||||
"com.iqoo.secure/.safeguard.PurviewTabActivity",
|
||||
"com.vivo.permissionmanager/.activity.BgStartUpManagerActivity", //"com.iqoo.secure/.ui.phoneoptimize.AddWhiteListActivity", //这是白名单, 不是自启动
|
||||
"com.iqoo.secure",
|
||||
"com.vivo.permissionmanager"
|
||||
"com.iqoo.secure/.ui.phoneoptimize.BgStartUpManager", "com.iqoo.secure/.safeguard.PurviewTabActivity", "com.vivo.permissionmanager/.activity.BgStartUpManagerActivity", //"com.iqoo.secure/.ui.phoneoptimize.AddWhiteListActivity", //这是白名单, 不是自启动
|
||||
"com.iqoo.secure", "com.vivo.permissionmanager"
|
||||
)
|
||||
)
|
||||
put(
|
||||
|
@ -956,95 +875,77 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding?>(), View.OnClickL
|
|||
)
|
||||
put(
|
||||
"OPPO", listOf(
|
||||
"com.coloros.safecenter/.startupapp.StartupAppListActivity",
|
||||
"com.coloros.safecenter/.permission.startup.StartupAppListActivity",
|
||||
"com.oppo.safe/.permission.startup.StartupAppListActivity",
|
||||
"com.coloros.oppoguardelf/com.coloros.powermanager.fuelgaue.PowerUsageModelActivity",
|
||||
"com.coloros.safecenter/com.coloros.privacypermissionsentry.PermissionTopActivity",
|
||||
"com.coloros.safecenter",
|
||||
"com.oppo.safe",
|
||||
"com.coloros.oppoguardelf"
|
||||
"com.coloros.safecenter/.startupapp.StartupAppListActivity", "com.coloros.safecenter/.permission.startup.StartupAppListActivity", "com.oppo.safe/.permission.startup.StartupAppListActivity", "com.coloros.oppoguardelf/com.coloros.powermanager.fuelgaue.PowerUsageModelActivity", "com.coloros.safecenter/com.coloros.privacypermissionsentry.PermissionTopActivity", "com.coloros.safecenter", "com.oppo.safe", "com.coloros.oppoguardelf"
|
||||
)
|
||||
)
|
||||
put(
|
||||
"oneplus", listOf(
|
||||
"com.oneplus.security/.chainlaunch.view.ChainLaunchAppListActivity",
|
||||
"com.oneplus.security"
|
||||
"com.oneplus.security/.chainlaunch.view.ChainLaunchAppListActivity", "com.oneplus.security"
|
||||
)
|
||||
)
|
||||
put(
|
||||
"letv", listOf(
|
||||
"com.letv.android.letvsafe/.AutobootManageActivity",
|
||||
"com.letv.android.letvsafe/.BackgroundAppManageActivity", //应用保护
|
||||
"com.letv.android.letvsafe/.AutobootManageActivity", "com.letv.android.letvsafe/.BackgroundAppManageActivity", //应用保护
|
||||
"com.letv.android.letvsafe"
|
||||
)
|
||||
)
|
||||
put(
|
||||
"zte", listOf(
|
||||
"com.zte.heartyservice/.autorun.AppAutoRunManager",
|
||||
"com.zte.heartyservice"
|
||||
"com.zte.heartyservice/.autorun.AppAutoRunManager", "com.zte.heartyservice"
|
||||
)
|
||||
)
|
||||
|
||||
//金立
|
||||
put(
|
||||
"F", listOf(
|
||||
"com.gionee.softmanager/.MainActivity",
|
||||
"com.gionee.softmanager"
|
||||
"com.gionee.softmanager/.MainActivity", "com.gionee.softmanager"
|
||||
)
|
||||
)
|
||||
|
||||
//以下为未确定(厂商名也不确定)
|
||||
put(
|
||||
"smartisanos", listOf(
|
||||
"com.smartisanos.security/.invokeHistory.InvokeHistoryActivity",
|
||||
"com.smartisanos.security"
|
||||
"com.smartisanos.security/.invokeHistory.InvokeHistoryActivity", "com.smartisanos.security"
|
||||
)
|
||||
)
|
||||
|
||||
//360
|
||||
put(
|
||||
"360", listOf(
|
||||
"com.yulong.android.coolsafe/.ui.activity.autorun.AutoRunListActivity",
|
||||
"com.yulong.android.coolsafe"
|
||||
"com.yulong.android.coolsafe/.ui.activity.autorun.AutoRunListActivity", "com.yulong.android.coolsafe"
|
||||
)
|
||||
)
|
||||
|
||||
//360
|
||||
put(
|
||||
"ulong", listOf(
|
||||
"com.yulong.android.coolsafe/.ui.activity.autorun.AutoRunListActivity",
|
||||
"com.yulong.android.coolsafe"
|
||||
"com.yulong.android.coolsafe/.ui.activity.autorun.AutoRunListActivity", "com.yulong.android.coolsafe"
|
||||
)
|
||||
)
|
||||
|
||||
//酷派
|
||||
put(
|
||||
"coolpad" /*厂商名称不确定是否正确*/, listOf(
|
||||
"com.yulong.android.security/com.yulong.android.seccenter.tabbarmain",
|
||||
"com.yulong.android.security"
|
||||
"com.yulong.android.security/com.yulong.android.seccenter.tabbarmain", "com.yulong.android.security"
|
||||
)
|
||||
)
|
||||
|
||||
//联想
|
||||
put(
|
||||
"lenovo" /*厂商名称不确定是否正确*/, listOf(
|
||||
"com.lenovo.security/.purebackground.PureBackgroundActivity",
|
||||
"com.lenovo.security"
|
||||
"com.lenovo.security/.purebackground.PureBackgroundActivity", "com.lenovo.security"
|
||||
)
|
||||
)
|
||||
put(
|
||||
"htc" /*厂商名称不确定是否正确*/, listOf(
|
||||
"com.htc.pitroad/.landingpage.activity.LandingPageActivity",
|
||||
"com.htc.pitroad"
|
||||
"com.htc.pitroad/.landingpage.activity.LandingPageActivity", "com.htc.pitroad"
|
||||
)
|
||||
)
|
||||
|
||||
//华硕
|
||||
put(
|
||||
"asus" /*厂商名称不确定是否正确*/, listOf(
|
||||
"com.asus.mobilemanager/.MainActivity",
|
||||
"com.asus.mobilemanager"
|
||||
"com.asus.mobilemanager/.MainActivity", "com.asus.mobilemanager"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@ -1069,8 +970,7 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding?>(), View.OnClickL
|
|||
} else {
|
||||
//找不到? 网上的做法都是跳转到设置... 这基本上是没意义的 基本上自启动这个功能是第三方厂商自己写的安全管家类app
|
||||
//所以我是直接跳转到对应的安全管家/安全中心
|
||||
intent =
|
||||
act?.let { context.packageManager.getLaunchIntentForPackage(it) }
|
||||
intent = act?.let { context.packageManager.getLaunchIntentForPackage(it) }
|
||||
}
|
||||
context.startActivity(intent)
|
||||
has = true
|
||||
|
|
|
@ -1,151 +1,151 @@
|
|||
package com.idormy.sms.forwarder.fragment.client
|
||||
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.idormy.sms.forwarder.core.BaseFragment
|
||||
import com.idormy.sms.forwarder.databinding.FragmentClientBatteryQueryBinding
|
||||
import com.idormy.sms.forwarder.entity.BatteryInfo
|
||||
import com.idormy.sms.forwarder.server.model.BaseResponse
|
||||
import com.idormy.sms.forwarder.utils.*
|
||||
import com.xuexiang.xhttp2.XHttp
|
||||
import com.xuexiang.xhttp2.cache.model.CacheMode
|
||||
import com.xuexiang.xhttp2.callback.SimpleCallBack
|
||||
import com.xuexiang.xhttp2.exception.ApiException
|
||||
import com.xuexiang.xpage.annotation.Page
|
||||
import com.xuexiang.xrouter.utils.TextUtils
|
||||
import com.xuexiang.xui.utils.ResUtils
|
||||
import com.xuexiang.xui.widget.actionbar.TitleBar
|
||||
import com.xuexiang.xui.widget.grouplist.XUIGroupListView
|
||||
import com.xuexiang.xutil.data.ConvertTools
|
||||
|
||||
@Suppress("PropertyName")
|
||||
@Page(name = "远程查电量")
|
||||
class BatteryQueryFragment : BaseFragment<FragmentClientBatteryQueryBinding?>() {
|
||||
|
||||
val TAG: String = BatteryQueryFragment::class.java.simpleName
|
||||
|
||||
override fun viewBindingInflate(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup,
|
||||
): FragmentClientBatteryQueryBinding {
|
||||
return FragmentClientBatteryQueryBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
override fun initTitle(): TitleBar? {
|
||||
val titleBar = super.initTitle()!!.setImmersive(false)
|
||||
titleBar.setTitle(R.string.api_battery_query)
|
||||
return titleBar
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化控件
|
||||
*/
|
||||
override fun initViews() {
|
||||
|
||||
val requestUrl: String = HttpServerUtils.serverAddress + "/battery/query"
|
||||
Log.i(TAG, "requestUrl:$requestUrl")
|
||||
|
||||
val msgMap: MutableMap<String, Any> = mutableMapOf()
|
||||
val timestamp = System.currentTimeMillis()
|
||||
msgMap["timestamp"] = timestamp
|
||||
val clientSignKey = HttpServerUtils.clientSignKey
|
||||
if (!TextUtils.isEmpty(clientSignKey)) {
|
||||
msgMap["sign"] = HttpServerUtils.calcSign(timestamp.toString(), clientSignKey.toString())
|
||||
}
|
||||
val dataMap: MutableMap<String, Any> = mutableMapOf()
|
||||
msgMap["data"] = dataMap
|
||||
|
||||
var requestMsg: String = Gson().toJson(msgMap)
|
||||
Log.i(TAG, "requestMsg:$requestMsg")
|
||||
|
||||
val postRequest = XHttp.post(requestUrl)
|
||||
.keepJson(true)
|
||||
.timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
|
||||
.cacheMode(CacheMode.NO_CACHE)
|
||||
.timeStamp(true)
|
||||
|
||||
when (HttpServerUtils.clientSafetyMeasures) {
|
||||
2 -> {
|
||||
try {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey.toString())
|
||||
requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
requestMsg = RSACrypt.encryptByPublicKey(requestMsg, publicKey)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
3 -> {
|
||||
try {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey.toString())
|
||||
//requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
val encryptCBC = SM4Crypt.encrypt(requestMsg.toByteArray(), sm4Key)
|
||||
requestMsg = ConvertTools.bytes2HexString(encryptCBC)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
else -> {
|
||||
postRequest.upJson(requestMsg)
|
||||
}
|
||||
}
|
||||
|
||||
postRequest.execute(object : SimpleCallBack<String>() {
|
||||
override fun onError(e: ApiException) {
|
||||
XToastUtils.error(e.displayMessage)
|
||||
}
|
||||
|
||||
override fun onSuccess(response: String) {
|
||||
Log.i(TAG, response)
|
||||
try {
|
||||
var json = response
|
||||
if (HttpServerUtils.clientSafetyMeasures == 2) {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey.toString())
|
||||
json = RSACrypt.decryptByPublicKey(json, publicKey)
|
||||
json = String(Base64.decode(json))
|
||||
} else if (HttpServerUtils.clientSafetyMeasures == 3) {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey.toString())
|
||||
val encryptCBC = ConvertTools.hexStringToByteArray(json)
|
||||
val decryptCBC = SM4Crypt.decrypt(encryptCBC, sm4Key)
|
||||
json = String(decryptCBC)
|
||||
}
|
||||
val resp: BaseResponse<BatteryInfo> = Gson().fromJson(json, object : TypeToken<BaseResponse<BatteryInfo>>() {}.type)
|
||||
if (resp.code == 200) {
|
||||
XToastUtils.success(ResUtils.getString(R.string.request_succeeded))
|
||||
val batteryInfo = resp.data ?: return
|
||||
|
||||
val groupListView = binding!!.infoList
|
||||
val section = XUIGroupListView.newSection(context)
|
||||
section.addItemView(groupListView.createItemView(String.format(ResUtils.getString(R.string.battery_level), batteryInfo.level))) {}
|
||||
if (batteryInfo.scale != "") section.addItemView(groupListView.createItemView(String.format(ResUtils.getString(R.string.battery_scale), batteryInfo.scale))) {}
|
||||
if (batteryInfo.voltage != "") section.addItemView(groupListView.createItemView(String.format(ResUtils.getString(R.string.battery_voltage), batteryInfo.voltage))) {}
|
||||
if (batteryInfo.temperature != "") section.addItemView(groupListView.createItemView(String.format(ResUtils.getString(R.string.battery_temperature), batteryInfo.temperature))) {}
|
||||
section.addItemView(groupListView.createItemView(String.format(ResUtils.getString(R.string.battery_status), batteryInfo.status))) {}
|
||||
section.addItemView(groupListView.createItemView(String.format(ResUtils.getString(R.string.battery_health), batteryInfo.health))) {}
|
||||
section.addItemView(groupListView.createItemView(String.format(ResUtils.getString(R.string.battery_plugged), batteryInfo.plugged))) {}
|
||||
section.addTo(groupListView)
|
||||
|
||||
} else {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + resp.msg)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + response)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
package com.idormy.sms.forwarder.fragment.client
|
||||
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.idormy.sms.forwarder.core.BaseFragment
|
||||
import com.idormy.sms.forwarder.databinding.FragmentClientBatteryQueryBinding
|
||||
import com.idormy.sms.forwarder.entity.BatteryInfo
|
||||
import com.idormy.sms.forwarder.server.model.BaseResponse
|
||||
import com.idormy.sms.forwarder.utils.*
|
||||
import com.xuexiang.xhttp2.XHttp
|
||||
import com.xuexiang.xhttp2.cache.model.CacheMode
|
||||
import com.xuexiang.xhttp2.callback.SimpleCallBack
|
||||
import com.xuexiang.xhttp2.exception.ApiException
|
||||
import com.xuexiang.xpage.annotation.Page
|
||||
import com.xuexiang.xrouter.utils.TextUtils
|
||||
import com.xuexiang.xui.utils.ResUtils
|
||||
import com.xuexiang.xui.widget.actionbar.TitleBar
|
||||
import com.xuexiang.xui.widget.grouplist.XUIGroupListView
|
||||
import com.xuexiang.xutil.data.ConvertTools
|
||||
|
||||
@Suppress("PropertyName")
|
||||
@Page(name = "远程查电量")
|
||||
class BatteryQueryFragment : BaseFragment<FragmentClientBatteryQueryBinding?>() {
|
||||
|
||||
val TAG: String = BatteryQueryFragment::class.java.simpleName
|
||||
|
||||
override fun viewBindingInflate(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup,
|
||||
): FragmentClientBatteryQueryBinding {
|
||||
return FragmentClientBatteryQueryBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
override fun initTitle(): TitleBar? {
|
||||
val titleBar = super.initTitle()!!.setImmersive(false)
|
||||
titleBar.setTitle(R.string.api_battery_query)
|
||||
return titleBar
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化控件
|
||||
*/
|
||||
override fun initViews() {
|
||||
|
||||
val requestUrl: String = HttpServerUtils.serverAddress + "/battery/query"
|
||||
Log.i(TAG, "requestUrl:$requestUrl")
|
||||
|
||||
val msgMap: MutableMap<String, Any> = mutableMapOf()
|
||||
val timestamp = System.currentTimeMillis()
|
||||
msgMap["timestamp"] = timestamp
|
||||
val clientSignKey = HttpServerUtils.clientSignKey
|
||||
if (!TextUtils.isEmpty(clientSignKey)) {
|
||||
msgMap["sign"] = HttpServerUtils.calcSign(timestamp.toString(), clientSignKey)
|
||||
}
|
||||
val dataMap: MutableMap<String, Any> = mutableMapOf()
|
||||
msgMap["data"] = dataMap
|
||||
|
||||
var requestMsg: String = Gson().toJson(msgMap)
|
||||
Log.i(TAG, "requestMsg:$requestMsg")
|
||||
|
||||
val postRequest = XHttp.post(requestUrl)
|
||||
.keepJson(true)
|
||||
.timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
|
||||
.cacheMode(CacheMode.NO_CACHE)
|
||||
.timeStamp(true)
|
||||
|
||||
when (HttpServerUtils.clientSafetyMeasures) {
|
||||
2 -> {
|
||||
try {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey)
|
||||
requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
requestMsg = RSACrypt.encryptByPublicKey(requestMsg, publicKey)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
3 -> {
|
||||
try {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey)
|
||||
//requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
val encryptCBC = SM4Crypt.encrypt(requestMsg.toByteArray(), sm4Key)
|
||||
requestMsg = ConvertTools.bytes2HexString(encryptCBC)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
else -> {
|
||||
postRequest.upJson(requestMsg)
|
||||
}
|
||||
}
|
||||
|
||||
postRequest.execute(object : SimpleCallBack<String>() {
|
||||
override fun onError(e: ApiException) {
|
||||
XToastUtils.error(e.displayMessage)
|
||||
}
|
||||
|
||||
override fun onSuccess(response: String) {
|
||||
Log.i(TAG, response)
|
||||
try {
|
||||
var json = response
|
||||
if (HttpServerUtils.clientSafetyMeasures == 2) {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey)
|
||||
json = RSACrypt.decryptByPublicKey(json, publicKey)
|
||||
json = String(Base64.decode(json))
|
||||
} else if (HttpServerUtils.clientSafetyMeasures == 3) {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey)
|
||||
val encryptCBC = ConvertTools.hexStringToByteArray(json)
|
||||
val decryptCBC = SM4Crypt.decrypt(encryptCBC, sm4Key)
|
||||
json = String(decryptCBC)
|
||||
}
|
||||
val resp: BaseResponse<BatteryInfo> = Gson().fromJson(json, object : TypeToken<BaseResponse<BatteryInfo>>() {}.type)
|
||||
if (resp.code == 200) {
|
||||
XToastUtils.success(ResUtils.getString(R.string.request_succeeded))
|
||||
val batteryInfo = resp.data ?: return
|
||||
|
||||
val groupListView = binding!!.infoList
|
||||
val section = XUIGroupListView.newSection(context)
|
||||
section.addItemView(groupListView.createItemView(String.format(ResUtils.getString(R.string.battery_level), batteryInfo.level))) {}
|
||||
if (batteryInfo.scale != "") section.addItemView(groupListView.createItemView(String.format(ResUtils.getString(R.string.battery_scale), batteryInfo.scale))) {}
|
||||
if (batteryInfo.voltage != "") section.addItemView(groupListView.createItemView(String.format(ResUtils.getString(R.string.battery_voltage), batteryInfo.voltage))) {}
|
||||
if (batteryInfo.temperature != "") section.addItemView(groupListView.createItemView(String.format(ResUtils.getString(R.string.battery_temperature), batteryInfo.temperature))) {}
|
||||
section.addItemView(groupListView.createItemView(String.format(ResUtils.getString(R.string.battery_status), batteryInfo.status))) {}
|
||||
section.addItemView(groupListView.createItemView(String.format(ResUtils.getString(R.string.battery_health), batteryInfo.health))) {}
|
||||
section.addItemView(groupListView.createItemView(String.format(ResUtils.getString(R.string.battery_plugged), batteryInfo.plugged))) {}
|
||||
section.addTo(groupListView)
|
||||
|
||||
} else {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + resp.msg)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + response)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,295 +1,295 @@
|
|||
package com.idormy.sms.forwarder.fragment.client
|
||||
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.alibaba.android.vlayout.DelegateAdapter
|
||||
import com.alibaba.android.vlayout.VirtualLayoutManager
|
||||
import com.alibaba.android.vlayout.layout.LinearLayoutHelper
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.idormy.sms.forwarder.adapter.base.broccoli.BroccoliSimpleDelegateAdapter
|
||||
import com.idormy.sms.forwarder.adapter.base.delegate.SimpleDelegateAdapter
|
||||
import com.idormy.sms.forwarder.core.BaseFragment
|
||||
import com.idormy.sms.forwarder.databinding.FragmentClientCallQueryBinding
|
||||
import com.idormy.sms.forwarder.entity.CallInfo
|
||||
import com.idormy.sms.forwarder.server.model.BaseResponse
|
||||
import com.idormy.sms.forwarder.server.model.CallQueryData
|
||||
import com.idormy.sms.forwarder.utils.*
|
||||
import com.jeremyliao.liveeventbus.LiveEventBus
|
||||
import com.scwang.smartrefresh.layout.api.RefreshLayout
|
||||
import com.xuexiang.xaop.annotation.SingleClick
|
||||
import com.xuexiang.xhttp2.XHttp
|
||||
import com.xuexiang.xhttp2.cache.model.CacheMode
|
||||
import com.xuexiang.xhttp2.callback.SimpleCallBack
|
||||
import com.xuexiang.xhttp2.exception.ApiException
|
||||
import com.xuexiang.xpage.annotation.Page
|
||||
import com.xuexiang.xpage.base.XPageActivity
|
||||
import com.xuexiang.xpage.core.PageOption
|
||||
import com.xuexiang.xrouter.utils.TextUtils
|
||||
import com.xuexiang.xui.adapter.recyclerview.RecyclerViewHolder
|
||||
import com.xuexiang.xui.utils.ResUtils
|
||||
import com.xuexiang.xui.utils.SnackbarUtils
|
||||
import com.xuexiang.xui.widget.actionbar.TitleBar
|
||||
import com.xuexiang.xui.widget.searchview.MaterialSearchView
|
||||
import com.xuexiang.xutil.data.ConvertTools
|
||||
import com.xuexiang.xutil.data.DateUtils
|
||||
import com.xuexiang.xutil.system.ClipboardUtils
|
||||
import me.samlss.broccoli.Broccoli
|
||||
|
||||
@Suppress("PropertyName")
|
||||
@Page(name = "远程查通话")
|
||||
class CallQueryFragment : BaseFragment<FragmentClientCallQueryBinding?>() {
|
||||
|
||||
val TAG: String = CallQueryFragment::class.java.simpleName
|
||||
private var mAdapter: SimpleDelegateAdapter<CallInfo>? = null
|
||||
private var callType: Int = 3
|
||||
private var pageNum: Int = 1
|
||||
private val pageSize: Int = 20
|
||||
private var keyword: String = ""
|
||||
|
||||
override fun viewBindingInflate(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup,
|
||||
): FragmentClientCallQueryBinding {
|
||||
return FragmentClientCallQueryBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
override fun initTitle(): TitleBar {
|
||||
val titleBar = super.initTitle()!!.setImmersive(false)
|
||||
titleBar.setTitle(R.string.api_call_query)
|
||||
titleBar!!.addAction(object : TitleBar.ImageAction(R.drawable.ic_query) {
|
||||
@SingleClick
|
||||
override fun performAction(view: View) {
|
||||
binding!!.searchView.showSearch()
|
||||
}
|
||||
})
|
||||
return titleBar
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化控件
|
||||
*/
|
||||
override fun initViews() {
|
||||
val virtualLayoutManager = VirtualLayoutManager(requireContext())
|
||||
binding!!.recyclerView.layoutManager = virtualLayoutManager
|
||||
val viewPool = RecyclerView.RecycledViewPool()
|
||||
binding!!.recyclerView.setRecycledViewPool(viewPool)
|
||||
viewPool.setMaxRecycledViews(0, 10)
|
||||
|
||||
mAdapter = object : BroccoliSimpleDelegateAdapter<CallInfo>(
|
||||
R.layout.adapter_call_card_view_list_item,
|
||||
LinearLayoutHelper(),
|
||||
DataProvider.emptyCallInfo
|
||||
) {
|
||||
override fun onBindData(
|
||||
holder: RecyclerViewHolder,
|
||||
model: CallInfo,
|
||||
position: Int,
|
||||
) {
|
||||
val from = if (TextUtils.isEmpty(model.name)) model.number else model.number + " | " + model.name
|
||||
holder.text(R.id.tv_from, from)
|
||||
holder.text(R.id.tv_time, DateUtils.getFriendlyTimeSpanByNow(model.dateLong))
|
||||
holder.image(R.id.iv_image, model.typeImageId)
|
||||
holder.image(R.id.iv_sim_image, model.simImageId)
|
||||
holder.text(R.id.tv_duration, ResUtils.getString(R.string.call_duration) + model.duration + ResUtils.getString(R.string.seconds))
|
||||
holder.image(R.id.iv_copy, R.drawable.ic_copy)
|
||||
holder.image(R.id.iv_call, R.drawable.ic_phone_out)
|
||||
holder.image(R.id.iv_reply, R.drawable.ic_reply)
|
||||
holder.click(R.id.iv_copy) {
|
||||
XToastUtils.info(String.format(getString(R.string.copied_to_clipboard), from))
|
||||
ClipboardUtils.copyText(from)
|
||||
}
|
||||
holder.click(R.id.iv_call) {
|
||||
XToastUtils.info(getString(R.string.local_call) + model.number)
|
||||
PhoneUtils.dial(model.number)
|
||||
}
|
||||
holder.click(R.id.iv_reply) {
|
||||
XToastUtils.info(getString(R.string.remote_sms) + model.number)
|
||||
LiveEventBus.get<Int>(EVENT_KEY_SIM_SLOT).post(model.simId)
|
||||
LiveEventBus.get<String>(EVENT_KEY_PHONE_NUMBERS).post(model.number)
|
||||
PageOption.to(SmsSendFragment::class.java).setNewActivity(true).open((context as XPageActivity?)!!)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindBroccoli(holder: RecyclerViewHolder, broccoli: Broccoli) {
|
||||
broccoli.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.tv_from)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.tv_time)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.iv_sim_image)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.tv_duration)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.iv_image)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.iv_copy)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.iv_call)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.iv_reply)))
|
||||
}
|
||||
}
|
||||
|
||||
val delegateAdapter = DelegateAdapter(virtualLayoutManager)
|
||||
delegateAdapter.addAdapter(mAdapter)
|
||||
binding!!.recyclerView.adapter = delegateAdapter
|
||||
|
||||
binding!!.tabBar.setTabTitles(ResUtils.getStringArray(R.array.call_type_option))
|
||||
binding!!.tabBar.setOnTabClickListener { _, position ->
|
||||
//XToastUtils.toast("点击了$title--$position")
|
||||
callType = 3 - position
|
||||
loadRemoteData(true)
|
||||
binding!!.recyclerView.scrollToPosition(0)
|
||||
}
|
||||
|
||||
//搜索框
|
||||
binding!!.searchView.findViewById<View>(com.xuexiang.xui.R.id.search_layout).visibility = View.GONE
|
||||
binding!!.searchView.setVoiceSearch(true)
|
||||
binding!!.searchView.setEllipsize(true)
|
||||
binding!!.searchView.setSuggestions(resources.getStringArray(R.array.query_suggestions))
|
||||
binding!!.searchView.setOnQueryTextListener(object : MaterialSearchView.OnQueryTextListener {
|
||||
override fun onQueryTextSubmit(query: String): Boolean {
|
||||
SnackbarUtils.Indefinite(view, String.format(getString(R.string.search_keyword), query)).info()
|
||||
.actionColor(ResUtils.getColor(R.color.xui_config_color_white))
|
||||
.setAction(getString(R.string.clear)) {
|
||||
keyword = ""
|
||||
loadRemoteData(true)
|
||||
}.show()
|
||||
if (keyword != query) {
|
||||
keyword = query
|
||||
loadRemoteData(true)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onQueryTextChange(newText: String): Boolean {
|
||||
//Do some magic
|
||||
return false
|
||||
}
|
||||
})
|
||||
binding!!.searchView.setOnSearchViewListener(object : MaterialSearchView.SearchViewListener {
|
||||
override fun onSearchViewShown() {
|
||||
//Do some magic
|
||||
}
|
||||
|
||||
override fun onSearchViewClosed() {
|
||||
//Do some magic
|
||||
}
|
||||
})
|
||||
binding!!.searchView.setSubmitOnClick(true)
|
||||
}
|
||||
|
||||
override fun initListeners() {
|
||||
//下拉刷新
|
||||
binding!!.refreshLayout.setOnRefreshListener { refreshLayout: RefreshLayout ->
|
||||
refreshLayout.layout.postDelayed({
|
||||
loadRemoteData(true)
|
||||
}, 1000)
|
||||
}
|
||||
//上拉加载
|
||||
binding!!.refreshLayout.setOnLoadMoreListener { refreshLayout: RefreshLayout ->
|
||||
refreshLayout.layout.postDelayed({
|
||||
loadRemoteData(false)
|
||||
}, 1000)
|
||||
}
|
||||
binding!!.refreshLayout.autoRefresh() //第一次进入触发自动刷新,演示效果
|
||||
}
|
||||
|
||||
private fun loadRemoteData(refresh: Boolean) {
|
||||
|
||||
val requestUrl: String = HttpServerUtils.serverAddress + "/call/query"
|
||||
Log.i(TAG, "requestUrl:$requestUrl")
|
||||
|
||||
val msgMap: MutableMap<String, Any> = mutableMapOf()
|
||||
val timestamp = System.currentTimeMillis()
|
||||
msgMap["timestamp"] = timestamp
|
||||
val clientSignKey = HttpServerUtils.clientSignKey
|
||||
if (!TextUtils.isEmpty(clientSignKey)) {
|
||||
msgMap["sign"] = HttpServerUtils.calcSign(timestamp.toString(), clientSignKey.toString())
|
||||
}
|
||||
|
||||
if (refresh) pageNum = 1
|
||||
msgMap["data"] = CallQueryData(callType, pageNum, pageSize, keyword)
|
||||
|
||||
var requestMsg: String = Gson().toJson(msgMap)
|
||||
Log.i(TAG, "requestMsg:$requestMsg")
|
||||
|
||||
val postRequest = XHttp.post(requestUrl)
|
||||
.keepJson(true)
|
||||
.timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
|
||||
.cacheMode(CacheMode.NO_CACHE)
|
||||
.timeStamp(true)
|
||||
|
||||
when (HttpServerUtils.clientSafetyMeasures) {
|
||||
2 -> {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey.toString())
|
||||
try {
|
||||
requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
requestMsg = RSACrypt.encryptByPublicKey(requestMsg, publicKey)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
3 -> {
|
||||
try {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey.toString())
|
||||
//requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
val encryptCBC = SM4Crypt.encrypt(requestMsg.toByteArray(), sm4Key)
|
||||
requestMsg = ConvertTools.bytes2HexString(encryptCBC)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
else -> {
|
||||
postRequest.upJson(requestMsg)
|
||||
}
|
||||
}
|
||||
|
||||
postRequest.execute(object : SimpleCallBack<String>() {
|
||||
override fun onError(e: ApiException) {
|
||||
XToastUtils.error(e.displayMessage)
|
||||
}
|
||||
|
||||
override fun onSuccess(response: String) {
|
||||
Log.i(TAG, response)
|
||||
try {
|
||||
var json = response
|
||||
if (HttpServerUtils.clientSafetyMeasures == 2) {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey.toString())
|
||||
json = RSACrypt.decryptByPublicKey(json, publicKey)
|
||||
json = String(Base64.decode(json))
|
||||
} else if (HttpServerUtils.clientSafetyMeasures == 3) {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey.toString())
|
||||
val encryptCBC = ConvertTools.hexStringToByteArray(json)
|
||||
val decryptCBC = SM4Crypt.decrypt(encryptCBC, sm4Key)
|
||||
json = String(decryptCBC)
|
||||
}
|
||||
val resp: BaseResponse<List<CallInfo>?> = Gson().fromJson(json, object : TypeToken<BaseResponse<List<CallInfo>?>>() {}.type)
|
||||
if (resp.code == 200) {
|
||||
pageNum++
|
||||
if (refresh) {
|
||||
mAdapter!!.refresh(resp.data)
|
||||
binding!!.refreshLayout.finishRefresh()
|
||||
binding!!.recyclerView.scrollToPosition(0)
|
||||
} else {
|
||||
mAdapter!!.loadMore(resp.data)
|
||||
binding!!.refreshLayout.finishLoadMore()
|
||||
}
|
||||
} else {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + resp.msg)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + response)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
package com.idormy.sms.forwarder.fragment.client
|
||||
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.alibaba.android.vlayout.DelegateAdapter
|
||||
import com.alibaba.android.vlayout.VirtualLayoutManager
|
||||
import com.alibaba.android.vlayout.layout.LinearLayoutHelper
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.idormy.sms.forwarder.adapter.base.broccoli.BroccoliSimpleDelegateAdapter
|
||||
import com.idormy.sms.forwarder.adapter.base.delegate.SimpleDelegateAdapter
|
||||
import com.idormy.sms.forwarder.core.BaseFragment
|
||||
import com.idormy.sms.forwarder.databinding.FragmentClientCallQueryBinding
|
||||
import com.idormy.sms.forwarder.entity.CallInfo
|
||||
import com.idormy.sms.forwarder.server.model.BaseResponse
|
||||
import com.idormy.sms.forwarder.server.model.CallQueryData
|
||||
import com.idormy.sms.forwarder.utils.*
|
||||
import com.jeremyliao.liveeventbus.LiveEventBus
|
||||
import com.scwang.smartrefresh.layout.api.RefreshLayout
|
||||
import com.xuexiang.xaop.annotation.SingleClick
|
||||
import com.xuexiang.xhttp2.XHttp
|
||||
import com.xuexiang.xhttp2.cache.model.CacheMode
|
||||
import com.xuexiang.xhttp2.callback.SimpleCallBack
|
||||
import com.xuexiang.xhttp2.exception.ApiException
|
||||
import com.xuexiang.xpage.annotation.Page
|
||||
import com.xuexiang.xpage.base.XPageActivity
|
||||
import com.xuexiang.xpage.core.PageOption
|
||||
import com.xuexiang.xrouter.utils.TextUtils
|
||||
import com.xuexiang.xui.adapter.recyclerview.RecyclerViewHolder
|
||||
import com.xuexiang.xui.utils.ResUtils
|
||||
import com.xuexiang.xui.utils.SnackbarUtils
|
||||
import com.xuexiang.xui.widget.actionbar.TitleBar
|
||||
import com.xuexiang.xui.widget.searchview.MaterialSearchView
|
||||
import com.xuexiang.xutil.data.ConvertTools
|
||||
import com.xuexiang.xutil.data.DateUtils
|
||||
import com.xuexiang.xutil.system.ClipboardUtils
|
||||
import me.samlss.broccoli.Broccoli
|
||||
|
||||
@Suppress("PropertyName")
|
||||
@Page(name = "远程查通话")
|
||||
class CallQueryFragment : BaseFragment<FragmentClientCallQueryBinding?>() {
|
||||
|
||||
val TAG: String = CallQueryFragment::class.java.simpleName
|
||||
private var mAdapter: SimpleDelegateAdapter<CallInfo>? = null
|
||||
private var callType: Int = 3
|
||||
private var pageNum: Int = 1
|
||||
private val pageSize: Int = 20
|
||||
private var keyword: String = ""
|
||||
|
||||
override fun viewBindingInflate(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup,
|
||||
): FragmentClientCallQueryBinding {
|
||||
return FragmentClientCallQueryBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
override fun initTitle(): TitleBar {
|
||||
val titleBar = super.initTitle()!!.setImmersive(false)
|
||||
titleBar.setTitle(R.string.api_call_query)
|
||||
titleBar!!.addAction(object : TitleBar.ImageAction(R.drawable.ic_query) {
|
||||
@SingleClick
|
||||
override fun performAction(view: View) {
|
||||
binding!!.searchView.showSearch()
|
||||
}
|
||||
})
|
||||
return titleBar
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化控件
|
||||
*/
|
||||
override fun initViews() {
|
||||
val virtualLayoutManager = VirtualLayoutManager(requireContext())
|
||||
binding!!.recyclerView.layoutManager = virtualLayoutManager
|
||||
val viewPool = RecyclerView.RecycledViewPool()
|
||||
binding!!.recyclerView.setRecycledViewPool(viewPool)
|
||||
viewPool.setMaxRecycledViews(0, 10)
|
||||
|
||||
mAdapter = object : BroccoliSimpleDelegateAdapter<CallInfo>(
|
||||
R.layout.adapter_call_card_view_list_item,
|
||||
LinearLayoutHelper(),
|
||||
DataProvider.emptyCallInfo
|
||||
) {
|
||||
override fun onBindData(
|
||||
holder: RecyclerViewHolder,
|
||||
model: CallInfo,
|
||||
position: Int,
|
||||
) {
|
||||
val from = if (TextUtils.isEmpty(model.name)) model.number else model.number + " | " + model.name
|
||||
holder.text(R.id.tv_from, from)
|
||||
holder.text(R.id.tv_time, DateUtils.getFriendlyTimeSpanByNow(model.dateLong))
|
||||
holder.image(R.id.iv_image, model.typeImageId)
|
||||
holder.image(R.id.iv_sim_image, model.simImageId)
|
||||
holder.text(R.id.tv_duration, ResUtils.getString(R.string.call_duration) + model.duration + ResUtils.getString(R.string.seconds))
|
||||
holder.image(R.id.iv_copy, R.drawable.ic_copy)
|
||||
holder.image(R.id.iv_call, R.drawable.ic_phone_out)
|
||||
holder.image(R.id.iv_reply, R.drawable.ic_reply)
|
||||
holder.click(R.id.iv_copy) {
|
||||
XToastUtils.info(String.format(getString(R.string.copied_to_clipboard), from))
|
||||
ClipboardUtils.copyText(from)
|
||||
}
|
||||
holder.click(R.id.iv_call) {
|
||||
XToastUtils.info(getString(R.string.local_call) + model.number)
|
||||
PhoneUtils.dial(model.number)
|
||||
}
|
||||
holder.click(R.id.iv_reply) {
|
||||
XToastUtils.info(getString(R.string.remote_sms) + model.number)
|
||||
LiveEventBus.get<Int>(EVENT_KEY_SIM_SLOT).post(model.simId)
|
||||
LiveEventBus.get<String>(EVENT_KEY_PHONE_NUMBERS).post(model.number)
|
||||
PageOption.to(SmsSendFragment::class.java).setNewActivity(true).open((context as XPageActivity?)!!)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindBroccoli(holder: RecyclerViewHolder, broccoli: Broccoli) {
|
||||
broccoli.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.tv_from)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.tv_time)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.iv_sim_image)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.tv_duration)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.iv_image)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.iv_copy)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.iv_call)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.iv_reply)))
|
||||
}
|
||||
}
|
||||
|
||||
val delegateAdapter = DelegateAdapter(virtualLayoutManager)
|
||||
delegateAdapter.addAdapter(mAdapter)
|
||||
binding!!.recyclerView.adapter = delegateAdapter
|
||||
|
||||
binding!!.tabBar.setTabTitles(ResUtils.getStringArray(R.array.call_type_option))
|
||||
binding!!.tabBar.setOnTabClickListener { _, position ->
|
||||
//XToastUtils.toast("点击了$title--$position")
|
||||
callType = 3 - position
|
||||
loadRemoteData(true)
|
||||
binding!!.recyclerView.scrollToPosition(0)
|
||||
}
|
||||
|
||||
//搜索框
|
||||
binding!!.searchView.findViewById<View>(com.xuexiang.xui.R.id.search_layout).visibility = View.GONE
|
||||
binding!!.searchView.setVoiceSearch(true)
|
||||
binding!!.searchView.setEllipsize(true)
|
||||
binding!!.searchView.setSuggestions(resources.getStringArray(R.array.query_suggestions))
|
||||
binding!!.searchView.setOnQueryTextListener(object : MaterialSearchView.OnQueryTextListener {
|
||||
override fun onQueryTextSubmit(query: String): Boolean {
|
||||
SnackbarUtils.Indefinite(view, String.format(getString(R.string.search_keyword), query)).info()
|
||||
.actionColor(ResUtils.getColor(R.color.xui_config_color_white))
|
||||
.setAction(getString(R.string.clear)) {
|
||||
keyword = ""
|
||||
loadRemoteData(true)
|
||||
}.show()
|
||||
if (keyword != query) {
|
||||
keyword = query
|
||||
loadRemoteData(true)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onQueryTextChange(newText: String): Boolean {
|
||||
//Do some magic
|
||||
return false
|
||||
}
|
||||
})
|
||||
binding!!.searchView.setOnSearchViewListener(object : MaterialSearchView.SearchViewListener {
|
||||
override fun onSearchViewShown() {
|
||||
//Do some magic
|
||||
}
|
||||
|
||||
override fun onSearchViewClosed() {
|
||||
//Do some magic
|
||||
}
|
||||
})
|
||||
binding!!.searchView.setSubmitOnClick(true)
|
||||
}
|
||||
|
||||
override fun initListeners() {
|
||||
//下拉刷新
|
||||
binding!!.refreshLayout.setOnRefreshListener { refreshLayout: RefreshLayout ->
|
||||
refreshLayout.layout.postDelayed({
|
||||
loadRemoteData(true)
|
||||
}, 1000)
|
||||
}
|
||||
//上拉加载
|
||||
binding!!.refreshLayout.setOnLoadMoreListener { refreshLayout: RefreshLayout ->
|
||||
refreshLayout.layout.postDelayed({
|
||||
loadRemoteData(false)
|
||||
}, 1000)
|
||||
}
|
||||
binding!!.refreshLayout.autoRefresh() //第一次进入触发自动刷新,演示效果
|
||||
}
|
||||
|
||||
private fun loadRemoteData(refresh: Boolean) {
|
||||
|
||||
val requestUrl: String = HttpServerUtils.serverAddress + "/call/query"
|
||||
Log.i(TAG, "requestUrl:$requestUrl")
|
||||
|
||||
val msgMap: MutableMap<String, Any> = mutableMapOf()
|
||||
val timestamp = System.currentTimeMillis()
|
||||
msgMap["timestamp"] = timestamp
|
||||
val clientSignKey = HttpServerUtils.clientSignKey
|
||||
if (!TextUtils.isEmpty(clientSignKey)) {
|
||||
msgMap["sign"] = HttpServerUtils.calcSign(timestamp.toString(), clientSignKey)
|
||||
}
|
||||
|
||||
if (refresh) pageNum = 1
|
||||
msgMap["data"] = CallQueryData(callType, pageNum, pageSize, keyword)
|
||||
|
||||
var requestMsg: String = Gson().toJson(msgMap)
|
||||
Log.i(TAG, "requestMsg:$requestMsg")
|
||||
|
||||
val postRequest = XHttp.post(requestUrl)
|
||||
.keepJson(true)
|
||||
.timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
|
||||
.cacheMode(CacheMode.NO_CACHE)
|
||||
.timeStamp(true)
|
||||
|
||||
when (HttpServerUtils.clientSafetyMeasures) {
|
||||
2 -> {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey)
|
||||
try {
|
||||
requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
requestMsg = RSACrypt.encryptByPublicKey(requestMsg, publicKey)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
3 -> {
|
||||
try {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey)
|
||||
//requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
val encryptCBC = SM4Crypt.encrypt(requestMsg.toByteArray(), sm4Key)
|
||||
requestMsg = ConvertTools.bytes2HexString(encryptCBC)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
else -> {
|
||||
postRequest.upJson(requestMsg)
|
||||
}
|
||||
}
|
||||
|
||||
postRequest.execute(object : SimpleCallBack<String>() {
|
||||
override fun onError(e: ApiException) {
|
||||
XToastUtils.error(e.displayMessage)
|
||||
}
|
||||
|
||||
override fun onSuccess(response: String) {
|
||||
Log.i(TAG, response)
|
||||
try {
|
||||
var json = response
|
||||
if (HttpServerUtils.clientSafetyMeasures == 2) {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey)
|
||||
json = RSACrypt.decryptByPublicKey(json, publicKey)
|
||||
json = String(Base64.decode(json))
|
||||
} else if (HttpServerUtils.clientSafetyMeasures == 3) {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey)
|
||||
val encryptCBC = ConvertTools.hexStringToByteArray(json)
|
||||
val decryptCBC = SM4Crypt.decrypt(encryptCBC, sm4Key)
|
||||
json = String(decryptCBC)
|
||||
}
|
||||
val resp: BaseResponse<List<CallInfo>?> = Gson().fromJson(json, object : TypeToken<BaseResponse<List<CallInfo>?>>() {}.type)
|
||||
if (resp.code == 200) {
|
||||
pageNum++
|
||||
if (refresh) {
|
||||
mAdapter!!.refresh(resp.data)
|
||||
binding!!.refreshLayout.finishRefresh()
|
||||
binding!!.recyclerView.scrollToPosition(0)
|
||||
} else {
|
||||
mAdapter!!.loadMore(resp.data)
|
||||
binding!!.refreshLayout.finishLoadMore()
|
||||
}
|
||||
} else {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + resp.msg)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + response)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,459 +1,459 @@
|
|||
package com.idormy.sms.forwarder.fragment.client
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.os.Environment
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.JsonDeserializer
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.hjq.permissions.OnPermissionCallback
|
||||
import com.hjq.permissions.Permission
|
||||
import com.hjq.permissions.XXPermissions
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.idormy.sms.forwarder.core.BaseFragment
|
||||
import com.idormy.sms.forwarder.databinding.FragmentClientCloneBinding
|
||||
import com.idormy.sms.forwarder.entity.CloneInfo
|
||||
import com.idormy.sms.forwarder.server.model.BaseResponse
|
||||
import com.idormy.sms.forwarder.utils.*
|
||||
import com.idormy.sms.forwarder.utils.Base64
|
||||
import com.xuexiang.xaop.annotation.SingleClick
|
||||
import com.xuexiang.xhttp2.XHttp
|
||||
import com.xuexiang.xhttp2.cache.model.CacheMode
|
||||
import com.xuexiang.xhttp2.callback.SimpleCallBack
|
||||
import com.xuexiang.xhttp2.exception.ApiException
|
||||
import com.xuexiang.xpage.annotation.Page
|
||||
import com.xuexiang.xrouter.utils.TextUtils
|
||||
import com.xuexiang.xui.utils.CountDownButtonHelper
|
||||
import com.xuexiang.xui.utils.ResUtils
|
||||
import com.xuexiang.xui.widget.actionbar.TitleBar
|
||||
import com.xuexiang.xutil.app.AppUtils
|
||||
import com.xuexiang.xutil.data.ConvertTools
|
||||
import com.xuexiang.xutil.file.FileIOUtils
|
||||
import com.xuexiang.xutil.file.FileUtils
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
|
||||
|
||||
@Suppress("PropertyName")
|
||||
@Page(name = "一键换新机")
|
||||
class CloneFragment : BaseFragment<FragmentClientCloneBinding?>(), View.OnClickListener {
|
||||
|
||||
val TAG: String = SmsQueryFragment::class.java.simpleName
|
||||
private var backupPath: String? = null
|
||||
private val backupFile = "SmsForwarder.json"
|
||||
private var pushCountDownHelper: CountDownButtonHelper? = null
|
||||
private var pullCountDownHelper: CountDownButtonHelper? = null
|
||||
private var exportCountDownHelper: CountDownButtonHelper? = null
|
||||
private var importCountDownHelper: CountDownButtonHelper? = null
|
||||
|
||||
override fun viewBindingInflate(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup,
|
||||
): FragmentClientCloneBinding {
|
||||
return FragmentClientCloneBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
override fun initTitle(): TitleBar? {
|
||||
val titleBar = super.initTitle()!!.setImmersive(false)
|
||||
titleBar.setTitle(R.string.api_clone)
|
||||
return titleBar
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化控件
|
||||
*/
|
||||
override fun initViews() {
|
||||
// 申请储存权限
|
||||
XXPermissions.with(this)
|
||||
//.permission(*Permission.Group.STORAGE)
|
||||
.permission(Permission.MANAGE_EXTERNAL_STORAGE)
|
||||
.request(object : OnPermissionCallback {
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun onGranted(permissions: List<String>, all: Boolean) {
|
||||
backupPath =
|
||||
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).path
|
||||
binding!!.tvBackupPath.text = backupPath + File.separator + backupFile
|
||||
}
|
||||
|
||||
override fun onDenied(permissions: List<String>, never: Boolean) {
|
||||
if (never) {
|
||||
XToastUtils.error(R.string.toast_denied_never)
|
||||
// 如果是被永久拒绝就跳转到应用权限系统设置页面
|
||||
XXPermissions.startPermissionActivity(requireContext(), permissions)
|
||||
} else {
|
||||
XToastUtils.error(R.string.toast_denied)
|
||||
}
|
||||
binding!!.tvBackupPath.text = getString(R.string.storage_permission_tips)
|
||||
}
|
||||
})
|
||||
|
||||
binding!!.tabBar.setTabTitles(ResUtils.getStringArray(R.array.clone_type_option))
|
||||
binding!!.tabBar.setOnTabClickListener { _, position ->
|
||||
//XToastUtils.toast("点击了$title--$position")
|
||||
if (position == 1) {
|
||||
binding!!.layoutNetwork.visibility = View.GONE
|
||||
binding!!.layoutOffline.visibility = View.VISIBLE
|
||||
} else {
|
||||
binding!!.layoutNetwork.visibility = View.VISIBLE
|
||||
binding!!.layoutOffline.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
//按钮增加倒计时,避免重复点击
|
||||
pushCountDownHelper = CountDownButtonHelper(binding!!.btnPush, SettingUtils.requestTimeout)
|
||||
pushCountDownHelper!!.setOnCountDownListener(object :
|
||||
CountDownButtonHelper.OnCountDownListener {
|
||||
override fun onCountDown(time: Int) {
|
||||
binding!!.btnPush.text = String.format(getString(R.string.seconds_n), time)
|
||||
}
|
||||
|
||||
override fun onFinished() {
|
||||
binding!!.btnPush.text = getString(R.string.push)
|
||||
}
|
||||
})
|
||||
pullCountDownHelper = CountDownButtonHelper(binding!!.btnPull, SettingUtils.requestTimeout)
|
||||
pullCountDownHelper!!.setOnCountDownListener(object :
|
||||
CountDownButtonHelper.OnCountDownListener {
|
||||
override fun onCountDown(time: Int) {
|
||||
binding!!.btnPull.text = String.format(getString(R.string.seconds_n), time)
|
||||
}
|
||||
|
||||
override fun onFinished() {
|
||||
binding!!.btnPull.text = getString(R.string.pull)
|
||||
}
|
||||
})
|
||||
exportCountDownHelper = CountDownButtonHelper(binding!!.btnExport, 3)
|
||||
exportCountDownHelper!!.setOnCountDownListener(object :
|
||||
CountDownButtonHelper.OnCountDownListener {
|
||||
override fun onCountDown(time: Int) {
|
||||
binding!!.btnExport.text = String.format(getString(R.string.seconds_n), time)
|
||||
}
|
||||
|
||||
override fun onFinished() {
|
||||
binding!!.btnExport.text = getString(R.string.export)
|
||||
}
|
||||
})
|
||||
importCountDownHelper = CountDownButtonHelper(binding!!.btnImport, 3)
|
||||
importCountDownHelper!!.setOnCountDownListener(object :
|
||||
CountDownButtonHelper.OnCountDownListener {
|
||||
override fun onCountDown(time: Int) {
|
||||
binding!!.btnImport.text = String.format(getString(R.string.seconds_n), time)
|
||||
}
|
||||
|
||||
override fun onFinished() {
|
||||
binding!!.btnImport.text = getString(R.string.imports)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun initListeners() {
|
||||
binding!!.btnPush.setOnClickListener(this)
|
||||
binding!!.btnPull.setOnClickListener(this)
|
||||
binding!!.btnExport.setOnClickListener(this)
|
||||
binding!!.btnImport.setOnClickListener(this)
|
||||
}
|
||||
|
||||
@SingleClick
|
||||
override fun onClick(v: View) {
|
||||
when (v.id) {
|
||||
//推送配置
|
||||
R.id.btn_push -> pushData()
|
||||
//拉取配置
|
||||
R.id.btn_pull -> pullData()
|
||||
//导出配置
|
||||
R.id.btn_export -> {
|
||||
try {
|
||||
exportCountDownHelper?.start()
|
||||
val file = File(backupPath + File.separator + backupFile)
|
||||
//判断文件是否存在,存在则在创建之前删除
|
||||
FileUtils.createFileByDeleteOldFile(file)
|
||||
val cloneInfo = HttpServerUtils.exportSettings()
|
||||
val jsonStr = Gson().toJson(cloneInfo)
|
||||
if (FileIOUtils.writeFileFromString(file, jsonStr)) {
|
||||
XToastUtils.success(getString(R.string.export_succeeded))
|
||||
} else {
|
||||
binding!!.tvExport.text = getString(R.string.export_failed)
|
||||
XToastUtils.error(getString(R.string.export_failed))
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(String.format(getString(R.string.export_failed_tips), e.message))
|
||||
}
|
||||
}
|
||||
//导入配置
|
||||
R.id.btn_import -> {
|
||||
try {
|
||||
importCountDownHelper?.start()
|
||||
val file = File(backupPath + File.separator + backupFile)
|
||||
//判断文件是否存在
|
||||
if (!FileUtils.isFileExists(file)) {
|
||||
XToastUtils.error(getString(R.string.import_failed_file_not_exist))
|
||||
return
|
||||
}
|
||||
|
||||
val jsonStr = FileIOUtils.readFile2String(file)
|
||||
Log.d(TAG, "jsonStr = $jsonStr")
|
||||
if (TextUtils.isEmpty(jsonStr)) {
|
||||
XToastUtils.error(getString(R.string.import_failed))
|
||||
return
|
||||
}
|
||||
|
||||
//替换Date字段为当前时间
|
||||
val builder = GsonBuilder()
|
||||
builder.registerTypeAdapter(
|
||||
Date::class.java,
|
||||
JsonDeserializer<Any?> { _, _, _ -> Date() })
|
||||
val gson = builder.create()
|
||||
val cloneInfo = gson.fromJson(jsonStr, CloneInfo::class.java)
|
||||
Log.d(TAG, "cloneInfo = $cloneInfo")
|
||||
|
||||
//判断版本是否一致
|
||||
HttpServerUtils.compareVersion(cloneInfo)
|
||||
|
||||
if (HttpServerUtils.restoreSettings(cloneInfo)) {
|
||||
XToastUtils.success(getString(R.string.import_succeeded))
|
||||
} else {
|
||||
XToastUtils.error(getString(R.string.import_failed))
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(String.format(getString(R.string.import_failed_tips), e.message))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//推送配置
|
||||
private fun pushData() {
|
||||
if (!CommonUtils.checkUrl(HttpServerUtils.serverAddress)) {
|
||||
XToastUtils.error(getString(R.string.invalid_service_address))
|
||||
return
|
||||
}
|
||||
|
||||
pushCountDownHelper?.start()
|
||||
|
||||
val requestUrl: String = HttpServerUtils.serverAddress + "/clone/push"
|
||||
Log.i(TAG, "requestUrl:$requestUrl")
|
||||
|
||||
val msgMap: MutableMap<String, Any> = mutableMapOf()
|
||||
val timestamp = System.currentTimeMillis()
|
||||
msgMap["timestamp"] = timestamp
|
||||
val clientSignKey = HttpServerUtils.clientSignKey
|
||||
if (!TextUtils.isEmpty(clientSignKey)) {
|
||||
msgMap["sign"] =
|
||||
HttpServerUtils.calcSign(timestamp.toString(), clientSignKey.toString())
|
||||
}
|
||||
msgMap["data"] = HttpServerUtils.exportSettings()
|
||||
|
||||
var requestMsg: String = Gson().toJson(msgMap)
|
||||
Log.i(TAG, "requestMsg:$requestMsg")
|
||||
|
||||
val postRequest = XHttp.post(requestUrl)
|
||||
.keepJson(true)
|
||||
.timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
|
||||
.cacheMode(CacheMode.NO_CACHE)
|
||||
.timeStamp(true)
|
||||
|
||||
when (HttpServerUtils.clientSafetyMeasures) {
|
||||
2 -> {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey.toString())
|
||||
try {
|
||||
requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
requestMsg = RSACrypt.encryptByPublicKey(requestMsg, publicKey)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
3 -> {
|
||||
try {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey.toString())
|
||||
//requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
val encryptCBC = SM4Crypt.encrypt(requestMsg.toByteArray(), sm4Key)
|
||||
requestMsg = ConvertTools.bytes2HexString(encryptCBC)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
else -> {
|
||||
postRequest.upJson(requestMsg)
|
||||
}
|
||||
}
|
||||
|
||||
postRequest.execute(object : SimpleCallBack<String>() {
|
||||
override fun onError(e: ApiException) {
|
||||
XToastUtils.error(e.displayMessage)
|
||||
pushCountDownHelper?.finish()
|
||||
}
|
||||
|
||||
override fun onSuccess(response: String) {
|
||||
Log.i(TAG, response)
|
||||
try {
|
||||
var json = response
|
||||
if (HttpServerUtils.clientSafetyMeasures == 2) {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey.toString())
|
||||
json = RSACrypt.decryptByPublicKey(json, publicKey)
|
||||
json = String(Base64.decode(json))
|
||||
} else if (HttpServerUtils.clientSafetyMeasures == 3) {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey.toString())
|
||||
val encryptCBC = ConvertTools.hexStringToByteArray(json)
|
||||
val decryptCBC = SM4Crypt.decrypt(encryptCBC, sm4Key)
|
||||
json = String(decryptCBC)
|
||||
}
|
||||
val resp: BaseResponse<String> = Gson().fromJson(json, object : TypeToken<BaseResponse<String>>() {}.type)
|
||||
if (resp.code == 200) {
|
||||
XToastUtils.success(ResUtils.getString(R.string.request_succeeded))
|
||||
} else {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + resp.msg)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + response)
|
||||
}
|
||||
pushCountDownHelper?.finish()
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
//拉取配置
|
||||
private fun pullData() {
|
||||
if (!CommonUtils.checkUrl(HttpServerUtils.serverAddress)) {
|
||||
XToastUtils.error(getString(R.string.invalid_service_address))
|
||||
return
|
||||
}
|
||||
|
||||
exportCountDownHelper?.start()
|
||||
|
||||
val requestUrl: String = HttpServerUtils.serverAddress + "/clone/pull"
|
||||
Log.i(TAG, "requestUrl:$requestUrl")
|
||||
|
||||
val msgMap: MutableMap<String, Any> = mutableMapOf()
|
||||
val timestamp = System.currentTimeMillis()
|
||||
msgMap["timestamp"] = timestamp
|
||||
val clientSignKey = HttpServerUtils.clientSignKey
|
||||
if (!TextUtils.isEmpty(clientSignKey)) {
|
||||
msgMap["sign"] =
|
||||
HttpServerUtils.calcSign(timestamp.toString(), clientSignKey.toString())
|
||||
}
|
||||
|
||||
val dataMap: MutableMap<String, Any> = mutableMapOf()
|
||||
dataMap["version_code"] = AppUtils.getAppVersionCode()
|
||||
msgMap["data"] = dataMap
|
||||
|
||||
var requestMsg: String = Gson().toJson(msgMap)
|
||||
Log.i(TAG, "requestMsg:$requestMsg")
|
||||
|
||||
val postRequest = XHttp.post(requestUrl)
|
||||
.keepJson(true)
|
||||
.timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
|
||||
.cacheMode(CacheMode.NO_CACHE)
|
||||
.timeStamp(true)
|
||||
|
||||
when (HttpServerUtils.clientSafetyMeasures) {
|
||||
2 -> {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey.toString())
|
||||
try {
|
||||
requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
requestMsg = RSACrypt.encryptByPublicKey(requestMsg, publicKey)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
3 -> {
|
||||
try {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey.toString())
|
||||
//requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
val encryptCBC = SM4Crypt.encrypt(requestMsg.toByteArray(), sm4Key)
|
||||
requestMsg = ConvertTools.bytes2HexString(encryptCBC)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
else -> {
|
||||
postRequest.upJson(requestMsg)
|
||||
}
|
||||
}
|
||||
|
||||
postRequest.execute(object : SimpleCallBack<String>() {
|
||||
override fun onError(e: ApiException) {
|
||||
XToastUtils.error(e.displayMessage)
|
||||
exportCountDownHelper?.finish()
|
||||
}
|
||||
|
||||
override fun onSuccess(response: String) {
|
||||
Log.i(TAG, response)
|
||||
try {
|
||||
var json = response
|
||||
if (HttpServerUtils.clientSafetyMeasures == 2) {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey.toString())
|
||||
json = RSACrypt.decryptByPublicKey(json, publicKey)
|
||||
json = String(Base64.decode(json))
|
||||
} else if (HttpServerUtils.clientSafetyMeasures == 3) {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey.toString())
|
||||
val encryptCBC = ConvertTools.hexStringToByteArray(json)
|
||||
val decryptCBC = SM4Crypt.decrypt(encryptCBC, sm4Key)
|
||||
json = String(decryptCBC)
|
||||
}
|
||||
|
||||
//替换Date字段为当前时间
|
||||
val builder = GsonBuilder()
|
||||
builder.registerTypeAdapter(
|
||||
Date::class.java,
|
||||
JsonDeserializer<Any?> { _, _, _ -> Date() })
|
||||
val gson = builder.create()
|
||||
val resp: BaseResponse<CloneInfo> = gson.fromJson(json, object : TypeToken<BaseResponse<CloneInfo>>() {}.type)
|
||||
if (resp.code == 200) {
|
||||
val cloneInfo = resp.data
|
||||
Log.d(TAG, "cloneInfo = $cloneInfo")
|
||||
|
||||
if (cloneInfo == null) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed))
|
||||
return
|
||||
}
|
||||
|
||||
//判断版本是否一致
|
||||
HttpServerUtils.compareVersion(cloneInfo)
|
||||
|
||||
if (HttpServerUtils.restoreSettings(cloneInfo)) {
|
||||
XToastUtils.success(getString(R.string.import_succeeded))
|
||||
}
|
||||
} else {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + resp.msg)
|
||||
}
|
||||
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + response)
|
||||
}
|
||||
exportCountDownHelper?.finish()
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
if (pushCountDownHelper != null) pushCountDownHelper!!.recycle()
|
||||
if (pullCountDownHelper != null) pullCountDownHelper!!.recycle()
|
||||
if (exportCountDownHelper != null) exportCountDownHelper!!.recycle()
|
||||
if (importCountDownHelper != null) importCountDownHelper!!.recycle()
|
||||
super.onDestroyView()
|
||||
}
|
||||
package com.idormy.sms.forwarder.fragment.client
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.os.Environment
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.JsonDeserializer
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.hjq.permissions.OnPermissionCallback
|
||||
import com.hjq.permissions.Permission
|
||||
import com.hjq.permissions.XXPermissions
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.idormy.sms.forwarder.core.BaseFragment
|
||||
import com.idormy.sms.forwarder.databinding.FragmentClientCloneBinding
|
||||
import com.idormy.sms.forwarder.entity.CloneInfo
|
||||
import com.idormy.sms.forwarder.server.model.BaseResponse
|
||||
import com.idormy.sms.forwarder.utils.*
|
||||
import com.idormy.sms.forwarder.utils.Base64
|
||||
import com.xuexiang.xaop.annotation.SingleClick
|
||||
import com.xuexiang.xhttp2.XHttp
|
||||
import com.xuexiang.xhttp2.cache.model.CacheMode
|
||||
import com.xuexiang.xhttp2.callback.SimpleCallBack
|
||||
import com.xuexiang.xhttp2.exception.ApiException
|
||||
import com.xuexiang.xpage.annotation.Page
|
||||
import com.xuexiang.xrouter.utils.TextUtils
|
||||
import com.xuexiang.xui.utils.CountDownButtonHelper
|
||||
import com.xuexiang.xui.utils.ResUtils
|
||||
import com.xuexiang.xui.widget.actionbar.TitleBar
|
||||
import com.xuexiang.xutil.app.AppUtils
|
||||
import com.xuexiang.xutil.data.ConvertTools
|
||||
import com.xuexiang.xutil.file.FileIOUtils
|
||||
import com.xuexiang.xutil.file.FileUtils
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
|
||||
|
||||
@Suppress("PropertyName")
|
||||
@Page(name = "一键换新机")
|
||||
class CloneFragment : BaseFragment<FragmentClientCloneBinding?>(), View.OnClickListener {
|
||||
|
||||
val TAG: String = SmsQueryFragment::class.java.simpleName
|
||||
private var backupPath: String? = null
|
||||
private val backupFile = "SmsForwarder.json"
|
||||
private var pushCountDownHelper: CountDownButtonHelper? = null
|
||||
private var pullCountDownHelper: CountDownButtonHelper? = null
|
||||
private var exportCountDownHelper: CountDownButtonHelper? = null
|
||||
private var importCountDownHelper: CountDownButtonHelper? = null
|
||||
|
||||
override fun viewBindingInflate(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup,
|
||||
): FragmentClientCloneBinding {
|
||||
return FragmentClientCloneBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
override fun initTitle(): TitleBar? {
|
||||
val titleBar = super.initTitle()!!.setImmersive(false)
|
||||
titleBar.setTitle(R.string.api_clone)
|
||||
return titleBar
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化控件
|
||||
*/
|
||||
override fun initViews() {
|
||||
// 申请储存权限
|
||||
XXPermissions.with(this)
|
||||
//.permission(*Permission.Group.STORAGE)
|
||||
.permission(Permission.MANAGE_EXTERNAL_STORAGE)
|
||||
.request(object : OnPermissionCallback {
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun onGranted(permissions: List<String>, all: Boolean) {
|
||||
backupPath =
|
||||
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).path
|
||||
binding!!.tvBackupPath.text = backupPath + File.separator + backupFile
|
||||
}
|
||||
|
||||
override fun onDenied(permissions: List<String>, never: Boolean) {
|
||||
if (never) {
|
||||
XToastUtils.error(R.string.toast_denied_never)
|
||||
// 如果是被永久拒绝就跳转到应用权限系统设置页面
|
||||
XXPermissions.startPermissionActivity(requireContext(), permissions)
|
||||
} else {
|
||||
XToastUtils.error(R.string.toast_denied)
|
||||
}
|
||||
binding!!.tvBackupPath.text = getString(R.string.storage_permission_tips)
|
||||
}
|
||||
})
|
||||
|
||||
binding!!.tabBar.setTabTitles(ResUtils.getStringArray(R.array.clone_type_option))
|
||||
binding!!.tabBar.setOnTabClickListener { _, position ->
|
||||
//XToastUtils.toast("点击了$title--$position")
|
||||
if (position == 1) {
|
||||
binding!!.layoutNetwork.visibility = View.GONE
|
||||
binding!!.layoutOffline.visibility = View.VISIBLE
|
||||
} else {
|
||||
binding!!.layoutNetwork.visibility = View.VISIBLE
|
||||
binding!!.layoutOffline.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
//按钮增加倒计时,避免重复点击
|
||||
pushCountDownHelper = CountDownButtonHelper(binding!!.btnPush, SettingUtils.requestTimeout)
|
||||
pushCountDownHelper!!.setOnCountDownListener(object :
|
||||
CountDownButtonHelper.OnCountDownListener {
|
||||
override fun onCountDown(time: Int) {
|
||||
binding!!.btnPush.text = String.format(getString(R.string.seconds_n), time)
|
||||
}
|
||||
|
||||
override fun onFinished() {
|
||||
binding!!.btnPush.text = getString(R.string.push)
|
||||
}
|
||||
})
|
||||
pullCountDownHelper = CountDownButtonHelper(binding!!.btnPull, SettingUtils.requestTimeout)
|
||||
pullCountDownHelper!!.setOnCountDownListener(object :
|
||||
CountDownButtonHelper.OnCountDownListener {
|
||||
override fun onCountDown(time: Int) {
|
||||
binding!!.btnPull.text = String.format(getString(R.string.seconds_n), time)
|
||||
}
|
||||
|
||||
override fun onFinished() {
|
||||
binding!!.btnPull.text = getString(R.string.pull)
|
||||
}
|
||||
})
|
||||
exportCountDownHelper = CountDownButtonHelper(binding!!.btnExport, 3)
|
||||
exportCountDownHelper!!.setOnCountDownListener(object :
|
||||
CountDownButtonHelper.OnCountDownListener {
|
||||
override fun onCountDown(time: Int) {
|
||||
binding!!.btnExport.text = String.format(getString(R.string.seconds_n), time)
|
||||
}
|
||||
|
||||
override fun onFinished() {
|
||||
binding!!.btnExport.text = getString(R.string.export)
|
||||
}
|
||||
})
|
||||
importCountDownHelper = CountDownButtonHelper(binding!!.btnImport, 3)
|
||||
importCountDownHelper!!.setOnCountDownListener(object :
|
||||
CountDownButtonHelper.OnCountDownListener {
|
||||
override fun onCountDown(time: Int) {
|
||||
binding!!.btnImport.text = String.format(getString(R.string.seconds_n), time)
|
||||
}
|
||||
|
||||
override fun onFinished() {
|
||||
binding!!.btnImport.text = getString(R.string.imports)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun initListeners() {
|
||||
binding!!.btnPush.setOnClickListener(this)
|
||||
binding!!.btnPull.setOnClickListener(this)
|
||||
binding!!.btnExport.setOnClickListener(this)
|
||||
binding!!.btnImport.setOnClickListener(this)
|
||||
}
|
||||
|
||||
@SingleClick
|
||||
override fun onClick(v: View) {
|
||||
when (v.id) {
|
||||
//推送配置
|
||||
R.id.btn_push -> pushData()
|
||||
//拉取配置
|
||||
R.id.btn_pull -> pullData()
|
||||
//导出配置
|
||||
R.id.btn_export -> {
|
||||
try {
|
||||
exportCountDownHelper?.start()
|
||||
val file = File(backupPath + File.separator + backupFile)
|
||||
//判断文件是否存在,存在则在创建之前删除
|
||||
FileUtils.createFileByDeleteOldFile(file)
|
||||
val cloneInfo = HttpServerUtils.exportSettings()
|
||||
val jsonStr = Gson().toJson(cloneInfo)
|
||||
if (FileIOUtils.writeFileFromString(file, jsonStr)) {
|
||||
XToastUtils.success(getString(R.string.export_succeeded))
|
||||
} else {
|
||||
binding!!.tvExport.text = getString(R.string.export_failed)
|
||||
XToastUtils.error(getString(R.string.export_failed))
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(String.format(getString(R.string.export_failed_tips), e.message))
|
||||
}
|
||||
}
|
||||
//导入配置
|
||||
R.id.btn_import -> {
|
||||
try {
|
||||
importCountDownHelper?.start()
|
||||
val file = File(backupPath + File.separator + backupFile)
|
||||
//判断文件是否存在
|
||||
if (!FileUtils.isFileExists(file)) {
|
||||
XToastUtils.error(getString(R.string.import_failed_file_not_exist))
|
||||
return
|
||||
}
|
||||
|
||||
val jsonStr = FileIOUtils.readFile2String(file)
|
||||
Log.d(TAG, "jsonStr = $jsonStr")
|
||||
if (TextUtils.isEmpty(jsonStr)) {
|
||||
XToastUtils.error(getString(R.string.import_failed))
|
||||
return
|
||||
}
|
||||
|
||||
//替换Date字段为当前时间
|
||||
val builder = GsonBuilder()
|
||||
builder.registerTypeAdapter(
|
||||
Date::class.java,
|
||||
JsonDeserializer<Any?> { _, _, _ -> Date() })
|
||||
val gson = builder.create()
|
||||
val cloneInfo = gson.fromJson(jsonStr, CloneInfo::class.java)
|
||||
Log.d(TAG, "cloneInfo = $cloneInfo")
|
||||
|
||||
//判断版本是否一致
|
||||
HttpServerUtils.compareVersion(cloneInfo)
|
||||
|
||||
if (HttpServerUtils.restoreSettings(cloneInfo)) {
|
||||
XToastUtils.success(getString(R.string.import_succeeded))
|
||||
} else {
|
||||
XToastUtils.error(getString(R.string.import_failed))
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(String.format(getString(R.string.import_failed_tips), e.message))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//推送配置
|
||||
private fun pushData() {
|
||||
if (!CommonUtils.checkUrl(HttpServerUtils.serverAddress)) {
|
||||
XToastUtils.error(getString(R.string.invalid_service_address))
|
||||
return
|
||||
}
|
||||
|
||||
pushCountDownHelper?.start()
|
||||
|
||||
val requestUrl: String = HttpServerUtils.serverAddress + "/clone/push"
|
||||
Log.i(TAG, "requestUrl:$requestUrl")
|
||||
|
||||
val msgMap: MutableMap<String, Any> = mutableMapOf()
|
||||
val timestamp = System.currentTimeMillis()
|
||||
msgMap["timestamp"] = timestamp
|
||||
val clientSignKey = HttpServerUtils.clientSignKey
|
||||
if (!TextUtils.isEmpty(clientSignKey)) {
|
||||
msgMap["sign"] =
|
||||
HttpServerUtils.calcSign(timestamp.toString(), clientSignKey)
|
||||
}
|
||||
msgMap["data"] = HttpServerUtils.exportSettings()
|
||||
|
||||
var requestMsg: String = Gson().toJson(msgMap)
|
||||
Log.i(TAG, "requestMsg:$requestMsg")
|
||||
|
||||
val postRequest = XHttp.post(requestUrl)
|
||||
.keepJson(true)
|
||||
.timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
|
||||
.cacheMode(CacheMode.NO_CACHE)
|
||||
.timeStamp(true)
|
||||
|
||||
when (HttpServerUtils.clientSafetyMeasures) {
|
||||
2 -> {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey)
|
||||
try {
|
||||
requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
requestMsg = RSACrypt.encryptByPublicKey(requestMsg, publicKey)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
3 -> {
|
||||
try {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey)
|
||||
//requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
val encryptCBC = SM4Crypt.encrypt(requestMsg.toByteArray(), sm4Key)
|
||||
requestMsg = ConvertTools.bytes2HexString(encryptCBC)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
else -> {
|
||||
postRequest.upJson(requestMsg)
|
||||
}
|
||||
}
|
||||
|
||||
postRequest.execute(object : SimpleCallBack<String>() {
|
||||
override fun onError(e: ApiException) {
|
||||
XToastUtils.error(e.displayMessage)
|
||||
pushCountDownHelper?.finish()
|
||||
}
|
||||
|
||||
override fun onSuccess(response: String) {
|
||||
Log.i(TAG, response)
|
||||
try {
|
||||
var json = response
|
||||
if (HttpServerUtils.clientSafetyMeasures == 2) {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey)
|
||||
json = RSACrypt.decryptByPublicKey(json, publicKey)
|
||||
json = String(Base64.decode(json))
|
||||
} else if (HttpServerUtils.clientSafetyMeasures == 3) {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey)
|
||||
val encryptCBC = ConvertTools.hexStringToByteArray(json)
|
||||
val decryptCBC = SM4Crypt.decrypt(encryptCBC, sm4Key)
|
||||
json = String(decryptCBC)
|
||||
}
|
||||
val resp: BaseResponse<String> = Gson().fromJson(json, object : TypeToken<BaseResponse<String>>() {}.type)
|
||||
if (resp.code == 200) {
|
||||
XToastUtils.success(ResUtils.getString(R.string.request_succeeded))
|
||||
} else {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + resp.msg)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + response)
|
||||
}
|
||||
pushCountDownHelper?.finish()
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
//拉取配置
|
||||
private fun pullData() {
|
||||
if (!CommonUtils.checkUrl(HttpServerUtils.serverAddress)) {
|
||||
XToastUtils.error(getString(R.string.invalid_service_address))
|
||||
return
|
||||
}
|
||||
|
||||
exportCountDownHelper?.start()
|
||||
|
||||
val requestUrl: String = HttpServerUtils.serverAddress + "/clone/pull"
|
||||
Log.i(TAG, "requestUrl:$requestUrl")
|
||||
|
||||
val msgMap: MutableMap<String, Any> = mutableMapOf()
|
||||
val timestamp = System.currentTimeMillis()
|
||||
msgMap["timestamp"] = timestamp
|
||||
val clientSignKey = HttpServerUtils.clientSignKey
|
||||
if (!TextUtils.isEmpty(clientSignKey)) {
|
||||
msgMap["sign"] =
|
||||
HttpServerUtils.calcSign(timestamp.toString(), clientSignKey)
|
||||
}
|
||||
|
||||
val dataMap: MutableMap<String, Any> = mutableMapOf()
|
||||
dataMap["version_code"] = AppUtils.getAppVersionCode()
|
||||
msgMap["data"] = dataMap
|
||||
|
||||
var requestMsg: String = Gson().toJson(msgMap)
|
||||
Log.i(TAG, "requestMsg:$requestMsg")
|
||||
|
||||
val postRequest = XHttp.post(requestUrl)
|
||||
.keepJson(true)
|
||||
.timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
|
||||
.cacheMode(CacheMode.NO_CACHE)
|
||||
.timeStamp(true)
|
||||
|
||||
when (HttpServerUtils.clientSafetyMeasures) {
|
||||
2 -> {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey)
|
||||
try {
|
||||
requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
requestMsg = RSACrypt.encryptByPublicKey(requestMsg, publicKey)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
3 -> {
|
||||
try {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey)
|
||||
//requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
val encryptCBC = SM4Crypt.encrypt(requestMsg.toByteArray(), sm4Key)
|
||||
requestMsg = ConvertTools.bytes2HexString(encryptCBC)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
else -> {
|
||||
postRequest.upJson(requestMsg)
|
||||
}
|
||||
}
|
||||
|
||||
postRequest.execute(object : SimpleCallBack<String>() {
|
||||
override fun onError(e: ApiException) {
|
||||
XToastUtils.error(e.displayMessage)
|
||||
exportCountDownHelper?.finish()
|
||||
}
|
||||
|
||||
override fun onSuccess(response: String) {
|
||||
Log.i(TAG, response)
|
||||
try {
|
||||
var json = response
|
||||
if (HttpServerUtils.clientSafetyMeasures == 2) {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey)
|
||||
json = RSACrypt.decryptByPublicKey(json, publicKey)
|
||||
json = String(Base64.decode(json))
|
||||
} else if (HttpServerUtils.clientSafetyMeasures == 3) {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey)
|
||||
val encryptCBC = ConvertTools.hexStringToByteArray(json)
|
||||
val decryptCBC = SM4Crypt.decrypt(encryptCBC, sm4Key)
|
||||
json = String(decryptCBC)
|
||||
}
|
||||
|
||||
//替换Date字段为当前时间
|
||||
val builder = GsonBuilder()
|
||||
builder.registerTypeAdapter(
|
||||
Date::class.java,
|
||||
JsonDeserializer<Any?> { _, _, _ -> Date() })
|
||||
val gson = builder.create()
|
||||
val resp: BaseResponse<CloneInfo> = gson.fromJson(json, object : TypeToken<BaseResponse<CloneInfo>>() {}.type)
|
||||
if (resp.code == 200) {
|
||||
val cloneInfo = resp.data
|
||||
Log.d(TAG, "cloneInfo = $cloneInfo")
|
||||
|
||||
if (cloneInfo == null) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed))
|
||||
return
|
||||
}
|
||||
|
||||
//判断版本是否一致
|
||||
HttpServerUtils.compareVersion(cloneInfo)
|
||||
|
||||
if (HttpServerUtils.restoreSettings(cloneInfo)) {
|
||||
XToastUtils.success(getString(R.string.import_succeeded))
|
||||
}
|
||||
} else {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + resp.msg)
|
||||
}
|
||||
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + response)
|
||||
}
|
||||
exportCountDownHelper?.finish()
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
if (pushCountDownHelper != null) pushCountDownHelper!!.recycle()
|
||||
if (pullCountDownHelper != null) pullCountDownHelper!!.recycle()
|
||||
if (exportCountDownHelper != null) exportCountDownHelper!!.recycle()
|
||||
if (importCountDownHelper != null) importCountDownHelper!!.recycle()
|
||||
super.onDestroyView()
|
||||
}
|
||||
}
|
|
@ -1,273 +1,273 @@
|
|||
package com.idormy.sms.forwarder.fragment.client
|
||||
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.text.isDigitsOnly
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.alibaba.android.vlayout.DelegateAdapter
|
||||
import com.alibaba.android.vlayout.VirtualLayoutManager
|
||||
import com.alibaba.android.vlayout.layout.LinearLayoutHelper
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.idormy.sms.forwarder.adapter.base.broccoli.BroccoliSimpleDelegateAdapter
|
||||
import com.idormy.sms.forwarder.adapter.base.delegate.SimpleDelegateAdapter
|
||||
import com.idormy.sms.forwarder.core.BaseFragment
|
||||
import com.idormy.sms.forwarder.databinding.FragmentClientContactQueryBinding
|
||||
import com.idormy.sms.forwarder.entity.ContactInfo
|
||||
import com.idormy.sms.forwarder.server.model.BaseResponse
|
||||
import com.idormy.sms.forwarder.server.model.ContactQueryData
|
||||
import com.idormy.sms.forwarder.utils.*
|
||||
import com.jeremyliao.liveeventbus.LiveEventBus
|
||||
import com.scwang.smartrefresh.layout.api.RefreshLayout
|
||||
import com.xuexiang.xaop.annotation.SingleClick
|
||||
import com.xuexiang.xhttp2.XHttp
|
||||
import com.xuexiang.xhttp2.cache.model.CacheMode
|
||||
import com.xuexiang.xhttp2.callback.SimpleCallBack
|
||||
import com.xuexiang.xhttp2.exception.ApiException
|
||||
import com.xuexiang.xpage.annotation.Page
|
||||
import com.xuexiang.xpage.base.XPageActivity
|
||||
import com.xuexiang.xpage.core.PageOption
|
||||
import com.xuexiang.xrouter.utils.TextUtils
|
||||
import com.xuexiang.xui.adapter.recyclerview.RecyclerViewHolder
|
||||
import com.xuexiang.xui.utils.ResUtils
|
||||
import com.xuexiang.xui.utils.SnackbarUtils
|
||||
import com.xuexiang.xui.widget.actionbar.TitleBar
|
||||
import com.xuexiang.xui.widget.searchview.MaterialSearchView
|
||||
import com.xuexiang.xutil.data.ConvertTools
|
||||
import com.xuexiang.xutil.system.ClipboardUtils
|
||||
import me.samlss.broccoli.Broccoli
|
||||
|
||||
|
||||
@Suppress("PropertyName")
|
||||
@Page(name = "远程查话簿")
|
||||
class ContactQueryFragment : BaseFragment<FragmentClientContactQueryBinding?>() {
|
||||
|
||||
val TAG: String = ContactQueryFragment::class.java.simpleName
|
||||
private var mAdapter: SimpleDelegateAdapter<ContactInfo>? = null
|
||||
private var keyword: String = ""
|
||||
|
||||
override fun viewBindingInflate(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup,
|
||||
): FragmentClientContactQueryBinding {
|
||||
return FragmentClientContactQueryBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
override fun initTitle(): TitleBar {
|
||||
val titleBar = super.initTitle()!!.setImmersive(false)
|
||||
titleBar.setTitle(R.string.api_contact_query)
|
||||
titleBar!!.addAction(object : TitleBar.ImageAction(R.drawable.ic_query) {
|
||||
@SingleClick
|
||||
override fun performAction(view: View) {
|
||||
binding!!.searchView.showSearch()
|
||||
}
|
||||
})
|
||||
return titleBar
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化控件
|
||||
*/
|
||||
override fun initViews() {
|
||||
val virtualLayoutManager = VirtualLayoutManager(requireContext())
|
||||
binding!!.recyclerView.layoutManager = virtualLayoutManager
|
||||
val viewPool = RecyclerView.RecycledViewPool()
|
||||
binding!!.recyclerView.setRecycledViewPool(viewPool)
|
||||
viewPool.setMaxRecycledViews(0, 10)
|
||||
|
||||
mAdapter = object : BroccoliSimpleDelegateAdapter<ContactInfo>(
|
||||
R.layout.adapter_contact_card_view_list_item,
|
||||
LinearLayoutHelper(),
|
||||
DataProvider.emptyContactInfo
|
||||
) {
|
||||
override fun onBindData(
|
||||
holder: RecyclerViewHolder,
|
||||
model: ContactInfo,
|
||||
position: Int,
|
||||
) {
|
||||
holder.text(R.id.sb_letter, model.firstLetter)
|
||||
holder.text(R.id.tv_name, model.name)
|
||||
holder.text(R.id.tv_phone_number, model.phoneNumber)
|
||||
holder.image(R.id.iv_copy, R.drawable.ic_copy)
|
||||
holder.image(R.id.iv_call, R.drawable.ic_phone_out)
|
||||
holder.image(R.id.iv_reply, R.drawable.ic_reply)
|
||||
holder.click(R.id.iv_copy) {
|
||||
val str = model.toString()
|
||||
XToastUtils.info(String.format(getString(R.string.copied_to_clipboard), str))
|
||||
ClipboardUtils.copyText(str)
|
||||
}
|
||||
holder.click(R.id.iv_call) {
|
||||
XToastUtils.info(getString(R.string.local_call) + model.phoneNumber)
|
||||
PhoneUtils.dial(model.phoneNumber)
|
||||
}
|
||||
holder.click(R.id.iv_reply) {
|
||||
XToastUtils.info(getString(R.string.remote_sms) + model.phoneNumber)
|
||||
/*val params = Bundle()
|
||||
params.putString(KEY_PHONE_NUMBERS, model.phoneNumber)
|
||||
openPage(SmsSendFragment::class.java, params)*/
|
||||
LiveEventBus.get<String>(EVENT_KEY_PHONE_NUMBERS).post(model.phoneNumber)
|
||||
PageOption.to(SmsSendFragment::class.java).setNewActivity(true).open((context as XPageActivity?)!!)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindBroccoli(holder: RecyclerViewHolder, broccoli: Broccoli) {
|
||||
broccoli.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.tv_name)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.tv_phone_number)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.sb_letter)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.iv_copy)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.iv_call)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.iv_reply)))
|
||||
}
|
||||
}
|
||||
|
||||
val delegateAdapter = DelegateAdapter(virtualLayoutManager)
|
||||
delegateAdapter.addAdapter(mAdapter)
|
||||
binding!!.recyclerView.adapter = delegateAdapter
|
||||
|
||||
//搜索框
|
||||
binding!!.searchView.findViewById<View>(com.xuexiang.xui.R.id.search_layout).visibility = View.GONE
|
||||
binding!!.searchView.setVoiceSearch(true)
|
||||
binding!!.searchView.setEllipsize(true)
|
||||
binding!!.searchView.setSuggestions(resources.getStringArray(R.array.query_suggestions))
|
||||
binding!!.searchView.setOnQueryTextListener(object : MaterialSearchView.OnQueryTextListener {
|
||||
override fun onQueryTextSubmit(query: String): Boolean {
|
||||
SnackbarUtils.Indefinite(view, String.format(getString(R.string.search_keyword), query)).info()
|
||||
.actionColor(ResUtils.getColor(R.color.xui_config_color_white))
|
||||
.setAction(getString(R.string.clear)) {
|
||||
keyword = ""
|
||||
loadRemoteData()
|
||||
}.show()
|
||||
if (keyword != query) {
|
||||
keyword = query
|
||||
loadRemoteData()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onQueryTextChange(newText: String): Boolean {
|
||||
//Do some magic
|
||||
return false
|
||||
}
|
||||
})
|
||||
binding!!.searchView.setOnSearchViewListener(object : MaterialSearchView.SearchViewListener {
|
||||
override fun onSearchViewShown() {
|
||||
//Do some magic
|
||||
}
|
||||
|
||||
override fun onSearchViewClosed() {
|
||||
//Do some magic
|
||||
}
|
||||
})
|
||||
binding!!.searchView.setSubmitOnClick(true)
|
||||
}
|
||||
|
||||
override fun initListeners() {
|
||||
//下拉刷新
|
||||
binding!!.refreshLayout.setOnRefreshListener { refreshLayout: RefreshLayout ->
|
||||
refreshLayout.layout.postDelayed({
|
||||
loadRemoteData()
|
||||
}, 1000)
|
||||
}
|
||||
binding!!.refreshLayout.autoRefresh() //第一次进入触发自动刷新,演示效果
|
||||
}
|
||||
|
||||
private fun loadRemoteData() {
|
||||
|
||||
val requestUrl: String = HttpServerUtils.serverAddress + "/contact/query"
|
||||
Log.i(TAG, "requestUrl:$requestUrl")
|
||||
|
||||
val msgMap: MutableMap<String, Any> = mutableMapOf()
|
||||
val timestamp = System.currentTimeMillis()
|
||||
msgMap["timestamp"] = timestamp
|
||||
val clientSignKey = HttpServerUtils.clientSignKey
|
||||
if (!TextUtils.isEmpty(clientSignKey)) {
|
||||
msgMap["sign"] = HttpServerUtils.calcSign(timestamp.toString(), clientSignKey.toString())
|
||||
}
|
||||
msgMap["data"] = if (keyword.isDigitsOnly())
|
||||
ContactQueryData(1, 20, keyword, null)
|
||||
else
|
||||
ContactQueryData(1, 20, null, keyword)
|
||||
|
||||
var requestMsg: String = Gson().toJson(msgMap)
|
||||
Log.i(TAG, "requestMsg:$requestMsg")
|
||||
|
||||
val postRequest = XHttp.post(requestUrl)
|
||||
.keepJson(true)
|
||||
.timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
|
||||
.cacheMode(CacheMode.NO_CACHE)
|
||||
.timeStamp(true)
|
||||
|
||||
when (HttpServerUtils.clientSafetyMeasures) {
|
||||
2 -> {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey.toString())
|
||||
try {
|
||||
requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
requestMsg = RSACrypt.encryptByPublicKey(requestMsg, publicKey)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
3 -> {
|
||||
try {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey.toString())
|
||||
//requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
val encryptCBC = SM4Crypt.encrypt(requestMsg.toByteArray(), sm4Key)
|
||||
requestMsg = ConvertTools.bytes2HexString(encryptCBC)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
else -> {
|
||||
postRequest.upJson(requestMsg)
|
||||
}
|
||||
}
|
||||
|
||||
postRequest.execute(object : SimpleCallBack<String>() {
|
||||
override fun onError(e: ApiException) {
|
||||
XToastUtils.error(e.displayMessage)
|
||||
}
|
||||
|
||||
override fun onSuccess(response: String) {
|
||||
Log.i(TAG, response)
|
||||
try {
|
||||
var json = response
|
||||
if (HttpServerUtils.clientSafetyMeasures == 2) {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey.toString())
|
||||
json = RSACrypt.decryptByPublicKey(json, publicKey)
|
||||
json = String(Base64.decode(json))
|
||||
} else if (HttpServerUtils.clientSafetyMeasures == 3) {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey.toString())
|
||||
val encryptCBC = ConvertTools.hexStringToByteArray(json)
|
||||
val decryptCBC = SM4Crypt.decrypt(encryptCBC, sm4Key)
|
||||
json = String(decryptCBC)
|
||||
}
|
||||
val resp: BaseResponse<List<ContactInfo>?> = Gson().fromJson(json, object : TypeToken<BaseResponse<List<ContactInfo>?>>() {}.type)
|
||||
if (resp.code == 200) {
|
||||
//XToastUtils.success(ResUtils.getString(R.string.request_succeeded))
|
||||
mAdapter!!.refresh(resp.data)
|
||||
binding!!.refreshLayout.finishRefresh()
|
||||
binding!!.recyclerView.scrollToPosition(0)
|
||||
} else {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + resp.msg)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + response)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
package com.idormy.sms.forwarder.fragment.client
|
||||
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.text.isDigitsOnly
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.alibaba.android.vlayout.DelegateAdapter
|
||||
import com.alibaba.android.vlayout.VirtualLayoutManager
|
||||
import com.alibaba.android.vlayout.layout.LinearLayoutHelper
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.idormy.sms.forwarder.adapter.base.broccoli.BroccoliSimpleDelegateAdapter
|
||||
import com.idormy.sms.forwarder.adapter.base.delegate.SimpleDelegateAdapter
|
||||
import com.idormy.sms.forwarder.core.BaseFragment
|
||||
import com.idormy.sms.forwarder.databinding.FragmentClientContactQueryBinding
|
||||
import com.idormy.sms.forwarder.entity.ContactInfo
|
||||
import com.idormy.sms.forwarder.server.model.BaseResponse
|
||||
import com.idormy.sms.forwarder.server.model.ContactQueryData
|
||||
import com.idormy.sms.forwarder.utils.*
|
||||
import com.jeremyliao.liveeventbus.LiveEventBus
|
||||
import com.scwang.smartrefresh.layout.api.RefreshLayout
|
||||
import com.xuexiang.xaop.annotation.SingleClick
|
||||
import com.xuexiang.xhttp2.XHttp
|
||||
import com.xuexiang.xhttp2.cache.model.CacheMode
|
||||
import com.xuexiang.xhttp2.callback.SimpleCallBack
|
||||
import com.xuexiang.xhttp2.exception.ApiException
|
||||
import com.xuexiang.xpage.annotation.Page
|
||||
import com.xuexiang.xpage.base.XPageActivity
|
||||
import com.xuexiang.xpage.core.PageOption
|
||||
import com.xuexiang.xrouter.utils.TextUtils
|
||||
import com.xuexiang.xui.adapter.recyclerview.RecyclerViewHolder
|
||||
import com.xuexiang.xui.utils.ResUtils
|
||||
import com.xuexiang.xui.utils.SnackbarUtils
|
||||
import com.xuexiang.xui.widget.actionbar.TitleBar
|
||||
import com.xuexiang.xui.widget.searchview.MaterialSearchView
|
||||
import com.xuexiang.xutil.data.ConvertTools
|
||||
import com.xuexiang.xutil.system.ClipboardUtils
|
||||
import me.samlss.broccoli.Broccoli
|
||||
|
||||
|
||||
@Suppress("PropertyName")
|
||||
@Page(name = "远程查话簿")
|
||||
class ContactQueryFragment : BaseFragment<FragmentClientContactQueryBinding?>() {
|
||||
|
||||
val TAG: String = ContactQueryFragment::class.java.simpleName
|
||||
private var mAdapter: SimpleDelegateAdapter<ContactInfo>? = null
|
||||
private var keyword: String = ""
|
||||
|
||||
override fun viewBindingInflate(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup,
|
||||
): FragmentClientContactQueryBinding {
|
||||
return FragmentClientContactQueryBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
override fun initTitle(): TitleBar {
|
||||
val titleBar = super.initTitle()!!.setImmersive(false)
|
||||
titleBar.setTitle(R.string.api_contact_query)
|
||||
titleBar!!.addAction(object : TitleBar.ImageAction(R.drawable.ic_query) {
|
||||
@SingleClick
|
||||
override fun performAction(view: View) {
|
||||
binding!!.searchView.showSearch()
|
||||
}
|
||||
})
|
||||
return titleBar
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化控件
|
||||
*/
|
||||
override fun initViews() {
|
||||
val virtualLayoutManager = VirtualLayoutManager(requireContext())
|
||||
binding!!.recyclerView.layoutManager = virtualLayoutManager
|
||||
val viewPool = RecyclerView.RecycledViewPool()
|
||||
binding!!.recyclerView.setRecycledViewPool(viewPool)
|
||||
viewPool.setMaxRecycledViews(0, 10)
|
||||
|
||||
mAdapter = object : BroccoliSimpleDelegateAdapter<ContactInfo>(
|
||||
R.layout.adapter_contact_card_view_list_item,
|
||||
LinearLayoutHelper(),
|
||||
DataProvider.emptyContactInfo
|
||||
) {
|
||||
override fun onBindData(
|
||||
holder: RecyclerViewHolder,
|
||||
model: ContactInfo,
|
||||
position: Int,
|
||||
) {
|
||||
holder.text(R.id.sb_letter, model.firstLetter)
|
||||
holder.text(R.id.tv_name, model.name)
|
||||
holder.text(R.id.tv_phone_number, model.phoneNumber)
|
||||
holder.image(R.id.iv_copy, R.drawable.ic_copy)
|
||||
holder.image(R.id.iv_call, R.drawable.ic_phone_out)
|
||||
holder.image(R.id.iv_reply, R.drawable.ic_reply)
|
||||
holder.click(R.id.iv_copy) {
|
||||
val str = model.toString()
|
||||
XToastUtils.info(String.format(getString(R.string.copied_to_clipboard), str))
|
||||
ClipboardUtils.copyText(str)
|
||||
}
|
||||
holder.click(R.id.iv_call) {
|
||||
XToastUtils.info(getString(R.string.local_call) + model.phoneNumber)
|
||||
PhoneUtils.dial(model.phoneNumber)
|
||||
}
|
||||
holder.click(R.id.iv_reply) {
|
||||
XToastUtils.info(getString(R.string.remote_sms) + model.phoneNumber)
|
||||
/*val params = Bundle()
|
||||
params.putString(KEY_PHONE_NUMBERS, model.phoneNumber)
|
||||
openPage(SmsSendFragment::class.java, params)*/
|
||||
LiveEventBus.get<String>(EVENT_KEY_PHONE_NUMBERS).post(model.phoneNumber)
|
||||
PageOption.to(SmsSendFragment::class.java).setNewActivity(true).open((context as XPageActivity?)!!)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindBroccoli(holder: RecyclerViewHolder, broccoli: Broccoli) {
|
||||
broccoli.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.tv_name)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.tv_phone_number)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.sb_letter)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.iv_copy)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.iv_call)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.iv_reply)))
|
||||
}
|
||||
}
|
||||
|
||||
val delegateAdapter = DelegateAdapter(virtualLayoutManager)
|
||||
delegateAdapter.addAdapter(mAdapter)
|
||||
binding!!.recyclerView.adapter = delegateAdapter
|
||||
|
||||
//搜索框
|
||||
binding!!.searchView.findViewById<View>(com.xuexiang.xui.R.id.search_layout).visibility = View.GONE
|
||||
binding!!.searchView.setVoiceSearch(true)
|
||||
binding!!.searchView.setEllipsize(true)
|
||||
binding!!.searchView.setSuggestions(resources.getStringArray(R.array.query_suggestions))
|
||||
binding!!.searchView.setOnQueryTextListener(object : MaterialSearchView.OnQueryTextListener {
|
||||
override fun onQueryTextSubmit(query: String): Boolean {
|
||||
SnackbarUtils.Indefinite(view, String.format(getString(R.string.search_keyword), query)).info()
|
||||
.actionColor(ResUtils.getColor(R.color.xui_config_color_white))
|
||||
.setAction(getString(R.string.clear)) {
|
||||
keyword = ""
|
||||
loadRemoteData()
|
||||
}.show()
|
||||
if (keyword != query) {
|
||||
keyword = query
|
||||
loadRemoteData()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onQueryTextChange(newText: String): Boolean {
|
||||
//Do some magic
|
||||
return false
|
||||
}
|
||||
})
|
||||
binding!!.searchView.setOnSearchViewListener(object : MaterialSearchView.SearchViewListener {
|
||||
override fun onSearchViewShown() {
|
||||
//Do some magic
|
||||
}
|
||||
|
||||
override fun onSearchViewClosed() {
|
||||
//Do some magic
|
||||
}
|
||||
})
|
||||
binding!!.searchView.setSubmitOnClick(true)
|
||||
}
|
||||
|
||||
override fun initListeners() {
|
||||
//下拉刷新
|
||||
binding!!.refreshLayout.setOnRefreshListener { refreshLayout: RefreshLayout ->
|
||||
refreshLayout.layout.postDelayed({
|
||||
loadRemoteData()
|
||||
}, 1000)
|
||||
}
|
||||
binding!!.refreshLayout.autoRefresh() //第一次进入触发自动刷新,演示效果
|
||||
}
|
||||
|
||||
private fun loadRemoteData() {
|
||||
|
||||
val requestUrl: String = HttpServerUtils.serverAddress + "/contact/query"
|
||||
Log.i(TAG, "requestUrl:$requestUrl")
|
||||
|
||||
val msgMap: MutableMap<String, Any> = mutableMapOf()
|
||||
val timestamp = System.currentTimeMillis()
|
||||
msgMap["timestamp"] = timestamp
|
||||
val clientSignKey = HttpServerUtils.clientSignKey
|
||||
if (!TextUtils.isEmpty(clientSignKey)) {
|
||||
msgMap["sign"] = HttpServerUtils.calcSign(timestamp.toString(), clientSignKey)
|
||||
}
|
||||
msgMap["data"] = if (keyword.isDigitsOnly())
|
||||
ContactQueryData(1, 20, keyword, null)
|
||||
else
|
||||
ContactQueryData(1, 20, null, keyword)
|
||||
|
||||
var requestMsg: String = Gson().toJson(msgMap)
|
||||
Log.i(TAG, "requestMsg:$requestMsg")
|
||||
|
||||
val postRequest = XHttp.post(requestUrl)
|
||||
.keepJson(true)
|
||||
.timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
|
||||
.cacheMode(CacheMode.NO_CACHE)
|
||||
.timeStamp(true)
|
||||
|
||||
when (HttpServerUtils.clientSafetyMeasures) {
|
||||
2 -> {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey)
|
||||
try {
|
||||
requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
requestMsg = RSACrypt.encryptByPublicKey(requestMsg, publicKey)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
3 -> {
|
||||
try {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey)
|
||||
//requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
val encryptCBC = SM4Crypt.encrypt(requestMsg.toByteArray(), sm4Key)
|
||||
requestMsg = ConvertTools.bytes2HexString(encryptCBC)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
else -> {
|
||||
postRequest.upJson(requestMsg)
|
||||
}
|
||||
}
|
||||
|
||||
postRequest.execute(object : SimpleCallBack<String>() {
|
||||
override fun onError(e: ApiException) {
|
||||
XToastUtils.error(e.displayMessage)
|
||||
}
|
||||
|
||||
override fun onSuccess(response: String) {
|
||||
Log.i(TAG, response)
|
||||
try {
|
||||
var json = response
|
||||
if (HttpServerUtils.clientSafetyMeasures == 2) {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey)
|
||||
json = RSACrypt.decryptByPublicKey(json, publicKey)
|
||||
json = String(Base64.decode(json))
|
||||
} else if (HttpServerUtils.clientSafetyMeasures == 3) {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey)
|
||||
val encryptCBC = ConvertTools.hexStringToByteArray(json)
|
||||
val decryptCBC = SM4Crypt.decrypt(encryptCBC, sm4Key)
|
||||
json = String(decryptCBC)
|
||||
}
|
||||
val resp: BaseResponse<List<ContactInfo>?> = Gson().fromJson(json, object : TypeToken<BaseResponse<List<ContactInfo>?>>() {}.type)
|
||||
if (resp.code == 200) {
|
||||
//XToastUtils.success(ResUtils.getString(R.string.request_succeeded))
|
||||
mAdapter!!.refresh(resp.data)
|
||||
binding!!.refreshLayout.finishRefresh()
|
||||
binding!!.recyclerView.scrollToPosition(0)
|
||||
} else {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + resp.msg)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + response)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,286 +1,286 @@
|
|||
package com.idormy.sms.forwarder.fragment.client
|
||||
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.alibaba.android.vlayout.DelegateAdapter
|
||||
import com.alibaba.android.vlayout.VirtualLayoutManager
|
||||
import com.alibaba.android.vlayout.layout.LinearLayoutHelper
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.idormy.sms.forwarder.adapter.base.broccoli.BroccoliSimpleDelegateAdapter
|
||||
import com.idormy.sms.forwarder.adapter.base.delegate.SimpleDelegateAdapter
|
||||
import com.idormy.sms.forwarder.core.BaseFragment
|
||||
import com.idormy.sms.forwarder.databinding.FragmentClientSmsQueryBinding
|
||||
import com.idormy.sms.forwarder.entity.SmsInfo
|
||||
import com.idormy.sms.forwarder.server.model.BaseResponse
|
||||
import com.idormy.sms.forwarder.server.model.SmsQueryData
|
||||
import com.idormy.sms.forwarder.utils.*
|
||||
import com.idormy.sms.forwarder.utils.DataProvider.emptySmsInfo
|
||||
import com.jeremyliao.liveeventbus.LiveEventBus
|
||||
import com.scwang.smartrefresh.layout.api.RefreshLayout
|
||||
import com.xuexiang.xaop.annotation.SingleClick
|
||||
import com.xuexiang.xhttp2.XHttp
|
||||
import com.xuexiang.xhttp2.cache.model.CacheMode
|
||||
import com.xuexiang.xhttp2.callback.SimpleCallBack
|
||||
import com.xuexiang.xhttp2.exception.ApiException
|
||||
import com.xuexiang.xpage.annotation.Page
|
||||
import com.xuexiang.xpage.base.XPageActivity
|
||||
import com.xuexiang.xpage.core.PageOption
|
||||
import com.xuexiang.xrouter.utils.TextUtils
|
||||
import com.xuexiang.xui.adapter.recyclerview.RecyclerViewHolder
|
||||
import com.xuexiang.xui.utils.ResUtils
|
||||
import com.xuexiang.xui.utils.SnackbarUtils
|
||||
import com.xuexiang.xui.widget.actionbar.TitleBar
|
||||
import com.xuexiang.xui.widget.searchview.MaterialSearchView
|
||||
import com.xuexiang.xui.widget.searchview.MaterialSearchView.SearchViewListener
|
||||
import com.xuexiang.xutil.data.ConvertTools
|
||||
import com.xuexiang.xutil.data.DateUtils
|
||||
import me.samlss.broccoli.Broccoli
|
||||
|
||||
|
||||
@Suppress("PropertyName")
|
||||
@Page(name = "远程查短信")
|
||||
class SmsQueryFragment : BaseFragment<FragmentClientSmsQueryBinding?>() {
|
||||
|
||||
val TAG: String = SmsQueryFragment::class.java.simpleName
|
||||
private var mAdapter: SimpleDelegateAdapter<SmsInfo>? = null
|
||||
private var smsType: Int = 1
|
||||
private var pageNum: Int = 1
|
||||
private val pageSize: Int = 20
|
||||
private var keyword: String = ""
|
||||
|
||||
override fun viewBindingInflate(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup,
|
||||
): FragmentClientSmsQueryBinding {
|
||||
return FragmentClientSmsQueryBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
override fun initTitle(): TitleBar {
|
||||
val titleBar = super.initTitle()!!.setImmersive(false)
|
||||
titleBar.setTitle(R.string.api_sms_query)
|
||||
titleBar!!.addAction(object : TitleBar.ImageAction(R.drawable.ic_query) {
|
||||
@SingleClick
|
||||
override fun performAction(view: View) {
|
||||
binding!!.searchView.showSearch()
|
||||
}
|
||||
})
|
||||
return titleBar
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化控件
|
||||
*/
|
||||
override fun initViews() {
|
||||
val virtualLayoutManager = VirtualLayoutManager(requireContext())
|
||||
binding!!.recyclerView.layoutManager = virtualLayoutManager
|
||||
val viewPool = RecyclerView.RecycledViewPool()
|
||||
binding!!.recyclerView.setRecycledViewPool(viewPool)
|
||||
viewPool.setMaxRecycledViews(0, 10)
|
||||
|
||||
mAdapter = object : BroccoliSimpleDelegateAdapter<SmsInfo>(
|
||||
R.layout.adapter_sms_card_view_list_item,
|
||||
LinearLayoutHelper(),
|
||||
emptySmsInfo
|
||||
) {
|
||||
override fun onBindData(
|
||||
holder: RecyclerViewHolder,
|
||||
model: SmsInfo,
|
||||
position: Int,
|
||||
) {
|
||||
holder.text(R.id.tv_from, model.number)
|
||||
holder.text(R.id.tv_time, DateUtils.getFriendlyTimeSpanByNow(model.date))
|
||||
holder.image(R.id.iv_image, model.typeImageId)
|
||||
holder.image(R.id.iv_sim_image, model.simImageId)
|
||||
holder.text(R.id.tv_content, model.content)
|
||||
holder.image(R.id.iv_reply, R.drawable.ic_reply)
|
||||
holder.click(R.id.iv_reply) {
|
||||
XToastUtils.info(getString(R.string.remote_sms) + model.number)
|
||||
LiveEventBus.get<Int>(EVENT_KEY_SIM_SLOT).post(model.simId)
|
||||
LiveEventBus.get<String>(EVENT_KEY_PHONE_NUMBERS).post(model.number)
|
||||
PageOption.to(SmsSendFragment::class.java).setNewActivity(true).open((context as XPageActivity?)!!)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindBroccoli(holder: RecyclerViewHolder, broccoli: Broccoli) {
|
||||
broccoli.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.tv_from)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.tv_time)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.iv_sim_image)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.tv_content)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.iv_image)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.iv_reply)))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
val delegateAdapter = DelegateAdapter(virtualLayoutManager)
|
||||
delegateAdapter.addAdapter(mAdapter)
|
||||
binding!!.recyclerView.adapter = delegateAdapter
|
||||
|
||||
binding!!.tabBar.setTabTitles(ResUtils.getStringArray(R.array.sms_type_option))
|
||||
binding!!.tabBar.setOnTabClickListener { _, position ->
|
||||
//XToastUtils.toast("点击了$title--$position")
|
||||
smsType = position + 1
|
||||
loadRemoteData(true)
|
||||
binding!!.recyclerView.scrollToPosition(0)
|
||||
}
|
||||
|
||||
//搜索框
|
||||
binding!!.searchView.findViewById<View>(com.xuexiang.xui.R.id.search_layout).visibility = View.GONE
|
||||
binding!!.searchView.setVoiceSearch(true)
|
||||
binding!!.searchView.setEllipsize(true)
|
||||
binding!!.searchView.setSuggestions(resources.getStringArray(R.array.query_suggestions))
|
||||
binding!!.searchView.setOnQueryTextListener(object : MaterialSearchView.OnQueryTextListener {
|
||||
override fun onQueryTextSubmit(query: String): Boolean {
|
||||
SnackbarUtils.Indefinite(view, String.format(getString(R.string.search_keyword), query)).info()
|
||||
.actionColor(ResUtils.getColor(R.color.xui_config_color_white))
|
||||
.setAction(getString(R.string.clear)) {
|
||||
keyword = ""
|
||||
loadRemoteData(true)
|
||||
}.show()
|
||||
if (keyword != query) {
|
||||
keyword = query
|
||||
loadRemoteData(true)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onQueryTextChange(newText: String): Boolean {
|
||||
//Do some magic
|
||||
return false
|
||||
}
|
||||
})
|
||||
binding!!.searchView.setOnSearchViewListener(object : SearchViewListener {
|
||||
override fun onSearchViewShown() {
|
||||
//Do some magic
|
||||
}
|
||||
|
||||
override fun onSearchViewClosed() {
|
||||
//Do some magic
|
||||
}
|
||||
})
|
||||
binding!!.searchView.setSubmitOnClick(true)
|
||||
}
|
||||
|
||||
override fun initListeners() {
|
||||
//下拉刷新
|
||||
binding!!.refreshLayout.setOnRefreshListener { refreshLayout: RefreshLayout ->
|
||||
refreshLayout.layout.postDelayed({
|
||||
loadRemoteData(true)
|
||||
}, 1000)
|
||||
}
|
||||
//上拉加载
|
||||
binding!!.refreshLayout.setOnLoadMoreListener { refreshLayout: RefreshLayout ->
|
||||
refreshLayout.layout.postDelayed({
|
||||
loadRemoteData(false)
|
||||
}, 1000)
|
||||
}
|
||||
binding!!.refreshLayout.autoRefresh() //第一次进入触发自动刷新,演示效果
|
||||
}
|
||||
|
||||
private fun loadRemoteData(refresh: Boolean) {
|
||||
|
||||
val requestUrl: String = HttpServerUtils.serverAddress + "/sms/query"
|
||||
Log.i(TAG, "requestUrl:$requestUrl")
|
||||
|
||||
val msgMap: MutableMap<String, Any> = mutableMapOf()
|
||||
val timestamp = System.currentTimeMillis()
|
||||
msgMap["timestamp"] = timestamp
|
||||
val clientSignKey = HttpServerUtils.clientSignKey
|
||||
if (!TextUtils.isEmpty(clientSignKey)) {
|
||||
msgMap["sign"] = HttpServerUtils.calcSign(timestamp.toString(), clientSignKey.toString())
|
||||
}
|
||||
|
||||
if (refresh) pageNum = 1
|
||||
msgMap["data"] = SmsQueryData(smsType, pageNum, pageSize, keyword)
|
||||
|
||||
var requestMsg: String = Gson().toJson(msgMap)
|
||||
Log.i(TAG, "requestMsg:$requestMsg")
|
||||
|
||||
val postRequest = XHttp.post(requestUrl)
|
||||
.keepJson(true)
|
||||
.timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
|
||||
.cacheMode(CacheMode.NO_CACHE)
|
||||
.timeStamp(true)
|
||||
|
||||
when (HttpServerUtils.clientSafetyMeasures) {
|
||||
2 -> {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey.toString())
|
||||
try {
|
||||
requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
requestMsg = RSACrypt.encryptByPublicKey(requestMsg, publicKey)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
3 -> {
|
||||
try {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey.toString())
|
||||
//requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
val encryptCBC = SM4Crypt.encrypt(requestMsg.toByteArray(), sm4Key)
|
||||
requestMsg = ConvertTools.bytes2HexString(encryptCBC)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
else -> {
|
||||
postRequest.upJson(requestMsg)
|
||||
}
|
||||
}
|
||||
|
||||
postRequest.execute(object : SimpleCallBack<String>() {
|
||||
override fun onError(e: ApiException) {
|
||||
XToastUtils.error(e.displayMessage)
|
||||
}
|
||||
|
||||
override fun onSuccess(response: String) {
|
||||
Log.i(TAG, response)
|
||||
try {
|
||||
var json = response
|
||||
if (HttpServerUtils.clientSafetyMeasures == 2) {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey.toString())
|
||||
json = RSACrypt.decryptByPublicKey(json, publicKey)
|
||||
json = String(Base64.decode(json))
|
||||
} else if (HttpServerUtils.clientSafetyMeasures == 3) {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey.toString())
|
||||
val encryptCBC = ConvertTools.hexStringToByteArray(json)
|
||||
val decryptCBC = SM4Crypt.decrypt(encryptCBC, sm4Key)
|
||||
json = String(decryptCBC)
|
||||
}
|
||||
val resp: BaseResponse<List<SmsInfo>?> = Gson().fromJson(json, object : TypeToken<BaseResponse<List<SmsInfo>?>>() {}.type)
|
||||
if (resp.code == 200) {
|
||||
//XToastUtils.success(ResUtils.getString(R.string.request_succeeded))
|
||||
pageNum++
|
||||
if (refresh) {
|
||||
mAdapter!!.refresh(resp.data)
|
||||
binding!!.refreshLayout.finishRefresh()
|
||||
binding!!.recyclerView.scrollToPosition(0)
|
||||
} else {
|
||||
mAdapter!!.loadMore(resp.data)
|
||||
binding!!.refreshLayout.finishLoadMore()
|
||||
}
|
||||
} else {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + resp.msg)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + response)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
package com.idormy.sms.forwarder.fragment.client
|
||||
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.alibaba.android.vlayout.DelegateAdapter
|
||||
import com.alibaba.android.vlayout.VirtualLayoutManager
|
||||
import com.alibaba.android.vlayout.layout.LinearLayoutHelper
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.idormy.sms.forwarder.adapter.base.broccoli.BroccoliSimpleDelegateAdapter
|
||||
import com.idormy.sms.forwarder.adapter.base.delegate.SimpleDelegateAdapter
|
||||
import com.idormy.sms.forwarder.core.BaseFragment
|
||||
import com.idormy.sms.forwarder.databinding.FragmentClientSmsQueryBinding
|
||||
import com.idormy.sms.forwarder.entity.SmsInfo
|
||||
import com.idormy.sms.forwarder.server.model.BaseResponse
|
||||
import com.idormy.sms.forwarder.server.model.SmsQueryData
|
||||
import com.idormy.sms.forwarder.utils.*
|
||||
import com.idormy.sms.forwarder.utils.DataProvider.emptySmsInfo
|
||||
import com.jeremyliao.liveeventbus.LiveEventBus
|
||||
import com.scwang.smartrefresh.layout.api.RefreshLayout
|
||||
import com.xuexiang.xaop.annotation.SingleClick
|
||||
import com.xuexiang.xhttp2.XHttp
|
||||
import com.xuexiang.xhttp2.cache.model.CacheMode
|
||||
import com.xuexiang.xhttp2.callback.SimpleCallBack
|
||||
import com.xuexiang.xhttp2.exception.ApiException
|
||||
import com.xuexiang.xpage.annotation.Page
|
||||
import com.xuexiang.xpage.base.XPageActivity
|
||||
import com.xuexiang.xpage.core.PageOption
|
||||
import com.xuexiang.xrouter.utils.TextUtils
|
||||
import com.xuexiang.xui.adapter.recyclerview.RecyclerViewHolder
|
||||
import com.xuexiang.xui.utils.ResUtils
|
||||
import com.xuexiang.xui.utils.SnackbarUtils
|
||||
import com.xuexiang.xui.widget.actionbar.TitleBar
|
||||
import com.xuexiang.xui.widget.searchview.MaterialSearchView
|
||||
import com.xuexiang.xui.widget.searchview.MaterialSearchView.SearchViewListener
|
||||
import com.xuexiang.xutil.data.ConvertTools
|
||||
import com.xuexiang.xutil.data.DateUtils
|
||||
import me.samlss.broccoli.Broccoli
|
||||
|
||||
|
||||
@Suppress("PropertyName")
|
||||
@Page(name = "远程查短信")
|
||||
class SmsQueryFragment : BaseFragment<FragmentClientSmsQueryBinding?>() {
|
||||
|
||||
val TAG: String = SmsQueryFragment::class.java.simpleName
|
||||
private var mAdapter: SimpleDelegateAdapter<SmsInfo>? = null
|
||||
private var smsType: Int = 1
|
||||
private var pageNum: Int = 1
|
||||
private val pageSize: Int = 20
|
||||
private var keyword: String = ""
|
||||
|
||||
override fun viewBindingInflate(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup,
|
||||
): FragmentClientSmsQueryBinding {
|
||||
return FragmentClientSmsQueryBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
override fun initTitle(): TitleBar {
|
||||
val titleBar = super.initTitle()!!.setImmersive(false)
|
||||
titleBar.setTitle(R.string.api_sms_query)
|
||||
titleBar!!.addAction(object : TitleBar.ImageAction(R.drawable.ic_query) {
|
||||
@SingleClick
|
||||
override fun performAction(view: View) {
|
||||
binding!!.searchView.showSearch()
|
||||
}
|
||||
})
|
||||
return titleBar
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化控件
|
||||
*/
|
||||
override fun initViews() {
|
||||
val virtualLayoutManager = VirtualLayoutManager(requireContext())
|
||||
binding!!.recyclerView.layoutManager = virtualLayoutManager
|
||||
val viewPool = RecyclerView.RecycledViewPool()
|
||||
binding!!.recyclerView.setRecycledViewPool(viewPool)
|
||||
viewPool.setMaxRecycledViews(0, 10)
|
||||
|
||||
mAdapter = object : BroccoliSimpleDelegateAdapter<SmsInfo>(
|
||||
R.layout.adapter_sms_card_view_list_item,
|
||||
LinearLayoutHelper(),
|
||||
emptySmsInfo
|
||||
) {
|
||||
override fun onBindData(
|
||||
holder: RecyclerViewHolder,
|
||||
model: SmsInfo,
|
||||
position: Int,
|
||||
) {
|
||||
holder.text(R.id.tv_from, model.number)
|
||||
holder.text(R.id.tv_time, DateUtils.getFriendlyTimeSpanByNow(model.date))
|
||||
holder.image(R.id.iv_image, model.typeImageId)
|
||||
holder.image(R.id.iv_sim_image, model.simImageId)
|
||||
holder.text(R.id.tv_content, model.content)
|
||||
holder.image(R.id.iv_reply, R.drawable.ic_reply)
|
||||
holder.click(R.id.iv_reply) {
|
||||
XToastUtils.info(getString(R.string.remote_sms) + model.number)
|
||||
LiveEventBus.get<Int>(EVENT_KEY_SIM_SLOT).post(model.simId)
|
||||
LiveEventBus.get<String>(EVENT_KEY_PHONE_NUMBERS).post(model.number)
|
||||
PageOption.to(SmsSendFragment::class.java).setNewActivity(true).open((context as XPageActivity?)!!)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindBroccoli(holder: RecyclerViewHolder, broccoli: Broccoli) {
|
||||
broccoli.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.tv_from)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.tv_time)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.iv_sim_image)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.tv_content)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.iv_image)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.iv_reply)))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
val delegateAdapter = DelegateAdapter(virtualLayoutManager)
|
||||
delegateAdapter.addAdapter(mAdapter)
|
||||
binding!!.recyclerView.adapter = delegateAdapter
|
||||
|
||||
binding!!.tabBar.setTabTitles(ResUtils.getStringArray(R.array.sms_type_option))
|
||||
binding!!.tabBar.setOnTabClickListener { _, position ->
|
||||
//XToastUtils.toast("点击了$title--$position")
|
||||
smsType = position + 1
|
||||
loadRemoteData(true)
|
||||
binding!!.recyclerView.scrollToPosition(0)
|
||||
}
|
||||
|
||||
//搜索框
|
||||
binding!!.searchView.findViewById<View>(com.xuexiang.xui.R.id.search_layout).visibility = View.GONE
|
||||
binding!!.searchView.setVoiceSearch(true)
|
||||
binding!!.searchView.setEllipsize(true)
|
||||
binding!!.searchView.setSuggestions(resources.getStringArray(R.array.query_suggestions))
|
||||
binding!!.searchView.setOnQueryTextListener(object : MaterialSearchView.OnQueryTextListener {
|
||||
override fun onQueryTextSubmit(query: String): Boolean {
|
||||
SnackbarUtils.Indefinite(view, String.format(getString(R.string.search_keyword), query)).info()
|
||||
.actionColor(ResUtils.getColor(R.color.xui_config_color_white))
|
||||
.setAction(getString(R.string.clear)) {
|
||||
keyword = ""
|
||||
loadRemoteData(true)
|
||||
}.show()
|
||||
if (keyword != query) {
|
||||
keyword = query
|
||||
loadRemoteData(true)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onQueryTextChange(newText: String): Boolean {
|
||||
//Do some magic
|
||||
return false
|
||||
}
|
||||
})
|
||||
binding!!.searchView.setOnSearchViewListener(object : SearchViewListener {
|
||||
override fun onSearchViewShown() {
|
||||
//Do some magic
|
||||
}
|
||||
|
||||
override fun onSearchViewClosed() {
|
||||
//Do some magic
|
||||
}
|
||||
})
|
||||
binding!!.searchView.setSubmitOnClick(true)
|
||||
}
|
||||
|
||||
override fun initListeners() {
|
||||
//下拉刷新
|
||||
binding!!.refreshLayout.setOnRefreshListener { refreshLayout: RefreshLayout ->
|
||||
refreshLayout.layout.postDelayed({
|
||||
loadRemoteData(true)
|
||||
}, 1000)
|
||||
}
|
||||
//上拉加载
|
||||
binding!!.refreshLayout.setOnLoadMoreListener { refreshLayout: RefreshLayout ->
|
||||
refreshLayout.layout.postDelayed({
|
||||
loadRemoteData(false)
|
||||
}, 1000)
|
||||
}
|
||||
binding!!.refreshLayout.autoRefresh() //第一次进入触发自动刷新,演示效果
|
||||
}
|
||||
|
||||
private fun loadRemoteData(refresh: Boolean) {
|
||||
|
||||
val requestUrl: String = HttpServerUtils.serverAddress + "/sms/query"
|
||||
Log.i(TAG, "requestUrl:$requestUrl")
|
||||
|
||||
val msgMap: MutableMap<String, Any> = mutableMapOf()
|
||||
val timestamp = System.currentTimeMillis()
|
||||
msgMap["timestamp"] = timestamp
|
||||
val clientSignKey = HttpServerUtils.clientSignKey
|
||||
if (!TextUtils.isEmpty(clientSignKey)) {
|
||||
msgMap["sign"] = HttpServerUtils.calcSign(timestamp.toString(), clientSignKey)
|
||||
}
|
||||
|
||||
if (refresh) pageNum = 1
|
||||
msgMap["data"] = SmsQueryData(smsType, pageNum, pageSize, keyword)
|
||||
|
||||
var requestMsg: String = Gson().toJson(msgMap)
|
||||
Log.i(TAG, "requestMsg:$requestMsg")
|
||||
|
||||
val postRequest = XHttp.post(requestUrl)
|
||||
.keepJson(true)
|
||||
.timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
|
||||
.cacheMode(CacheMode.NO_CACHE)
|
||||
.timeStamp(true)
|
||||
|
||||
when (HttpServerUtils.clientSafetyMeasures) {
|
||||
2 -> {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey)
|
||||
try {
|
||||
requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
requestMsg = RSACrypt.encryptByPublicKey(requestMsg, publicKey)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
3 -> {
|
||||
try {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey)
|
||||
//requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
val encryptCBC = SM4Crypt.encrypt(requestMsg.toByteArray(), sm4Key)
|
||||
requestMsg = ConvertTools.bytes2HexString(encryptCBC)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
else -> {
|
||||
postRequest.upJson(requestMsg)
|
||||
}
|
||||
}
|
||||
|
||||
postRequest.execute(object : SimpleCallBack<String>() {
|
||||
override fun onError(e: ApiException) {
|
||||
XToastUtils.error(e.displayMessage)
|
||||
}
|
||||
|
||||
override fun onSuccess(response: String) {
|
||||
Log.i(TAG, response)
|
||||
try {
|
||||
var json = response
|
||||
if (HttpServerUtils.clientSafetyMeasures == 2) {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey)
|
||||
json = RSACrypt.decryptByPublicKey(json, publicKey)
|
||||
json = String(Base64.decode(json))
|
||||
} else if (HttpServerUtils.clientSafetyMeasures == 3) {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey)
|
||||
val encryptCBC = ConvertTools.hexStringToByteArray(json)
|
||||
val decryptCBC = SM4Crypt.decrypt(encryptCBC, sm4Key)
|
||||
json = String(decryptCBC)
|
||||
}
|
||||
val resp: BaseResponse<List<SmsInfo>?> = Gson().fromJson(json, object : TypeToken<BaseResponse<List<SmsInfo>?>>() {}.type)
|
||||
if (resp.code == 200) {
|
||||
//XToastUtils.success(ResUtils.getString(R.string.request_succeeded))
|
||||
pageNum++
|
||||
if (refresh) {
|
||||
mAdapter!!.refresh(resp.data)
|
||||
binding!!.refreshLayout.finishRefresh()
|
||||
binding!!.recyclerView.scrollToPosition(0)
|
||||
} else {
|
||||
mAdapter!!.loadMore(resp.data)
|
||||
binding!!.refreshLayout.finishLoadMore()
|
||||
}
|
||||
} else {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + resp.msg)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + response)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,202 +1,204 @@
|
|||
package com.idormy.sms.forwarder.fragment.client
|
||||
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.idormy.sms.forwarder.core.BaseFragment
|
||||
import com.idormy.sms.forwarder.databinding.FragmentClientSmsSendBinding
|
||||
import com.idormy.sms.forwarder.server.model.BaseResponse
|
||||
import com.idormy.sms.forwarder.server.model.ConfigData
|
||||
import com.idormy.sms.forwarder.utils.*
|
||||
import com.jeremyliao.liveeventbus.LiveEventBus
|
||||
import com.xuexiang.xaop.annotation.SingleClick
|
||||
import com.xuexiang.xhttp2.XHttp
|
||||
import com.xuexiang.xhttp2.cache.model.CacheMode
|
||||
import com.xuexiang.xhttp2.callback.SimpleCallBack
|
||||
import com.xuexiang.xhttp2.exception.ApiException
|
||||
import com.xuexiang.xpage.annotation.Page
|
||||
import com.xuexiang.xrouter.utils.TextUtils
|
||||
import com.xuexiang.xui.utils.CountDownButtonHelper
|
||||
import com.xuexiang.xui.utils.ResUtils
|
||||
import com.xuexiang.xui.widget.actionbar.TitleBar
|
||||
import com.xuexiang.xutil.data.ConvertTools
|
||||
|
||||
@Suppress("PropertyName")
|
||||
@Page(name = "远程发短信")
|
||||
class SmsSendFragment : BaseFragment<FragmentClientSmsSendBinding?>(), View.OnClickListener {
|
||||
|
||||
val TAG: String = SmsSendFragment::class.java.simpleName
|
||||
private var mCountDownHelper: CountDownButtonHelper? = null
|
||||
|
||||
override fun viewBindingInflate(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup,
|
||||
): FragmentClientSmsSendBinding {
|
||||
return FragmentClientSmsSendBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
override fun initTitle(): TitleBar? {
|
||||
return super.initTitle()!!.setImmersive(false).setTitle(R.string.api_sms_send)
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化控件
|
||||
*/
|
||||
override fun initViews() {
|
||||
//发送按钮增加倒计时,避免重复点击
|
||||
mCountDownHelper = CountDownButtonHelper(binding!!.btnSubmit, SettingUtils.requestTimeout)
|
||||
mCountDownHelper!!.setOnCountDownListener(object : CountDownButtonHelper.OnCountDownListener {
|
||||
override fun onCountDown(time: Int) {
|
||||
binding!!.btnSubmit.text = String.format(getString(R.string.seconds_n), time)
|
||||
}
|
||||
|
||||
override fun onFinished() {
|
||||
binding!!.btnSubmit.text = getString(R.string.send)
|
||||
}
|
||||
})
|
||||
|
||||
//卡槽信息
|
||||
val serverConfigStr = HttpServerUtils.serverConfig
|
||||
if (!TextUtils.isEmpty(serverConfigStr)) {
|
||||
val serverConfig: ConfigData = Gson().fromJson(serverConfigStr, object : TypeToken<ConfigData>() {}.type)
|
||||
binding!!.rbSimSlot1.text = "SIM1:" + serverConfig.extraSim1
|
||||
binding!!.rbSimSlot2.text = "SIM2:" + serverConfig.extraSim2
|
||||
}
|
||||
}
|
||||
|
||||
override fun initListeners() {
|
||||
binding!!.btnSubmit.setOnClickListener(this)
|
||||
LiveEventBus.get(EVENT_KEY_SIM_SLOT, Int::class.java).observeSticky(this) { value: Int ->
|
||||
binding!!.rgSimSlot.check(if (value == 1) R.id.rb_sim_slot_2 else R.id.rb_sim_slot_1)
|
||||
}
|
||||
LiveEventBus.get(EVENT_KEY_PHONE_NUMBERS, String::class.java).observeSticky(this) { value: String ->
|
||||
binding!!.etPhoneNumbers.setText(value)
|
||||
}
|
||||
}
|
||||
|
||||
@SingleClick
|
||||
override fun onClick(v: View) {
|
||||
when (v.id) {
|
||||
R.id.btn_submit -> {
|
||||
val requestUrl: String = HttpServerUtils.serverAddress + "/sms/send"
|
||||
Log.i(TAG, "requestUrl:$requestUrl")
|
||||
|
||||
val msgMap: MutableMap<String, Any> = mutableMapOf()
|
||||
val timestamp = System.currentTimeMillis()
|
||||
msgMap["timestamp"] = timestamp
|
||||
val clientSignKey = HttpServerUtils.clientSignKey
|
||||
if (!TextUtils.isEmpty(clientSignKey)) {
|
||||
msgMap["sign"] = HttpServerUtils.calcSign(timestamp.toString(), clientSignKey.toString())
|
||||
}
|
||||
|
||||
val phoneNumbers = binding!!.etPhoneNumbers.text.toString()
|
||||
val phoneRegex = getString(R.string.phone_numbers_regex).toRegex()
|
||||
if (!phoneRegex.matches(phoneNumbers)) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.phone_numbers_error))
|
||||
return
|
||||
}
|
||||
|
||||
val msgContent = binding!!.etMsgContent.text.toString()
|
||||
val msgRegex = getString(R.string.msg_content_regex).toRegex()
|
||||
if (!msgRegex.matches(msgContent)) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.msg_content_error))
|
||||
return
|
||||
}
|
||||
|
||||
val dataMap: MutableMap<String, Any> = mutableMapOf()
|
||||
dataMap["sim_slot"] = if (binding!!.rgSimSlot.checkedRadioButtonId == R.id.rb_sim_slot_2) 2 else 1
|
||||
dataMap["phone_numbers"] = phoneNumbers
|
||||
dataMap["msg_content"] = msgContent
|
||||
msgMap["data"] = dataMap
|
||||
|
||||
var requestMsg: String = Gson().toJson(msgMap)
|
||||
Log.i(TAG, "requestMsg:$requestMsg")
|
||||
|
||||
val postRequest = XHttp.post(requestUrl)
|
||||
.keepJson(true)
|
||||
.timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
|
||||
.cacheMode(CacheMode.NO_CACHE)
|
||||
.timeStamp(true)
|
||||
|
||||
when (HttpServerUtils.clientSafetyMeasures) {
|
||||
2 -> {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey.toString())
|
||||
try {
|
||||
requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
requestMsg = RSACrypt.encryptByPublicKey(requestMsg, publicKey)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
3 -> {
|
||||
try {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey.toString())
|
||||
//requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
val encryptCBC = SM4Crypt.encrypt(requestMsg.toByteArray(), sm4Key)
|
||||
requestMsg = ConvertTools.bytes2HexString(encryptCBC)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
else -> {
|
||||
postRequest.upJson(requestMsg)
|
||||
}
|
||||
}
|
||||
|
||||
mCountDownHelper?.start()
|
||||
postRequest.execute(object : SimpleCallBack<String>() {
|
||||
override fun onError(e: ApiException) {
|
||||
XToastUtils.error(e.displayMessage)
|
||||
mCountDownHelper?.finish()
|
||||
}
|
||||
|
||||
override fun onSuccess(response: String) {
|
||||
Log.i(TAG, response)
|
||||
try {
|
||||
var json = response
|
||||
if (HttpServerUtils.clientSafetyMeasures == 2) {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey.toString())
|
||||
json = RSACrypt.decryptByPublicKey(json, publicKey)
|
||||
json = String(Base64.decode(json))
|
||||
} else if (HttpServerUtils.clientSafetyMeasures == 3) {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey.toString())
|
||||
val encryptCBC = ConvertTools.hexStringToByteArray(json)
|
||||
val decryptCBC = SM4Crypt.decrypt(encryptCBC, sm4Key)
|
||||
json = String(decryptCBC)
|
||||
}
|
||||
val resp: BaseResponse<String> = Gson().fromJson(json, object : TypeToken<BaseResponse<String>>() {}.type)
|
||||
if (resp.code == 200) {
|
||||
XToastUtils.success(ResUtils.getString(R.string.request_succeeded))
|
||||
} else {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + resp.msg)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + response)
|
||||
}
|
||||
mCountDownHelper?.finish()
|
||||
}
|
||||
})
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
if (mCountDownHelper != null) mCountDownHelper!!.recycle()
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
package com.idormy.sms.forwarder.fragment.client
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.idormy.sms.forwarder.core.BaseFragment
|
||||
import com.idormy.sms.forwarder.databinding.FragmentClientSmsSendBinding
|
||||
import com.idormy.sms.forwarder.server.model.BaseResponse
|
||||
import com.idormy.sms.forwarder.server.model.ConfigData
|
||||
import com.idormy.sms.forwarder.utils.*
|
||||
import com.jeremyliao.liveeventbus.LiveEventBus
|
||||
import com.xuexiang.xaop.annotation.SingleClick
|
||||
import com.xuexiang.xhttp2.XHttp
|
||||
import com.xuexiang.xhttp2.cache.model.CacheMode
|
||||
import com.xuexiang.xhttp2.callback.SimpleCallBack
|
||||
import com.xuexiang.xhttp2.exception.ApiException
|
||||
import com.xuexiang.xpage.annotation.Page
|
||||
import com.xuexiang.xrouter.utils.TextUtils
|
||||
import com.xuexiang.xui.utils.CountDownButtonHelper
|
||||
import com.xuexiang.xui.utils.ResUtils
|
||||
import com.xuexiang.xui.widget.actionbar.TitleBar
|
||||
import com.xuexiang.xutil.data.ConvertTools
|
||||
|
||||
@Suppress("PropertyName")
|
||||
@Page(name = "远程发短信")
|
||||
class SmsSendFragment : BaseFragment<FragmentClientSmsSendBinding?>(), View.OnClickListener {
|
||||
|
||||
val TAG: String = SmsSendFragment::class.java.simpleName
|
||||
private var mCountDownHelper: CountDownButtonHelper? = null
|
||||
|
||||
override fun viewBindingInflate(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup,
|
||||
): FragmentClientSmsSendBinding {
|
||||
return FragmentClientSmsSendBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
override fun initTitle(): TitleBar? {
|
||||
return super.initTitle()!!.setImmersive(false).setTitle(R.string.api_sms_send)
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化控件
|
||||
*/
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun initViews() {
|
||||
//发送按钮增加倒计时,避免重复点击
|
||||
mCountDownHelper = CountDownButtonHelper(binding!!.btnSubmit, SettingUtils.requestTimeout)
|
||||
mCountDownHelper!!.setOnCountDownListener(object : CountDownButtonHelper.OnCountDownListener {
|
||||
override fun onCountDown(time: Int) {
|
||||
binding!!.btnSubmit.text = String.format(getString(R.string.seconds_n), time)
|
||||
}
|
||||
|
||||
override fun onFinished() {
|
||||
binding!!.btnSubmit.text = getString(R.string.send)
|
||||
}
|
||||
})
|
||||
|
||||
//卡槽信息
|
||||
val serverConfigStr = HttpServerUtils.serverConfig
|
||||
if (!TextUtils.isEmpty(serverConfigStr)) {
|
||||
val serverConfig: ConfigData = Gson().fromJson(serverConfigStr, object : TypeToken<ConfigData>() {}.type)
|
||||
binding!!.rbSimSlot1.text = "SIM1:" + serverConfig.extraSim1
|
||||
binding!!.rbSimSlot2.text = "SIM2:" + serverConfig.extraSim2
|
||||
}
|
||||
}
|
||||
|
||||
override fun initListeners() {
|
||||
binding!!.btnSubmit.setOnClickListener(this)
|
||||
LiveEventBus.get(EVENT_KEY_SIM_SLOT, Int::class.java).observeSticky(this) { value: Int ->
|
||||
binding!!.rgSimSlot.check(if (value == 1) R.id.rb_sim_slot_2 else R.id.rb_sim_slot_1)
|
||||
}
|
||||
LiveEventBus.get(EVENT_KEY_PHONE_NUMBERS, String::class.java).observeSticky(this) { value: String ->
|
||||
binding!!.etPhoneNumbers.setText(value)
|
||||
}
|
||||
}
|
||||
|
||||
@SingleClick
|
||||
override fun onClick(v: View) {
|
||||
when (v.id) {
|
||||
R.id.btn_submit -> {
|
||||
val requestUrl: String = HttpServerUtils.serverAddress + "/sms/send"
|
||||
Log.i(TAG, "requestUrl:$requestUrl")
|
||||
|
||||
val msgMap: MutableMap<String, Any> = mutableMapOf()
|
||||
val timestamp = System.currentTimeMillis()
|
||||
msgMap["timestamp"] = timestamp
|
||||
val clientSignKey = HttpServerUtils.clientSignKey
|
||||
if (!TextUtils.isEmpty(clientSignKey)) {
|
||||
msgMap["sign"] = HttpServerUtils.calcSign(timestamp.toString(), clientSignKey)
|
||||
}
|
||||
|
||||
val phoneNumbers = binding!!.etPhoneNumbers.text.toString()
|
||||
val phoneRegex = getString(R.string.phone_numbers_regex).toRegex()
|
||||
if (!phoneRegex.matches(phoneNumbers)) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.phone_numbers_error))
|
||||
return
|
||||
}
|
||||
|
||||
val msgContent = binding!!.etMsgContent.text.toString()
|
||||
val msgRegex = getString(R.string.msg_content_regex).toRegex()
|
||||
if (!msgRegex.matches(msgContent)) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.msg_content_error))
|
||||
return
|
||||
}
|
||||
|
||||
val dataMap: MutableMap<String, Any> = mutableMapOf()
|
||||
dataMap["sim_slot"] = if (binding!!.rgSimSlot.checkedRadioButtonId == R.id.rb_sim_slot_2) 2 else 1
|
||||
dataMap["phone_numbers"] = phoneNumbers
|
||||
dataMap["msg_content"] = msgContent
|
||||
msgMap["data"] = dataMap
|
||||
|
||||
var requestMsg: String = Gson().toJson(msgMap)
|
||||
Log.i(TAG, "requestMsg:$requestMsg")
|
||||
|
||||
val postRequest = XHttp.post(requestUrl)
|
||||
.keepJson(true)
|
||||
.timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
|
||||
.cacheMode(CacheMode.NO_CACHE)
|
||||
.timeStamp(true)
|
||||
|
||||
when (HttpServerUtils.clientSafetyMeasures) {
|
||||
2 -> {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey)
|
||||
try {
|
||||
requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
requestMsg = RSACrypt.encryptByPublicKey(requestMsg, publicKey)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
3 -> {
|
||||
try {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey)
|
||||
//requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
val encryptCBC = SM4Crypt.encrypt(requestMsg.toByteArray(), sm4Key)
|
||||
requestMsg = ConvertTools.bytes2HexString(encryptCBC)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
else -> {
|
||||
postRequest.upJson(requestMsg)
|
||||
}
|
||||
}
|
||||
|
||||
mCountDownHelper?.start()
|
||||
postRequest.execute(object : SimpleCallBack<String>() {
|
||||
override fun onError(e: ApiException) {
|
||||
XToastUtils.error(e.displayMessage)
|
||||
mCountDownHelper?.finish()
|
||||
}
|
||||
|
||||
override fun onSuccess(response: String) {
|
||||
Log.i(TAG, response)
|
||||
try {
|
||||
var json = response
|
||||
if (HttpServerUtils.clientSafetyMeasures == 2) {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey)
|
||||
json = RSACrypt.decryptByPublicKey(json, publicKey)
|
||||
json = String(Base64.decode(json))
|
||||
} else if (HttpServerUtils.clientSafetyMeasures == 3) {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey)
|
||||
val encryptCBC = ConvertTools.hexStringToByteArray(json)
|
||||
val decryptCBC = SM4Crypt.decrypt(encryptCBC, sm4Key)
|
||||
json = String(decryptCBC)
|
||||
}
|
||||
val resp: BaseResponse<String> = Gson().fromJson(json, object : TypeToken<BaseResponse<String>>() {}.type)
|
||||
if (resp.code == 200) {
|
||||
XToastUtils.success(ResUtils.getString(R.string.request_succeeded))
|
||||
} else {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + resp.msg)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + response)
|
||||
}
|
||||
mCountDownHelper?.finish()
|
||||
}
|
||||
})
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
if (mCountDownHelper != null) mCountDownHelper!!.recycle()
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
}
|
|
@ -1,235 +1,235 @@
|
|||
package com.idormy.sms.forwarder.fragment.client
|
||||
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.idormy.sms.forwarder.core.BaseFragment
|
||||
import com.idormy.sms.forwarder.databinding.FragmentClientWolSendBinding
|
||||
import com.idormy.sms.forwarder.server.model.BaseResponse
|
||||
import com.idormy.sms.forwarder.utils.*
|
||||
import com.xuexiang.xaop.annotation.SingleClick
|
||||
import com.xuexiang.xhttp2.XHttp
|
||||
import com.xuexiang.xhttp2.cache.model.CacheMode
|
||||
import com.xuexiang.xhttp2.callback.SimpleCallBack
|
||||
import com.xuexiang.xhttp2.exception.ApiException
|
||||
import com.xuexiang.xpage.annotation.Page
|
||||
import com.xuexiang.xrouter.utils.TextUtils
|
||||
import com.xuexiang.xui.utils.CountDownButtonHelper
|
||||
import com.xuexiang.xui.utils.ResUtils
|
||||
import com.xuexiang.xui.widget.actionbar.TitleBar
|
||||
import com.xuexiang.xui.widget.dialog.materialdialog.DialogAction
|
||||
import com.xuexiang.xui.widget.dialog.materialdialog.MaterialDialog
|
||||
import com.xuexiang.xutil.data.ConvertTools
|
||||
|
||||
@Suppress("PropertyName")
|
||||
@Page(name = "远程WOL")
|
||||
class WolSendFragment : BaseFragment<FragmentClientWolSendBinding?>(), View.OnClickListener {
|
||||
|
||||
val TAG: String = WolSendFragment::class.java.simpleName
|
||||
private var mCountDownHelper: CountDownButtonHelper? = null
|
||||
private var wolHistory: MutableMap<String, String> = mutableMapOf()
|
||||
|
||||
override fun viewBindingInflate(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup,
|
||||
): FragmentClientWolSendBinding {
|
||||
return FragmentClientWolSendBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
override fun initTitle(): TitleBar? {
|
||||
return super.initTitle()!!.setImmersive(false).setTitle(R.string.api_wol)
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化控件
|
||||
*/
|
||||
override fun initViews() {
|
||||
//发送按钮增加倒计时,避免重复点击
|
||||
mCountDownHelper = CountDownButtonHelper(binding!!.btnSubmit, SettingUtils.requestTimeout)
|
||||
mCountDownHelper!!.setOnCountDownListener(object :
|
||||
CountDownButtonHelper.OnCountDownListener {
|
||||
override fun onCountDown(time: Int) {
|
||||
binding!!.btnSubmit.text = String.format(getString(R.string.seconds_n), time)
|
||||
}
|
||||
|
||||
override fun onFinished() {
|
||||
binding!!.btnSubmit.text = getString(R.string.send)
|
||||
}
|
||||
})
|
||||
|
||||
//取出历史记录
|
||||
val history = HttpServerUtils.wolHistory
|
||||
if (!TextUtils.isEmpty(history)) {
|
||||
wolHistory =
|
||||
Gson().fromJson(history, object : TypeToken<MutableMap<String, String>>() {}.type)
|
||||
}
|
||||
}
|
||||
|
||||
override fun initListeners() {
|
||||
binding!!.btnServerHistory.setOnClickListener(this)
|
||||
binding!!.btnSubmit.setOnClickListener(this)
|
||||
}
|
||||
|
||||
@SingleClick
|
||||
override fun onClick(v: View) {
|
||||
when (v.id) {
|
||||
R.id.btn_server_history -> {
|
||||
if (wolHistory.isEmpty()) {
|
||||
XToastUtils.warning(getString(R.string.no_server_history))
|
||||
return
|
||||
}
|
||||
Log.d(TAG, "wolHistory = $wolHistory")
|
||||
|
||||
MaterialDialog.Builder(requireContext())
|
||||
.title(R.string.server_history)
|
||||
.items(wolHistory.keys)
|
||||
.itemsCallbackSingleChoice(0) { _: MaterialDialog?, _: View?, _: Int, text: CharSequence ->
|
||||
//XToastUtils.info("$which: $text")
|
||||
binding!!.etMac.setText(text)
|
||||
binding!!.etIp.setText(wolHistory[text])
|
||||
true // allow selection
|
||||
}
|
||||
.positiveText(R.string.select)
|
||||
.negativeText(R.string.cancel)
|
||||
.neutralText(R.string.clear_history)
|
||||
.neutralColor(ResUtils.getColors(R.color.red))
|
||||
.onNeutral { _: MaterialDialog?, _: DialogAction? ->
|
||||
wolHistory.clear()
|
||||
HttpServerUtils.wolHistory = ""
|
||||
}
|
||||
.show()
|
||||
}
|
||||
R.id.btn_submit -> {
|
||||
val requestUrl: String = HttpServerUtils.serverAddress + "/wol/send"
|
||||
Log.i(TAG, "requestUrl:$requestUrl")
|
||||
|
||||
val msgMap: MutableMap<String, Any> = mutableMapOf()
|
||||
val timestamp = System.currentTimeMillis()
|
||||
msgMap["timestamp"] = timestamp
|
||||
val clientSignKey = HttpServerUtils.clientSignKey
|
||||
if (!TextUtils.isEmpty(clientSignKey)) {
|
||||
msgMap["sign"] =
|
||||
HttpServerUtils.calcSign(timestamp.toString(), clientSignKey.toString())
|
||||
}
|
||||
|
||||
val mac = binding!!.etMac.text.toString()
|
||||
val macRegex = getString(R.string.mac_regex).toRegex()
|
||||
if (!macRegex.matches(mac)) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.mac_error))
|
||||
return
|
||||
}
|
||||
|
||||
val ip = binding!!.etIp.text.toString()
|
||||
val ipRegex = getString(R.string.ip_regex).toRegex()
|
||||
if (!TextUtils.isEmpty(ip) && !ipRegex.matches(ip)) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.ip_error))
|
||||
return
|
||||
}
|
||||
|
||||
val port = binding!!.etPort.text.toString()
|
||||
val portRegex = getString(R.string.wol_port_regex).toRegex()
|
||||
if (!TextUtils.isEmpty(port) && !portRegex.matches(port)) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.wol_port_error))
|
||||
return
|
||||
}
|
||||
|
||||
val dataMap: MutableMap<String, Any> = mutableMapOf()
|
||||
dataMap["ip"] = ip
|
||||
dataMap["mac"] = mac
|
||||
dataMap["port"] = port
|
||||
msgMap["data"] = dataMap
|
||||
|
||||
var requestMsg: String = Gson().toJson(msgMap)
|
||||
Log.i(TAG, "requestMsg:$requestMsg")
|
||||
|
||||
val postRequest = XHttp.post(requestUrl)
|
||||
.keepJson(true)
|
||||
.timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
|
||||
.cacheMode(CacheMode.NO_CACHE)
|
||||
.timeStamp(true)
|
||||
|
||||
when (HttpServerUtils.clientSafetyMeasures) {
|
||||
2 -> {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey.toString())
|
||||
try {
|
||||
requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
requestMsg = RSACrypt.encryptByPublicKey(requestMsg, publicKey)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
3 -> {
|
||||
try {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey.toString())
|
||||
//requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
val encryptCBC = SM4Crypt.encrypt(requestMsg.toByteArray(), sm4Key)
|
||||
requestMsg = ConvertTools.bytes2HexString(encryptCBC)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
else -> {
|
||||
postRequest.upJson(requestMsg)
|
||||
}
|
||||
}
|
||||
|
||||
mCountDownHelper?.start()
|
||||
postRequest.execute(object : SimpleCallBack<String>() {
|
||||
override fun onError(e: ApiException) {
|
||||
XToastUtils.error(e.displayMessage)
|
||||
mCountDownHelper?.finish()
|
||||
}
|
||||
|
||||
override fun onSuccess(response: String) {
|
||||
Log.i(TAG, response)
|
||||
try {
|
||||
var json = response
|
||||
if (HttpServerUtils.clientSafetyMeasures == 2) {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey.toString())
|
||||
json = RSACrypt.decryptByPublicKey(json, publicKey)
|
||||
json = String(Base64.decode(json))
|
||||
} else if (HttpServerUtils.clientSafetyMeasures == 3) {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey.toString())
|
||||
val encryptCBC = ConvertTools.hexStringToByteArray(json)
|
||||
val decryptCBC = SM4Crypt.decrypt(encryptCBC, sm4Key)
|
||||
json = String(decryptCBC)
|
||||
}
|
||||
val resp: BaseResponse<String> = Gson().fromJson(json, object : TypeToken<BaseResponse<String>>() {}.type)
|
||||
if (resp.code == 200) {
|
||||
XToastUtils.success(ResUtils.getString(R.string.request_succeeded))
|
||||
//添加到历史记录
|
||||
wolHistory[mac] = ip
|
||||
HttpServerUtils.wolHistory = Gson().toJson(wolHistory)
|
||||
} else {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + resp.msg)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + response)
|
||||
}
|
||||
mCountDownHelper?.finish()
|
||||
}
|
||||
})
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
if (mCountDownHelper != null) mCountDownHelper!!.recycle()
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
package com.idormy.sms.forwarder.fragment.client
|
||||
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.idormy.sms.forwarder.core.BaseFragment
|
||||
import com.idormy.sms.forwarder.databinding.FragmentClientWolSendBinding
|
||||
import com.idormy.sms.forwarder.server.model.BaseResponse
|
||||
import com.idormy.sms.forwarder.utils.*
|
||||
import com.xuexiang.xaop.annotation.SingleClick
|
||||
import com.xuexiang.xhttp2.XHttp
|
||||
import com.xuexiang.xhttp2.cache.model.CacheMode
|
||||
import com.xuexiang.xhttp2.callback.SimpleCallBack
|
||||
import com.xuexiang.xhttp2.exception.ApiException
|
||||
import com.xuexiang.xpage.annotation.Page
|
||||
import com.xuexiang.xrouter.utils.TextUtils
|
||||
import com.xuexiang.xui.utils.CountDownButtonHelper
|
||||
import com.xuexiang.xui.utils.ResUtils
|
||||
import com.xuexiang.xui.widget.actionbar.TitleBar
|
||||
import com.xuexiang.xui.widget.dialog.materialdialog.DialogAction
|
||||
import com.xuexiang.xui.widget.dialog.materialdialog.MaterialDialog
|
||||
import com.xuexiang.xutil.data.ConvertTools
|
||||
|
||||
@Suppress("PropertyName")
|
||||
@Page(name = "远程WOL")
|
||||
class WolSendFragment : BaseFragment<FragmentClientWolSendBinding?>(), View.OnClickListener {
|
||||
|
||||
val TAG: String = WolSendFragment::class.java.simpleName
|
||||
private var mCountDownHelper: CountDownButtonHelper? = null
|
||||
private var wolHistory: MutableMap<String, String> = mutableMapOf()
|
||||
|
||||
override fun viewBindingInflate(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup,
|
||||
): FragmentClientWolSendBinding {
|
||||
return FragmentClientWolSendBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
override fun initTitle(): TitleBar? {
|
||||
return super.initTitle()!!.setImmersive(false).setTitle(R.string.api_wol)
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化控件
|
||||
*/
|
||||
override fun initViews() {
|
||||
//发送按钮增加倒计时,避免重复点击
|
||||
mCountDownHelper = CountDownButtonHelper(binding!!.btnSubmit, SettingUtils.requestTimeout)
|
||||
mCountDownHelper!!.setOnCountDownListener(object :
|
||||
CountDownButtonHelper.OnCountDownListener {
|
||||
override fun onCountDown(time: Int) {
|
||||
binding!!.btnSubmit.text = String.format(getString(R.string.seconds_n), time)
|
||||
}
|
||||
|
||||
override fun onFinished() {
|
||||
binding!!.btnSubmit.text = getString(R.string.send)
|
||||
}
|
||||
})
|
||||
|
||||
//取出历史记录
|
||||
val history = HttpServerUtils.wolHistory
|
||||
if (!TextUtils.isEmpty(history)) {
|
||||
wolHistory =
|
||||
Gson().fromJson(history, object : TypeToken<MutableMap<String, String>>() {}.type)
|
||||
}
|
||||
}
|
||||
|
||||
override fun initListeners() {
|
||||
binding!!.btnServerHistory.setOnClickListener(this)
|
||||
binding!!.btnSubmit.setOnClickListener(this)
|
||||
}
|
||||
|
||||
@SingleClick
|
||||
override fun onClick(v: View) {
|
||||
when (v.id) {
|
||||
R.id.btn_server_history -> {
|
||||
if (wolHistory.isEmpty()) {
|
||||
XToastUtils.warning(getString(R.string.no_server_history))
|
||||
return
|
||||
}
|
||||
Log.d(TAG, "wolHistory = $wolHistory")
|
||||
|
||||
MaterialDialog.Builder(requireContext())
|
||||
.title(R.string.server_history)
|
||||
.items(wolHistory.keys)
|
||||
.itemsCallbackSingleChoice(0) { _: MaterialDialog?, _: View?, _: Int, text: CharSequence ->
|
||||
//XToastUtils.info("$which: $text")
|
||||
binding!!.etMac.setText(text)
|
||||
binding!!.etIp.setText(wolHistory[text])
|
||||
true // allow selection
|
||||
}
|
||||
.positiveText(R.string.select)
|
||||
.negativeText(R.string.cancel)
|
||||
.neutralText(R.string.clear_history)
|
||||
.neutralColor(ResUtils.getColors(R.color.red))
|
||||
.onNeutral { _: MaterialDialog?, _: DialogAction? ->
|
||||
wolHistory.clear()
|
||||
HttpServerUtils.wolHistory = ""
|
||||
}
|
||||
.show()
|
||||
}
|
||||
R.id.btn_submit -> {
|
||||
val requestUrl: String = HttpServerUtils.serverAddress + "/wol/send"
|
||||
Log.i(TAG, "requestUrl:$requestUrl")
|
||||
|
||||
val msgMap: MutableMap<String, Any> = mutableMapOf()
|
||||
val timestamp = System.currentTimeMillis()
|
||||
msgMap["timestamp"] = timestamp
|
||||
val clientSignKey = HttpServerUtils.clientSignKey
|
||||
if (!TextUtils.isEmpty(clientSignKey)) {
|
||||
msgMap["sign"] =
|
||||
HttpServerUtils.calcSign(timestamp.toString(), clientSignKey)
|
||||
}
|
||||
|
||||
val mac = binding!!.etMac.text.toString()
|
||||
val macRegex = getString(R.string.mac_regex).toRegex()
|
||||
if (!macRegex.matches(mac)) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.mac_error))
|
||||
return
|
||||
}
|
||||
|
||||
val ip = binding!!.etIp.text.toString()
|
||||
val ipRegex = getString(R.string.ip_regex).toRegex()
|
||||
if (!TextUtils.isEmpty(ip) && !ipRegex.matches(ip)) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.ip_error))
|
||||
return
|
||||
}
|
||||
|
||||
val port = binding!!.etPort.text.toString()
|
||||
val portRegex = getString(R.string.wol_port_regex).toRegex()
|
||||
if (!TextUtils.isEmpty(port) && !portRegex.matches(port)) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.wol_port_error))
|
||||
return
|
||||
}
|
||||
|
||||
val dataMap: MutableMap<String, Any> = mutableMapOf()
|
||||
dataMap["ip"] = ip
|
||||
dataMap["mac"] = mac
|
||||
dataMap["port"] = port
|
||||
msgMap["data"] = dataMap
|
||||
|
||||
var requestMsg: String = Gson().toJson(msgMap)
|
||||
Log.i(TAG, "requestMsg:$requestMsg")
|
||||
|
||||
val postRequest = XHttp.post(requestUrl)
|
||||
.keepJson(true)
|
||||
.timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
|
||||
.cacheMode(CacheMode.NO_CACHE)
|
||||
.timeStamp(true)
|
||||
|
||||
when (HttpServerUtils.clientSafetyMeasures) {
|
||||
2 -> {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey)
|
||||
try {
|
||||
requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
requestMsg = RSACrypt.encryptByPublicKey(requestMsg, publicKey)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
3 -> {
|
||||
try {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey)
|
||||
//requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
val encryptCBC = SM4Crypt.encrypt(requestMsg.toByteArray(), sm4Key)
|
||||
requestMsg = ConvertTools.bytes2HexString(encryptCBC)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
else -> {
|
||||
postRequest.upJson(requestMsg)
|
||||
}
|
||||
}
|
||||
|
||||
mCountDownHelper?.start()
|
||||
postRequest.execute(object : SimpleCallBack<String>() {
|
||||
override fun onError(e: ApiException) {
|
||||
XToastUtils.error(e.displayMessage)
|
||||
mCountDownHelper?.finish()
|
||||
}
|
||||
|
||||
override fun onSuccess(response: String) {
|
||||
Log.i(TAG, response)
|
||||
try {
|
||||
var json = response
|
||||
if (HttpServerUtils.clientSafetyMeasures == 2) {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey)
|
||||
json = RSACrypt.decryptByPublicKey(json, publicKey)
|
||||
json = String(Base64.decode(json))
|
||||
} else if (HttpServerUtils.clientSafetyMeasures == 3) {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey)
|
||||
val encryptCBC = ConvertTools.hexStringToByteArray(json)
|
||||
val decryptCBC = SM4Crypt.decrypt(encryptCBC, sm4Key)
|
||||
json = String(decryptCBC)
|
||||
}
|
||||
val resp: BaseResponse<String> = Gson().fromJson(json, object : TypeToken<BaseResponse<String>>() {}.type)
|
||||
if (resp.code == 200) {
|
||||
XToastUtils.success(ResUtils.getString(R.string.request_succeeded))
|
||||
//添加到历史记录
|
||||
wolHistory[mac] = ip
|
||||
HttpServerUtils.wolHistory = Gson().toJson(wolHistory)
|
||||
} else {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + resp.msg)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + response)
|
||||
}
|
||||
mCountDownHelper?.finish()
|
||||
}
|
||||
})
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
if (mCountDownHelper != null) mCountDownHelper!!.recycle()
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
}
|
|
@ -3,11 +3,8 @@ package com.idormy.sms.forwarder.receiver
|
|||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import com.idormy.sms.forwarder.activity.SplashActivity
|
||||
import com.idormy.sms.forwarder.service.ForegroundService
|
||||
import com.idormy.sms.forwarder.utils.SettingUtils
|
||||
|
||||
@Suppress("PropertyName")
|
||||
class BootReceiver : BroadcastReceiver() {
|
||||
|
@ -17,32 +14,13 @@ class BootReceiver : BroadcastReceiver() {
|
|||
override fun onReceive(context: Context, intent: Intent?) {
|
||||
val receiveAction: String? = intent?.action
|
||||
Log.d(TAG, "onReceive intent $receiveAction")
|
||||
if (receiveAction == "android.intent.action.BOOT_COMPLETED") {
|
||||
if (receiveAction == "android.intent.action.BOOT_COMPLETED" || receiveAction == "android.intent.action.LOCKED_BOOT_COMPLETED") {
|
||||
try {
|
||||
val i = Intent(context, SplashActivity::class.java)
|
||||
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
context.startActivity(i)
|
||||
|
||||
//纯客户端模式
|
||||
if (SettingUtils.enablePureClientMode) return
|
||||
|
||||
//前台服务
|
||||
val frontServiceIntent = Intent(context, ForegroundService::class.java)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
context.startForegroundService(frontServiceIntent)
|
||||
} else {
|
||||
context.startService(frontServiceIntent)
|
||||
}
|
||||
|
||||
/*InitUtils.init(context)
|
||||
//电池状态监听
|
||||
val batteryServiceIntent = Intent(context, BatteryService::class.java)
|
||||
context.startService(batteryServiceIntent)
|
||||
|
||||
//后台播放无声音乐
|
||||
if (SettingUtils.getPlaySilenceMusic()) {
|
||||
context.startService(Intent(context, MusicService::class.java))
|
||||
}*/
|
||||
Log.d(TAG, "强制重启APP一次")
|
||||
val intent1 = Intent(context, SplashActivity::class.java)
|
||||
intent1.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
context.startActivity(intent1)
|
||||
android.os.Process.killProcess(android.os.Process.myPid()) //杀掉以前进程
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
|
|
@ -1,206 +1,175 @@
|
|||
package com.idormy.sms.forwarder.receiver
|
||||
|
||||
import android.Manifest
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.telephony.TelephonyManager
|
||||
import android.text.TextUtils
|
||||
import android.util.Log
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.work.OneTimeWorkRequestBuilder
|
||||
import androidx.work.WorkManager
|
||||
import androidx.work.workDataOf
|
||||
import com.google.gson.Gson
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.idormy.sms.forwarder.entity.CallInfo
|
||||
import com.idormy.sms.forwarder.entity.MsgInfo
|
||||
import com.idormy.sms.forwarder.utils.MMKVUtils
|
||||
import com.idormy.sms.forwarder.utils.PhoneUtils
|
||||
import com.idormy.sms.forwarder.utils.SettingUtils
|
||||
import com.idormy.sms.forwarder.utils.Worker
|
||||
import com.idormy.sms.forwarder.workers.SendWorker
|
||||
import com.xuexiang.xutil.resource.ResUtils.getString
|
||||
import java.util.*
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
class PhoneStateReceiver : BroadcastReceiver() {
|
||||
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
try {
|
||||
//纯客户端模式
|
||||
if (SettingUtils.enablePureClientMode) return
|
||||
|
||||
//总开关
|
||||
if (!SettingUtils.enablePhone) return
|
||||
|
||||
//过滤广播
|
||||
if (TelephonyManager.ACTION_PHONE_STATE_CHANGED != intent.action) return
|
||||
|
||||
//权限判断
|
||||
if (ActivityCompat.checkSelfPermission(
|
||||
context,
|
||||
Manifest.permission.READ_PHONE_STATE
|
||||
) != PackageManager.PERMISSION_GRANTED
|
||||
) return
|
||||
|
||||
//获取来电号码
|
||||
val number = intent.extras!!.getString(TelephonyManager.EXTRA_INCOMING_NUMBER)
|
||||
val stateStr = intent.extras!!.getString(TelephonyManager.EXTRA_STATE)
|
||||
var state = 0
|
||||
when (stateStr) {
|
||||
TelephonyManager.EXTRA_STATE_IDLE -> state = TelephonyManager.CALL_STATE_IDLE
|
||||
TelephonyManager.EXTRA_STATE_OFFHOOK -> state = TelephonyManager.CALL_STATE_OFFHOOK
|
||||
TelephonyManager.EXTRA_STATE_RINGING -> state = TelephonyManager.CALL_STATE_RINGING
|
||||
}
|
||||
Log.d(TAG, "state=$state, number=$number")
|
||||
|
||||
onCallStateChanged(context, state, number)
|
||||
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, e.message.toString())
|
||||
}
|
||||
}
|
||||
|
||||
//Incoming call- goes from IDLE to RINGING when it rings, to OFFHOOK when it's answered, to IDLE when its hung up
|
||||
//Outgoing call- goes from IDLE to OFFHOOK when it dials out, to IDLE when hung up
|
||||
private fun onCallStateChanged(context: Context, state: Int, number: String?) {
|
||||
val lastState = MMKVUtils.getInt("CALL_LAST_STATE", TelephonyManager.CALL_STATE_IDLE)
|
||||
if (lastState == state || (state == TelephonyManager.CALL_STATE_RINGING && number == null)) {
|
||||
//No change, debounce extras
|
||||
return
|
||||
}
|
||||
|
||||
MMKVUtils.put("CALL_LAST_STATE", state)
|
||||
when (state) {
|
||||
TelephonyManager.CALL_STATE_RINGING -> {
|
||||
Log.d(TAG, "来电响铃")
|
||||
MMKVUtils.put("CALL_IS_INCOMING", true)
|
||||
//MMKVUtils.put("CALL_START_TIME", Date())
|
||||
MMKVUtils.put("CALL_SAVED_NUMBER", number)
|
||||
|
||||
//来电提醒
|
||||
if (!TextUtils.isEmpty(number) && SettingUtils.enableCallType4) {
|
||||
val contacts = PhoneUtils.getContactByNumber(number)
|
||||
val contactName =
|
||||
if (contacts.isNotEmpty()) contacts[0].name else getString(R.string.unknown_number)
|
||||
|
||||
val sb = StringBuilder()
|
||||
sb.append(getString(R.string.linkman)).append(contactName).append("\n")
|
||||
sb.append(getString(R.string.mandatory_type))
|
||||
sb.append(getString(R.string.incoming_call))
|
||||
|
||||
val msgInfo = MsgInfo("call", number.toString(), sb.toString(), Date(), "", -1)
|
||||
val request = OneTimeWorkRequestBuilder<SendWorker>()
|
||||
.setInputData(
|
||||
workDataOf(
|
||||
Worker.sendMsgInfo to Gson().toJson(msgInfo)
|
||||
)
|
||||
)
|
||||
.build()
|
||||
WorkManager.getInstance(context).enqueue(request)
|
||||
}
|
||||
}
|
||||
TelephonyManager.CALL_STATE_OFFHOOK ->
|
||||
//Transition of ringing->offhook are pickups of incoming calls. Nothing done on them
|
||||
when {
|
||||
lastState != TelephonyManager.CALL_STATE_RINGING -> {
|
||||
Log.d(TAG, "去电接通")
|
||||
MMKVUtils.put("CALL_IS_INCOMING", false)
|
||||
//MMKVUtils.put("CALL_START_TIME", Date())
|
||||
}
|
||||
else -> {
|
||||
Log.d(TAG, "来电接通")
|
||||
MMKVUtils.put("CALL_IS_INCOMING", true)
|
||||
//MMKVUtils.put("CALL_START_TIME", Date())
|
||||
}
|
||||
}
|
||||
TelephonyManager.CALL_STATE_IDLE ->
|
||||
//Went to idle- this is the end of a call. What type depends on previous state(s)
|
||||
when {
|
||||
lastState == TelephonyManager.CALL_STATE_RINGING -> {
|
||||
Log.d(TAG, "来电未接")
|
||||
sendReceiveCallMsg(
|
||||
context,
|
||||
3,
|
||||
MMKVUtils.getString("CALL_SAVED_NUMBER", null)
|
||||
)
|
||||
}
|
||||
MMKVUtils.getBoolean("CALL_IS_INCOMING", false) -> {
|
||||
Log.d(TAG, "来电挂机")
|
||||
sendReceiveCallMsg(
|
||||
context,
|
||||
1,
|
||||
MMKVUtils.getString("CALL_SAVED_NUMBER", null)
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
Log.d(TAG, "去电挂机")
|
||||
sendReceiveCallMsg(
|
||||
context,
|
||||
2,
|
||||
MMKVUtils.getString("CALL_SAVED_NUMBER", null)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun sendReceiveCallMsg(context: Context, callType: Int, phoneNumber: String?) {
|
||||
//必须休眠才能获取来电记录,否则可能获取到上一次通话的
|
||||
Thread.sleep(500)
|
||||
//获取后一条通话记录
|
||||
Log.d(TAG, "callType = $callType, phoneNumber = $phoneNumber")
|
||||
val callInfo: CallInfo? = PhoneUtils.getLastCallInfo(callType, phoneNumber)
|
||||
Log.d(TAG, "callInfo = $callInfo")
|
||||
if (callInfo?.number == null) return
|
||||
|
||||
//判断是否开启该类型转发
|
||||
if ((callInfo.type == 1 && !SettingUtils.enableCallType1)
|
||||
|| (callInfo.type == 2 && !SettingUtils.enableCallType2)
|
||||
|| (callInfo.type == 3 && !SettingUtils.enableCallType3)
|
||||
) {
|
||||
Log.w(TAG, "未开启该类型转发,type=" + callInfo.type)
|
||||
return
|
||||
}
|
||||
|
||||
//卡槽id:-1=获取失败、0=卡槽1、1=卡槽2
|
||||
val simSlot = callInfo.simId
|
||||
//获取卡槽信息
|
||||
val simInfo = when (simSlot) {
|
||||
0 -> "SIM1_" + SettingUtils.extraSim1
|
||||
1 -> "SIM2_" + SettingUtils.extraSim2
|
||||
else -> ""
|
||||
}
|
||||
|
||||
//获取联系人姓名
|
||||
if (TextUtils.isEmpty(callInfo.name)) {
|
||||
val contacts = PhoneUtils.getContactByNumber(phoneNumber)
|
||||
callInfo.name =
|
||||
if (contacts.isNotEmpty()) contacts[0].name else getString(R.string.unknown_number)
|
||||
}
|
||||
|
||||
val msgInfo = MsgInfo(
|
||||
"call",
|
||||
callInfo.number,
|
||||
PhoneUtils.getCallMsg(callInfo),
|
||||
Date(),
|
||||
simInfo,
|
||||
simSlot
|
||||
)
|
||||
val request = OneTimeWorkRequestBuilder<SendWorker>()
|
||||
.setInputData(
|
||||
workDataOf(
|
||||
Worker.sendMsgInfo to Gson().toJson(msgInfo)
|
||||
)
|
||||
)
|
||||
.build()
|
||||
WorkManager.getInstance(context).enqueue(request)
|
||||
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "PhoneStateReceiver"
|
||||
}
|
||||
package com.idormy.sms.forwarder.receiver
|
||||
|
||||
import android.Manifest
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.telephony.TelephonyManager
|
||||
import android.text.TextUtils
|
||||
import android.util.Log
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.work.OneTimeWorkRequestBuilder
|
||||
import androidx.work.WorkManager
|
||||
import androidx.work.workDataOf
|
||||
import com.google.gson.Gson
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.idormy.sms.forwarder.entity.CallInfo
|
||||
import com.idormy.sms.forwarder.entity.MsgInfo
|
||||
import com.idormy.sms.forwarder.utils.*
|
||||
import com.idormy.sms.forwarder.workers.SendWorker
|
||||
import com.xuexiang.xutil.resource.ResUtils.getString
|
||||
import java.util.*
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
class PhoneStateReceiver : BroadcastReceiver() {
|
||||
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
try {
|
||||
//纯客户端模式
|
||||
if (SettingUtils.enablePureClientMode) return
|
||||
|
||||
//总开关
|
||||
if (!SettingUtils.enablePhone) return
|
||||
|
||||
//过滤广播
|
||||
if (TelephonyManager.ACTION_PHONE_STATE_CHANGED != intent.action) return
|
||||
|
||||
//权限判断
|
||||
if (ActivityCompat.checkSelfPermission(
|
||||
context, Manifest.permission.READ_PHONE_STATE
|
||||
) != PackageManager.PERMISSION_GRANTED
|
||||
) return
|
||||
|
||||
//获取来电号码
|
||||
val number = intent.extras!!.getString(TelephonyManager.EXTRA_INCOMING_NUMBER)
|
||||
val stateStr = intent.extras!!.getString(TelephonyManager.EXTRA_STATE)
|
||||
var state = 0
|
||||
when (stateStr) {
|
||||
TelephonyManager.EXTRA_STATE_IDLE -> state = TelephonyManager.CALL_STATE_IDLE
|
||||
TelephonyManager.EXTRA_STATE_OFFHOOK -> state = TelephonyManager.CALL_STATE_OFFHOOK
|
||||
TelephonyManager.EXTRA_STATE_RINGING -> state = TelephonyManager.CALL_STATE_RINGING
|
||||
}
|
||||
Log.d(TAG, "state=$state, number=$number")
|
||||
|
||||
onCallStateChanged(context, state, number)
|
||||
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, e.message.toString())
|
||||
}
|
||||
}
|
||||
|
||||
//Incoming call- goes from IDLE to RINGING when it rings, to OFFHOOK when it's answered, to IDLE when its hung up
|
||||
//Outgoing call- goes from IDLE to OFFHOOK when it dials out, to IDLE when hung up
|
||||
private fun onCallStateChanged(context: Context, state: Int, number: String?) {
|
||||
var lastState: Int by SharedPreference("CALL_LAST_STATE", TelephonyManager.CALL_STATE_IDLE)
|
||||
if (lastState == state || (state == TelephonyManager.CALL_STATE_RINGING && number == null)) {
|
||||
//No change, debounce extras
|
||||
return
|
||||
}
|
||||
|
||||
lastState = state
|
||||
var callIsIncoming: Boolean by SharedPreference("CALL_IS_INCOMING", false)
|
||||
var callSavedNumber: String by SharedPreference("CALL_SAVED_NUMBER", "")
|
||||
when (state) {
|
||||
TelephonyManager.CALL_STATE_RINGING -> {
|
||||
Log.d(TAG, "来电响铃")
|
||||
callIsIncoming = true
|
||||
callSavedNumber = number.toString()
|
||||
|
||||
//来电提醒
|
||||
if (!TextUtils.isEmpty(number) && SettingUtils.enableCallType4) {
|
||||
val contacts = PhoneUtils.getContactByNumber(number)
|
||||
val contactName = if (contacts.isNotEmpty()) contacts[0].name else getString(R.string.unknown_number)
|
||||
|
||||
val sb = StringBuilder()
|
||||
sb.append(getString(R.string.linkman)).append(contactName).append("\n")
|
||||
sb.append(getString(R.string.mandatory_type))
|
||||
sb.append(getString(R.string.incoming_call))
|
||||
|
||||
val msgInfo = MsgInfo("call", number.toString(), sb.toString(), Date(), "", -1)
|
||||
val request = OneTimeWorkRequestBuilder<SendWorker>().setInputData(
|
||||
workDataOf(
|
||||
Worker.sendMsgInfo to Gson().toJson(msgInfo)
|
||||
)
|
||||
).build()
|
||||
WorkManager.getInstance(context).enqueue(request)
|
||||
}
|
||||
}
|
||||
TelephonyManager.CALL_STATE_OFFHOOK ->
|
||||
//Transition of ringing->offhook are pickups of incoming calls. Nothing done on them
|
||||
callIsIncoming = when {
|
||||
lastState != TelephonyManager.CALL_STATE_RINGING -> {
|
||||
Log.d(TAG, "去电接通")
|
||||
false
|
||||
}
|
||||
else -> {
|
||||
Log.d(TAG, "来电接通")
|
||||
true
|
||||
}
|
||||
}
|
||||
TelephonyManager.CALL_STATE_IDLE ->
|
||||
//Went to idle- this is the end of a call. What type depends on previous state(s)
|
||||
when {
|
||||
lastState == TelephonyManager.CALL_STATE_RINGING -> {
|
||||
Log.d(TAG, "来电未接")
|
||||
sendReceiveCallMsg(context, 3, callSavedNumber)
|
||||
}
|
||||
callIsIncoming -> {
|
||||
Log.d(TAG, "来电挂机")
|
||||
sendReceiveCallMsg(context, 1, callSavedNumber)
|
||||
}
|
||||
else -> {
|
||||
Log.d(TAG, "去电挂机")
|
||||
sendReceiveCallMsg(context, 2, callSavedNumber)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun sendReceiveCallMsg(context: Context, callType: Int, phoneNumber: String?) {
|
||||
//必须休眠才能获取来电记录,否则可能获取到上一次通话的
|
||||
Thread.sleep(500)
|
||||
//获取后一条通话记录
|
||||
Log.d(TAG, "callType = $callType, phoneNumber = $phoneNumber")
|
||||
val callInfo: CallInfo? = PhoneUtils.getLastCallInfo(callType, phoneNumber)
|
||||
Log.d(TAG, "callInfo = $callInfo")
|
||||
if (callInfo?.number == null) return
|
||||
|
||||
//判断是否开启该类型转发
|
||||
if ((callInfo.type == 1 && !SettingUtils.enableCallType1) || (callInfo.type == 2 && !SettingUtils.enableCallType2) || (callInfo.type == 3 && !SettingUtils.enableCallType3)) {
|
||||
Log.w(TAG, "未开启该类型转发,type=" + callInfo.type)
|
||||
return
|
||||
}
|
||||
|
||||
//卡槽id:-1=获取失败、0=卡槽1、1=卡槽2
|
||||
val simSlot = callInfo.simId
|
||||
//获取卡槽信息
|
||||
val simInfo = when (simSlot) {
|
||||
0 -> "SIM1_" + SettingUtils.extraSim1
|
||||
1 -> "SIM2_" + SettingUtils.extraSim2
|
||||
else -> ""
|
||||
}
|
||||
|
||||
//获取联系人姓名
|
||||
if (TextUtils.isEmpty(callInfo.name)) {
|
||||
val contacts = PhoneUtils.getContactByNumber(phoneNumber)
|
||||
callInfo.name = if (contacts.isNotEmpty()) contacts[0].name else getString(R.string.unknown_number)
|
||||
}
|
||||
|
||||
val msgInfo = MsgInfo(
|
||||
"call", callInfo.number, PhoneUtils.getCallMsg(callInfo), Date(), simInfo, simSlot
|
||||
)
|
||||
val request = OneTimeWorkRequestBuilder<SendWorker>().setInputData(
|
||||
workDataOf(
|
||||
Worker.sendMsgInfo to Gson().toJson(msgInfo)
|
||||
)
|
||||
).build()
|
||||
WorkManager.getInstance(context).enqueue(request)
|
||||
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "PhoneStateReceiver"
|
||||
}
|
||||
}
|
|
@ -1,35 +1,35 @@
|
|||
package com.idormy.sms.forwarder.server.component
|
||||
|
||||
import android.content.Context
|
||||
import com.idormy.sms.forwarder.utils.HttpServerUtils
|
||||
import com.xuexiang.xrouter.utils.TextUtils
|
||||
import com.yanzhenjie.andserver.annotation.Config
|
||||
import com.yanzhenjie.andserver.framework.config.WebConfig
|
||||
import com.yanzhenjie.andserver.framework.website.AssetsWebsite
|
||||
import com.yanzhenjie.andserver.framework.website.StorageWebsite
|
||||
|
||||
@Config
|
||||
class AppConfig : WebConfig {
|
||||
|
||||
override fun onConfig(context: Context, delegate: WebConfig.Delegate) {
|
||||
|
||||
val serverWebPath = HttpServerUtils.serverWebPath
|
||||
if (!TextUtils.isEmpty(serverWebPath)) {
|
||||
// 增加一个位于/sdcard/Download/目录下的网站
|
||||
delegate.addWebsite(StorageWebsite(serverWebPath.toString()))
|
||||
} else {
|
||||
// 增加一个位于assets的web目录的网站
|
||||
delegate.addWebsite(AssetsWebsite(context, "/web/"))
|
||||
}
|
||||
|
||||
/*delegate.setMultipart(
|
||||
Multipart.newBuilder()
|
||||
.allFileMaxSize(1024 * 1024 * 20) // 单个请求所有文件总大小
|
||||
.fileMaxSize(1024 * 1024 * 5) // 单个请求每个文件大小
|
||||
.maxInMemorySize(1024 * 20) // 内存缓存大小
|
||||
.uploadTempDir(context.cacheDir) // 上传文件保存目录
|
||||
.build()
|
||||
)*/
|
||||
}
|
||||
|
||||
package com.idormy.sms.forwarder.server.component
|
||||
|
||||
import android.content.Context
|
||||
import com.idormy.sms.forwarder.utils.HttpServerUtils
|
||||
import com.xuexiang.xrouter.utils.TextUtils
|
||||
import com.yanzhenjie.andserver.annotation.Config
|
||||
import com.yanzhenjie.andserver.framework.config.WebConfig
|
||||
import com.yanzhenjie.andserver.framework.website.AssetsWebsite
|
||||
import com.yanzhenjie.andserver.framework.website.StorageWebsite
|
||||
|
||||
@Config
|
||||
class AppConfig : WebConfig {
|
||||
|
||||
override fun onConfig(context: Context, delegate: WebConfig.Delegate) {
|
||||
|
||||
val serverWebPath = HttpServerUtils.serverWebPath
|
||||
if (!TextUtils.isEmpty(serverWebPath)) {
|
||||
// 增加一个位于/sdcard/Download/目录下的网站
|
||||
delegate.addWebsite(StorageWebsite(serverWebPath))
|
||||
} else {
|
||||
// 增加一个位于assets的web目录的网站
|
||||
delegate.addWebsite(AssetsWebsite(context, "/web/"))
|
||||
}
|
||||
|
||||
/*delegate.setMultipart(
|
||||
Multipart.newBuilder()
|
||||
.allFileMaxSize(1024 * 1024 * 20) // 单个请求所有文件总大小
|
||||
.fileMaxSize(1024 * 1024 * 5) // 单个请求每个文件大小
|
||||
.maxInMemorySize(1024 * 20) // 内存缓存大小
|
||||
.uploadTempDir(context.cacheDir) // 上传文件保存目录
|
||||
.build()
|
||||
)*/
|
||||
}
|
||||
|
||||
}
|
|
@ -1,56 +1,56 @@
|
|||
package com.idormy.sms.forwarder.server.component
|
||||
|
||||
import android.util.Log
|
||||
import com.idormy.sms.forwarder.utils.Base64
|
||||
import com.idormy.sms.forwarder.utils.HttpServerUtils
|
||||
import com.idormy.sms.forwarder.utils.RSACrypt
|
||||
import com.idormy.sms.forwarder.utils.SM4Crypt
|
||||
import com.xuexiang.xutil.data.ConvertTools
|
||||
import com.yanzhenjie.andserver.annotation.Resolver
|
||||
import com.yanzhenjie.andserver.error.HttpException
|
||||
import com.yanzhenjie.andserver.framework.ExceptionResolver
|
||||
import com.yanzhenjie.andserver.framework.body.JsonBody
|
||||
import com.yanzhenjie.andserver.framework.body.StringBody
|
||||
import com.yanzhenjie.andserver.http.HttpRequest
|
||||
import com.yanzhenjie.andserver.http.HttpResponse
|
||||
import com.yanzhenjie.andserver.http.StatusCode
|
||||
|
||||
@Suppress("PrivatePropertyName")
|
||||
@Resolver
|
||||
class AppExceptionResolver : ExceptionResolver {
|
||||
|
||||
private val TAG: String = "AppExceptionResolver"
|
||||
|
||||
override fun onResolve(request: HttpRequest, response: HttpResponse, e: Throwable) {
|
||||
e.printStackTrace()
|
||||
if (e is HttpException) {
|
||||
//response.status = e.statusCode
|
||||
//异常捕获返回 http 200
|
||||
response.status = StatusCode.SC_OK
|
||||
} else {
|
||||
response.status = StatusCode.SC_INTERNAL_SERVER_ERROR
|
||||
}
|
||||
|
||||
//返回统一结构报文
|
||||
var resp = HttpServerUtils.response(e.message.toString())
|
||||
Log.d(TAG, "resp: $resp")
|
||||
when (HttpServerUtils.safetyMeasures) {
|
||||
2 -> {
|
||||
val privateKey = RSACrypt.getPrivateKey(HttpServerUtils.serverPrivateKey.toString())
|
||||
resp = Base64.encode(resp.toByteArray())
|
||||
resp = RSACrypt.encryptByPrivateKey(resp, privateKey)
|
||||
response.setBody(StringBody(resp))
|
||||
}
|
||||
3 -> {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.serverSm4Key.toString())
|
||||
//response = Base64.encode(response.toByteArray())
|
||||
val encryptCBC = SM4Crypt.encrypt(resp.toByteArray(), sm4Key)
|
||||
response.setBody(StringBody(ConvertTools.bytes2HexString(encryptCBC)))
|
||||
}
|
||||
else -> {
|
||||
response.setBody(JsonBody(resp))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
package com.idormy.sms.forwarder.server.component
|
||||
|
||||
import android.util.Log
|
||||
import com.idormy.sms.forwarder.utils.Base64
|
||||
import com.idormy.sms.forwarder.utils.HttpServerUtils
|
||||
import com.idormy.sms.forwarder.utils.RSACrypt
|
||||
import com.idormy.sms.forwarder.utils.SM4Crypt
|
||||
import com.xuexiang.xutil.data.ConvertTools
|
||||
import com.yanzhenjie.andserver.annotation.Resolver
|
||||
import com.yanzhenjie.andserver.error.HttpException
|
||||
import com.yanzhenjie.andserver.framework.ExceptionResolver
|
||||
import com.yanzhenjie.andserver.framework.body.JsonBody
|
||||
import com.yanzhenjie.andserver.framework.body.StringBody
|
||||
import com.yanzhenjie.andserver.http.HttpRequest
|
||||
import com.yanzhenjie.andserver.http.HttpResponse
|
||||
import com.yanzhenjie.andserver.http.StatusCode
|
||||
|
||||
@Suppress("PrivatePropertyName")
|
||||
@Resolver
|
||||
class AppExceptionResolver : ExceptionResolver {
|
||||
|
||||
private val TAG: String = "AppExceptionResolver"
|
||||
|
||||
override fun onResolve(request: HttpRequest, response: HttpResponse, e: Throwable) {
|
||||
e.printStackTrace()
|
||||
if (e is HttpException) {
|
||||
//response.status = e.statusCode
|
||||
//异常捕获返回 http 200
|
||||
response.status = StatusCode.SC_OK
|
||||
} else {
|
||||
response.status = StatusCode.SC_INTERNAL_SERVER_ERROR
|
||||
}
|
||||
|
||||
//返回统一结构报文
|
||||
var resp = HttpServerUtils.response(e.message.toString())
|
||||
Log.d(TAG, "resp: $resp")
|
||||
when (HttpServerUtils.safetyMeasures) {
|
||||
2 -> {
|
||||
val privateKey = RSACrypt.getPrivateKey(HttpServerUtils.serverPrivateKey)
|
||||
resp = Base64.encode(resp.toByteArray())
|
||||
resp = RSACrypt.encryptByPrivateKey(resp, privateKey)
|
||||
response.setBody(StringBody(resp))
|
||||
}
|
||||
3 -> {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.serverSm4Key)
|
||||
//response = Base64.encode(response.toByteArray())
|
||||
val encryptCBC = SM4Crypt.encrypt(resp.toByteArray(), sm4Key)
|
||||
response.setBody(StringBody(ConvertTools.bytes2HexString(encryptCBC)))
|
||||
}
|
||||
else -> {
|
||||
response.setBody(JsonBody(resp))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,102 +1,102 @@
|
|||
package com.idormy.sms.forwarder.server.component
|
||||
|
||||
import android.util.Log
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.idormy.sms.forwarder.server.model.BaseRequest
|
||||
import com.idormy.sms.forwarder.utils.Base64
|
||||
import com.idormy.sms.forwarder.utils.HttpServerUtils
|
||||
import com.idormy.sms.forwarder.utils.RSACrypt
|
||||
import com.idormy.sms.forwarder.utils.SM4Crypt
|
||||
import com.xuexiang.xrouter.utils.TextUtils
|
||||
import com.xuexiang.xutil.data.ConvertTools
|
||||
import com.yanzhenjie.andserver.annotation.Converter
|
||||
import com.yanzhenjie.andserver.error.HttpException
|
||||
import com.yanzhenjie.andserver.framework.MessageConverter
|
||||
import com.yanzhenjie.andserver.framework.body.JsonBody
|
||||
import com.yanzhenjie.andserver.framework.body.StringBody
|
||||
import com.yanzhenjie.andserver.http.ResponseBody
|
||||
import com.yanzhenjie.andserver.util.IOUtils
|
||||
import com.yanzhenjie.andserver.util.MediaType
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.lang.reflect.Type
|
||||
import java.nio.charset.Charset
|
||||
|
||||
|
||||
@Suppress("PrivatePropertyName")
|
||||
@Converter
|
||||
class AppMessageConverter : MessageConverter {
|
||||
|
||||
private val TAG: String = "AppMessageConverter"
|
||||
|
||||
override fun convert(output: Any?, mediaType: MediaType?): ResponseBody {
|
||||
//返回统一结构报文
|
||||
var response = HttpServerUtils.response(output)
|
||||
Log.d(TAG, "response: $response")
|
||||
|
||||
return when (HttpServerUtils.safetyMeasures) {
|
||||
2 -> {
|
||||
val privateKey = RSACrypt.getPrivateKey(HttpServerUtils.serverPrivateKey.toString())
|
||||
response = Base64.encode(response.toByteArray())
|
||||
response = RSACrypt.encryptByPrivateKey(response, privateKey)
|
||||
StringBody(response)
|
||||
}
|
||||
3 -> {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.serverSm4Key.toString())
|
||||
//response = Base64.encode(response.toByteArray())
|
||||
val encryptCBC = SM4Crypt.encrypt(response.toByteArray(), sm4Key)
|
||||
StringBody(ConvertTools.bytes2HexString(encryptCBC))
|
||||
}
|
||||
else -> JsonBody(response)
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
override fun <T : Any?> convert(stream: InputStream, mediaType: MediaType?, type: Type?): T? {
|
||||
val charset: Charset? = mediaType?.charset
|
||||
Log.d(TAG, "Charset: $charset")
|
||||
|
||||
var json = if (charset == null) IOUtils.toString(stream) else IOUtils.toString(stream, charset)
|
||||
Log.d(TAG, "Json: $json")
|
||||
|
||||
if (HttpServerUtils.safetyMeasures == 2) {
|
||||
if (TextUtils.isEmpty(HttpServerUtils.serverPrivateKey)) {
|
||||
Log.e(TAG, "RSA解密失败: 私钥为空")
|
||||
throw HttpException(500, "服务端未配置私钥")
|
||||
}
|
||||
|
||||
val privateKey = RSACrypt.getPrivateKey(HttpServerUtils.serverPrivateKey.toString())
|
||||
json = RSACrypt.decryptByPrivateKey(json, privateKey)
|
||||
json = String(Base64.decode(json))
|
||||
Log.d(TAG, "Json: $json")
|
||||
} else if (HttpServerUtils.safetyMeasures == 3) {
|
||||
if (TextUtils.isEmpty(HttpServerUtils.serverSm4Key)) {
|
||||
Log.e(TAG, "SM4解密失败: SM4密钥为空")
|
||||
throw HttpException(500, "服务端未配置SM4密钥")
|
||||
}
|
||||
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.serverSm4Key.toString())
|
||||
val encryptCBC = ConvertTools.hexStringToByteArray(json)
|
||||
val decryptCBC = SM4Crypt.decrypt(encryptCBC, sm4Key)
|
||||
//json = String(Base64.decode(decryptCBC.toString()))
|
||||
json = String(decryptCBC)
|
||||
Log.d(TAG, "Json: $json")
|
||||
}
|
||||
|
||||
//修改接口数据中的null、“”为默认值
|
||||
val builder = GsonBuilder()
|
||||
builder.registerTypeAdapter(Int::class.java, IntegerDefaultAdapter())
|
||||
builder.registerTypeAdapter(String::class.java, StringDefaultAdapter())
|
||||
val gson = builder.create()
|
||||
val t: T? = gson.fromJson(json, type)
|
||||
Log.d(TAG, "Bean: $t")
|
||||
|
||||
//校验时间戳(时间误差不能超过1小时)&& 签名
|
||||
if (HttpServerUtils.safetyMeasures == 1) {
|
||||
HttpServerUtils.checkSign(t as BaseRequest<*>)
|
||||
}
|
||||
|
||||
return t
|
||||
}
|
||||
|
||||
package com.idormy.sms.forwarder.server.component
|
||||
|
||||
import android.util.Log
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.idormy.sms.forwarder.server.model.BaseRequest
|
||||
import com.idormy.sms.forwarder.utils.Base64
|
||||
import com.idormy.sms.forwarder.utils.HttpServerUtils
|
||||
import com.idormy.sms.forwarder.utils.RSACrypt
|
||||
import com.idormy.sms.forwarder.utils.SM4Crypt
|
||||
import com.xuexiang.xrouter.utils.TextUtils
|
||||
import com.xuexiang.xutil.data.ConvertTools
|
||||
import com.yanzhenjie.andserver.annotation.Converter
|
||||
import com.yanzhenjie.andserver.error.HttpException
|
||||
import com.yanzhenjie.andserver.framework.MessageConverter
|
||||
import com.yanzhenjie.andserver.framework.body.JsonBody
|
||||
import com.yanzhenjie.andserver.framework.body.StringBody
|
||||
import com.yanzhenjie.andserver.http.ResponseBody
|
||||
import com.yanzhenjie.andserver.util.IOUtils
|
||||
import com.yanzhenjie.andserver.util.MediaType
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.lang.reflect.Type
|
||||
import java.nio.charset.Charset
|
||||
|
||||
|
||||
@Suppress("PrivatePropertyName")
|
||||
@Converter
|
||||
class AppMessageConverter : MessageConverter {
|
||||
|
||||
private val TAG: String = "AppMessageConverter"
|
||||
|
||||
override fun convert(output: Any?, mediaType: MediaType?): ResponseBody {
|
||||
//返回统一结构报文
|
||||
var response = HttpServerUtils.response(output)
|
||||
Log.d(TAG, "response: $response")
|
||||
|
||||
return when (HttpServerUtils.safetyMeasures) {
|
||||
2 -> {
|
||||
val privateKey = RSACrypt.getPrivateKey(HttpServerUtils.serverPrivateKey)
|
||||
response = Base64.encode(response.toByteArray())
|
||||
response = RSACrypt.encryptByPrivateKey(response, privateKey)
|
||||
StringBody(response)
|
||||
}
|
||||
3 -> {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.serverSm4Key)
|
||||
//response = Base64.encode(response.toByteArray())
|
||||
val encryptCBC = SM4Crypt.encrypt(response.toByteArray(), sm4Key)
|
||||
StringBody(ConvertTools.bytes2HexString(encryptCBC))
|
||||
}
|
||||
else -> JsonBody(response)
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
override fun <T : Any?> convert(stream: InputStream, mediaType: MediaType?, type: Type?): T? {
|
||||
val charset: Charset? = mediaType?.charset
|
||||
Log.d(TAG, "Charset: $charset")
|
||||
|
||||
var json = if (charset == null) IOUtils.toString(stream) else IOUtils.toString(stream, charset)
|
||||
Log.d(TAG, "Json: $json")
|
||||
|
||||
if (HttpServerUtils.safetyMeasures == 2) {
|
||||
if (TextUtils.isEmpty(HttpServerUtils.serverPrivateKey)) {
|
||||
Log.e(TAG, "RSA解密失败: 私钥为空")
|
||||
throw HttpException(500, "服务端未配置私钥")
|
||||
}
|
||||
|
||||
val privateKey = RSACrypt.getPrivateKey(HttpServerUtils.serverPrivateKey)
|
||||
json = RSACrypt.decryptByPrivateKey(json, privateKey)
|
||||
json = String(Base64.decode(json))
|
||||
Log.d(TAG, "Json: $json")
|
||||
} else if (HttpServerUtils.safetyMeasures == 3) {
|
||||
if (TextUtils.isEmpty(HttpServerUtils.serverSm4Key)) {
|
||||
Log.e(TAG, "SM4解密失败: SM4密钥为空")
|
||||
throw HttpException(500, "服务端未配置SM4密钥")
|
||||
}
|
||||
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.serverSm4Key)
|
||||
val encryptCBC = ConvertTools.hexStringToByteArray(json)
|
||||
val decryptCBC = SM4Crypt.decrypt(encryptCBC, sm4Key)
|
||||
//json = String(Base64.decode(decryptCBC.toString()))
|
||||
json = String(decryptCBC)
|
||||
Log.d(TAG, "Json: $json")
|
||||
}
|
||||
|
||||
//修改接口数据中的null、“”为默认值
|
||||
val builder = GsonBuilder()
|
||||
builder.registerTypeAdapter(Int::class.java, IntegerDefaultAdapter())
|
||||
builder.registerTypeAdapter(String::class.java, StringDefaultAdapter())
|
||||
val gson = builder.create()
|
||||
val t: T? = gson.fromJson(json, type)
|
||||
Log.d(TAG, "Bean: $t")
|
||||
|
||||
//校验时间戳(时间误差不能超过1小时)&& 签名
|
||||
if (HttpServerUtils.safetyMeasures == 1) {
|
||||
HttpServerUtils.checkSign(t as BaseRequest<*>)
|
||||
}
|
||||
|
||||
return t
|
||||
}
|
||||
|
||||
}
|
|
@ -1,49 +1,49 @@
|
|||
package com.idormy.sms.forwarder.server.controller
|
||||
|
||||
import android.util.Log
|
||||
import com.idormy.sms.forwarder.App
|
||||
import com.idormy.sms.forwarder.server.model.BaseRequest
|
||||
import com.idormy.sms.forwarder.server.model.ConfigData
|
||||
import com.idormy.sms.forwarder.utils.HttpServerUtils
|
||||
import com.idormy.sms.forwarder.utils.PhoneUtils
|
||||
import com.idormy.sms.forwarder.utils.SettingUtils
|
||||
import com.xuexiang.xutil.app.AppUtils
|
||||
import com.yanzhenjie.andserver.annotation.*
|
||||
|
||||
@Suppress("PrivatePropertyName")
|
||||
@RestController
|
||||
@RequestMapping(path = ["/config"])
|
||||
class ConfigController {
|
||||
|
||||
private val TAG: String = CloneController::class.java.simpleName
|
||||
|
||||
//远程查配置
|
||||
@CrossOrigin(methods = [RequestMethod.POST])
|
||||
@PostMapping("/query")
|
||||
fun test(@RequestBody bean: BaseRequest<*>): ConfigData {
|
||||
Log.d(TAG, bean.data.toString())
|
||||
|
||||
//获取卡槽信息
|
||||
if (App.SimInfoList.isEmpty()) {
|
||||
App.SimInfoList = PhoneUtils.getSimMultiInfo()
|
||||
}
|
||||
Log.d(TAG, App.SimInfoList.toString())
|
||||
|
||||
return ConfigData(
|
||||
HttpServerUtils.enableApiClone,
|
||||
HttpServerUtils.enableApiSmsSend,
|
||||
HttpServerUtils.enableApiSmsQuery,
|
||||
HttpServerUtils.enableApiCallQuery,
|
||||
HttpServerUtils.enableApiContactQuery,
|
||||
HttpServerUtils.enableApiBatteryQuery,
|
||||
HttpServerUtils.enableApiWol,
|
||||
SettingUtils.extraDeviceMark.toString(),
|
||||
SettingUtils.extraSim1.toString(),
|
||||
SettingUtils.extraSim2.toString(),
|
||||
App.SimInfoList,
|
||||
AppUtils.getAppVersionCode(),
|
||||
AppUtils.getAppVersionName(),
|
||||
)
|
||||
}
|
||||
|
||||
package com.idormy.sms.forwarder.server.controller
|
||||
|
||||
import android.util.Log
|
||||
import com.idormy.sms.forwarder.App
|
||||
import com.idormy.sms.forwarder.server.model.BaseRequest
|
||||
import com.idormy.sms.forwarder.server.model.ConfigData
|
||||
import com.idormy.sms.forwarder.utils.HttpServerUtils
|
||||
import com.idormy.sms.forwarder.utils.PhoneUtils
|
||||
import com.idormy.sms.forwarder.utils.SettingUtils
|
||||
import com.xuexiang.xutil.app.AppUtils
|
||||
import com.yanzhenjie.andserver.annotation.*
|
||||
|
||||
@Suppress("PrivatePropertyName")
|
||||
@RestController
|
||||
@RequestMapping(path = ["/config"])
|
||||
class ConfigController {
|
||||
|
||||
private val TAG: String = CloneController::class.java.simpleName
|
||||
|
||||
//远程查配置
|
||||
@CrossOrigin(methods = [RequestMethod.POST])
|
||||
@PostMapping("/query")
|
||||
fun test(@RequestBody bean: BaseRequest<*>): ConfigData {
|
||||
Log.d(TAG, bean.data.toString())
|
||||
|
||||
//获取卡槽信息
|
||||
if (App.SimInfoList.isEmpty()) {
|
||||
App.SimInfoList = PhoneUtils.getSimMultiInfo()
|
||||
}
|
||||
Log.d(TAG, App.SimInfoList.toString())
|
||||
|
||||
return ConfigData(
|
||||
HttpServerUtils.enableApiClone,
|
||||
HttpServerUtils.enableApiSmsSend,
|
||||
HttpServerUtils.enableApiSmsQuery,
|
||||
HttpServerUtils.enableApiCallQuery,
|
||||
HttpServerUtils.enableApiContactQuery,
|
||||
HttpServerUtils.enableApiBatteryQuery,
|
||||
HttpServerUtils.enableApiWol,
|
||||
SettingUtils.extraDeviceMark,
|
||||
SettingUtils.extraSim1,
|
||||
SettingUtils.extraSim2,
|
||||
App.SimInfoList,
|
||||
AppUtils.getAppVersionCode(),
|
||||
AppUtils.getAppVersionName(),
|
||||
)
|
||||
}
|
||||
|
||||
}
|
|
@ -1,32 +1,15 @@
|
|||
package com.idormy.sms.forwarder.utils
|
||||
|
||||
object CactusSave {
|
||||
//Cactus存活时间
|
||||
var timer: Long
|
||||
get() = MMKVUtils.getLong(CACTUS_TIMER, 0L)
|
||||
set(timer) {
|
||||
MMKVUtils.put(CACTUS_TIMER, timer)
|
||||
}
|
||||
|
||||
//Cactus上次存活时间
|
||||
var lastTimer: Long
|
||||
get() = MMKVUtils.getLong(CACTUS_LAST_TIMER, 0L)
|
||||
set(timer) {
|
||||
MMKVUtils.put(CACTUS_LAST_TIMER, timer)
|
||||
}
|
||||
|
||||
//Cactus运行时间
|
||||
var date: String?
|
||||
get() = MMKVUtils.getString(SP_EXTRA_DEVICE_MARK, "0000-01-01 00:00:00")
|
||||
set(extraDeviceMark) {
|
||||
MMKVUtils.put(SP_EXTRA_DEVICE_MARK, extraDeviceMark)
|
||||
}
|
||||
|
||||
//Cactus结束时间
|
||||
var endDate: String?
|
||||
get() = MMKVUtils.getString(CACTUS_DATE, "0000-01-01 00:00:00")
|
||||
set(extraDeviceMark) {
|
||||
MMKVUtils.put(CACTUS_END_DATE, extraDeviceMark)
|
||||
}
|
||||
|
||||
package com.idormy.sms.forwarder.utils
|
||||
|
||||
object CactusSave {
|
||||
//Cactus存活时间
|
||||
var timer: Long by SharedPreference(CACTUS_TIMER, 0L)
|
||||
|
||||
//Cactus上次存活时间
|
||||
var lastTimer: Long by SharedPreference(CACTUS_LAST_TIMER, 0L)
|
||||
|
||||
//Cactus运行时间
|
||||
var date: String by SharedPreference(SP_EXTRA_DEVICE_MARK, "0000-01-01 00:00:00")
|
||||
|
||||
//Cactus结束时间
|
||||
var endDate: String by SharedPreference(CACTUS_DATE, "0000-01-01 00:00:00")
|
||||
}
|
|
@ -1,8 +1,11 @@
|
|||
package com.idormy.sms.forwarder.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Parcelable
|
||||
import com.tencent.mmkv.MMKV
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Build
|
||||
import java.io.*
|
||||
import kotlin.properties.ReadWriteProperty
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
/**
|
||||
* 转发历史工具类
|
||||
|
@ -11,264 +14,102 @@ import com.tencent.mmkv.MMKV
|
|||
* @since 2022年5月9日
|
||||
*/
|
||||
@Suppress("PropertyName", "UNCHECKED_CAST", "MemberVisibilityCanBePrivate", "unused")
|
||||
class HistoryUtils private constructor() {
|
||||
class HistoryUtils<T>(private val name: String, private val default: T) : ReadWriteProperty<Any?, T> {
|
||||
|
||||
companion object {
|
||||
private var sMMKV: MMKV? = null
|
||||
lateinit var preference: SharedPreferences
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
*
|
||||
* @param context
|
||||
*/
|
||||
fun init(context: Context) {
|
||||
MMKV.initialize(context.applicationContext)
|
||||
sMMKV = MMKV.mmkvWithID("History")
|
||||
}
|
||||
|
||||
fun getsMMKV(): MMKV? {
|
||||
if (sMMKV == null) {
|
||||
sMMKV = MMKV.mmkvWithID("History")
|
||||
}
|
||||
return sMMKV
|
||||
}
|
||||
//=======================================键值保存==================================================//
|
||||
/**
|
||||
* 保存键值
|
||||
*
|
||||
* @param key
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
fun put(key: String?, value: Any?): Boolean {
|
||||
when (value) {
|
||||
is Int -> {
|
||||
return getsMMKV()!!.encode(key, (value as Int?)!!)
|
||||
}
|
||||
is Float -> {
|
||||
return getsMMKV()!!.encode(key, (value as Float?)!!)
|
||||
}
|
||||
is String -> {
|
||||
return getsMMKV()!!.encode(key, value as String?)
|
||||
}
|
||||
is Boolean -> {
|
||||
return getsMMKV()!!.encode(key, (value as Boolean?)!!)
|
||||
}
|
||||
is Long -> {
|
||||
return getsMMKV()!!.encode(key, (value as Long?)!!)
|
||||
}
|
||||
is Double -> {
|
||||
return getsMMKV()!!.encode(key, (value as Double?)!!)
|
||||
}
|
||||
is Parcelable -> {
|
||||
return getsMMKV()!!.encode(key, value as Parcelable?)
|
||||
}
|
||||
is ByteArray -> {
|
||||
return getsMMKV()!!.encode(key, value as ByteArray?)
|
||||
}
|
||||
is Set<*> -> {
|
||||
return getsMMKV()!!.encode(key, value as Set<String?>?)
|
||||
}
|
||||
else -> return false
|
||||
}
|
||||
}
|
||||
//=======================================键值获取==================================================//
|
||||
/**
|
||||
* 获取键值
|
||||
*
|
||||
* @param key
|
||||
* @param defaultValue
|
||||
* @return
|
||||
*/
|
||||
operator fun get(key: String?, defaultValue: Any?): Any? {
|
||||
when (defaultValue) {
|
||||
is Int -> {
|
||||
return getsMMKV()!!
|
||||
.decodeInt(key, (defaultValue as Int?)!!)
|
||||
}
|
||||
is Float -> {
|
||||
return getsMMKV()!!
|
||||
.decodeFloat(key, (defaultValue as Float?)!!)
|
||||
}
|
||||
is String -> {
|
||||
return getsMMKV()!!.decodeString(key, defaultValue as String?)
|
||||
}
|
||||
is Boolean -> {
|
||||
return getsMMKV()!!
|
||||
.decodeBool(key, (defaultValue as Boolean?)!!)
|
||||
}
|
||||
is Long -> {
|
||||
return getsMMKV()!!
|
||||
.decodeLong(key, (defaultValue as Long?)!!)
|
||||
}
|
||||
is Double -> {
|
||||
return getsMMKV()!!
|
||||
.decodeDouble(key, (defaultValue as Double?)!!)
|
||||
}
|
||||
is ByteArray -> {
|
||||
return getsMMKV()!!.decodeBytes(key)
|
||||
}
|
||||
is Set<*> -> {
|
||||
return getsMMKV()!!.decodeStringSet(key, defaultValue as Set<String?>?)
|
||||
}
|
||||
else -> return null
|
||||
preference = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
val directBootContext: Context = context.createDeviceProtectedStorageContext()
|
||||
directBootContext.getSharedPreferences(context.packageName + ".history", Context.MODE_PRIVATE)
|
||||
} else {
|
||||
context.getSharedPreferences(context.packageName + ".history", Context.MODE_PRIVATE)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据key获取boolean值
|
||||
*
|
||||
* @param key
|
||||
* @param defValue
|
||||
* @return
|
||||
*/
|
||||
fun getBoolean(key: String?, defValue: Boolean): Boolean {
|
||||
try {
|
||||
return getsMMKV()!!.getBoolean(key, defValue)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
return defValue
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据key获取long值
|
||||
*
|
||||
* @param key
|
||||
* @param defValue
|
||||
* @return
|
||||
*/
|
||||
fun getLong(key: String?, defValue: Long): Long {
|
||||
try {
|
||||
return getsMMKV()!!.getLong(key, defValue)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
return defValue
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据key获取float值
|
||||
*
|
||||
* @param key
|
||||
* @param defValue
|
||||
* @return
|
||||
*/
|
||||
fun getFloat(key: String?, defValue: Float): Float {
|
||||
try {
|
||||
return getsMMKV()!!.getFloat(key, defValue)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
return defValue
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据key获取String值
|
||||
*
|
||||
* @param key
|
||||
* @param defValue
|
||||
* @return
|
||||
*/
|
||||
fun getString(key: String?, defValue: String?): String? {
|
||||
try {
|
||||
return getsMMKV()!!.getString(key, defValue)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
return defValue
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据key获取int值
|
||||
*
|
||||
* @param key
|
||||
* @param defValue
|
||||
* @return
|
||||
*/
|
||||
fun getInt(key: String?, defValue: Int): Int {
|
||||
try {
|
||||
return getsMMKV()!!.getInt(key, defValue)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
return defValue
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据key获取double值
|
||||
*
|
||||
* @param key
|
||||
* @param defValue
|
||||
* @return
|
||||
*/
|
||||
fun getDouble(key: String?, defValue: Double): Double {
|
||||
try {
|
||||
return getsMMKV()!!.decodeDouble(key, defValue)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
return defValue
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对象
|
||||
*
|
||||
* @param key
|
||||
* @param tClass 类型
|
||||
* @param <T>
|
||||
* @return
|
||||
</T> */
|
||||
fun <T : Parcelable?> getObject(key: String?, tClass: Class<T>?): T? {
|
||||
return getsMMKV()!!.decodeParcelable(key, tClass)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对象
|
||||
*
|
||||
* @param key
|
||||
* @param tClass 类型
|
||||
* @param <T>
|
||||
* @return
|
||||
</T> */
|
||||
fun <T : Parcelable?> getObject(key: String?, tClass: Class<T>?, defValue: T): T? {
|
||||
try {
|
||||
return getsMMKV()!!.decodeParcelable(key, tClass, defValue)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
return defValue
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断键值对是否存在
|
||||
*
|
||||
* @param key 键
|
||||
* @return 键值对是否存在
|
||||
*/
|
||||
fun containsKey(key: String?): Boolean {
|
||||
return getsMMKV()!!.containsKey(key)
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除指定键值对
|
||||
*
|
||||
* @param key 键
|
||||
*/
|
||||
fun remove(key: String?) {
|
||||
getsMMKV()!!.remove(key).apply()
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除所有键值对
|
||||
*/
|
||||
fun clear() {
|
||||
getsMMKV()!!.clearAll()
|
||||
}
|
||||
//删除全部数据
|
||||
fun clearPreference() = preference.edit().clear().apply()
|
||||
|
||||
//根据key删除存储数据
|
||||
fun clearPreference(key: String) = preference.edit().remove(key).commit()
|
||||
}
|
||||
|
||||
init {
|
||||
throw UnsupportedOperationException("u can't instantiate me...")
|
||||
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
|
||||
return putPreference(name, value)
|
||||
}
|
||||
|
||||
override fun getValue(thisRef: Any?, property: KProperty<*>): T {
|
||||
return getPreference(name, default)
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找数据 返回给调用方法一个具体的对象
|
||||
* 如果查找不到类型就采用反序列化方法来返回类型
|
||||
* default是默认对象 以防止会返回空对象的异常
|
||||
* 即如果name没有查找到value 就返回默认的序列化对象,然后经过反序列化返回
|
||||
*/
|
||||
private fun getPreference(name: String, default: T): T = with(preference) {
|
||||
val res: Any = when (default) {
|
||||
is Long -> getLong(name, default)
|
||||
is String -> this.getString(name, default)!!
|
||||
is Int -> getInt(name, default)
|
||||
is Boolean -> getBoolean(name, default)
|
||||
is Float -> getFloat(name, default)
|
||||
//else -> throw IllegalArgumentException("This type can be get from Preferences")
|
||||
else -> deSerialization(getString(name, serialize(default)).toString())
|
||||
}
|
||||
return res as T
|
||||
}
|
||||
|
||||
private fun putPreference(name: String, value: T) = with(preference.edit()) {
|
||||
when (value) {
|
||||
is Long -> putLong(name, value)
|
||||
is Int -> putInt(name, value)
|
||||
is String -> putString(name, value)
|
||||
is Boolean -> putBoolean(name, value)
|
||||
is Float -> putFloat(name, value)
|
||||
//else -> throw IllegalArgumentException("This type can be saved into Preferences")
|
||||
else -> putString(name, serialize(value))
|
||||
}.apply()
|
||||
}
|
||||
|
||||
/**
|
||||
* 序列化对象
|
||||
* @throws IOException
|
||||
*/
|
||||
@Throws(IOException::class)
|
||||
private fun <T> serialize(obj: T): String {
|
||||
val byteArrayOutputStream = ByteArrayOutputStream()
|
||||
val objectOutputStream = ObjectOutputStream(
|
||||
byteArrayOutputStream
|
||||
)
|
||||
objectOutputStream.writeObject(obj)
|
||||
var serStr = byteArrayOutputStream.toString("ISO-8859-1")
|
||||
serStr = java.net.URLEncoder.encode(serStr, "UTF-8")
|
||||
objectOutputStream.close()
|
||||
byteArrayOutputStream.close()
|
||||
return serStr
|
||||
}
|
||||
|
||||
/**
|
||||
* 反序列化对象
|
||||
* @param str
|
||||
* @throws IOException
|
||||
* @throws ClassNotFoundException
|
||||
*/
|
||||
@Throws(IOException::class, ClassNotFoundException::class)
|
||||
private fun <T> deSerialization(str: String): T {
|
||||
val redStr = java.net.URLDecoder.decode(str, "UTF-8")
|
||||
val byteArrayInputStream = ByteArrayInputStream(
|
||||
redStr.toByteArray(charset("ISO-8859-1"))
|
||||
)
|
||||
val objectInputStream = ObjectInputStream(
|
||||
byteArrayInputStream
|
||||
)
|
||||
val obj = objectInputStream.readObject() as T
|
||||
objectInputStream.close()
|
||||
byteArrayInputStream.close()
|
||||
return obj
|
||||
}
|
||||
}
|
|
@ -1,366 +1,261 @@
|
|||
package com.idormy.sms.forwarder.utils
|
||||
|
||||
|
||||
import android.text.TextUtils
|
||||
import android.util.Base64
|
||||
import android.util.Log
|
||||
import com.google.gson.Gson
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.idormy.sms.forwarder.core.Core
|
||||
import com.idormy.sms.forwarder.entity.CloneInfo
|
||||
import com.idormy.sms.forwarder.server.model.BaseRequest
|
||||
import com.xuexiang.xui.utils.ResUtils.getString
|
||||
import com.xuexiang.xutil.app.AppUtils
|
||||
import com.yanzhenjie.andserver.error.HttpException
|
||||
import java.net.URLEncoder
|
||||
import java.nio.charset.StandardCharsets
|
||||
import javax.crypto.Mac
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
|
||||
/**
|
||||
* HttpServer工具类
|
||||
*/
|
||||
class HttpServerUtils private constructor() {
|
||||
|
||||
companion object {
|
||||
|
||||
//是否启用HttpServer开机自启
|
||||
@JvmStatic
|
||||
var enableServerAutorun: Boolean
|
||||
get() = MMKVUtils.getBoolean(SP_ENABLE_SERVER_AUTORUN, false)
|
||||
set(enableServerAutorun) {
|
||||
MMKVUtils.put(SP_ENABLE_SERVER_AUTORUN, enableServerAutorun)
|
||||
}
|
||||
|
||||
//服务端安全设置
|
||||
@JvmStatic
|
||||
var safetyMeasures: Int
|
||||
get() = MMKVUtils.getInt(SP_SERVER_SAFETY_MEASURES, if (TextUtils.isEmpty(serverSignKey)) 0 else 1)
|
||||
set(safetyMeasures) {
|
||||
MMKVUtils.put(SP_SERVER_SAFETY_MEASURES, safetyMeasures)
|
||||
}
|
||||
|
||||
//服务端SM4密钥
|
||||
@JvmStatic
|
||||
var serverSm4Key: String?
|
||||
get() = MMKVUtils.getString(SP_SERVER_SM4_KEY, "")
|
||||
set(serverSm4Key) {
|
||||
MMKVUtils.put(SP_SERVER_SM4_KEY, serverSm4Key)
|
||||
}
|
||||
|
||||
//服务端RSA公钥
|
||||
@JvmStatic
|
||||
var serverPublicKey: String?
|
||||
get() = MMKVUtils.getString(SP_SERVER_PUBLIC_KEY, "")
|
||||
set(serverPublicKey) {
|
||||
MMKVUtils.put(SP_SERVER_PUBLIC_KEY, serverPublicKey)
|
||||
}
|
||||
|
||||
//服务端RSA私钥
|
||||
@JvmStatic
|
||||
var serverPrivateKey: String?
|
||||
get() = MMKVUtils.getString(SP_SERVER_PRIVATE_KEY, "")
|
||||
set(serverPrivateKey) {
|
||||
MMKVUtils.put(SP_SERVER_PRIVATE_KEY, serverPrivateKey)
|
||||
}
|
||||
|
||||
//服务端签名密钥
|
||||
@JvmStatic
|
||||
var serverSignKey: String?
|
||||
get() = MMKVUtils.getString(SP_SERVER_SIGN_KEY, "")
|
||||
set(serverSignKey) {
|
||||
MMKVUtils.put(SP_SERVER_SIGN_KEY, serverSignKey)
|
||||
}
|
||||
|
||||
//时间容差
|
||||
@JvmStatic
|
||||
var timeTolerance: Int
|
||||
get() = MMKVUtils.getInt(SP_SERVER_TIME_TOLERANCE, 600)
|
||||
set(timeTolerance) {
|
||||
MMKVUtils.put(SP_SERVER_TIME_TOLERANCE, timeTolerance)
|
||||
}
|
||||
|
||||
//自定义web客户端目录
|
||||
@JvmStatic
|
||||
var serverWebPath: String?
|
||||
get() = MMKVUtils.getString(SP_SERVER_WEB_PATH, "")
|
||||
set(serverWebPath) {
|
||||
MMKVUtils.put(SP_SERVER_WEB_PATH, serverWebPath)
|
||||
}
|
||||
|
||||
//服务地址
|
||||
@JvmStatic
|
||||
var serverAddress: String?
|
||||
get() = MMKVUtils.getString(SP_SERVER_ADDRESS, "")
|
||||
set(clientSignKey) {
|
||||
MMKVUtils.put(SP_SERVER_ADDRESS, clientSignKey)
|
||||
}
|
||||
|
||||
//服务地址历史记录
|
||||
@JvmStatic
|
||||
var serverHistory: String?
|
||||
get() = MMKVUtils.getString(SP_SERVER_HISTORY, "")
|
||||
set(serverHistory) {
|
||||
MMKVUtils.put(SP_SERVER_HISTORY, serverHistory)
|
||||
}
|
||||
|
||||
//服务端配置
|
||||
@JvmStatic
|
||||
var serverConfig: String?
|
||||
get() = MMKVUtils.getString(SP_SERVER_CONFIG, "")
|
||||
set(serverConfig) {
|
||||
MMKVUtils.put(SP_SERVER_CONFIG, serverConfig)
|
||||
}
|
||||
|
||||
//服务端安全设置
|
||||
@JvmStatic
|
||||
var clientSafetyMeasures: Int
|
||||
get() = MMKVUtils.getInt(SP_CLIENT_SAFETY_MEASURES, if (TextUtils.isEmpty(clientSignKey)) 0 else 1)
|
||||
set(clientSafetyMeasures) {
|
||||
MMKVUtils.put(SP_CLIENT_SAFETY_MEASURES, clientSafetyMeasures)
|
||||
}
|
||||
|
||||
//客户端签名密钥/RSA公钥
|
||||
@JvmStatic
|
||||
var clientSignKey: String?
|
||||
get() = MMKVUtils.getString(SP_CLIENT_SIGN_KEY, "")
|
||||
set(clientSignKey) {
|
||||
MMKVUtils.put(SP_CLIENT_SIGN_KEY, clientSignKey)
|
||||
}
|
||||
|
||||
//是否启用一键克隆
|
||||
@JvmStatic
|
||||
var enableApiClone: Boolean
|
||||
get() = MMKVUtils.getBoolean(SP_ENABLE_API_CLONE, false)
|
||||
set(enableApiClone) {
|
||||
MMKVUtils.put(SP_ENABLE_API_CLONE, enableApiClone)
|
||||
}
|
||||
|
||||
//是否启用远程发短信
|
||||
@JvmStatic
|
||||
var enableApiSmsSend: Boolean
|
||||
get() = MMKVUtils.getBoolean(SP_ENABLE_API_SMS_SEND, false)
|
||||
set(enableApiSendSms) {
|
||||
MMKVUtils.put(SP_ENABLE_API_SMS_SEND, enableApiSendSms)
|
||||
}
|
||||
|
||||
//是否启用远程查短信
|
||||
@JvmStatic
|
||||
var enableApiSmsQuery: Boolean
|
||||
get() = MMKVUtils.getBoolean(SP_ENABLE_API_SMS_QUERY, false)
|
||||
set(enableApiQuerySms) {
|
||||
MMKVUtils.put(SP_ENABLE_API_SMS_QUERY, enableApiQuerySms)
|
||||
}
|
||||
|
||||
//是否启用远程查通话
|
||||
@JvmStatic
|
||||
var enableApiCallQuery: Boolean
|
||||
get() = MMKVUtils.getBoolean(SP_ENABLE_API_CALL_QUERY, false)
|
||||
set(enableApiQueryCall) {
|
||||
MMKVUtils.put(SP_ENABLE_API_CALL_QUERY, enableApiQueryCall)
|
||||
}
|
||||
|
||||
//是否启用远程查话簿
|
||||
@JvmStatic
|
||||
var enableApiContactQuery: Boolean
|
||||
get() = MMKVUtils.getBoolean(SP_ENABLE_API_CONTACT_QUERY, false)
|
||||
set(enableApiQueryLinkman) {
|
||||
MMKVUtils.put(SP_ENABLE_API_CONTACT_QUERY, enableApiQueryLinkman)
|
||||
}
|
||||
|
||||
//是否启用远程查电量
|
||||
@JvmStatic
|
||||
var enableApiBatteryQuery: Boolean
|
||||
get() = MMKVUtils.getBoolean(SP_ENABLE_API_BATTERY_QUERY, false)
|
||||
set(enableApiQueryBattery) {
|
||||
MMKVUtils.put(SP_ENABLE_API_BATTERY_QUERY, enableApiQueryBattery)
|
||||
}
|
||||
|
||||
//是否启用远程WOL
|
||||
@JvmStatic
|
||||
var enableApiWol: Boolean
|
||||
get() = MMKVUtils.getBoolean(SP_ENABLE_API_WOL, false)
|
||||
set(enableApiWol) {
|
||||
MMKVUtils.put(SP_ENABLE_API_WOL, enableApiWol)
|
||||
}
|
||||
|
||||
//WOL历史记录
|
||||
@JvmStatic
|
||||
var wolHistory: String?
|
||||
get() = MMKVUtils.getString(SP_WOL_HISTORY, "")
|
||||
set(wolHistory) {
|
||||
MMKVUtils.put(SP_WOL_HISTORY, wolHistory)
|
||||
}
|
||||
|
||||
//计算签名
|
||||
fun calcSign(timestamp: String, signSecret: String): String {
|
||||
val stringToSign = "$timestamp\n" + signSecret
|
||||
val mac = Mac.getInstance("HmacSHA256")
|
||||
mac.init(SecretKeySpec(signSecret.toByteArray(StandardCharsets.UTF_8), "HmacSHA256"))
|
||||
val signData = mac.doFinal(stringToSign.toByteArray(StandardCharsets.UTF_8))
|
||||
return URLEncoder.encode(String(Base64.encode(signData, Base64.NO_WRAP)), "UTF-8")
|
||||
}
|
||||
|
||||
//校验签名
|
||||
@Throws(HttpException::class)
|
||||
fun checkSign(req: BaseRequest<*>) {
|
||||
val signSecret = serverSignKey
|
||||
if (TextUtils.isEmpty(signSecret)) return
|
||||
|
||||
if (TextUtils.isEmpty(req.sign)) throw HttpException(500, getString(R.string.sign_required))
|
||||
if (req.timestamp == 0L) throw HttpException(500, getString(R.string.timestamp_required))
|
||||
|
||||
val timestamp = System.currentTimeMillis()
|
||||
val diffTime = kotlin.math.abs(timestamp - req.timestamp)
|
||||
val tolerance = timeTolerance * 1000L
|
||||
if (diffTime > tolerance) {
|
||||
throw HttpException(500, String.format(getString(R.string.timestamp_verify_failed), timestamp, timeTolerance, diffTime))
|
||||
}
|
||||
|
||||
val sign = calcSign(req.timestamp.toString(), signSecret.toString())
|
||||
if (sign != req.sign) {
|
||||
Log.e("calcSign", sign)
|
||||
Log.e("reqSign", req.sign.toString())
|
||||
throw HttpException(500, getString(R.string.sign_verify_failed))
|
||||
}
|
||||
}
|
||||
|
||||
//判断版本是否一致
|
||||
@Throws(HttpException::class)
|
||||
fun compareVersion(cloneInfo: CloneInfo) {
|
||||
val versionCodeRequest = cloneInfo.versionCode
|
||||
if (versionCodeRequest == 0) throw HttpException(500, getString(R.string.version_code_required))
|
||||
val versionCodeLocal = AppUtils.getAppVersionCode().toString().substring(1)
|
||||
if (!versionCodeRequest.toString().endsWith(versionCodeLocal)) throw HttpException(500, getString(R.string.inconsistent_version))
|
||||
}
|
||||
|
||||
//导出设置
|
||||
fun exportSettings(): CloneInfo {
|
||||
val cloneInfo = CloneInfo()
|
||||
cloneInfo.versionCode = AppUtils.getAppVersionCode()
|
||||
cloneInfo.versionName = AppUtils.getAppVersionName()
|
||||
cloneInfo.enableSms = SettingUtils.enableSms
|
||||
cloneInfo.enablePhone = SettingUtils.enablePhone
|
||||
cloneInfo.callType1 = SettingUtils.enableCallType1
|
||||
cloneInfo.callType2 = SettingUtils.enableCallType2
|
||||
cloneInfo.callType3 = SettingUtils.enableCallType3
|
||||
cloneInfo.enableAppNotify = SettingUtils.enableAppNotify
|
||||
cloneInfo.cancelAppNotify = SettingUtils.enableCancelAppNotify
|
||||
cloneInfo.enableNotUserPresent = SettingUtils.enableNotUserPresent
|
||||
cloneInfo.enableLoadAppList = SettingUtils.enableLoadAppList
|
||||
cloneInfo.enableLoadUserAppList = SettingUtils.enableLoadUserAppList
|
||||
cloneInfo.enableLoadSystemAppList = SettingUtils.enableLoadSystemAppList
|
||||
cloneInfo.duplicateMessagesLimits = SettingUtils.duplicateMessagesLimits
|
||||
cloneInfo.enableBatteryReceiver = SettingUtils.enableBatteryReceiver
|
||||
cloneInfo.batteryLevelMin = SettingUtils.batteryLevelMin
|
||||
cloneInfo.batteryLevelMax = SettingUtils.batteryLevelMax
|
||||
cloneInfo.batteryLevelOnce = SettingUtils.batteryLevelOnce
|
||||
cloneInfo.enableBatteryCron = SettingUtils.enableBatteryCron
|
||||
cloneInfo.batteryCronStartTime = SettingUtils.batteryCronStartTime
|
||||
cloneInfo.batteryCronInterval = SettingUtils.batteryCronInterval
|
||||
cloneInfo.enableExcludeFromRecents = SettingUtils.enableExcludeFromRecents
|
||||
cloneInfo.enableCactus = SettingUtils.enableCactus
|
||||
cloneInfo.enablePlaySilenceMusic = SettingUtils.enablePlaySilenceMusic
|
||||
cloneInfo.enableOnePixelActivity = SettingUtils.enableOnePixelActivity
|
||||
cloneInfo.requestRetryTimes = SettingUtils.requestRetryTimes
|
||||
cloneInfo.requestDelayTime = SettingUtils.requestDelayTime
|
||||
cloneInfo.requestTimeout = SettingUtils.requestTimeout
|
||||
cloneInfo.notifyContent = SettingUtils.notifyContent
|
||||
cloneInfo.enableSmsTemplate = SettingUtils.enableSmsTemplate
|
||||
cloneInfo.smsTemplate = SettingUtils.smsTemplate
|
||||
cloneInfo.enableHelpTip = SettingUtils.enableHelpTip
|
||||
cloneInfo.enablePureClientMode = SettingUtils.enablePureClientMode
|
||||
cloneInfo.senderList = Core.sender.all
|
||||
cloneInfo.ruleList = Core.rule.all
|
||||
cloneInfo.frpcList = Core.frpc.all
|
||||
|
||||
return cloneInfo
|
||||
}
|
||||
|
||||
//还原设置
|
||||
fun restoreSettings(cloneInfo: CloneInfo): Boolean {
|
||||
return try {
|
||||
//应用配置
|
||||
SettingUtils.enableSms = cloneInfo.enableSms
|
||||
SettingUtils.enablePhone = cloneInfo.enablePhone
|
||||
SettingUtils.enableCallType1 = cloneInfo.callType1
|
||||
SettingUtils.enableCallType2 = cloneInfo.callType2
|
||||
SettingUtils.enableCallType3 = cloneInfo.callType3
|
||||
SettingUtils.enableAppNotify = cloneInfo.enableAppNotify
|
||||
SettingUtils.enableCancelAppNotify = cloneInfo.cancelAppNotify
|
||||
SettingUtils.enableNotUserPresent = cloneInfo.enableNotUserPresent
|
||||
SettingUtils.enableLoadAppList = cloneInfo.enableLoadAppList
|
||||
SettingUtils.enableLoadUserAppList = cloneInfo.enableLoadUserAppList
|
||||
SettingUtils.enableLoadSystemAppList = cloneInfo.enableLoadSystemAppList
|
||||
SettingUtils.duplicateMessagesLimits = cloneInfo.duplicateMessagesLimits
|
||||
SettingUtils.enableBatteryReceiver = cloneInfo.enableBatteryReceiver
|
||||
SettingUtils.batteryLevelMin = cloneInfo.batteryLevelMin
|
||||
SettingUtils.batteryLevelMax = cloneInfo.batteryLevelMax
|
||||
SettingUtils.batteryLevelOnce = cloneInfo.batteryLevelOnce
|
||||
SettingUtils.enableBatteryCron = cloneInfo.enableBatteryCron
|
||||
SettingUtils.batteryCronStartTime = cloneInfo.batteryCronStartTime
|
||||
SettingUtils.batteryCronInterval = cloneInfo.batteryCronInterval
|
||||
SettingUtils.enableExcludeFromRecents = cloneInfo.enableExcludeFromRecents
|
||||
SettingUtils.enableCactus = cloneInfo.enableCactus
|
||||
SettingUtils.enablePlaySilenceMusic = cloneInfo.enablePlaySilenceMusic
|
||||
SettingUtils.enableOnePixelActivity = cloneInfo.enableOnePixelActivity
|
||||
SettingUtils.requestRetryTimes = cloneInfo.requestRetryTimes
|
||||
SettingUtils.requestDelayTime = cloneInfo.requestDelayTime
|
||||
SettingUtils.requestTimeout = cloneInfo.requestTimeout
|
||||
SettingUtils.notifyContent = cloneInfo.notifyContent
|
||||
SettingUtils.enableSmsTemplate = cloneInfo.enableSmsTemplate
|
||||
SettingUtils.smsTemplate = cloneInfo.smsTemplate
|
||||
SettingUtils.enableHelpTip = cloneInfo.enableHelpTip
|
||||
SettingUtils.enablePureClientMode = cloneInfo.enablePureClientMode
|
||||
//删除发送通道、转发规则、转发日志
|
||||
Core.sender.deleteAll()
|
||||
//发送通道
|
||||
if (!cloneInfo.senderList.isNullOrEmpty()) {
|
||||
for (sender in cloneInfo.senderList!!) {
|
||||
Core.sender.insert(sender)
|
||||
}
|
||||
}
|
||||
//转发规则
|
||||
if (!cloneInfo.ruleList.isNullOrEmpty()) {
|
||||
for (rule in cloneInfo.ruleList!!) {
|
||||
Core.rule.insert(rule)
|
||||
}
|
||||
}
|
||||
//Frpc配置
|
||||
Core.frpc.deleteAll()
|
||||
if (!cloneInfo.frpcList.isNullOrEmpty()) {
|
||||
for (frpc in cloneInfo.frpcList!!) {
|
||||
Core.frpc.insert(frpc)
|
||||
}
|
||||
}
|
||||
true
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
throw HttpException(500, e.message)
|
||||
//false
|
||||
}
|
||||
}
|
||||
|
||||
//返回统一结构报文
|
||||
fun response(output: Any?): String {
|
||||
val resp: MutableMap<String, Any> = mutableMapOf()
|
||||
val timestamp = System.currentTimeMillis()
|
||||
resp["timestamp"] = timestamp
|
||||
if (output is String && output != "success") {
|
||||
resp["code"] = HTTP_FAILURE_CODE
|
||||
resp["msg"] = output
|
||||
} else {
|
||||
resp["code"] = HTTP_SUCCESS_CODE
|
||||
resp["msg"] = "success"
|
||||
if (output != null) {
|
||||
resp["data"] = output
|
||||
}
|
||||
if (safetyMeasures == 1) {
|
||||
resp["sign"] = calcSign(timestamp.toString(), serverSignKey.toString())
|
||||
}
|
||||
}
|
||||
|
||||
return Gson().toJson(resp)
|
||||
}
|
||||
}
|
||||
package com.idormy.sms.forwarder.utils
|
||||
|
||||
|
||||
import android.text.TextUtils
|
||||
import android.util.Base64
|
||||
import android.util.Log
|
||||
import com.google.gson.Gson
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.idormy.sms.forwarder.core.Core
|
||||
import com.idormy.sms.forwarder.entity.CloneInfo
|
||||
import com.idormy.sms.forwarder.server.model.BaseRequest
|
||||
import com.xuexiang.xui.utils.ResUtils.getString
|
||||
import com.xuexiang.xutil.app.AppUtils
|
||||
import com.yanzhenjie.andserver.error.HttpException
|
||||
import java.net.URLEncoder
|
||||
import java.nio.charset.StandardCharsets
|
||||
import javax.crypto.Mac
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
|
||||
/**
|
||||
* HttpServer工具类
|
||||
*/
|
||||
class HttpServerUtils private constructor() {
|
||||
|
||||
companion object {
|
||||
|
||||
//是否启用HttpServer开机自启
|
||||
var enableServerAutorun: Boolean by SharedPreference(SP_ENABLE_SERVER_AUTORUN, true)
|
||||
|
||||
//服务端签名密钥
|
||||
var serverSignKey: String by SharedPreference(SP_SERVER_SIGN_KEY, "")
|
||||
|
||||
//服务端安全设置
|
||||
var safetyMeasures: Int by SharedPreference(SP_SERVER_SAFETY_MEASURES, if (TextUtils.isEmpty(serverSignKey)) 0 else 1)
|
||||
|
||||
//服务端SM4密钥
|
||||
var serverSm4Key: String by SharedPreference(SP_SERVER_SM4_KEY, "")
|
||||
|
||||
//服务端RSA公钥
|
||||
var serverPublicKey: String by SharedPreference(SP_SERVER_PUBLIC_KEY, "")
|
||||
|
||||
//服务端RSA私钥
|
||||
var serverPrivateKey: String by SharedPreference(SP_SERVER_PRIVATE_KEY, "")
|
||||
|
||||
//时间容差
|
||||
var timeTolerance: Int by SharedPreference(SP_SERVER_TIME_TOLERANCE, 600)
|
||||
|
||||
//自定义web客户端目录
|
||||
var serverWebPath: String by SharedPreference(SP_SERVER_WEB_PATH, "")
|
||||
|
||||
//服务地址
|
||||
var serverAddress: String by SharedPreference(SP_SERVER_ADDRESS, "")
|
||||
|
||||
//服务地址历史记录
|
||||
var serverHistory: String by SharedPreference(SP_SERVER_HISTORY, "")
|
||||
|
||||
//服务端配置
|
||||
var serverConfig: String by SharedPreference(SP_SERVER_CONFIG, "")
|
||||
|
||||
//客户端签名密钥/RSA公钥
|
||||
var clientSignKey: String by SharedPreference(SP_CLIENT_SIGN_KEY, "")
|
||||
|
||||
//服务端安全设置
|
||||
var clientSafetyMeasures: Int by SharedPreference(SP_CLIENT_SAFETY_MEASURES, if (TextUtils.isEmpty(clientSignKey)) 0 else 1)
|
||||
|
||||
//是否启用一键克隆
|
||||
var enableApiClone: Boolean by SharedPreference(SP_ENABLE_API_CLONE, true)
|
||||
|
||||
//是否启用远程发短信
|
||||
var enableApiSmsSend: Boolean by SharedPreference(SP_ENABLE_API_SMS_SEND, true)
|
||||
|
||||
//是否启用远程查短信
|
||||
var enableApiSmsQuery: Boolean by SharedPreference(SP_ENABLE_API_SMS_QUERY, true)
|
||||
|
||||
//是否启用远程查通话
|
||||
var enableApiCallQuery: Boolean by SharedPreference(SP_ENABLE_API_CALL_QUERY, true)
|
||||
|
||||
//是否启用远程查话簿
|
||||
var enableApiContactQuery: Boolean by SharedPreference(SP_ENABLE_API_CONTACT_QUERY, true)
|
||||
|
||||
//是否启用远程查电量
|
||||
var enableApiBatteryQuery: Boolean by SharedPreference(SP_ENABLE_API_BATTERY_QUERY, true)
|
||||
|
||||
//是否启用远程WOL
|
||||
var enableApiWol: Boolean by SharedPreference(SP_ENABLE_API_WOL, true)
|
||||
|
||||
//WOL历史记录
|
||||
var wolHistory: String by SharedPreference(SP_WOL_HISTORY, "")
|
||||
|
||||
//计算签名
|
||||
fun calcSign(timestamp: String, signSecret: String): String {
|
||||
val stringToSign = "$timestamp\n" + signSecret
|
||||
val mac = Mac.getInstance("HmacSHA256")
|
||||
mac.init(SecretKeySpec(signSecret.toByteArray(StandardCharsets.UTF_8), "HmacSHA256"))
|
||||
val signData = mac.doFinal(stringToSign.toByteArray(StandardCharsets.UTF_8))
|
||||
return URLEncoder.encode(String(Base64.encode(signData, Base64.NO_WRAP)), "UTF-8")
|
||||
}
|
||||
|
||||
//校验签名
|
||||
@Throws(HttpException::class)
|
||||
fun checkSign(req: BaseRequest<*>) {
|
||||
val signSecret = serverSignKey
|
||||
if (TextUtils.isEmpty(signSecret)) return
|
||||
|
||||
if (TextUtils.isEmpty(req.sign)) throw HttpException(500, getString(R.string.sign_required))
|
||||
if (req.timestamp == 0L) throw HttpException(500, getString(R.string.timestamp_required))
|
||||
|
||||
val timestamp = System.currentTimeMillis()
|
||||
val diffTime = kotlin.math.abs(timestamp - req.timestamp)
|
||||
val tolerance = timeTolerance * 1000L
|
||||
if (diffTime > tolerance) {
|
||||
throw HttpException(500, String.format(getString(R.string.timestamp_verify_failed), timestamp, timeTolerance, diffTime))
|
||||
}
|
||||
|
||||
val sign = calcSign(req.timestamp.toString(), signSecret)
|
||||
if (sign != req.sign) {
|
||||
Log.e("calcSign", sign)
|
||||
Log.e("reqSign", req.sign.toString())
|
||||
throw HttpException(500, getString(R.string.sign_verify_failed))
|
||||
}
|
||||
}
|
||||
|
||||
//判断版本是否一致
|
||||
@Throws(HttpException::class)
|
||||
fun compareVersion(cloneInfo: CloneInfo) {
|
||||
val versionCodeRequest = cloneInfo.versionCode
|
||||
if (versionCodeRequest == 0) throw HttpException(500, getString(R.string.version_code_required))
|
||||
val versionCodeLocal = AppUtils.getAppVersionCode().toString().substring(1)
|
||||
if (!versionCodeRequest.toString().endsWith(versionCodeLocal)) throw HttpException(500, getString(R.string.inconsistent_version))
|
||||
}
|
||||
|
||||
//导出设置
|
||||
fun exportSettings(): CloneInfo {
|
||||
val cloneInfo = CloneInfo()
|
||||
cloneInfo.versionCode = AppUtils.getAppVersionCode()
|
||||
cloneInfo.versionName = AppUtils.getAppVersionName()
|
||||
cloneInfo.enableSms = SettingUtils.enableSms
|
||||
cloneInfo.enablePhone = SettingUtils.enablePhone
|
||||
cloneInfo.callType1 = SettingUtils.enableCallType1
|
||||
cloneInfo.callType2 = SettingUtils.enableCallType2
|
||||
cloneInfo.callType3 = SettingUtils.enableCallType3
|
||||
cloneInfo.enableAppNotify = SettingUtils.enableAppNotify
|
||||
cloneInfo.cancelAppNotify = SettingUtils.enableCancelAppNotify
|
||||
cloneInfo.enableNotUserPresent = SettingUtils.enableNotUserPresent
|
||||
cloneInfo.enableLoadAppList = SettingUtils.enableLoadAppList
|
||||
cloneInfo.enableLoadUserAppList = SettingUtils.enableLoadUserAppList
|
||||
cloneInfo.enableLoadSystemAppList = SettingUtils.enableLoadSystemAppList
|
||||
cloneInfo.duplicateMessagesLimits = SettingUtils.duplicateMessagesLimits
|
||||
cloneInfo.enableBatteryReceiver = SettingUtils.enableBatteryReceiver
|
||||
cloneInfo.batteryLevelMin = SettingUtils.batteryLevelMin
|
||||
cloneInfo.batteryLevelMax = SettingUtils.batteryLevelMax
|
||||
cloneInfo.batteryLevelOnce = SettingUtils.batteryLevelOnce
|
||||
cloneInfo.enableBatteryCron = SettingUtils.enableBatteryCron
|
||||
cloneInfo.batteryCronStartTime = SettingUtils.batteryCronStartTime
|
||||
cloneInfo.batteryCronInterval = SettingUtils.batteryCronInterval
|
||||
cloneInfo.enableExcludeFromRecents = SettingUtils.enableExcludeFromRecents
|
||||
cloneInfo.enableCactus = SettingUtils.enableCactus
|
||||
cloneInfo.enablePlaySilenceMusic = SettingUtils.enablePlaySilenceMusic
|
||||
cloneInfo.enableOnePixelActivity = SettingUtils.enableOnePixelActivity
|
||||
cloneInfo.requestRetryTimes = SettingUtils.requestRetryTimes
|
||||
cloneInfo.requestDelayTime = SettingUtils.requestDelayTime
|
||||
cloneInfo.requestTimeout = SettingUtils.requestTimeout
|
||||
cloneInfo.notifyContent = SettingUtils.notifyContent
|
||||
cloneInfo.enableSmsTemplate = SettingUtils.enableSmsTemplate
|
||||
cloneInfo.smsTemplate = SettingUtils.smsTemplate
|
||||
cloneInfo.enableHelpTip = SettingUtils.enableHelpTip
|
||||
cloneInfo.enablePureClientMode = SettingUtils.enablePureClientMode
|
||||
cloneInfo.senderList = Core.sender.all
|
||||
cloneInfo.ruleList = Core.rule.all
|
||||
cloneInfo.frpcList = Core.frpc.all
|
||||
|
||||
return cloneInfo
|
||||
}
|
||||
|
||||
//还原设置
|
||||
fun restoreSettings(cloneInfo: CloneInfo): Boolean {
|
||||
return try {
|
||||
//应用配置
|
||||
SettingUtils.enableSms = cloneInfo.enableSms
|
||||
SettingUtils.enablePhone = cloneInfo.enablePhone
|
||||
SettingUtils.enableCallType1 = cloneInfo.callType1
|
||||
SettingUtils.enableCallType2 = cloneInfo.callType2
|
||||
SettingUtils.enableCallType3 = cloneInfo.callType3
|
||||
SettingUtils.enableAppNotify = cloneInfo.enableAppNotify
|
||||
SettingUtils.enableCancelAppNotify = cloneInfo.cancelAppNotify
|
||||
SettingUtils.enableNotUserPresent = cloneInfo.enableNotUserPresent
|
||||
SettingUtils.enableLoadAppList = cloneInfo.enableLoadAppList
|
||||
SettingUtils.enableLoadUserAppList = cloneInfo.enableLoadUserAppList
|
||||
SettingUtils.enableLoadSystemAppList = cloneInfo.enableLoadSystemAppList
|
||||
SettingUtils.duplicateMessagesLimits = cloneInfo.duplicateMessagesLimits
|
||||
SettingUtils.enableBatteryReceiver = cloneInfo.enableBatteryReceiver
|
||||
SettingUtils.batteryLevelMin = cloneInfo.batteryLevelMin
|
||||
SettingUtils.batteryLevelMax = cloneInfo.batteryLevelMax
|
||||
SettingUtils.batteryLevelOnce = cloneInfo.batteryLevelOnce
|
||||
SettingUtils.enableBatteryCron = cloneInfo.enableBatteryCron
|
||||
SettingUtils.batteryCronStartTime = cloneInfo.batteryCronStartTime.toString()
|
||||
SettingUtils.batteryCronInterval = cloneInfo.batteryCronInterval
|
||||
SettingUtils.enableExcludeFromRecents = cloneInfo.enableExcludeFromRecents
|
||||
SettingUtils.enableCactus = cloneInfo.enableCactus
|
||||
SettingUtils.enablePlaySilenceMusic = cloneInfo.enablePlaySilenceMusic
|
||||
SettingUtils.enableOnePixelActivity = cloneInfo.enableOnePixelActivity
|
||||
SettingUtils.requestRetryTimes = cloneInfo.requestRetryTimes
|
||||
SettingUtils.requestDelayTime = cloneInfo.requestDelayTime
|
||||
SettingUtils.requestTimeout = cloneInfo.requestTimeout
|
||||
SettingUtils.notifyContent = cloneInfo.notifyContent.toString()
|
||||
SettingUtils.enableSmsTemplate = cloneInfo.enableSmsTemplate
|
||||
SettingUtils.smsTemplate = cloneInfo.smsTemplate.toString()
|
||||
SettingUtils.enableHelpTip = cloneInfo.enableHelpTip
|
||||
SettingUtils.enablePureClientMode = cloneInfo.enablePureClientMode
|
||||
//删除发送通道、转发规则、转发日志
|
||||
Core.sender.deleteAll()
|
||||
//发送通道
|
||||
if (!cloneInfo.senderList.isNullOrEmpty()) {
|
||||
for (sender in cloneInfo.senderList!!) {
|
||||
Core.sender.insert(sender)
|
||||
}
|
||||
}
|
||||
//转发规则
|
||||
if (!cloneInfo.ruleList.isNullOrEmpty()) {
|
||||
for (rule in cloneInfo.ruleList!!) {
|
||||
Core.rule.insert(rule)
|
||||
}
|
||||
}
|
||||
//Frpc配置
|
||||
Core.frpc.deleteAll()
|
||||
if (!cloneInfo.frpcList.isNullOrEmpty()) {
|
||||
for (frpc in cloneInfo.frpcList!!) {
|
||||
Core.frpc.insert(frpc)
|
||||
}
|
||||
}
|
||||
true
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
throw HttpException(500, e.message)
|
||||
//false
|
||||
}
|
||||
}
|
||||
|
||||
//返回统一结构报文
|
||||
fun response(output: Any?): String {
|
||||
val resp: MutableMap<String, Any> = mutableMapOf()
|
||||
val timestamp = System.currentTimeMillis()
|
||||
resp["timestamp"] = timestamp
|
||||
if (output is String && output != "success") {
|
||||
resp["code"] = HTTP_FAILURE_CODE
|
||||
resp["msg"] = output
|
||||
} else {
|
||||
resp["code"] = HTTP_SUCCESS_CODE
|
||||
resp["msg"] = "success"
|
||||
if (output != null) {
|
||||
resp["data"] = output
|
||||
}
|
||||
if (safetyMeasures == 1) {
|
||||
resp["sign"] = calcSign(timestamp.toString(), serverSignKey)
|
||||
}
|
||||
}
|
||||
|
||||
return Gson().toJson(resp)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,332 +0,0 @@
|
|||
package com.idormy.sms.forwarder.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Parcelable
|
||||
import android.util.Log
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.tencent.mmkv.MMKV
|
||||
|
||||
/**
|
||||
* MMKV工具类
|
||||
*
|
||||
* @author xuexiang
|
||||
* @since 2019-07-04 10:20
|
||||
*/
|
||||
@Suppress("PropertyName", "UNCHECKED_CAST", "MemberVisibilityCanBePrivate", "unused")
|
||||
class MMKVUtils private constructor() {
|
||||
|
||||
companion object {
|
||||
private var TAG: String = "MMKVUtils"
|
||||
private var sMMKV: MMKV? = null
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
*
|
||||
* @param context
|
||||
*/
|
||||
fun init(context: Context) {
|
||||
MMKV.initialize(context.applicationContext)
|
||||
sMMKV = MMKV.defaultMMKV()
|
||||
}
|
||||
|
||||
fun getsMMKV(): MMKV? {
|
||||
if (sMMKV == null) {
|
||||
sMMKV = MMKV.defaultMMKV()
|
||||
}
|
||||
return sMMKV
|
||||
}
|
||||
//=======================================键值保存==================================================//
|
||||
/**
|
||||
* 保存键值
|
||||
*
|
||||
* @param key
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
fun put(key: String?, value: Any?): Boolean {
|
||||
when (value) {
|
||||
is Int -> {
|
||||
return getsMMKV()!!.encode(key, (value as Int?)!!)
|
||||
}
|
||||
is Float -> {
|
||||
return getsMMKV()!!.encode(key, (value as Float?)!!)
|
||||
}
|
||||
is String -> {
|
||||
return getsMMKV()!!.encode(key, value as String?)
|
||||
}
|
||||
is Boolean -> {
|
||||
return getsMMKV()!!.encode(key, (value as Boolean?)!!)
|
||||
}
|
||||
is Long -> {
|
||||
return getsMMKV()!!.encode(key, (value as Long?)!!)
|
||||
}
|
||||
is Double -> {
|
||||
return getsMMKV()!!.encode(key, (value as Double?)!!)
|
||||
}
|
||||
is Parcelable -> {
|
||||
return getsMMKV()!!.encode(key, value as Parcelable?)
|
||||
}
|
||||
is ByteArray -> {
|
||||
return getsMMKV()!!.encode(key, value as ByteArray?)
|
||||
}
|
||||
is Set<*> -> {
|
||||
return getsMMKV()!!.encode(key, value as Set<String?>?)
|
||||
}
|
||||
else -> return false
|
||||
}
|
||||
}
|
||||
//=======================================键值获取==================================================//
|
||||
/**
|
||||
* 获取键值
|
||||
*
|
||||
* @param key
|
||||
* @param defaultValue
|
||||
* @return
|
||||
*/
|
||||
operator fun get(key: String?, defaultValue: Any?): Any? {
|
||||
when (defaultValue) {
|
||||
is Int -> {
|
||||
return getsMMKV()!!
|
||||
.decodeInt(key, (defaultValue as Int?)!!)
|
||||
}
|
||||
is Float -> {
|
||||
return getsMMKV()!!
|
||||
.decodeFloat(key, (defaultValue as Float?)!!)
|
||||
}
|
||||
is String -> {
|
||||
return getsMMKV()!!.decodeString(key, defaultValue as String?)
|
||||
}
|
||||
is Boolean -> {
|
||||
return getsMMKV()!!
|
||||
.decodeBool(key, (defaultValue as Boolean?)!!)
|
||||
}
|
||||
is Long -> {
|
||||
return getsMMKV()!!
|
||||
.decodeLong(key, (defaultValue as Long?)!!)
|
||||
}
|
||||
is Double -> {
|
||||
return getsMMKV()!!
|
||||
.decodeDouble(key, (defaultValue as Double?)!!)
|
||||
}
|
||||
is ByteArray -> {
|
||||
return getsMMKV()!!.decodeBytes(key)
|
||||
}
|
||||
is Set<*> -> {
|
||||
return getsMMKV()!!.decodeStringSet(key, defaultValue as Set<String?>?)
|
||||
}
|
||||
else -> return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据key获取boolean值
|
||||
*
|
||||
* @param key
|
||||
* @param defValue
|
||||
* @return
|
||||
*/
|
||||
fun getBoolean(key: String?, defValue: Boolean): Boolean {
|
||||
try {
|
||||
return getsMMKV()!!.getBoolean(key, defValue)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
return defValue
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据key获取long值
|
||||
*
|
||||
* @param key
|
||||
* @param defValue
|
||||
* @return
|
||||
*/
|
||||
fun getLong(key: String?, defValue: Long): Long {
|
||||
try {
|
||||
return getsMMKV()!!.getLong(key, defValue)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
return defValue
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据key获取float值
|
||||
*
|
||||
* @param key
|
||||
* @param defValue
|
||||
* @return
|
||||
*/
|
||||
fun getFloat(key: String?, defValue: Float): Float {
|
||||
try {
|
||||
return getsMMKV()!!.getFloat(key, defValue)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
return defValue
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据key获取String值
|
||||
*
|
||||
* @param key
|
||||
* @param defValue
|
||||
* @return
|
||||
*/
|
||||
fun getString(key: String?, defValue: String?): String? {
|
||||
try {
|
||||
return getsMMKV()!!.getString(key, defValue)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
return defValue
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据key获取int值
|
||||
*
|
||||
* @param key
|
||||
* @param defValue
|
||||
* @return
|
||||
*/
|
||||
fun getInt(key: String?, defValue: Int): Int {
|
||||
try {
|
||||
return getsMMKV()!!.getInt(key, defValue)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
return defValue
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据key获取double值
|
||||
*
|
||||
* @param key
|
||||
* @param defValue
|
||||
* @return
|
||||
*/
|
||||
fun getDouble(key: String?, defValue: Double): Double {
|
||||
try {
|
||||
return getsMMKV()!!.decodeDouble(key, defValue)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
return defValue
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对象
|
||||
*
|
||||
* @param key
|
||||
* @param tClass 类型
|
||||
* @param <T>
|
||||
* @return
|
||||
</T> */
|
||||
fun <T : Parcelable?> getObject(key: String?, tClass: Class<T>?): T? {
|
||||
return getsMMKV()!!.decodeParcelable(key, tClass)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对象
|
||||
*
|
||||
* @param key
|
||||
* @param tClass 类型
|
||||
* @param <T>
|
||||
* @return
|
||||
</T> */
|
||||
fun <T : Parcelable?> getObject(key: String?, tClass: Class<T>?, defValue: T): T? {
|
||||
try {
|
||||
return getsMMKV()!!.decodeParcelable(key, tClass, defValue)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
return defValue
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断键值对是否存在
|
||||
*
|
||||
* @param key 键
|
||||
* @return 键值对是否存在
|
||||
*/
|
||||
fun containsKey(key: String?): Boolean {
|
||||
return getsMMKV()!!.containsKey(key)
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除指定键值对
|
||||
*
|
||||
* @param key 键
|
||||
*/
|
||||
fun remove(key: String?) {
|
||||
getsMMKV()!!.remove(key).apply()
|
||||
}
|
||||
|
||||
/**
|
||||
* 从SP迁移数据
|
||||
*/
|
||||
fun importSharedPreferences(context: Context) {
|
||||
Log.d(TAG, "从SP迁移数据")
|
||||
val preferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
val editor = preferences.edit()
|
||||
getsMMKV()!!.importFromSharedPreferences(preferences)
|
||||
editor.clear().apply()
|
||||
|
||||
Log.d(TAG, "转换旧的SP配置")
|
||||
loop@ for (key: String in getsMMKV()!!.allKeys()!!) {
|
||||
when {
|
||||
key.startsWith("tsms_msg_key_switch_") || key.startsWith("tsms_msg_key_string_enable_") || key.endsWith("battery_level_once") -> {
|
||||
val newKey = key.replace("tsms_msg_key_switch_", "enable_")
|
||||
.replace("tsms_msg_key_string_", "enable_")
|
||||
.replace("enable_enable_", "enable_")
|
||||
val value = getBoolean(key, false)
|
||||
Log.d(TAG, String.format("oldKey=%s, newKey=%s, value=%s", key, newKey, value.toString()))
|
||||
put(newKey, value)
|
||||
remove(key)
|
||||
continue@loop
|
||||
}
|
||||
key.endsWith("battery_level_alarm") || key.endsWith("battery_level_max") || key.endsWith("battery_level_current") || key.endsWith("battery_status") || key.endsWith("battery_cron_interval") -> {
|
||||
val newKey = key.replace("tsms_msg_key_switch_", "")
|
||||
.replace("tsms_msg_key_string_", "")
|
||||
.replace("alarm", "min")
|
||||
.replace("tsms_msg_key_", "request_")
|
||||
val value = getInt(key, 0)
|
||||
Log.d(TAG, String.format("oldKey=%s, newKey=%s, value=%s", key, newKey, value.toString()))
|
||||
put(newKey, value)
|
||||
remove(key)
|
||||
continue@loop
|
||||
}
|
||||
key.startsWith("tsms_msg_key_") -> {
|
||||
val newKey = key.replace("tsms_msg_key_string_", "")
|
||||
.replace("add_", "")
|
||||
.replace("tsms_msg_key_", "request_")
|
||||
val value = getString(key, "")
|
||||
Log.d(TAG, String.format("oldKey=%s, newKey=%s, value=%s", key, newKey, value.toString()))
|
||||
put(newKey, value)
|
||||
remove(key)
|
||||
continue@loop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Log.d(TAG, "转换后的数据")
|
||||
for (key: String in getsMMKV()!!.allKeys()!!) {
|
||||
when {
|
||||
key.startsWith("enable_") -> {
|
||||
Log.d(TAG, String.format("key=%s, value=%s", key, getBoolean(key, false).toString()))
|
||||
}
|
||||
key.startsWith("battery_") || key.startsWith("request_") -> {
|
||||
Log.d(TAG, String.format("key=%s, value=%s", key, getInt(key, 0).toString()))
|
||||
}
|
||||
else -> {
|
||||
Log.d(TAG, String.format("key=%s, value=%s", key, getString(key, "").toString()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
throw UnsupportedOperationException("u can't instantiate me...")
|
||||
}
|
||||
}
|
|
@ -7,339 +7,131 @@ class SettingUtils private constructor() {
|
|||
companion object {
|
||||
|
||||
//是否是第一次启动
|
||||
var isFirstOpen: Boolean
|
||||
get() = MMKVUtils.getBoolean(IS_FIRST_OPEN_KEY, true)
|
||||
set(isFirstOpen) {
|
||||
MMKVUtils.put(IS_FIRST_OPEN_KEY, isFirstOpen)
|
||||
}
|
||||
var isFirstOpen: Boolean by SharedPreference(IS_FIRST_OPEN_KEY, true)
|
||||
|
||||
//是否同意隐私政策
|
||||
@JvmStatic
|
||||
var isAgreePrivacy: Boolean
|
||||
get() = MMKVUtils.getBoolean(IS_AGREE_PRIVACY_KEY, false)
|
||||
set(isAgreePrivacy) {
|
||||
MMKVUtils.put(IS_AGREE_PRIVACY_KEY, isAgreePrivacy)
|
||||
}
|
||||
var isAgreePrivacy: Boolean by SharedPreference(IS_AGREE_PRIVACY_KEY, false)
|
||||
|
||||
//是否转发短信
|
||||
@JvmStatic
|
||||
var enableSms: Boolean
|
||||
get() = MMKVUtils.getBoolean(SP_ENABLE_SMS, false)
|
||||
set(enableSms) {
|
||||
MMKVUtils.put(SP_ENABLE_SMS, enableSms)
|
||||
}
|
||||
var enableSms: Boolean by SharedPreference(SP_ENABLE_SMS, false)
|
||||
|
||||
//是否转发通话
|
||||
@JvmStatic
|
||||
var enablePhone: Boolean
|
||||
get() = MMKVUtils.getBoolean(SP_ENABLE_PHONE, false)
|
||||
set(enablePhone) {
|
||||
MMKVUtils.put(SP_ENABLE_PHONE, enablePhone)
|
||||
}
|
||||
var enablePhone: Boolean by SharedPreference(SP_ENABLE_PHONE, false)
|
||||
|
||||
//是否转发通话——已接来电
|
||||
@JvmStatic
|
||||
var enableCallType1: Boolean
|
||||
get() = MMKVUtils.getBoolean(SP_ENABLE_CALL_TYPE_1, false)
|
||||
set(enableCallType1) {
|
||||
MMKVUtils.put(SP_ENABLE_CALL_TYPE_1, enableCallType1)
|
||||
}
|
||||
var enableCallType1: Boolean by SharedPreference(SP_ENABLE_CALL_TYPE_1, false)
|
||||
|
||||
//是否转发通话——本机去电
|
||||
@JvmStatic
|
||||
var enableCallType2: Boolean
|
||||
get() = MMKVUtils.getBoolean(SP_ENABLE_CALL_TYPE_2, false)
|
||||
set(enableCallType2) {
|
||||
MMKVUtils.put(SP_ENABLE_CALL_TYPE_2, enableCallType2)
|
||||
}
|
||||
var enableCallType2: Boolean by SharedPreference(SP_ENABLE_CALL_TYPE_2, false)
|
||||
|
||||
//是否转发通话——未接来电
|
||||
@JvmStatic
|
||||
var enableCallType3: Boolean
|
||||
get() = MMKVUtils.getBoolean(SP_ENABLE_CALL_TYPE_3, false)
|
||||
set(enableCallType3) {
|
||||
MMKVUtils.put(SP_ENABLE_CALL_TYPE_3, enableCallType3)
|
||||
}
|
||||
var enableCallType3: Boolean by SharedPreference(SP_ENABLE_CALL_TYPE_3, false)
|
||||
|
||||
//是否转发通话——来电提醒
|
||||
@JvmStatic
|
||||
var enableCallType4: Boolean
|
||||
get() = MMKVUtils.getBoolean(SP_ENABLE_CALL_TYPE_4, false)
|
||||
set(enableCallType4) {
|
||||
MMKVUtils.put(SP_ENABLE_CALL_TYPE_4, enableCallType4)
|
||||
}
|
||||
var enableCallType4: Boolean by SharedPreference(SP_ENABLE_CALL_TYPE_4, false)
|
||||
|
||||
//是否转发应用通知
|
||||
@JvmStatic
|
||||
var enableAppNotify: Boolean
|
||||
get() = MMKVUtils.getBoolean(SP_ENABLE_APP_NOTIFY, false)
|
||||
set(enableAppNotify) {
|
||||
MMKVUtils.put(SP_ENABLE_APP_NOTIFY, enableAppNotify)
|
||||
}
|
||||
var enableAppNotify: Boolean by SharedPreference(SP_ENABLE_APP_NOTIFY, false)
|
||||
|
||||
//是否转发应用通知——自动消除通知
|
||||
@JvmStatic
|
||||
var enableCancelAppNotify: Boolean
|
||||
get() = MMKVUtils.getBoolean(SP_ENABLE_CANCEL_APP_NOTIFY, false)
|
||||
set(enableCancelAppNotify) {
|
||||
MMKVUtils.put(SP_ENABLE_CANCEL_APP_NOTIFY, enableCancelAppNotify)
|
||||
}
|
||||
var enableCancelAppNotify: Boolean by SharedPreference(SP_ENABLE_CANCEL_APP_NOTIFY, false)
|
||||
|
||||
//是否转发应用通知——仅锁屏状态
|
||||
@JvmStatic
|
||||
var enableNotUserPresent: Boolean
|
||||
get() = MMKVUtils.getBoolean(SP_ENABLE_NOT_USER_PRESENT, false)
|
||||
set(enableNotUserPresent) {
|
||||
MMKVUtils.put(SP_ENABLE_NOT_USER_PRESENT, enableNotUserPresent)
|
||||
}
|
||||
var enableNotUserPresent: Boolean by SharedPreference(SP_ENABLE_NOT_USER_PRESENT, false)
|
||||
|
||||
//是否加载应用列表
|
||||
@JvmStatic
|
||||
var enableLoadAppList: Boolean
|
||||
get() = MMKVUtils.getBoolean(ENABLE_LOAD_APP_LIST, false)
|
||||
set(enableLoadAppList) {
|
||||
MMKVUtils.put(ENABLE_LOAD_APP_LIST, enableLoadAppList)
|
||||
}
|
||||
var enableLoadAppList: Boolean by SharedPreference(ENABLE_LOAD_APP_LIST, false)
|
||||
|
||||
//是否加载应用列表——用户应用
|
||||
@JvmStatic
|
||||
var enableLoadUserAppList: Boolean
|
||||
get() = MMKVUtils.getBoolean(ENABLE_LOAD_USER_APP_LIST, false)
|
||||
set(enableLoadUserAppList) {
|
||||
MMKVUtils.put(ENABLE_LOAD_USER_APP_LIST, enableLoadUserAppList)
|
||||
}
|
||||
var enableLoadUserAppList: Boolean by SharedPreference(ENABLE_LOAD_USER_APP_LIST, false)
|
||||
|
||||
//是否加载应用列表——系统应用
|
||||
@JvmStatic
|
||||
var enableLoadSystemAppList: Boolean
|
||||
get() = MMKVUtils.getBoolean(ENABLE_LOAD_SYSTEM_APP_LIST, false)
|
||||
set(enableLoadSystemAppList) {
|
||||
MMKVUtils.put(ENABLE_LOAD_SYSTEM_APP_LIST, enableLoadSystemAppList)
|
||||
}
|
||||
var enableLoadSystemAppList: Boolean by SharedPreference(ENABLE_LOAD_SYSTEM_APP_LIST, false)
|
||||
|
||||
//过滤多久内重复消息
|
||||
@JvmStatic
|
||||
var duplicateMessagesLimits: Int
|
||||
get() = MMKVUtils.getInt(SP_DUPLICATE_MESSAGES_LIMITS, 0)
|
||||
set(duplicateMessagesLimits) {
|
||||
MMKVUtils.put(SP_DUPLICATE_MESSAGES_LIMITS, duplicateMessagesLimits)
|
||||
}
|
||||
var duplicateMessagesLimits: Int by SharedPreference(SP_DUPLICATE_MESSAGES_LIMITS, 0)
|
||||
|
||||
//免打扰(禁用转发)时间段——开始
|
||||
@JvmStatic
|
||||
var silentPeriodStart: Int
|
||||
get() = MMKVUtils.getInt(SP_SILENT_PERIOD_START, 0)
|
||||
set(silentPeriodStart) {
|
||||
MMKVUtils.put(SP_SILENT_PERIOD_START, silentPeriodStart)
|
||||
}
|
||||
var silentPeriodStart: Int by SharedPreference(SP_SILENT_PERIOD_START, 0)
|
||||
|
||||
//免打扰(禁用转发)时间段——结束
|
||||
@JvmStatic
|
||||
var silentPeriodEnd: Int
|
||||
get() = MMKVUtils.getInt(SP_SILENT_PERIOD_END, 0)
|
||||
set(silentPeriodEnd) {
|
||||
MMKVUtils.put(SP_SILENT_PERIOD_END, silentPeriodEnd)
|
||||
}
|
||||
var silentPeriodEnd: Int by SharedPreference(SP_SILENT_PERIOD_END, 0)
|
||||
|
||||
//自动删除N天前的转发记录
|
||||
@JvmStatic
|
||||
var autoCleanLogsDays: Int
|
||||
get() = MMKVUtils.getInt(SP_AUTO_CLEAN_LOGS_DAYS, 0)
|
||||
set(autoCleanLogsDays) {
|
||||
MMKVUtils.put(SP_AUTO_CLEAN_LOGS_DAYS, autoCleanLogsDays)
|
||||
}
|
||||
var autoCleanLogsDays: Int by SharedPreference(SP_AUTO_CLEAN_LOGS_DAYS, 0)
|
||||
|
||||
//是否监听电池状态变化
|
||||
@JvmStatic
|
||||
var enableBatteryReceiver: Boolean
|
||||
get() = MMKVUtils.getBoolean(SP_BATTERY_RECEIVER, false)
|
||||
set(enableBatteryReceiver) {
|
||||
MMKVUtils.put(SP_BATTERY_RECEIVER, enableBatteryReceiver)
|
||||
}
|
||||
var enableBatteryReceiver: Boolean by SharedPreference(SP_BATTERY_RECEIVER, false)
|
||||
|
||||
//电量预警当前状态
|
||||
@JvmStatic
|
||||
var batteryStatus: Int
|
||||
get() = MMKVUtils.getInt(SP_BATTERY_STATUS, 0)
|
||||
set(batteryStatus) {
|
||||
MMKVUtils.put(SP_BATTERY_STATUS, batteryStatus)
|
||||
}
|
||||
var batteryStatus: Int by SharedPreference(SP_BATTERY_STATUS, 0)
|
||||
|
||||
//电量预警当前值
|
||||
@JvmStatic
|
||||
var batteryLevelCurrent: Int
|
||||
get() = MMKVUtils.getInt(SP_BATTERY_LEVEL_CURRENT, 0)
|
||||
set(batteryLevelCurrent) {
|
||||
MMKVUtils.put(SP_BATTERY_LEVEL_CURRENT, batteryLevelCurrent)
|
||||
}
|
||||
var batteryLevelCurrent: Int by SharedPreference(SP_BATTERY_LEVEL_CURRENT, 0)
|
||||
|
||||
//电量预警最低值
|
||||
@JvmStatic
|
||||
var batteryLevelMin: Int
|
||||
get() = MMKVUtils.getInt(SP_BATTERY_LEVEL_MIN, 0)
|
||||
set(batteryLevelMin) {
|
||||
MMKVUtils.put(SP_BATTERY_LEVEL_MIN, batteryLevelMin)
|
||||
}
|
||||
var batteryLevelMin: Int by SharedPreference(SP_BATTERY_LEVEL_MIN, 0)
|
||||
|
||||
//电量预警最高值
|
||||
@JvmStatic
|
||||
var batteryLevelMax: Int
|
||||
get() = MMKVUtils.getInt(SP_BATTERY_LEVEL_MAX, 100)
|
||||
set(batteryLevelMax) {
|
||||
MMKVUtils.put(SP_BATTERY_LEVEL_MAX, batteryLevelMax)
|
||||
}
|
||||
var batteryLevelMax: Int by SharedPreference(SP_BATTERY_LEVEL_MAX, 100)
|
||||
|
||||
//是否持续电量预警
|
||||
@JvmStatic
|
||||
var batteryLevelOnce: Boolean
|
||||
get() = MMKVUtils.getBoolean(SP_BATTERY_LEVEL_ONCE, false)
|
||||
set(batteryLevelOnce) {
|
||||
MMKVUtils.put(SP_BATTERY_LEVEL_ONCE, batteryLevelOnce)
|
||||
}
|
||||
var batteryLevelOnce: Boolean by SharedPreference(SP_BATTERY_LEVEL_ONCE, false)
|
||||
|
||||
//是否定时推送电池状态
|
||||
@JvmStatic
|
||||
var enableBatteryCron: Boolean
|
||||
get() = MMKVUtils.getBoolean(SP_BATTERY_CRON, false)
|
||||
set(enableBatteryCron) {
|
||||
MMKVUtils.put(SP_BATTERY_CRON, enableBatteryCron)
|
||||
}
|
||||
var enableBatteryCron: Boolean by SharedPreference(SP_BATTERY_CRON, false)
|
||||
|
||||
//是否定时推送电池状态——开始时间
|
||||
@JvmStatic
|
||||
var batteryCronStartTime: String?
|
||||
get() = MMKVUtils.getString(SP_BATTERY_CRON_START_TIME, "00:00")
|
||||
set(batteryCronStartTime) {
|
||||
MMKVUtils.put(SP_BATTERY_CRON_START_TIME, batteryCronStartTime)
|
||||
}
|
||||
var batteryCronStartTime: String by SharedPreference(SP_BATTERY_CRON_START_TIME, "00:00")
|
||||
|
||||
//是否定时推送电池状态——间隔时间(分钟)
|
||||
@JvmStatic
|
||||
var batteryCronInterval: Int
|
||||
get() = MMKVUtils.getInt(SP_BATTERY_CRON_INTERVAL, 60)
|
||||
set(batteryCronInterval) {
|
||||
MMKVUtils.put(SP_BATTERY_CRON_INTERVAL, batteryCronInterval)
|
||||
}
|
||||
var batteryCronInterval: Int by SharedPreference(SP_BATTERY_CRON_INTERVAL, 60)
|
||||
|
||||
//是否不在最近任务列表中显示
|
||||
@JvmStatic
|
||||
var enableExcludeFromRecents: Boolean
|
||||
get() = MMKVUtils.getBoolean(SP_ENABLE_EXCLUDE_FROM_RECENTS, false)
|
||||
set(enableExcludeFromRecents) {
|
||||
MMKVUtils.put(SP_ENABLE_EXCLUDE_FROM_RECENTS, enableExcludeFromRecents)
|
||||
}
|
||||
var enableExcludeFromRecents: Boolean by SharedPreference(SP_ENABLE_EXCLUDE_FROM_RECENTS, false)
|
||||
|
||||
//是否转发应用通知
|
||||
@JvmStatic
|
||||
var enableCactus: Boolean
|
||||
get() = MMKVUtils.getBoolean(SP_ENABLE_CACTUS, false)
|
||||
set(enableAppNotify) {
|
||||
MMKVUtils.put(SP_ENABLE_CACTUS, enableAppNotify)
|
||||
}
|
||||
var enableCactus: Boolean by SharedPreference(SP_ENABLE_CACTUS, false)
|
||||
|
||||
//是否播放静音音乐
|
||||
@JvmStatic
|
||||
var enablePlaySilenceMusic: Boolean
|
||||
get() = MMKVUtils.getBoolean(SP_ENABLE_PLAY_SILENCE_MUSIC, false)
|
||||
set(enablePlaySilenceMusic) {
|
||||
MMKVUtils.put(SP_ENABLE_PLAY_SILENCE_MUSIC, enablePlaySilenceMusic)
|
||||
}
|
||||
var enablePlaySilenceMusic: Boolean by SharedPreference(SP_ENABLE_PLAY_SILENCE_MUSIC, false)
|
||||
|
||||
//是否启用1像素
|
||||
@JvmStatic
|
||||
var enableOnePixelActivity: Boolean
|
||||
get() = MMKVUtils.getBoolean(SP_ENABLE_ONE_PIXEL_ACTIVITY, false)
|
||||
set(enableOnePixelActivity) {
|
||||
MMKVUtils.put(SP_ENABLE_ONE_PIXEL_ACTIVITY, enableOnePixelActivity)
|
||||
}
|
||||
var enableOnePixelActivity: Boolean by SharedPreference(SP_ENABLE_ONE_PIXEL_ACTIVITY, false)
|
||||
|
||||
//请求接口失败重试次数
|
||||
@JvmStatic
|
||||
var requestRetryTimes: Int
|
||||
get() = MMKVUtils.getInt(SP_REQUEST_RETRY_TIMES, 0)
|
||||
set(requestRetryTimes) {
|
||||
MMKVUtils.put(SP_REQUEST_RETRY_TIMES, requestRetryTimes)
|
||||
}
|
||||
var requestRetryTimes: Int by SharedPreference(SP_REQUEST_RETRY_TIMES, 0)
|
||||
|
||||
//请求接口失败重试间隔(秒)
|
||||
@JvmStatic
|
||||
var requestDelayTime: Int
|
||||
get() = MMKVUtils.getInt(SP_REQUEST_DELAY_TIME, 1)
|
||||
set(requestDelayTime) {
|
||||
MMKVUtils.put(SP_REQUEST_DELAY_TIME, requestDelayTime)
|
||||
}
|
||||
var requestDelayTime: Int by SharedPreference(SP_REQUEST_DELAY_TIME, 1)
|
||||
|
||||
//请求接口失败超时时间(秒)
|
||||
@JvmStatic
|
||||
var requestTimeout: Int
|
||||
get() = MMKVUtils.getInt(SP_REQUEST_TIMEOUT, 10)
|
||||
set(requestTimeout) {
|
||||
MMKVUtils.put(SP_REQUEST_TIMEOUT, requestTimeout)
|
||||
}
|
||||
var requestTimeout: Int by SharedPreference(SP_REQUEST_TIMEOUT, 10)
|
||||
|
||||
//通知内容
|
||||
@JvmStatic
|
||||
var notifyContent: String?
|
||||
get() = MMKVUtils.getString(SP_NOTIFY_CONTENT, getString(R.string.notification_content))
|
||||
set(notificationContent) {
|
||||
MMKVUtils.put(SP_NOTIFY_CONTENT, notificationContent)
|
||||
}
|
||||
var notifyContent: String by SharedPreference(SP_NOTIFY_CONTENT, getString(R.string.notification_content))
|
||||
|
||||
//设备名称
|
||||
@JvmStatic
|
||||
var extraDeviceMark: String?
|
||||
get() = MMKVUtils.getString(SP_EXTRA_DEVICE_MARK, "")
|
||||
set(extraDeviceMark) {
|
||||
MMKVUtils.put(SP_EXTRA_DEVICE_MARK, extraDeviceMark)
|
||||
}
|
||||
var extraDeviceMark: String by SharedPreference(SP_EXTRA_DEVICE_MARK, "")
|
||||
|
||||
//SM1备注
|
||||
@JvmStatic
|
||||
var extraSim1: String?
|
||||
get() = MMKVUtils.getString(SP_EXTRA_SIM1, "")
|
||||
set(extraSim1) {
|
||||
MMKVUtils.put(SP_EXTRA_SIM1, extraSim1)
|
||||
}
|
||||
var extraSim1: String by SharedPreference(SP_EXTRA_SIM1, "")
|
||||
|
||||
//SM2备注
|
||||
@JvmStatic
|
||||
var extraSim2: String?
|
||||
get() = MMKVUtils.getString(SP_EXTRA_SIM2, "")
|
||||
set(extraSim2) {
|
||||
MMKVUtils.put(SP_EXTRA_SIM2, extraSim2)
|
||||
}
|
||||
var extraSim2: String by SharedPreference(SP_EXTRA_SIM2, "")
|
||||
|
||||
//是否启用自定义模板
|
||||
@JvmStatic
|
||||
var enableSmsTemplate: Boolean
|
||||
get() = MMKVUtils.getBoolean(SP_ENABLE_SMS_TEMPLATE, false)
|
||||
set(enableSmsTemplate) {
|
||||
MMKVUtils.put(SP_ENABLE_SMS_TEMPLATE, enableSmsTemplate)
|
||||
}
|
||||
var enableSmsTemplate: Boolean by SharedPreference(SP_ENABLE_SMS_TEMPLATE, false)
|
||||
|
||||
//自定义模板
|
||||
@JvmStatic
|
||||
var smsTemplate: String?
|
||||
get() = MMKVUtils.getString(SP_SMS_TEMPLATE, "")
|
||||
set(smsTemplate) {
|
||||
MMKVUtils.put(SP_SMS_TEMPLATE, smsTemplate)
|
||||
}
|
||||
var smsTemplate: String by SharedPreference(SP_SMS_TEMPLATE, "")
|
||||
|
||||
//是否显示页面帮助
|
||||
@JvmStatic
|
||||
var enableHelpTip: Boolean
|
||||
get() = MMKVUtils.getBoolean(SP_ENABLE_HELP_TIP, false)
|
||||
set(enableHelpTip) {
|
||||
MMKVUtils.put(SP_ENABLE_HELP_TIP, enableHelpTip)
|
||||
}
|
||||
var enableHelpTip: Boolean by SharedPreference(SP_ENABLE_HELP_TIP, false)
|
||||
|
||||
//是否纯客户端模式
|
||||
@JvmStatic
|
||||
var enablePureClientMode: Boolean
|
||||
get() = MMKVUtils.getBoolean(SP_PURE_CLIENT_MODE, false)
|
||||
set(enablePureClientMode) {
|
||||
MMKVUtils.put(SP_PURE_CLIENT_MODE, enablePureClientMode)
|
||||
}
|
||||
var enablePureClientMode: Boolean by SharedPreference(SP_PURE_CLIENT_MODE, false)
|
||||
|
||||
}
|
||||
|
||||
init {
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
package com.idormy.sms.forwarder.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Build
|
||||
import java.io.*
|
||||
import kotlin.properties.ReadWriteProperty
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
class SharedPreference<T>(private val name: String, private val default: T) : ReadWriteProperty<Any?, T> {
|
||||
|
||||
companion object {
|
||||
lateinit var preference: SharedPreferences
|
||||
|
||||
fun init(context: Context) {
|
||||
preference = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
val directBootContext: Context = context.createDeviceProtectedStorageContext()
|
||||
directBootContext.getSharedPreferences(context.packageName, Context.MODE_PRIVATE)
|
||||
} else {
|
||||
context.getSharedPreferences(context.packageName, Context.MODE_PRIVATE)
|
||||
}
|
||||
}
|
||||
|
||||
//删除全部数据
|
||||
fun clearPreference() = preference.edit().clear().apply()
|
||||
|
||||
//根据key删除存储数据
|
||||
fun clearPreference(key: String) = preference.edit().remove(key).commit()
|
||||
}
|
||||
|
||||
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
|
||||
return putPreference(name, value)
|
||||
}
|
||||
|
||||
override fun getValue(thisRef: Any?, property: KProperty<*>): T {
|
||||
return getPreference(name, default)
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找数据 返回给调用方法一个具体的对象
|
||||
* 如果查找不到类型就采用反序列化方法来返回类型
|
||||
* default是默认对象 以防止会返回空对象的异常
|
||||
* 即如果name没有查找到value 就返回默认的序列化对象,然后经过反序列化返回
|
||||
*/
|
||||
private fun getPreference(name: String, default: T): T = with(preference) {
|
||||
val res: Any = when (default) {
|
||||
is Long -> getLong(name, default)
|
||||
is String -> this.getString(name, default)!!
|
||||
is Int -> getInt(name, default)
|
||||
is Boolean -> getBoolean(name, default)
|
||||
is Float -> getFloat(name, default)
|
||||
//else -> throw IllegalArgumentException("This type can be get from Preferences")
|
||||
else -> deSerialization(getString(name, serialize(default)).toString())
|
||||
}
|
||||
return res as T
|
||||
}
|
||||
|
||||
private fun putPreference(name: String, value: T) = with(preference.edit()) {
|
||||
when (value) {
|
||||
is Long -> putLong(name, value)
|
||||
is Int -> putInt(name, value)
|
||||
is String -> putString(name, value)
|
||||
is Boolean -> putBoolean(name, value)
|
||||
is Float -> putFloat(name, value)
|
||||
//else -> throw IllegalArgumentException("This type can be saved into Preferences")
|
||||
else -> putString(name, serialize(value))
|
||||
}.apply()
|
||||
}
|
||||
|
||||
/**
|
||||
* 序列化对象
|
||||
* @throws IOException
|
||||
*/
|
||||
@Throws(IOException::class)
|
||||
private fun <T> serialize(obj: T): String {
|
||||
val byteArrayOutputStream = ByteArrayOutputStream()
|
||||
val objectOutputStream = ObjectOutputStream(
|
||||
byteArrayOutputStream
|
||||
)
|
||||
objectOutputStream.writeObject(obj)
|
||||
var serStr = byteArrayOutputStream.toString("ISO-8859-1")
|
||||
serStr = java.net.URLEncoder.encode(serStr, "UTF-8")
|
||||
objectOutputStream.close()
|
||||
byteArrayOutputStream.close()
|
||||
return serStr
|
||||
}
|
||||
|
||||
/**
|
||||
* 反序列化对象
|
||||
* @param str
|
||||
* @throws IOException
|
||||
* @throws ClassNotFoundException
|
||||
*/
|
||||
@Throws(IOException::class, ClassNotFoundException::class)
|
||||
private fun <T> deSerialization(str: String): T {
|
||||
val redStr = java.net.URLDecoder.decode(str, "UTF-8")
|
||||
val byteArrayInputStream = ByteArrayInputStream(
|
||||
redStr.toByteArray(charset("ISO-8859-1"))
|
||||
)
|
||||
val objectInputStream = ObjectInputStream(
|
||||
byteArrayInputStream
|
||||
)
|
||||
val obj = objectInputStream.readObject() as T
|
||||
objectInputStream.close()
|
||||
byteArrayInputStream.close()
|
||||
return obj
|
||||
}
|
||||
}
|
|
@ -8,9 +8,9 @@ import com.idormy.sms.forwarder.database.entity.Rule
|
|||
import com.idormy.sms.forwarder.entity.MsgInfo
|
||||
import com.idormy.sms.forwarder.entity.result.DingtalkInnerRobotResult
|
||||
import com.idormy.sms.forwarder.entity.setting.DingtalkInnerRobotSetting
|
||||
import com.idormy.sms.forwarder.utils.MMKVUtils
|
||||
import com.idormy.sms.forwarder.utils.SendUtils
|
||||
import com.idormy.sms.forwarder.utils.SettingUtils
|
||||
import com.idormy.sms.forwarder.utils.SharedPreference
|
||||
import com.xuexiang.xhttp2.XHttp
|
||||
import com.xuexiang.xhttp2.cache.model.CacheMode
|
||||
import com.xuexiang.xhttp2.callback.SimpleCallBack
|
||||
|
@ -38,9 +38,8 @@ class DingtalkInnerRobotUtils private constructor() {
|
|||
rule: Rule?,
|
||||
logId: Long?,
|
||||
) {
|
||||
|
||||
val accessToken: String? = MMKVUtils.getString("accessToken_" + setting.agentID, "")
|
||||
val expiresIn: Long = MMKVUtils.getLong("expiresIn_" + setting.agentID, 0L)
|
||||
var accessToken: String by SharedPreference("accessToken_" + setting.agentID, "")
|
||||
var expiresIn: Long by SharedPreference("expiresIn_" + setting.agentID, 0L)
|
||||
if (!TextUtils.isEmpty(accessToken) && expiresIn > System.currentTimeMillis()) {
|
||||
return sendTextMsg(setting, msgInfo, rule, logId)
|
||||
}
|
||||
|
@ -57,9 +56,7 @@ class DingtalkInnerRobotUtils private constructor() {
|
|||
val request = XHttp.post(requestUrl)
|
||||
|
||||
//设置代理
|
||||
if ((setting.proxyType == Proxy.Type.HTTP || setting.proxyType == Proxy.Type.SOCKS)
|
||||
&& !TextUtils.isEmpty(setting.proxyHost) && !TextUtils.isEmpty(setting.proxyPort)
|
||||
) {
|
||||
if ((setting.proxyType == Proxy.Type.HTTP || setting.proxyType == Proxy.Type.SOCKS) && !TextUtils.isEmpty(setting.proxyHost) && !TextUtils.isEmpty(setting.proxyPort)) {
|
||||
//代理服务器的IP和端口号
|
||||
Log.d(TAG, "proxyHost = ${setting.proxyHost}, proxyPort = ${setting.proxyPort}")
|
||||
val proxyHost = if (NetworkUtils.isIP(setting.proxyHost)) setting.proxyHost else NetworkUtils.getDomainAddress(setting.proxyHost)
|
||||
|
@ -72,18 +69,14 @@ class DingtalkInnerRobotUtils private constructor() {
|
|||
request.okproxy(Proxy(setting.proxyType, InetSocketAddress(proxyHost, proxyPort)))
|
||||
|
||||
//代理的鉴权账号密码
|
||||
if (setting.proxyAuthenticator == true
|
||||
&& (!TextUtils.isEmpty(setting.proxyUsername) || !TextUtils.isEmpty(setting.proxyPassword))
|
||||
) {
|
||||
if (setting.proxyAuthenticator == true && (!TextUtils.isEmpty(setting.proxyUsername) || !TextUtils.isEmpty(setting.proxyPassword))) {
|
||||
Log.i(TAG, "proxyUsername = ${setting.proxyUsername}, proxyPassword = ${setting.proxyPassword}")
|
||||
|
||||
if (setting.proxyType == Proxy.Type.HTTP) {
|
||||
request.okproxyAuthenticator { _: Route?, response: Response ->
|
||||
//设置代理服务器账号密码
|
||||
val credential = Credentials.basic(setting.proxyUsername.toString(), setting.proxyPassword.toString())
|
||||
response.request().newBuilder()
|
||||
.header("Proxy-Authorization", credential)
|
||||
.build()
|
||||
response.request().newBuilder().header("Proxy-Authorization", credential).build()
|
||||
}
|
||||
} else {
|
||||
Authenticator.setDefault(object : Authenticator() {
|
||||
|
@ -95,13 +88,8 @@ class DingtalkInnerRobotUtils private constructor() {
|
|||
}
|
||||
}
|
||||
|
||||
request.upJson(requestMsg)
|
||||
.keepJson(true)
|
||||
.ignoreHttpsCert()
|
||||
.timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
|
||||
.cacheMode(CacheMode.NO_CACHE)
|
||||
.timeStamp(true)
|
||||
.execute(object : SimpleCallBack<String>() {
|
||||
request.upJson(requestMsg).keepJson(true).ignoreHttpsCert().timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
|
||||
.cacheMode(CacheMode.NO_CACHE).timeStamp(true).execute(object : SimpleCallBack<String>() {
|
||||
|
||||
override fun onError(e: ApiException) {
|
||||
Log.e(TAG, e.detailMessage)
|
||||
|
@ -113,8 +101,8 @@ class DingtalkInnerRobotUtils private constructor() {
|
|||
|
||||
val resp = Gson().fromJson(response, DingtalkInnerRobotResult::class.java)
|
||||
if (!TextUtils.isEmpty(resp?.accessToken)) {
|
||||
MMKVUtils.put("accessToken_" + setting.agentID, resp.accessToken)
|
||||
MMKVUtils.put("expiresIn_" + setting.agentID, System.currentTimeMillis() + ((resp.expireIn ?: 7200) - 120) * 1000L) //提前2分钟过期
|
||||
accessToken = resp.accessToken.toString()
|
||||
expiresIn = System.currentTimeMillis() + ((resp.expireIn ?: 7200) - 120) * 1000L //提前2分钟过期
|
||||
sendTextMsg(setting, msgInfo, rule, logId)
|
||||
} else {
|
||||
SendUtils.updateLogs(logId, 0, String.format(getString(R.string.request_failed_tips), response))
|
||||
|
@ -165,9 +153,7 @@ class DingtalkInnerRobotUtils private constructor() {
|
|||
val request = XHttp.post(requestUrl)
|
||||
|
||||
//设置代理
|
||||
if ((setting.proxyType == Proxy.Type.HTTP || setting.proxyType == Proxy.Type.SOCKS)
|
||||
&& !TextUtils.isEmpty(setting.proxyHost) && !TextUtils.isEmpty(setting.proxyPort)
|
||||
) {
|
||||
if ((setting.proxyType == Proxy.Type.HTTP || setting.proxyType == Proxy.Type.SOCKS) && !TextUtils.isEmpty(setting.proxyHost) && !TextUtils.isEmpty(setting.proxyPort)) {
|
||||
//代理服务器的IP和端口号
|
||||
Log.d(TAG, "proxyHost = ${setting.proxyHost}, proxyPort = ${setting.proxyPort}")
|
||||
val proxyHost = if (NetworkUtils.isIP(setting.proxyHost)) setting.proxyHost else NetworkUtils.getDomainAddress(setting.proxyHost)
|
||||
|
@ -180,18 +166,14 @@ class DingtalkInnerRobotUtils private constructor() {
|
|||
request.okproxy(Proxy(setting.proxyType, InetSocketAddress(proxyHost, proxyPort)))
|
||||
|
||||
//代理的鉴权账号密码
|
||||
if (setting.proxyAuthenticator == true
|
||||
&& (!TextUtils.isEmpty(setting.proxyUsername) || !TextUtils.isEmpty(setting.proxyPassword))
|
||||
) {
|
||||
if (setting.proxyAuthenticator == true && (!TextUtils.isEmpty(setting.proxyUsername) || !TextUtils.isEmpty(setting.proxyPassword))) {
|
||||
Log.i(TAG, "proxyUsername = ${setting.proxyUsername}, proxyPassword = ${setting.proxyPassword}")
|
||||
|
||||
if (setting.proxyType == Proxy.Type.HTTP) {
|
||||
request.okproxyAuthenticator { _: Route?, response: Response ->
|
||||
//设置代理服务器账号密码
|
||||
val credential = Credentials.basic(setting.proxyUsername.toString(), setting.proxyPassword.toString())
|
||||
response.request().newBuilder()
|
||||
.header("Proxy-Authorization", credential)
|
||||
.build()
|
||||
response.request().newBuilder().header("Proxy-Authorization", credential).build()
|
||||
}
|
||||
} else {
|
||||
Authenticator.setDefault(object : Authenticator() {
|
||||
|
@ -203,17 +185,15 @@ class DingtalkInnerRobotUtils private constructor() {
|
|||
}
|
||||
}
|
||||
|
||||
request.upJson(requestMsg)
|
||||
.headers("x-acs-dingtalk-access-token", MMKVUtils.getString("accessToken_" + setting.agentID, ""))
|
||||
val accessToken: String by SharedPreference("accessToken_" + setting.agentID, "")
|
||||
request.upJson(requestMsg).headers("x-acs-dingtalk-access-token", accessToken)
|
||||
.keepJson(true)
|
||||
.ignoreHttpsCert()
|
||||
.timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
|
||||
.cacheMode(CacheMode.NO_CACHE)
|
||||
.retryCount(SettingUtils.requestRetryTimes) //超时重试的次数
|
||||
.cacheMode(CacheMode.NO_CACHE).retryCount(SettingUtils.requestRetryTimes) //超时重试的次数
|
||||
.retryDelay(SettingUtils.requestDelayTime) //超时重试的延迟时间
|
||||
.retryIncreaseDelay(SettingUtils.requestDelayTime) //超时重试叠加延时
|
||||
.timeStamp(true)
|
||||
.execute(object : SimpleCallBack<String>() {
|
||||
.timeStamp(true).execute(object : SimpleCallBack<String>() {
|
||||
|
||||
override fun onError(e: ApiException) {
|
||||
Log.e(TAG, e.detailMessage)
|
||||
|
|
|
@ -8,9 +8,9 @@ import com.idormy.sms.forwarder.database.entity.Rule
|
|||
import com.idormy.sms.forwarder.entity.MsgInfo
|
||||
import com.idormy.sms.forwarder.entity.result.FeishuAppResult
|
||||
import com.idormy.sms.forwarder.entity.setting.FeishuAppSetting
|
||||
import com.idormy.sms.forwarder.utils.MMKVUtils
|
||||
import com.idormy.sms.forwarder.utils.SendUtils
|
||||
import com.idormy.sms.forwarder.utils.SettingUtils
|
||||
import com.idormy.sms.forwarder.utils.SharedPreference
|
||||
import com.xuexiang.xhttp2.XHttp
|
||||
import com.xuexiang.xhttp2.cache.model.CacheMode
|
||||
import com.xuexiang.xhttp2.callback.SimpleCallBack
|
||||
|
@ -31,8 +31,8 @@ class FeishuAppUtils private constructor() {
|
|||
logId: Long?,
|
||||
) {
|
||||
|
||||
val accessToken: String? = MMKVUtils.getString("feishu_access_token_" + setting.appId, "")
|
||||
val expiresIn: Long = MMKVUtils.getLong("feishu_expires_in_" + setting.appId, 0L)
|
||||
var accessToken: String by SharedPreference("feishu_access_token_" + setting.appId, "")
|
||||
var expiresIn: Long by SharedPreference("feishu_expires_in_" + setting.appId, 0L)
|
||||
if (!TextUtils.isEmpty(accessToken) && expiresIn > System.currentTimeMillis()) {
|
||||
return sendTextMsg(setting, msgInfo, rule, logId)
|
||||
}
|
||||
|
@ -46,14 +46,8 @@ class FeishuAppUtils private constructor() {
|
|||
val requestMsg: String = Gson().toJson(msgMap)
|
||||
Log.i(TAG, "requestMsg:$requestMsg")
|
||||
|
||||
XHttp.post(requestUrl)
|
||||
.upJson(requestMsg)
|
||||
.keepJson(true)
|
||||
.ignoreHttpsCert()
|
||||
.timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
|
||||
.cacheMode(CacheMode.NO_CACHE)
|
||||
.timeStamp(true)
|
||||
.execute(object : SimpleCallBack<String>() {
|
||||
XHttp.post(requestUrl).upJson(requestMsg).keepJson(true).ignoreHttpsCert().timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
|
||||
.cacheMode(CacheMode.NO_CACHE).timeStamp(true).execute(object : SimpleCallBack<String>() {
|
||||
|
||||
override fun onError(e: ApiException) {
|
||||
Log.e(TAG, e.detailMessage)
|
||||
|
@ -65,8 +59,8 @@ class FeishuAppUtils private constructor() {
|
|||
|
||||
val resp = Gson().fromJson(response, FeishuAppResult::class.java)
|
||||
if (!TextUtils.isEmpty(resp?.tenant_access_token)) {
|
||||
MMKVUtils.put("feishu_access_token_" + setting.appId, resp.tenant_access_token)
|
||||
MMKVUtils.put("feishu_expires_in_" + setting.appId, System.currentTimeMillis() + ((resp.expire ?: 7010) - 120) * 1000L) //提前2分钟过期
|
||||
accessToken = resp.tenant_access_token.toString()
|
||||
expiresIn = System.currentTimeMillis() + ((resp.expire ?: 7010) - 120) * 1000L //提前2分钟过期
|
||||
sendTextMsg(setting, msgInfo, rule, logId)
|
||||
} else {
|
||||
SendUtils.updateLogs(logId, 0, String.format(getString(R.string.request_failed_tips), response))
|
||||
|
@ -99,9 +93,7 @@ class FeishuAppUtils private constructor() {
|
|||
} else {
|
||||
msgInfo.getTitleForSend(setting.titleTemplate)
|
||||
}
|
||||
"{\"elements\":[{\"tag\":\"markdown\",\"content\":\"**[{{MSG_TITLE}}]({{MSG_URL}})**\\n --------------\\n{{MSG_CONTENT}}\"}]}".trimIndent().replace("{{MSG_TITLE}}", jsonInnerStr(title))
|
||||
.replace("{{MSG_URL}}", jsonInnerStr("https://github.com/pppscn/SmsForwarder"))
|
||||
.replace("{{MSG_CONTENT}}", jsonInnerStr(content))
|
||||
"{\"elements\":[{\"tag\":\"markdown\",\"content\":\"**[{{MSG_TITLE}}]({{MSG_URL}})**\\n --------------\\n{{MSG_CONTENT}}\"}]}".trimIndent().replace("{{MSG_TITLE}}", jsonInnerStr(title)).replace("{{MSG_URL}}", jsonInnerStr("https://github.com/pppscn/SmsForwarder")).replace("{{MSG_CONTENT}}", jsonInnerStr(content))
|
||||
} else {
|
||||
"{\"text\":\"{{MSG_CONTENT}}\"}".trimIndent().replace("{{MSG_CONTENT}}", jsonInnerStr(content))
|
||||
}
|
||||
|
@ -114,18 +106,14 @@ class FeishuAppUtils private constructor() {
|
|||
val requestMsg: String = Gson().toJson(textMsgMap)
|
||||
Log.i(TAG, "requestMsg:$requestMsg")
|
||||
|
||||
XHttp.post(requestUrl)
|
||||
.upJson(requestMsg)
|
||||
.headers("Authorization", "Bearer " + MMKVUtils.getString("feishu_access_token_" + setting.appId, ""))
|
||||
.keepJson(true)
|
||||
val accessToken: String by SharedPreference("feishu_access_token_" + setting.appId, "")
|
||||
XHttp.post(requestUrl).upJson(requestMsg).headers("Authorization", "Bearer $accessToken").keepJson(true)
|
||||
//.ignoreHttpsCert()
|
||||
.timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
|
||||
.cacheMode(CacheMode.NO_CACHE)
|
||||
.retryCount(SettingUtils.requestRetryTimes) //超时重试的次数
|
||||
.cacheMode(CacheMode.NO_CACHE).retryCount(SettingUtils.requestRetryTimes) //超时重试的次数
|
||||
.retryDelay(SettingUtils.requestDelayTime) //超时重试的延迟时间
|
||||
.retryIncreaseDelay(SettingUtils.requestDelayTime) //超时重试叠加延时
|
||||
.timeStamp(true)
|
||||
.execute(object : SimpleCallBack<String>() {
|
||||
.timeStamp(true).execute(object : SimpleCallBack<String>() {
|
||||
|
||||
override fun onError(e: ApiException) {
|
||||
Log.e(TAG, e.detailMessage)
|
||||
|
|
|
@ -9,9 +9,9 @@ import com.idormy.sms.forwarder.entity.MsgInfo
|
|||
import com.idormy.sms.forwarder.entity.result.DingtalkResult
|
||||
import com.idormy.sms.forwarder.entity.result.WeworkAgentResult
|
||||
import com.idormy.sms.forwarder.entity.setting.WeworkAgentSetting
|
||||
import com.idormy.sms.forwarder.utils.MMKVUtils
|
||||
import com.idormy.sms.forwarder.utils.SendUtils
|
||||
import com.idormy.sms.forwarder.utils.SettingUtils
|
||||
import com.idormy.sms.forwarder.utils.SharedPreference
|
||||
import com.xuexiang.xhttp2.XHttp
|
||||
import com.xuexiang.xhttp2.cache.model.CacheMode
|
||||
import com.xuexiang.xhttp2.callback.SimpleCallBack
|
||||
|
@ -39,8 +39,8 @@ class WeworkAgentUtils private constructor() {
|
|||
logId: Long?,
|
||||
) {
|
||||
|
||||
val accessToken: String? = MMKVUtils.getString("access_token_" + setting.agentID, "")
|
||||
val expiresIn: Long = MMKVUtils.getLong("expires_in_" + setting.agentID, 0L)
|
||||
var accessToken: String by SharedPreference("access_token_" + setting.agentID, "")
|
||||
var expiresIn: Long by SharedPreference("expires_in_" + setting.agentID, 0L)
|
||||
if (!TextUtils.isEmpty(accessToken) && expiresIn > System.currentTimeMillis()) {
|
||||
return sendTextMsg(setting, msgInfo, rule, logId)
|
||||
}
|
||||
|
@ -53,9 +53,7 @@ class WeworkAgentUtils private constructor() {
|
|||
val request = XHttp.get(getTokenUrl)
|
||||
|
||||
//设置代理
|
||||
if ((setting.proxyType == Proxy.Type.HTTP || setting.proxyType == Proxy.Type.SOCKS)
|
||||
&& !TextUtils.isEmpty(setting.proxyHost) && !TextUtils.isEmpty(setting.proxyPort)
|
||||
) {
|
||||
if ((setting.proxyType == Proxy.Type.HTTP || setting.proxyType == Proxy.Type.SOCKS) && !TextUtils.isEmpty(setting.proxyHost) && !TextUtils.isEmpty(setting.proxyPort)) {
|
||||
//代理服务器的IP和端口号
|
||||
Log.d(TAG, "proxyHost = ${setting.proxyHost}, proxyPort = ${setting.proxyPort}")
|
||||
val proxyHost = if (NetworkUtils.isIP(setting.proxyHost)) setting.proxyHost else NetworkUtils.getDomainAddress(setting.proxyHost)
|
||||
|
@ -68,18 +66,14 @@ class WeworkAgentUtils private constructor() {
|
|||
request.okproxy(Proxy(setting.proxyType, InetSocketAddress(proxyHost, proxyPort)))
|
||||
|
||||
//代理的鉴权账号密码
|
||||
if (setting.proxyAuthenticator == true
|
||||
&& (!TextUtils.isEmpty(setting.proxyUsername) || !TextUtils.isEmpty(setting.proxyPassword))
|
||||
) {
|
||||
if (setting.proxyAuthenticator == true && (!TextUtils.isEmpty(setting.proxyUsername) || !TextUtils.isEmpty(setting.proxyPassword))) {
|
||||
Log.i(TAG, "proxyUsername = ${setting.proxyUsername}, proxyPassword = ${setting.proxyPassword}")
|
||||
|
||||
if (setting.proxyType == Proxy.Type.HTTP) {
|
||||
request.okproxyAuthenticator { _: Route?, response: Response ->
|
||||
//设置代理服务器账号密码
|
||||
val credential = Credentials.basic(setting.proxyUsername.toString(), setting.proxyPassword.toString())
|
||||
response.request().newBuilder()
|
||||
.header("Proxy-Authorization", credential)
|
||||
.build()
|
||||
response.request().newBuilder().header("Proxy-Authorization", credential).build()
|
||||
}
|
||||
} else {
|
||||
Authenticator.setDefault(object : Authenticator() {
|
||||
|
@ -91,12 +85,8 @@ class WeworkAgentUtils private constructor() {
|
|||
}
|
||||
}
|
||||
|
||||
request.keepJson(true)
|
||||
.ignoreHttpsCert()
|
||||
.timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
|
||||
.cacheMode(CacheMode.NO_CACHE)
|
||||
.timeStamp(true)
|
||||
.execute(object : SimpleCallBack<String>() {
|
||||
request.keepJson(true).ignoreHttpsCert().timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
|
||||
.cacheMode(CacheMode.NO_CACHE).timeStamp(true).execute(object : SimpleCallBack<String>() {
|
||||
|
||||
override fun onError(e: ApiException) {
|
||||
Log.e(TAG, e.detailMessage)
|
||||
|
@ -108,8 +98,8 @@ class WeworkAgentUtils private constructor() {
|
|||
|
||||
val resp = Gson().fromJson(response, WeworkAgentResult::class.java)
|
||||
if (resp?.errcode == 0L) {
|
||||
MMKVUtils.put("access_token_" + setting.agentID, resp.access_token)
|
||||
MMKVUtils.put("expires_in_" + setting.agentID, System.currentTimeMillis() + ((resp.expires_in ?: 7200) - 120) * 1000L) //提前2分钟过期
|
||||
accessToken = resp.access_token.toString()
|
||||
expiresIn = System.currentTimeMillis() + ((resp.expires_in ?: 7200) - 120) * 1000L //提前2分钟过期
|
||||
sendTextMsg(setting, msgInfo, rule, logId)
|
||||
} else {
|
||||
SendUtils.updateLogs(logId, 0, String.format(getString(R.string.request_failed_tips), response))
|
||||
|
@ -142,7 +132,8 @@ class WeworkAgentUtils private constructor() {
|
|||
val textText: MutableMap<String, Any> = mutableMapOf()
|
||||
textText["content"] = content
|
||||
textMsgMap["text"] = textText
|
||||
val requestUrl = "https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=" + MMKVUtils.getString("access_token_" + setting.agentID, "")
|
||||
var accessToken: String by SharedPreference("access_token_" + setting.agentID, "")
|
||||
val requestUrl = "https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=$accessToken"
|
||||
Log.i(TAG, "requestUrl:$requestUrl")
|
||||
val requestMsg: String = Gson().toJson(textMsgMap)
|
||||
Log.i(TAG, "requestMsg:$requestMsg")
|
||||
|
@ -150,9 +141,7 @@ class WeworkAgentUtils private constructor() {
|
|||
val request = XHttp.post(requestUrl)
|
||||
|
||||
//设置代理
|
||||
if ((setting.proxyType == Proxy.Type.HTTP || setting.proxyType == Proxy.Type.SOCKS)
|
||||
&& !TextUtils.isEmpty(setting.proxyHost) && !TextUtils.isEmpty(setting.proxyPort)
|
||||
) {
|
||||
if ((setting.proxyType == Proxy.Type.HTTP || setting.proxyType == Proxy.Type.SOCKS) && !TextUtils.isEmpty(setting.proxyHost) && !TextUtils.isEmpty(setting.proxyPort)) {
|
||||
//代理服务器的IP和端口号
|
||||
Log.d(TAG, "proxyHost = ${setting.proxyHost}, proxyPort = ${setting.proxyPort}")
|
||||
val proxyHost = if (NetworkUtils.isIP(setting.proxyHost)) setting.proxyHost else NetworkUtils.getDomainAddress(setting.proxyHost)
|
||||
|
@ -165,18 +154,14 @@ class WeworkAgentUtils private constructor() {
|
|||
request.okproxy(Proxy(setting.proxyType, InetSocketAddress(proxyHost, proxyPort)))
|
||||
|
||||
//代理的鉴权账号密码
|
||||
if (setting.proxyAuthenticator == true
|
||||
&& (!TextUtils.isEmpty(setting.proxyUsername) || !TextUtils.isEmpty(setting.proxyPassword))
|
||||
) {
|
||||
if (setting.proxyAuthenticator == true && (!TextUtils.isEmpty(setting.proxyUsername) || !TextUtils.isEmpty(setting.proxyPassword))) {
|
||||
Log.i(TAG, "proxyUsername = ${setting.proxyUsername}, proxyPassword = ${setting.proxyPassword}")
|
||||
|
||||
if (setting.proxyType == Proxy.Type.HTTP) {
|
||||
request.okproxyAuthenticator { _: Route?, response: Response ->
|
||||
//设置代理服务器账号密码
|
||||
val credential = Credentials.basic(setting.proxyUsername.toString(), setting.proxyPassword.toString())
|
||||
response.request().newBuilder()
|
||||
.header("Proxy-Authorization", credential)
|
||||
.build()
|
||||
response.request().newBuilder().header("Proxy-Authorization", credential).build()
|
||||
}
|
||||
} else {
|
||||
Authenticator.setDefault(object : Authenticator() {
|
||||
|
@ -188,16 +173,11 @@ class WeworkAgentUtils private constructor() {
|
|||
}
|
||||
}
|
||||
|
||||
request.upJson(requestMsg)
|
||||
.keepJson(true)
|
||||
.ignoreHttpsCert()
|
||||
.timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
|
||||
.cacheMode(CacheMode.NO_CACHE)
|
||||
.retryCount(SettingUtils.requestRetryTimes) //超时重试的次数
|
||||
request.upJson(requestMsg).keepJson(true).ignoreHttpsCert().timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
|
||||
.cacheMode(CacheMode.NO_CACHE).retryCount(SettingUtils.requestRetryTimes) //超时重试的次数
|
||||
.retryDelay(SettingUtils.requestDelayTime) //超时重试的延迟时间
|
||||
.retryIncreaseDelay(SettingUtils.requestDelayTime) //超时重试叠加延时
|
||||
.timeStamp(true)
|
||||
.execute(object : SimpleCallBack<String>() {
|
||||
.timeStamp(true).execute(object : SimpleCallBack<String>() {
|
||||
|
||||
override fun onError(e: ApiException) {
|
||||
Log.e(TAG, e.detailMessage)
|
||||
|
|
|
@ -10,7 +10,7 @@ import com.idormy.sms.forwarder.R
|
|||
import com.idormy.sms.forwarder.core.http.api.ApiService.IGetService
|
||||
import com.idormy.sms.forwarder.core.http.callback.NoTipCallBack
|
||||
import com.idormy.sms.forwarder.core.http.entity.TipInfo
|
||||
import com.idormy.sms.forwarder.utils.MMKVUtils
|
||||
import com.idormy.sms.forwarder.utils.SharedPreference
|
||||
import com.xuexiang.constant.TimeConstants
|
||||
import com.xuexiang.xaop.annotation.SingleClick
|
||||
import com.xuexiang.xhttp2.XHttp
|
||||
|
@ -175,11 +175,12 @@ class GuideTipsDialog(context: Context?, tips: List<TipInfo>) :
|
|||
}
|
||||
|
||||
fun setIsIgnoreTips(isIgnore: Boolean): Boolean {
|
||||
return MMKVUtils.put(KEY_IS_IGNORE_TIPS + AppUtils.getAppVersionCode(), isIgnore)
|
||||
this.isIgnoreTips = isIgnore
|
||||
return true
|
||||
}
|
||||
|
||||
val isIgnoreTips: Boolean
|
||||
get() = MMKVUtils.getBoolean(KEY_IS_IGNORE_TIPS + AppUtils.getAppVersionCode(), false)
|
||||
var isIgnoreTips: Boolean by SharedPreference(KEY_IS_IGNORE_TIPS + AppUtils.getAppVersionCode(), false)
|
||||
|
||||
}
|
||||
|
||||
init {
|
||||
|
|
|
@ -1,111 +1,101 @@
|
|||
package com.idormy.sms.forwarder.workers
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import androidx.work.CoroutineWorker
|
||||
import androidx.work.WorkerParameters
|
||||
import androidx.work.workDataOf
|
||||
import com.google.gson.Gson
|
||||
import com.idormy.sms.forwarder.core.Core
|
||||
import com.idormy.sms.forwarder.database.entity.Logs
|
||||
import com.idormy.sms.forwarder.database.entity.RuleAndSender
|
||||
import com.idormy.sms.forwarder.entity.MsgInfo
|
||||
import com.idormy.sms.forwarder.utils.*
|
||||
import com.xuexiang.xutil.security.CipherUtils
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.text.ParsePosition
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
class SendWorker(
|
||||
context: Context,
|
||||
workerParams: WorkerParameters,
|
||||
) : CoroutineWorker(context, workerParams) {
|
||||
|
||||
@SuppressLint("SimpleDateFormat")
|
||||
override suspend fun doWork(): Result {
|
||||
|
||||
return withContext(Dispatchers.IO) {
|
||||
try {
|
||||
// 免打扰(禁用转发)时间段
|
||||
if (SettingUtils.silentPeriodStart != SettingUtils.silentPeriodEnd) {
|
||||
val periodStartDay = Date()
|
||||
var periodStartEnd = Date()
|
||||
//跨天了
|
||||
if (SettingUtils.silentPeriodStart > SettingUtils.silentPeriodEnd) {
|
||||
val c: Calendar = Calendar.getInstance()
|
||||
c.time = periodStartEnd
|
||||
c.add(Calendar.DAY_OF_MONTH, 1)
|
||||
periodStartEnd = c.time
|
||||
}
|
||||
|
||||
val dateFmt = SimpleDateFormat("yyyy-MM-dd")
|
||||
val mTimeOption = DataProvider.timePeriodOption
|
||||
val periodStartStr =
|
||||
dateFmt.format(periodStartDay) + " " + mTimeOption[SettingUtils.silentPeriodStart] + ":00"
|
||||
val periodEndStr =
|
||||
dateFmt.format(periodStartEnd) + " " + mTimeOption[SettingUtils.silentPeriodEnd] + ":00"
|
||||
|
||||
val timeFmt = SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
|
||||
val periodStart = timeFmt.parse(periodStartStr, ParsePosition(0)).time
|
||||
val periodEnd = timeFmt.parse(periodEndStr, ParsePosition(0)).time
|
||||
|
||||
val now = System.currentTimeMillis()
|
||||
if (now in periodStart..periodEnd) {
|
||||
Log.e("SendWorker", "免打扰(禁用转发)时间段")
|
||||
return@withContext Result.failure(workDataOf("send" to "failed"))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
val msgInfoJson = inputData.getString(Worker.sendMsgInfo)
|
||||
val msgInfo = Gson().fromJson(msgInfoJson, MsgInfo::class.java)
|
||||
|
||||
// 过滤重复消息机制
|
||||
if (SettingUtils.duplicateMessagesLimits > 0) {
|
||||
val key = CipherUtils.md5(msgInfo.type + msgInfo.from + msgInfo.content)
|
||||
val timestamp: Long = System.currentTimeMillis() / 1000L
|
||||
if (HistoryUtils.containsKey(key)) {
|
||||
val timestampPrev = HistoryUtils.getLong(key, timestamp)
|
||||
if (timestamp - timestampPrev <= SettingUtils.duplicateMessagesLimits) {
|
||||
Log.e("SendWorker", "过滤重复消息机制")
|
||||
return@withContext Result.failure(workDataOf("send" to "failed"))
|
||||
}
|
||||
}
|
||||
HistoryUtils.put(key, timestamp)
|
||||
}
|
||||
|
||||
//【注意】卡槽id:-1=获取失败、0=卡槽1、1=卡槽2,但是 Rule 表里存的是 SIM1/SIM2
|
||||
val simSlot = "SIM" + (msgInfo.simSlot + 1)
|
||||
val ruleList: List<RuleAndSender> =
|
||||
Core.rule.getRuleAndSender(msgInfo.type, 1, simSlot)
|
||||
if (ruleList.isEmpty()) {
|
||||
return@withContext Result.failure(workDataOf("send" to "failed"))
|
||||
}
|
||||
|
||||
for (rule in ruleList) {
|
||||
if (!rule.rule.checkMsg(msgInfo)) continue
|
||||
val log = Logs(
|
||||
0,
|
||||
msgInfo.type,
|
||||
msgInfo.from,
|
||||
msgInfo.content,
|
||||
rule.rule.id,
|
||||
msgInfo.simInfo
|
||||
)
|
||||
val logId = Core.logs.insert(log)
|
||||
SendUtils.sendMsgSender(msgInfo, rule.rule, rule.sender, logId)
|
||||
}
|
||||
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
return@withContext Result.failure(workDataOf("send" to e.message.toString()))
|
||||
}
|
||||
|
||||
return@withContext Result.success()
|
||||
}
|
||||
}
|
||||
|
||||
package com.idormy.sms.forwarder.workers
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import androidx.work.CoroutineWorker
|
||||
import androidx.work.WorkerParameters
|
||||
import androidx.work.workDataOf
|
||||
import com.google.gson.Gson
|
||||
import com.idormy.sms.forwarder.core.Core
|
||||
import com.idormy.sms.forwarder.database.entity.Logs
|
||||
import com.idormy.sms.forwarder.database.entity.RuleAndSender
|
||||
import com.idormy.sms.forwarder.entity.MsgInfo
|
||||
import com.idormy.sms.forwarder.utils.*
|
||||
import com.xuexiang.xutil.security.CipherUtils
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.text.ParsePosition
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
class SendWorker(
|
||||
context: Context,
|
||||
workerParams: WorkerParameters,
|
||||
) : CoroutineWorker(context, workerParams) {
|
||||
|
||||
@SuppressLint("SimpleDateFormat")
|
||||
override suspend fun doWork(): Result {
|
||||
|
||||
return withContext(Dispatchers.IO) {
|
||||
try {
|
||||
// 免打扰(禁用转发)时间段
|
||||
if (SettingUtils.silentPeriodStart != SettingUtils.silentPeriodEnd) {
|
||||
val periodStartDay = Date()
|
||||
var periodStartEnd = Date()
|
||||
//跨天了
|
||||
if (SettingUtils.silentPeriodStart > SettingUtils.silentPeriodEnd) {
|
||||
val c: Calendar = Calendar.getInstance()
|
||||
c.time = periodStartEnd
|
||||
c.add(Calendar.DAY_OF_MONTH, 1)
|
||||
periodStartEnd = c.time
|
||||
}
|
||||
|
||||
val dateFmt = SimpleDateFormat("yyyy-MM-dd")
|
||||
val mTimeOption = DataProvider.timePeriodOption
|
||||
val periodStartStr = dateFmt.format(periodStartDay) + " " + mTimeOption[SettingUtils.silentPeriodStart] + ":00"
|
||||
val periodEndStr = dateFmt.format(periodStartEnd) + " " + mTimeOption[SettingUtils.silentPeriodEnd] + ":00"
|
||||
|
||||
val timeFmt = SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
|
||||
val periodStart = timeFmt.parse(periodStartStr, ParsePosition(0))?.time
|
||||
val periodEnd = timeFmt.parse(periodEndStr, ParsePosition(0))?.time
|
||||
|
||||
val now = System.currentTimeMillis()
|
||||
if (periodStart != null && periodEnd != null && now in periodStart..periodEnd) {
|
||||
Log.e("SendWorker", "免打扰(禁用转发)时间段")
|
||||
return@withContext Result.failure(workDataOf("send" to "failed"))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
val msgInfoJson = inputData.getString(Worker.sendMsgInfo)
|
||||
val msgInfo = Gson().fromJson(msgInfoJson, MsgInfo::class.java)
|
||||
|
||||
// 过滤重复消息机制
|
||||
if (SettingUtils.duplicateMessagesLimits > 0) {
|
||||
val key = CipherUtils.md5(msgInfo.type + msgInfo.from + msgInfo.content)
|
||||
val timestamp: Long = System.currentTimeMillis() / 1000L
|
||||
var timestampPrev: Long by HistoryUtils(key, timestamp)
|
||||
if (timestampPrev != timestamp && timestamp - timestampPrev <= SettingUtils.duplicateMessagesLimits) {
|
||||
Log.e("SendWorker", "过滤重复消息机制")
|
||||
return@withContext Result.failure(workDataOf("send" to "failed"))
|
||||
}
|
||||
timestampPrev = timestamp
|
||||
}
|
||||
|
||||
//【注意】卡槽id:-1=获取失败、0=卡槽1、1=卡槽2,但是 Rule 表里存的是 SIM1/SIM2
|
||||
val simSlot = "SIM" + (msgInfo.simSlot + 1)
|
||||
val ruleList: List<RuleAndSender> = Core.rule.getRuleAndSender(msgInfo.type, 1, simSlot)
|
||||
if (ruleList.isEmpty()) {
|
||||
return@withContext Result.failure(workDataOf("send" to "failed"))
|
||||
}
|
||||
|
||||
for (rule in ruleList) {
|
||||
if (!rule.rule.checkMsg(msgInfo)) continue
|
||||
val log = Logs(
|
||||
0, msgInfo.type, msgInfo.from, msgInfo.content, rule.rule.id, msgInfo.simInfo
|
||||
)
|
||||
val logId = Core.logs.insert(log)
|
||||
SendUtils.sendMsgSender(msgInfo, rule.rule, rule.sender, logId)
|
||||
}
|
||||
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
return@withContext Result.failure(workDataOf("send" to e.message.toString()))
|
||||
}
|
||||
|
||||
return@withContext Result.success()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -5,8 +5,8 @@ def build_versions = [:]
|
|||
build_versions.version_code = 49
|
||||
build_versions.version_name = "3.1.1"
|
||||
build_versions.min_sdk = 19
|
||||
build_versions.target_sdk = 33
|
||||
build_versions.build_tools = "33.0.0"
|
||||
build_versions.target_sdk = 32
|
||||
build_versions.build_tools = "33.0.1"
|
||||
ext.build_versions = build_versions
|
||||
|
||||
ext.deps = [:]
|
||||
|
|
Loading…
Reference in New Issue