修复:重启手机自动启动APP时加载配置失败 #233 #245

pull/286/head
pppscn 1 year ago
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'//
//mmkvhttps://github.com/Tencent/MMKV
implementation 'com.tencent:mmkv:1.2.15'
//AutoSizehttps://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 {

@ -65,7 +65,7 @@ class AboutFragment : BaseFragment<FragmentAboutBinding?>(), SuperTextView.OnSup
XUpdateInit.checkUpdate(requireContext(), true)
}
binding!!.btnCache.setOnClickListener {
HistoryUtils.clear()
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())))

@ -275,7 +275,7 @@ class ClientFragment : BaseFragment<FragmentClientBinding?>(), View.OnClickListe
val timestamp = System.currentTimeMillis()
msgMap["timestamp"] = timestamp
val clientSignKey = HttpServerUtils.clientSignKey.toString()
val clientSignKey = HttpServerUtils.clientSignKey
if (HttpServerUtils.clientSafetyMeasures != 0 && TextUtils.isEmpty(clientSignKey)) {
if (needToast) XToastUtils.error("请输入签名密钥/RSA公钥/SM4密钥")
return
@ -298,7 +298,7 @@ class ClientFragment : BaseFragment<FragmentClientBinding?>(), View.OnClickListe
when (HttpServerUtils.clientSafetyMeasures) {
2 -> {
try {
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey.toString())
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey)
requestMsg = Base64.encode(requestMsg.toByteArray())
requestMsg = RSACrypt.encryptByPublicKey(requestMsg, publicKey)
Log.i(TAG, "requestMsg: $requestMsg")
@ -311,7 +311,7 @@ class ClientFragment : BaseFragment<FragmentClientBinding?>(), View.OnClickListe
}
3 -> {
try {
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey.toString())
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey)
//requestMsg = Base64.encode(requestMsg.toByteArray())
val encryptCBC = SM4Crypt.encrypt(requestMsg.toByteArray(), sm4Key)
requestMsg = ConvertTools.bytes2HexString(encryptCBC)
@ -340,11 +340,11 @@ class ClientFragment : BaseFragment<FragmentClientBinding?>(), View.OnClickListe
try {
var json = response
if (HttpServerUtils.clientSafetyMeasures == 2) {
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey.toString())
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.toString())
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey)
val encryptCBC = ConvertTools.hexStringToByteArray(json)
val decryptCBC = SM4Crypt.decrypt(encryptCBC, sm4Key)
json = String(decryptCBC)
@ -354,9 +354,9 @@ class ClientFragment : BaseFragment<FragmentClientBinding?>(), View.OnClickListe
serverConfig = resp.data!!
if (needToast) XToastUtils.success(ResUtils.getString(R.string.request_succeeded))
//删除3.0.8之前保存的记录
serverHistory.remove(HttpServerUtils.serverAddress.toString())
serverHistory.remove(HttpServerUtils.serverAddress)
//添加到历史记录
val key = "${serverConfig?.extraDeviceMark}${HttpServerUtils.serverAddress.toString()}"
val key = "${serverConfig?.extraDeviceMark}${HttpServerUtils.serverAddress}"
if (TextUtils.isEmpty(HttpServerUtils.clientSignKey)) {
serverHistory[key] = "SMSFORWARDER##" + HttpServerUtils.clientSafetyMeasures.toString()
} else {

@ -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,18 +165,14 @@ 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 ->
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 {
}).setTitleText(getString(R.string.select_time_period)).setSelectOptions(SettingUtils.silentPeriodStart, SettingUtils.silentPeriodEnd).build<Any>().also {
it.setNPicker(mTimeOption, mTimeOption)
it.show()
}
@ -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? ->
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
requireContext(), permissions
)
}
.show()
}.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? ->
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()
}.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

@ -54,7 +54,7 @@ class BatteryQueryFragment : BaseFragment<FragmentClientBatteryQueryBinding?>()
msgMap["timestamp"] = timestamp
val clientSignKey = HttpServerUtils.clientSignKey
if (!TextUtils.isEmpty(clientSignKey)) {
msgMap["sign"] = HttpServerUtils.calcSign(timestamp.toString(), clientSignKey.toString())
msgMap["sign"] = HttpServerUtils.calcSign(timestamp.toString(), clientSignKey)
}
val dataMap: MutableMap<String, Any> = mutableMapOf()
msgMap["data"] = dataMap
@ -71,7 +71,7 @@ class BatteryQueryFragment : BaseFragment<FragmentClientBatteryQueryBinding?>()
when (HttpServerUtils.clientSafetyMeasures) {
2 -> {
try {
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey.toString())
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey)
requestMsg = Base64.encode(requestMsg.toByteArray())
requestMsg = RSACrypt.encryptByPublicKey(requestMsg, publicKey)
Log.i(TAG, "requestMsg: $requestMsg")
@ -84,7 +84,7 @@ class BatteryQueryFragment : BaseFragment<FragmentClientBatteryQueryBinding?>()
}
3 -> {
try {
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey.toString())
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey)
//requestMsg = Base64.encode(requestMsg.toByteArray())
val encryptCBC = SM4Crypt.encrypt(requestMsg.toByteArray(), sm4Key)
requestMsg = ConvertTools.bytes2HexString(encryptCBC)
@ -111,11 +111,11 @@ class BatteryQueryFragment : BaseFragment<FragmentClientBatteryQueryBinding?>()
try {
var json = response
if (HttpServerUtils.clientSafetyMeasures == 2) {
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey.toString())
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.toString())
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey)
val encryptCBC = ConvertTools.hexStringToByteArray(json)
val decryptCBC = SM4Crypt.decrypt(encryptCBC, sm4Key)
json = String(decryptCBC)

@ -202,7 +202,7 @@ class CallQueryFragment : BaseFragment<FragmentClientCallQueryBinding?>() {
msgMap["timestamp"] = timestamp
val clientSignKey = HttpServerUtils.clientSignKey
if (!TextUtils.isEmpty(clientSignKey)) {
msgMap["sign"] = HttpServerUtils.calcSign(timestamp.toString(), clientSignKey.toString())
msgMap["sign"] = HttpServerUtils.calcSign(timestamp.toString(), clientSignKey)
}
if (refresh) pageNum = 1
@ -219,7 +219,7 @@ class CallQueryFragment : BaseFragment<FragmentClientCallQueryBinding?>() {
when (HttpServerUtils.clientSafetyMeasures) {
2 -> {
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey.toString())
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey)
try {
requestMsg = Base64.encode(requestMsg.toByteArray())
requestMsg = RSACrypt.encryptByPublicKey(requestMsg, publicKey)
@ -233,7 +233,7 @@ class CallQueryFragment : BaseFragment<FragmentClientCallQueryBinding?>() {
}
3 -> {
try {
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey.toString())
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey)
//requestMsg = Base64.encode(requestMsg.toByteArray())
val encryptCBC = SM4Crypt.encrypt(requestMsg.toByteArray(), sm4Key)
requestMsg = ConvertTools.bytes2HexString(encryptCBC)
@ -260,11 +260,11 @@ class CallQueryFragment : BaseFragment<FragmentClientCallQueryBinding?>() {
try {
var json = response
if (HttpServerUtils.clientSafetyMeasures == 2) {
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey.toString())
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.toString())
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey)
val encryptCBC = ConvertTools.hexStringToByteArray(json)
val decryptCBC = SM4Crypt.decrypt(encryptCBC, sm4Key)
json = String(decryptCBC)

@ -243,7 +243,7 @@ class CloneFragment : BaseFragment<FragmentClientCloneBinding?>(), View.OnClickL
val clientSignKey = HttpServerUtils.clientSignKey
if (!TextUtils.isEmpty(clientSignKey)) {
msgMap["sign"] =
HttpServerUtils.calcSign(timestamp.toString(), clientSignKey.toString())
HttpServerUtils.calcSign(timestamp.toString(), clientSignKey)
}
msgMap["data"] = HttpServerUtils.exportSettings()
@ -258,7 +258,7 @@ class CloneFragment : BaseFragment<FragmentClientCloneBinding?>(), View.OnClickL
when (HttpServerUtils.clientSafetyMeasures) {
2 -> {
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey.toString())
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey)
try {
requestMsg = Base64.encode(requestMsg.toByteArray())
requestMsg = RSACrypt.encryptByPublicKey(requestMsg, publicKey)
@ -272,7 +272,7 @@ class CloneFragment : BaseFragment<FragmentClientCloneBinding?>(), View.OnClickL
}
3 -> {
try {
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey.toString())
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey)
//requestMsg = Base64.encode(requestMsg.toByteArray())
val encryptCBC = SM4Crypt.encrypt(requestMsg.toByteArray(), sm4Key)
requestMsg = ConvertTools.bytes2HexString(encryptCBC)
@ -300,11 +300,11 @@ class CloneFragment : BaseFragment<FragmentClientCloneBinding?>(), View.OnClickL
try {
var json = response
if (HttpServerUtils.clientSafetyMeasures == 2) {
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey.toString())
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.toString())
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey)
val encryptCBC = ConvertTools.hexStringToByteArray(json)
val decryptCBC = SM4Crypt.decrypt(encryptCBC, sm4Key)
json = String(decryptCBC)
@ -343,7 +343,7 @@ class CloneFragment : BaseFragment<FragmentClientCloneBinding?>(), View.OnClickL
val clientSignKey = HttpServerUtils.clientSignKey
if (!TextUtils.isEmpty(clientSignKey)) {
msgMap["sign"] =
HttpServerUtils.calcSign(timestamp.toString(), clientSignKey.toString())
HttpServerUtils.calcSign(timestamp.toString(), clientSignKey)
}
val dataMap: MutableMap<String, Any> = mutableMapOf()
@ -361,7 +361,7 @@ class CloneFragment : BaseFragment<FragmentClientCloneBinding?>(), View.OnClickL
when (HttpServerUtils.clientSafetyMeasures) {
2 -> {
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey.toString())
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey)
try {
requestMsg = Base64.encode(requestMsg.toByteArray())
requestMsg = RSACrypt.encryptByPublicKey(requestMsg, publicKey)
@ -375,7 +375,7 @@ class CloneFragment : BaseFragment<FragmentClientCloneBinding?>(), View.OnClickL
}
3 -> {
try {
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey.toString())
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey)
//requestMsg = Base64.encode(requestMsg.toByteArray())
val encryptCBC = SM4Crypt.encrypt(requestMsg.toByteArray(), sm4Key)
requestMsg = ConvertTools.bytes2HexString(encryptCBC)
@ -403,11 +403,11 @@ class CloneFragment : BaseFragment<FragmentClientCloneBinding?>(), View.OnClickL
try {
var json = response
if (HttpServerUtils.clientSafetyMeasures == 2) {
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey.toString())
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.toString())
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey)
val encryptCBC = ConvertTools.hexStringToByteArray(json)
val decryptCBC = SM4Crypt.decrypt(encryptCBC, sm4Key)
json = String(decryptCBC)

@ -184,7 +184,7 @@ class ContactQueryFragment : BaseFragment<FragmentClientContactQueryBinding?>()
msgMap["timestamp"] = timestamp
val clientSignKey = HttpServerUtils.clientSignKey
if (!TextUtils.isEmpty(clientSignKey)) {
msgMap["sign"] = HttpServerUtils.calcSign(timestamp.toString(), clientSignKey.toString())
msgMap["sign"] = HttpServerUtils.calcSign(timestamp.toString(), clientSignKey)
}
msgMap["data"] = if (keyword.isDigitsOnly())
ContactQueryData(1, 20, keyword, null)
@ -202,7 +202,7 @@ class ContactQueryFragment : BaseFragment<FragmentClientContactQueryBinding?>()
when (HttpServerUtils.clientSafetyMeasures) {
2 -> {
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey.toString())
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey)
try {
requestMsg = Base64.encode(requestMsg.toByteArray())
requestMsg = RSACrypt.encryptByPublicKey(requestMsg, publicKey)
@ -216,7 +216,7 @@ class ContactQueryFragment : BaseFragment<FragmentClientContactQueryBinding?>()
}
3 -> {
try {
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey.toString())
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey)
//requestMsg = Base64.encode(requestMsg.toByteArray())
val encryptCBC = SM4Crypt.encrypt(requestMsg.toByteArray(), sm4Key)
requestMsg = ConvertTools.bytes2HexString(encryptCBC)
@ -243,11 +243,11 @@ class ContactQueryFragment : BaseFragment<FragmentClientContactQueryBinding?>()
try {
var json = response
if (HttpServerUtils.clientSafetyMeasures == 2) {
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey.toString())
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.toString())
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey)
val encryptCBC = ConvertTools.hexStringToByteArray(json)
val decryptCBC = SM4Crypt.decrypt(encryptCBC, sm4Key)
json = String(decryptCBC)

@ -192,7 +192,7 @@ class SmsQueryFragment : BaseFragment<FragmentClientSmsQueryBinding?>() {
msgMap["timestamp"] = timestamp
val clientSignKey = HttpServerUtils.clientSignKey
if (!TextUtils.isEmpty(clientSignKey)) {
msgMap["sign"] = HttpServerUtils.calcSign(timestamp.toString(), clientSignKey.toString())
msgMap["sign"] = HttpServerUtils.calcSign(timestamp.toString(), clientSignKey)
}
if (refresh) pageNum = 1
@ -209,7 +209,7 @@ class SmsQueryFragment : BaseFragment<FragmentClientSmsQueryBinding?>() {
when (HttpServerUtils.clientSafetyMeasures) {
2 -> {
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey.toString())
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey)
try {
requestMsg = Base64.encode(requestMsg.toByteArray())
requestMsg = RSACrypt.encryptByPublicKey(requestMsg, publicKey)
@ -223,7 +223,7 @@ class SmsQueryFragment : BaseFragment<FragmentClientSmsQueryBinding?>() {
}
3 -> {
try {
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey.toString())
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey)
//requestMsg = Base64.encode(requestMsg.toByteArray())
val encryptCBC = SM4Crypt.encrypt(requestMsg.toByteArray(), sm4Key)
requestMsg = ConvertTools.bytes2HexString(encryptCBC)
@ -250,11 +250,11 @@ class SmsQueryFragment : BaseFragment<FragmentClientSmsQueryBinding?>() {
try {
var json = response
if (HttpServerUtils.clientSafetyMeasures == 2) {
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey.toString())
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.toString())
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey)
val encryptCBC = ConvertTools.hexStringToByteArray(json)
val decryptCBC = SM4Crypt.decrypt(encryptCBC, sm4Key)
json = String(decryptCBC)

@ -1,5 +1,6 @@
package com.idormy.sms.forwarder.fragment.client
import android.annotation.SuppressLint
import android.util.Log
import android.view.LayoutInflater
import android.view.View
@ -46,6 +47,7 @@ class SmsSendFragment : BaseFragment<FragmentClientSmsSendBinding?>(), View.OnCl
/**
* 初始化控件
*/
@SuppressLint("SetTextI18n")
override fun initViews() {
//发送按钮增加倒计时,避免重复点击
mCountDownHelper = CountDownButtonHelper(binding!!.btnSubmit, SettingUtils.requestTimeout)
@ -90,7 +92,7 @@ class SmsSendFragment : BaseFragment<FragmentClientSmsSendBinding?>(), View.OnCl
msgMap["timestamp"] = timestamp
val clientSignKey = HttpServerUtils.clientSignKey
if (!TextUtils.isEmpty(clientSignKey)) {
msgMap["sign"] = HttpServerUtils.calcSign(timestamp.toString(), clientSignKey.toString())
msgMap["sign"] = HttpServerUtils.calcSign(timestamp.toString(), clientSignKey)
}
val phoneNumbers = binding!!.etPhoneNumbers.text.toString()
@ -124,7 +126,7 @@ class SmsSendFragment : BaseFragment<FragmentClientSmsSendBinding?>(), View.OnCl
when (HttpServerUtils.clientSafetyMeasures) {
2 -> {
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey.toString())
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey)
try {
requestMsg = Base64.encode(requestMsg.toByteArray())
requestMsg = RSACrypt.encryptByPublicKey(requestMsg, publicKey)
@ -138,7 +140,7 @@ class SmsSendFragment : BaseFragment<FragmentClientSmsSendBinding?>(), View.OnCl
}
3 -> {
try {
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey.toString())
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey)
//requestMsg = Base64.encode(requestMsg.toByteArray())
val encryptCBC = SM4Crypt.encrypt(requestMsg.toByteArray(), sm4Key)
requestMsg = ConvertTools.bytes2HexString(encryptCBC)
@ -167,11 +169,11 @@ class SmsSendFragment : BaseFragment<FragmentClientSmsSendBinding?>(), View.OnCl
try {
var json = response
if (HttpServerUtils.clientSafetyMeasures == 2) {
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey.toString())
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.toString())
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey)
val encryptCBC = ConvertTools.hexStringToByteArray(json)
val decryptCBC = SM4Crypt.decrypt(encryptCBC, sm4Key)
json = String(decryptCBC)

@ -113,7 +113,7 @@ class WolSendFragment : BaseFragment<FragmentClientWolSendBinding?>(), View.OnCl
val clientSignKey = HttpServerUtils.clientSignKey
if (!TextUtils.isEmpty(clientSignKey)) {
msgMap["sign"] =
HttpServerUtils.calcSign(timestamp.toString(), clientSignKey.toString())
HttpServerUtils.calcSign(timestamp.toString(), clientSignKey)
}
val mac = binding!!.etMac.text.toString()
@ -154,7 +154,7 @@ class WolSendFragment : BaseFragment<FragmentClientWolSendBinding?>(), View.OnCl
when (HttpServerUtils.clientSafetyMeasures) {
2 -> {
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey.toString())
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey)
try {
requestMsg = Base64.encode(requestMsg.toByteArray())
requestMsg = RSACrypt.encryptByPublicKey(requestMsg, publicKey)
@ -168,7 +168,7 @@ class WolSendFragment : BaseFragment<FragmentClientWolSendBinding?>(), View.OnCl
}
3 -> {
try {
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey.toString())
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey)
//requestMsg = Base64.encode(requestMsg.toByteArray())
val encryptCBC = SM4Crypt.encrypt(requestMsg.toByteArray(), sm4Key)
requestMsg = ConvertTools.bytes2HexString(encryptCBC)
@ -197,11 +197,11 @@ class WolSendFragment : BaseFragment<FragmentClientWolSendBinding?>(), View.OnCl
try {
var json = response
if (HttpServerUtils.clientSafetyMeasures == 2) {
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey.toString())
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.toString())
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey)
val encryptCBC = ConvertTools.hexStringToByteArray(json)
val decryptCBC = SM4Crypt.decrypt(encryptCBC, sm4Key)
json = String(decryptCBC)

@ -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()
}

@ -16,10 +16,7 @@ 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.utils.*
import com.idormy.sms.forwarder.workers.SendWorker
import com.xuexiang.xutil.resource.ResUtils.getString
import java.util.*
@ -40,8 +37,7 @@ class PhoneStateReceiver : BroadcastReceiver() {
//权限判断
if (ActivityCompat.checkSelfPermission(
context,
Manifest.permission.READ_PHONE_STATE
context, Manifest.permission.READ_PHONE_STATE
) != PackageManager.PERMISSION_GRANTED
) return
@ -66,25 +62,25 @@ class PhoneStateReceiver : BroadcastReceiver() {
//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)
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
}
MMKVUtils.put("CALL_LAST_STATE", state)
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, "来电响铃")
MMKVUtils.put("CALL_IS_INCOMING", true)
//MMKVUtils.put("CALL_START_TIME", Date())
MMKVUtils.put("CALL_SAVED_NUMBER", number)
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 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")
@ -92,28 +88,24 @@ class PhoneStateReceiver : BroadcastReceiver() {
sb.append(getString(R.string.incoming_call))
val msgInfo = MsgInfo("call", number.toString(), sb.toString(), Date(), "", -1)
val request = OneTimeWorkRequestBuilder<SendWorker>()
.setInputData(
val request = OneTimeWorkRequestBuilder<SendWorker>().setInputData(
workDataOf(
Worker.sendMsgInfo to Gson().toJson(msgInfo)
)
)
.build()
).build()
WorkManager.getInstance(context).enqueue(request)
}
}
TelephonyManager.CALL_STATE_OFFHOOK ->
//Transition of ringing->offhook are pickups of incoming calls. Nothing done on them
when {
callIsIncoming = when {
lastState != TelephonyManager.CALL_STATE_RINGING -> {
Log.d(TAG, "去电接通")
MMKVUtils.put("CALL_IS_INCOMING", false)
//MMKVUtils.put("CALL_START_TIME", Date())
false
}
else -> {
Log.d(TAG, "来电接通")
MMKVUtils.put("CALL_IS_INCOMING", true)
//MMKVUtils.put("CALL_START_TIME", Date())
true
}
}
TelephonyManager.CALL_STATE_IDLE ->
@ -121,27 +113,15 @@ class PhoneStateReceiver : BroadcastReceiver() {
when {
lastState == TelephonyManager.CALL_STATE_RINGING -> {
Log.d(TAG, "来电未接")
sendReceiveCallMsg(
context,
3,
MMKVUtils.getString("CALL_SAVED_NUMBER", null)
)
sendReceiveCallMsg(context, 3, callSavedNumber)
}
MMKVUtils.getBoolean("CALL_IS_INCOMING", false) -> {
callIsIncoming -> {
Log.d(TAG, "来电挂机")
sendReceiveCallMsg(
context,
1,
MMKVUtils.getString("CALL_SAVED_NUMBER", null)
)
sendReceiveCallMsg(context, 1, callSavedNumber)
}
else -> {
Log.d(TAG, "去电挂机")
sendReceiveCallMsg(
context,
2,
MMKVUtils.getString("CALL_SAVED_NUMBER", null)
)
sendReceiveCallMsg(context, 2, callSavedNumber)
}
}
}
@ -157,10 +137,7 @@ class PhoneStateReceiver : BroadcastReceiver() {
if (callInfo?.number == null) return
//判断是否开启该类型转发
if ((callInfo.type == 1 && !SettingUtils.enableCallType1)
|| (callInfo.type == 2 && !SettingUtils.enableCallType2)
|| (callInfo.type == 3 && !SettingUtils.enableCallType3)
) {
if ((callInfo.type == 1 && !SettingUtils.enableCallType1) || (callInfo.type == 2 && !SettingUtils.enableCallType2) || (callInfo.type == 3 && !SettingUtils.enableCallType3)) {
Log.w(TAG, "未开启该类型转发type=" + callInfo.type)
return
}
@ -177,25 +154,17 @@ class PhoneStateReceiver : BroadcastReceiver() {
//获取联系人姓名
if (TextUtils.isEmpty(callInfo.name)) {
val contacts = PhoneUtils.getContactByNumber(phoneNumber)
callInfo.name =
if (contacts.isNotEmpty()) contacts[0].name else getString(R.string.unknown_number)
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
"call", callInfo.number, PhoneUtils.getCallMsg(callInfo), Date(), simInfo, simSlot
)
val request = OneTimeWorkRequestBuilder<SendWorker>()
.setInputData(
val request = OneTimeWorkRequestBuilder<SendWorker>().setInputData(
workDataOf(
Worker.sendMsgInfo to Gson().toJson(msgInfo)
)
)
.build()
).build()
WorkManager.getInstance(context).enqueue(request)
}

@ -16,7 +16,7 @@ class AppConfig : WebConfig {
val serverWebPath = HttpServerUtils.serverWebPath
if (!TextUtils.isEmpty(serverWebPath)) {
// 增加一个位于/sdcard/Download/目录下的网站
delegate.addWebsite(StorageWebsite(serverWebPath.toString()))
delegate.addWebsite(StorageWebsite(serverWebPath))
} else {
// 增加一个位于assets的web目录的网站
delegate.addWebsite(AssetsWebsite(context, "/web/"))

@ -36,13 +36,13 @@ class AppExceptionResolver : ExceptionResolver {
Log.d(TAG, "resp: $resp")
when (HttpServerUtils.safetyMeasures) {
2 -> {
val privateKey = RSACrypt.getPrivateKey(HttpServerUtils.serverPrivateKey.toString())
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.toString())
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.serverSm4Key)
//response = Base64.encode(response.toByteArray())
val encryptCBC = SM4Crypt.encrypt(resp.toByteArray(), sm4Key)
response.setBody(StringBody(ConvertTools.bytes2HexString(encryptCBC)))

@ -36,13 +36,13 @@ class AppMessageConverter : MessageConverter {
return when (HttpServerUtils.safetyMeasures) {
2 -> {
val privateKey = RSACrypt.getPrivateKey(HttpServerUtils.serverPrivateKey.toString())
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.toString())
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.serverSm4Key)
//response = Base64.encode(response.toByteArray())
val encryptCBC = SM4Crypt.encrypt(response.toByteArray(), sm4Key)
StringBody(ConvertTools.bytes2HexString(encryptCBC))
@ -65,7 +65,7 @@ class AppMessageConverter : MessageConverter {
throw HttpException(500, "服务端未配置私钥")
}
val privateKey = RSACrypt.getPrivateKey(HttpServerUtils.serverPrivateKey.toString())
val privateKey = RSACrypt.getPrivateKey(HttpServerUtils.serverPrivateKey)
json = RSACrypt.decryptByPrivateKey(json, privateKey)
json = String(Base64.decode(json))
Log.d(TAG, "Json: $json")
@ -75,7 +75,7 @@ class AppMessageConverter : MessageConverter {
throw HttpException(500, "服务端未配置SM4密钥")
}
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.serverSm4Key.toString())
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.serverSm4Key)
val encryptCBC = ConvertTools.hexStringToByteArray(json)
val decryptCBC = SM4Crypt.decrypt(encryptCBC, sm4Key)
//json = String(Base64.decode(decryptCBC.toString()))

@ -37,9 +37,9 @@ class ConfigController {
HttpServerUtils.enableApiContactQuery,
HttpServerUtils.enableApiBatteryQuery,
HttpServerUtils.enableApiWol,
SettingUtils.extraDeviceMark.toString(),
SettingUtils.extraSim1.toString(),
SettingUtils.extraSim2.toString(),
SettingUtils.extraDeviceMark,
SettingUtils.extraSim1,
SettingUtils.extraSim2,
App.SimInfoList,
AppUtils.getAppVersionCode(),
AppUtils.getAppVersionName(),

@ -2,31 +2,14 @@ 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)
}
var timer: Long by SharedPreference(CACTUS_TIMER, 0L)
//Cactus上次存活时间
var lastTimer: Long
get() = MMKVUtils.getLong(CACTUS_LAST_TIMER, 0L)
set(timer) {
MMKVUtils.put(CACTUS_LAST_TIMER, timer)
}
var lastTimer: Long by SharedPreference(CACTUS_LAST_TIMER, 0L)
//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)
}
var date: String by SharedPreference(SP_EXTRA_DEVICE_MARK, "0000-01-01 00:00:00")
//Cactus结束时间
var endDate: String?
get() = MMKVUtils.getString(CACTUS_DATE, "0000-01-01 00:00:00")
set(extraDeviceMark) {
MMKVUtils.put(CACTUS_END_DATE, extraDeviceMark)
}
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
}
//删除全部数据
fun clearPreference() = preference.edit().clear().apply()
/**
* 根据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删除存储数据
fun clearPreference(key: String) = preference.edit().remove(key).commit()
}
/**
* 根据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
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
return putPreference(name, value)
}
/**
* 根据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
override fun getValue(thisRef: Any?, property: KProperty<*>): T {
return getPreference(name, default)
}
/**
* 根据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
* 查找数据 返回给调用方法一个具体的对象
* 如果查找不到类型就采用反序列化方法来返回类型
* default是默认对象 以防止会返回空对象的异常
* 即如果name没有查找到value 就返回默认的序列化对象然后经过反序列化返回
*/
fun getDouble(key: String?, defValue: Double): Double {
try {
return getsMMKV()!!.decodeDouble(key, defValue)
} catch (e: Exception) {
e.printStackTrace()
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 defValue
return res as T
}
/**
* 获取对象
*
* @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)
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()
}
/**
* 清除指定键值对
*
* @param key
* 序列化对象
* @throws IOException
*/
fun remove(key: String?) {
getsMMKV()!!.remove(key).apply()
@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
*/
fun clear() {
getsMMKV()!!.clearAll()
}
}
init {
throw UnsupportedOperationException("u can't instantiate me...")
@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
}
}

@ -25,172 +25,67 @@ 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)
}
var enableServerAutorun: Boolean by SharedPreference(SP_ENABLE_SERVER_AUTORUN, true)
//服务端签名密钥
var serverSignKey: String by SharedPreference(SP_SERVER_SIGN_KEY, "")
//服务端安全设置
@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)
}
var safetyMeasures: Int by SharedPreference(SP_SERVER_SAFETY_MEASURES, if (TextUtils.isEmpty(serverSignKey)) 0 else 1)
//服务端SM4密钥
@JvmStatic
var serverSm4Key: String?
get() = MMKVUtils.getString(SP_SERVER_SM4_KEY, "")
set(serverSm4Key) {
MMKVUtils.put(SP_SERVER_SM4_KEY, serverSm4Key)
}
var serverSm4Key: String by SharedPreference(SP_SERVER_SM4_KEY, "")
//服务端RSA公钥
@JvmStatic
var serverPublicKey: String?
get() = MMKVUtils.getString(SP_SERVER_PUBLIC_KEY, "")
set(serverPublicKey) {
MMKVUtils.put(SP_SERVER_PUBLIC_KEY, serverPublicKey)
}
var serverPublicKey: String by SharedPreference(SP_SERVER_PUBLIC_KEY, "")
//服务端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)
}
var serverPrivateKey: String by SharedPreference(SP_SERVER_PRIVATE_KEY, "")
//时间容差
@JvmStatic
var timeTolerance: Int
get() = MMKVUtils.getInt(SP_SERVER_TIME_TOLERANCE, 600)
set(timeTolerance) {
MMKVUtils.put(SP_SERVER_TIME_TOLERANCE, timeTolerance)
}
var timeTolerance: Int by SharedPreference(SP_SERVER_TIME_TOLERANCE, 600)
//自定义web客户端目录
@JvmStatic
var serverWebPath: String?
get() = MMKVUtils.getString(SP_SERVER_WEB_PATH, "")
set(serverWebPath) {
MMKVUtils.put(SP_SERVER_WEB_PATH, serverWebPath)
}
var serverWebPath: String by SharedPreference(SP_SERVER_WEB_PATH, "")
//服务地址
@JvmStatic
var serverAddress: String?
get() = MMKVUtils.getString(SP_SERVER_ADDRESS, "")
set(clientSignKey) {
MMKVUtils.put(SP_SERVER_ADDRESS, clientSignKey)
}
var serverAddress: String by SharedPreference(SP_SERVER_ADDRESS, "")
//服务地址历史记录
@JvmStatic
var serverHistory: String?
get() = MMKVUtils.getString(SP_SERVER_HISTORY, "")
set(serverHistory) {
MMKVUtils.put(SP_SERVER_HISTORY, serverHistory)
}
var serverHistory: String by SharedPreference(SP_SERVER_HISTORY, "")
//服务端配置
@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)
}
var serverConfig: String by SharedPreference(SP_SERVER_CONFIG, "")
//客户端签名密钥/RSA公钥
@JvmStatic
var clientSignKey: String?
get() = MMKVUtils.getString(SP_CLIENT_SIGN_KEY, "")
set(clientSignKey) {
MMKVUtils.put(SP_CLIENT_SIGN_KEY, clientSignKey)
}
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)
//是否启用一键克隆
@JvmStatic
var enableApiClone: Boolean
get() = MMKVUtils.getBoolean(SP_ENABLE_API_CLONE, false)
set(enableApiClone) {
MMKVUtils.put(SP_ENABLE_API_CLONE, enableApiClone)
}
var enableApiClone: Boolean by SharedPreference(SP_ENABLE_API_CLONE, true)
//是否启用远程发短信
@JvmStatic
var enableApiSmsSend: Boolean
get() = MMKVUtils.getBoolean(SP_ENABLE_API_SMS_SEND, false)
set(enableApiSendSms) {
MMKVUtils.put(SP_ENABLE_API_SMS_SEND, enableApiSendSms)
}
var enableApiSmsSend: Boolean by SharedPreference(SP_ENABLE_API_SMS_SEND, true)
//是否启用远程查短信
@JvmStatic
var enableApiSmsQuery: Boolean
get() = MMKVUtils.getBoolean(SP_ENABLE_API_SMS_QUERY, false)
set(enableApiQuerySms) {
MMKVUtils.put(SP_ENABLE_API_SMS_QUERY, enableApiQuerySms)
}
var enableApiSmsQuery: Boolean by SharedPreference(SP_ENABLE_API_SMS_QUERY, true)
//是否启用远程查通话
@JvmStatic
var enableApiCallQuery: Boolean
get() = MMKVUtils.getBoolean(SP_ENABLE_API_CALL_QUERY, false)
set(enableApiQueryCall) {
MMKVUtils.put(SP_ENABLE_API_CALL_QUERY, enableApiQueryCall)
}
var enableApiCallQuery: Boolean by SharedPreference(SP_ENABLE_API_CALL_QUERY, true)
//是否启用远程查话簿
@JvmStatic
var enableApiContactQuery: Boolean
get() = MMKVUtils.getBoolean(SP_ENABLE_API_CONTACT_QUERY, false)
set(enableApiQueryLinkman) {
MMKVUtils.put(SP_ENABLE_API_CONTACT_QUERY, enableApiQueryLinkman)
}
var enableApiContactQuery: Boolean by SharedPreference(SP_ENABLE_API_CONTACT_QUERY, true)
//是否启用远程查电量
@JvmStatic
var enableApiBatteryQuery: Boolean
get() = MMKVUtils.getBoolean(SP_ENABLE_API_BATTERY_QUERY, false)
set(enableApiQueryBattery) {
MMKVUtils.put(SP_ENABLE_API_BATTERY_QUERY, enableApiQueryBattery)
}
var enableApiBatteryQuery: Boolean by SharedPreference(SP_ENABLE_API_BATTERY_QUERY, true)
//是否启用远程WOL
@JvmStatic
var enableApiWol: Boolean
get() = MMKVUtils.getBoolean(SP_ENABLE_API_WOL, false)
set(enableApiWol) {
MMKVUtils.put(SP_ENABLE_API_WOL, enableApiWol)
}
var enableApiWol: Boolean by SharedPreference(SP_ENABLE_API_WOL, true)
//WOL历史记录
@JvmStatic
var wolHistory: String?
get() = MMKVUtils.getString(SP_WOL_HISTORY, "")
set(wolHistory) {
MMKVUtils.put(SP_WOL_HISTORY, wolHistory)
}
var wolHistory: String by SharedPreference(SP_WOL_HISTORY, "")
//计算签名
fun calcSign(timestamp: String, signSecret: String): String {
@ -217,7 +112,7 @@ class HttpServerUtils private constructor() {
throw HttpException(500, String.format(getString(R.string.timestamp_verify_failed), timestamp, timeTolerance, diffTime))
}
val sign = calcSign(req.timestamp.toString(), signSecret.toString())
val sign = calcSign(req.timestamp.toString(), signSecret)
if (sign != req.sign) {
Log.e("calcSign", sign)
Log.e("reqSign", req.sign.toString())
@ -298,7 +193,7 @@ class HttpServerUtils private constructor() {
SettingUtils.batteryLevelMax = cloneInfo.batteryLevelMax
SettingUtils.batteryLevelOnce = cloneInfo.batteryLevelOnce
SettingUtils.enableBatteryCron = cloneInfo.enableBatteryCron
SettingUtils.batteryCronStartTime = cloneInfo.batteryCronStartTime
SettingUtils.batteryCronStartTime = cloneInfo.batteryCronStartTime.toString()
SettingUtils.batteryCronInterval = cloneInfo.batteryCronInterval
SettingUtils.enableExcludeFromRecents = cloneInfo.enableExcludeFromRecents
SettingUtils.enableCactus = cloneInfo.enableCactus
@ -307,9 +202,9 @@ class HttpServerUtils private constructor() {
SettingUtils.requestRetryTimes = cloneInfo.requestRetryTimes
SettingUtils.requestDelayTime = cloneInfo.requestDelayTime
SettingUtils.requestTimeout = cloneInfo.requestTimeout
SettingUtils.notifyContent = cloneInfo.notifyContent
SettingUtils.notifyContent = cloneInfo.notifyContent.toString()
SettingUtils.enableSmsTemplate = cloneInfo.enableSmsTemplate
SettingUtils.smsTemplate = cloneInfo.smsTemplate
SettingUtils.smsTemplate = cloneInfo.smsTemplate.toString()
SettingUtils.enableHelpTip = cloneInfo.enableHelpTip
SettingUtils.enablePureClientMode = cloneInfo.enablePureClientMode
//删除发送通道、转发规则、转发日志
@ -356,7 +251,7 @@ class HttpServerUtils private constructor() {
resp["data"] = output
}
if (safetyMeasures == 1) {
resp["sign"] = calcSign(timestamp.toString(), serverSignKey.toString())
resp["sign"] = calcSign(timestamp.toString(), serverSignKey)
}
}

@ -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 {

@ -43,17 +43,15 @@ class SendWorker(
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 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 periodStart = timeFmt.parse(periodStartStr, ParsePosition(0))?.time
val periodEnd = timeFmt.parse(periodEndStr, ParsePosition(0))?.time
val now = System.currentTimeMillis()
if (now in periodStart..periodEnd) {
if (periodStart != null && periodEnd != null && now in periodStart..periodEnd) {
Log.e("SendWorker", "免打扰(禁用转发)时间段")
return@withContext Result.failure(workDataOf("send" to "failed"))
}
@ -67,20 +65,17 @@ class SendWorker(
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) {
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"))
}
}
HistoryUtils.put(key, timestamp)
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)
val ruleList: List<RuleAndSender> = Core.rule.getRuleAndSender(msgInfo.type, 1, simSlot)
if (ruleList.isEmpty()) {
return@withContext Result.failure(workDataOf("send" to "failed"))
}
@ -88,12 +83,7 @@ class SendWorker(
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
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)

@ -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…
Cancel
Save