From 51149c95cd33ca6255bf4bf5fe58ecf3b0afb73e Mon Sep 17 00:00:00 2001
From: pppscn <35696959@qq.com>
Date: Wed, 6 Dec 2023 15:23:11 +0800
Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=EF=BC=9A=E8=87=AA=E5=8A=A8?=
=?UTF-8?q?=E4=BB=BB=E5=8A=A1=C2=B7=E5=BF=AB=E6=8D=B7=E6=8C=87=E4=BB=A4=20?=
=?UTF-8?q?=E2=80=94=E2=80=94=20=E5=AE=9A=E6=97=B6=E4=BB=BB=E5=8A=A1?=
=?UTF-8?q?=EF=BC=88AlarmManager=E6=96=B9=E6=A1=88=EF=BC=89=20#279?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
踩坑:间隔时间不准确,低于5秒,间隔成5秒
---
app/src/main/AndroidManifest.xml | 1 +
.../sms/forwarder/entity/task/SmsSetting.kt | 10 ++
.../forwarder/fragment/TasksEditFragment.kt | 55 ++++++-
.../forwarder/fragment/action/FrpcFragment.kt | 10 +-
.../fragment/action/HttpServerFragment.kt | 10 +-
.../fragment/action/NotificationFragment.kt | 2 +-
.../fragment/action/SendSmsFragment.kt | 128 ++++++++++++---
.../fragment/condition/BatteryFragment.kt | 8 +-
.../fragment/condition/ChargeFragment.kt | 8 +-
.../fragment/condition/CronFragment.kt | 8 +-
.../fragment/condition/NetworkFragment.kt | 8 +-
.../sms/forwarder/receiver/AlarmReceiver.kt | 62 ++++++-
.../forwarder/service/ForegroundService.kt | 2 +-
.../idormy/sms/forwarder/utils/Constants.kt | 1 +
.../sms/forwarder/utils/task/CronUtils.kt | 57 +++----
.../res/layout/fragment_client_sms_send.xml | 20 ++-
.../layout/fragment_tasks_action_send_sms.xml | 152 ++++++++++++++++++
....xml => fragment_tasks_condition_cron.xml} | 0
.../main/res/layout/fragment_tasks_edit.xml | 1 +
app/src/main/res/values-en/strings.xml | 6 +-
app/src/main/res/values/strings.xml | 6 +-
app/src/main/res/values/styles_widget.xml | 1 +
22 files changed, 460 insertions(+), 96 deletions(-)
create mode 100644 app/src/main/java/com/idormy/sms/forwarder/entity/task/SmsSetting.kt
create mode 100644 app/src/main/res/layout/fragment_tasks_action_send_sms.xml
rename app/src/main/res/layout/{fragment_tasks_cron.xml => fragment_tasks_condition_cron.xml} (100%)
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index ebc18951..eed96ea7 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -19,6 +19,7 @@
tools:ignore="ProtectedPermissions" />
+
(), View.OnClic
val taskNew = checkForm()
if (isClone) taskNew.id = 0
Log.d(TAG, taskNew.toString())
+ //应用任务
+ applyTask(taskNew)
+ //保存任务
viewModel.insertOrUpdate(taskNew)
XToastUtils.success(R.string.tipSaveSuccess)
popToBack()
@@ -254,6 +259,7 @@ class TasksEditFragment : BaseFragment(), View.OnClic
}
Log.d(TAG, "initForm: $itemListConditions")
conditionsAdapter.notifyDataSetChanged()
+ binding!!.layoutAddCondition.visibility = if (itemListConditions.size >= MAX_SETTING_NUM) View.GONE else View.VISIBLE
}
if (task.actions.isNotEmpty()) {
val actionList = Gson().fromJson(task.actions, Array::class.java).toMutableList()
@@ -262,6 +268,7 @@ class TasksEditFragment : BaseFragment(), View.OnClic
}
Log.d(TAG, "initForm: $itemListActions")
actionsAdapter.notifyDataSetChanged()
+ binding!!.layoutAddAction.visibility = if (itemListActions.size >= MAX_SETTING_NUM) View.GONE else View.VISIBLE
}
} catch (e: Exception) {
e.printStackTrace()
@@ -283,7 +290,23 @@ class TasksEditFragment : BaseFragment(), View.OnClic
if (itemListActions.size <= 0) {
throw Exception("请添加执行动作")
}
- taskType = itemListConditions[0].type
+
+ val lastExecTime = Date()
+ var nextExecTime = Date()
+ val firstCondition = itemListConditions[0]
+ taskType = firstCondition.type
+
+ when (taskType) {
+ TASK_CONDITION_CRON -> {
+ //检查定时任务的时间设置
+ val cronSetting = Gson().fromJson(firstCondition.setting, CronSetting::class.java)
+ if (cronSetting.expression.isEmpty()) {
+ throw Exception("请设置定时任务的时间")
+ }
+ val cronExpression = CronExpression(cronSetting.expression)
+ nextExecTime = cronExpression.getNextValidTimeAfter(lastExecTime)
+ }
+ }
//拼接任务描述
val description = StringBuilder()
@@ -293,12 +316,36 @@ class TasksEditFragment : BaseFragment(), View.OnClic
description.append(itemListActions.map { it.description }.toTypedArray().joinToString(","))
val status = if (binding!!.sbStatus.isChecked) STATUS_ON else STATUS_OFF
- return Task(taskId, taskType, taskName, description.toString(), Gson().toJson(itemListConditions), Gson().toJson(itemListActions), status)
+ return Task(
+ taskId,
+ taskType,
+ taskName,
+ description.toString(),
+ Gson().toJson(itemListConditions),
+ Gson().toJson(itemListActions),
+ status,
+ lastExecTime,
+ nextExecTime
+ )
}
+ //测试任务
private fun testTask(task: Task) {
}
+ //应用任务
+ private fun applyTask(task: Task) {
+ when (task.type) {
+ //定时任务
+ TASK_CONDITION_CRON -> {
+ //取消旧任务的定时器
+ CronUtils.cancelAlarm(task)
+ //设置新的定时器
+ CronUtils.scheduleAlarm(task)
+ }
+ }
+ }
+
@SingleClick
override fun onItemClick(itemView: View, widgetInfo: PageInfo, pos: Int) {
try {
@@ -394,6 +441,7 @@ class TasksEditFragment : BaseFragment(), View.OnClic
itemListConditions[requestCode - 1] = taskSetting
}
conditionsAdapter.notifyDataSetChanged()
+ binding!!.layoutAddCondition.visibility = if (itemListConditions.size >= MAX_SETTING_NUM) View.GONE else View.VISIBLE
} else if (resultCode in KEY_BACK_CODE_ACTION..KEY_BACK_CODE_ACTION + 999) {
setting = extras!!.getString(KEY_BACK_DATA_ACTION)
if (setting == null) return
@@ -448,6 +496,7 @@ class TasksEditFragment : BaseFragment(), View.OnClic
itemListActions[requestCode - 1] = taskSetting
}
actionsAdapter.notifyDataSetChanged()
+ binding!!.layoutAddAction.visibility = if (itemListActions.size >= MAX_SETTING_NUM) View.GONE else View.VISIBLE
}
Log.d(TAG, "requestCode:$requestCode resultCode:$resultCode setting:$setting")
}
@@ -487,6 +536,7 @@ class TasksEditFragment : BaseFragment(), View.OnClic
private fun removeCondition(position: Int) {
itemListConditions.removeAt(position)
conditionsAdapter.notifyItemRemoved(position)
+ binding!!.layoutAddCondition.visibility = if (itemListConditions.size >= MAX_SETTING_NUM) View.GONE else View.VISIBLE
}
private fun editAction(position: Int) {
@@ -507,5 +557,6 @@ class TasksEditFragment : BaseFragment(), View.OnClic
private fun removeAction(position: Int) {
itemListActions.removeAt(position)
actionsAdapter.notifyItemRemoved(position)
+ binding!!.layoutAddAction.visibility = if (itemListActions.size >= MAX_SETTING_NUM) View.GONE else View.VISIBLE
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/idormy/sms/forwarder/fragment/action/FrpcFragment.kt b/app/src/main/java/com/idormy/sms/forwarder/fragment/action/FrpcFragment.kt
index 0c63a125..a5bfd184 100644
--- a/app/src/main/java/com/idormy/sms/forwarder/fragment/action/FrpcFragment.kt
+++ b/app/src/main/java/com/idormy/sms/forwarder/fragment/action/FrpcFragment.kt
@@ -9,7 +9,7 @@ import android.view.ViewGroup
import com.google.gson.Gson
import com.idormy.sms.forwarder.R
import com.idormy.sms.forwarder.core.BaseFragment
-import com.idormy.sms.forwarder.databinding.FragmentTasksCronBinding
+import com.idormy.sms.forwarder.databinding.FragmentTasksActionSendSmsBinding
import com.idormy.sms.forwarder.entity.task.CronSetting
import com.idormy.sms.forwarder.utils.KEY_BACK_DATA_ACTION
import com.idormy.sms.forwarder.utils.KEY_EVENT_DATA_ACTION
@@ -26,7 +26,7 @@ import com.xuexiang.xui.widget.actionbar.TitleBar
@Page(name = "Frpc")
@Suppress("PrivatePropertyName")
-class FrpcFragment : BaseFragment(), View.OnClickListener {
+class FrpcFragment : BaseFragment(), View.OnClickListener {
private val TAG: String = FrpcFragment::class.java.simpleName
var titleBar: TitleBar? = null
@@ -46,12 +46,12 @@ class FrpcFragment : BaseFragment(), View.OnClickList
override fun viewBindingInflate(
inflater: LayoutInflater,
container: ViewGroup,
- ): FragmentTasksCronBinding {
- return FragmentTasksCronBinding.inflate(inflater, container, false)
+ ): FragmentTasksActionSendSmsBinding {
+ return FragmentTasksActionSendSmsBinding.inflate(inflater, container, false)
}
override fun initTitle(): TitleBar? {
- titleBar = super.initTitle()!!.setImmersive(false).setTitle(R.string.task_cron)
+ titleBar = super.initTitle()!!.setImmersive(false).setTitle(R.string.task_frpc)
return titleBar
}
diff --git a/app/src/main/java/com/idormy/sms/forwarder/fragment/action/HttpServerFragment.kt b/app/src/main/java/com/idormy/sms/forwarder/fragment/action/HttpServerFragment.kt
index 58bb4570..33975eef 100644
--- a/app/src/main/java/com/idormy/sms/forwarder/fragment/action/HttpServerFragment.kt
+++ b/app/src/main/java/com/idormy/sms/forwarder/fragment/action/HttpServerFragment.kt
@@ -9,7 +9,7 @@ import android.view.ViewGroup
import com.google.gson.Gson
import com.idormy.sms.forwarder.R
import com.idormy.sms.forwarder.core.BaseFragment
-import com.idormy.sms.forwarder.databinding.FragmentTasksCronBinding
+import com.idormy.sms.forwarder.databinding.FragmentTasksActionSendSmsBinding
import com.idormy.sms.forwarder.entity.task.CronSetting
import com.idormy.sms.forwarder.utils.KEY_BACK_DATA_ACTION
import com.idormy.sms.forwarder.utils.KEY_EVENT_DATA_ACTION
@@ -26,7 +26,7 @@ import com.xuexiang.xui.widget.actionbar.TitleBar
@Page(name = "HttpServer")
@Suppress("PrivatePropertyName")
-class HttpServerFragment : BaseFragment(), View.OnClickListener {
+class HttpServerFragment : BaseFragment(), View.OnClickListener {
private val TAG: String = HttpServerFragment::class.java.simpleName
var titleBar: TitleBar? = null
@@ -46,12 +46,12 @@ class HttpServerFragment : BaseFragment(), View.OnCli
override fun viewBindingInflate(
inflater: LayoutInflater,
container: ViewGroup,
- ): FragmentTasksCronBinding {
- return FragmentTasksCronBinding.inflate(inflater, container, false)
+ ): FragmentTasksActionSendSmsBinding {
+ return FragmentTasksActionSendSmsBinding.inflate(inflater, container, false)
}
override fun initTitle(): TitleBar? {
- titleBar = super.initTitle()!!.setImmersive(false).setTitle(R.string.task_cron)
+ titleBar = super.initTitle()!!.setImmersive(false).setTitle(R.string.task_server)
return titleBar
}
diff --git a/app/src/main/java/com/idormy/sms/forwarder/fragment/action/NotificationFragment.kt b/app/src/main/java/com/idormy/sms/forwarder/fragment/action/NotificationFragment.kt
index 9cc81864..c2cab18c 100644
--- a/app/src/main/java/com/idormy/sms/forwarder/fragment/action/NotificationFragment.kt
+++ b/app/src/main/java/com/idormy/sms/forwarder/fragment/action/NotificationFragment.kt
@@ -107,7 +107,7 @@ class NotificationFragment : BaseFragment(), View.OnC
override fun initTitle(): TitleBar? {
titleBar = super.initTitle()!!.setImmersive(false)
- titleBar!!.setTitle(R.string.menu_rules)
+ titleBar!!.setTitle(R.string.task_notification)
return titleBar
}
diff --git a/app/src/main/java/com/idormy/sms/forwarder/fragment/action/SendSmsFragment.kt b/app/src/main/java/com/idormy/sms/forwarder/fragment/action/SendSmsFragment.kt
index d8295c84..3cf3bcbe 100644
--- a/app/src/main/java/com/idormy/sms/forwarder/fragment/action/SendSmsFragment.kt
+++ b/app/src/main/java/com/idormy/sms/forwarder/fragment/action/SendSmsFragment.kt
@@ -1,19 +1,32 @@
package com.idormy.sms.forwarder.fragment.action
+import android.Manifest
import android.annotation.SuppressLint
import android.content.Intent
+import android.content.pm.PackageManager
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import androidx.core.app.ActivityCompat
import com.google.gson.Gson
+import com.google.gson.reflect.TypeToken
+import com.hjq.permissions.OnPermissionCallback
+import com.hjq.permissions.Permission
+import com.hjq.permissions.XXPermissions
+import com.idormy.sms.forwarder.App
import com.idormy.sms.forwarder.R
import com.idormy.sms.forwarder.core.BaseFragment
-import com.idormy.sms.forwarder.databinding.FragmentTasksCronBinding
-import com.idormy.sms.forwarder.entity.task.CronSetting
+import com.idormy.sms.forwarder.databinding.FragmentTasksActionSendSmsBinding
+import com.idormy.sms.forwarder.entity.task.SmsSetting
+import com.idormy.sms.forwarder.server.model.ConfigData
+import com.idormy.sms.forwarder.utils.EVENT_KEY_PHONE_NUMBERS
+import com.idormy.sms.forwarder.utils.EVENT_KEY_SIM_SLOT
+import com.idormy.sms.forwarder.utils.HttpServerUtils
import com.idormy.sms.forwarder.utils.KEY_BACK_DATA_ACTION
import com.idormy.sms.forwarder.utils.KEY_EVENT_DATA_ACTION
import com.idormy.sms.forwarder.utils.KEY_TEST_ACTION
+import com.idormy.sms.forwarder.utils.PhoneUtils
import com.idormy.sms.forwarder.utils.TASK_ACTION_SENDSMS
import com.idormy.sms.forwarder.utils.XToastUtils
import com.jeremyliao.liveeventbus.LiveEventBus
@@ -21,12 +34,15 @@ import com.xuexiang.xaop.annotation.SingleClick
import com.xuexiang.xpage.annotation.Page
import com.xuexiang.xrouter.annotation.AutoWired
import com.xuexiang.xrouter.launcher.XRouter
+import com.xuexiang.xrouter.utils.TextUtils
import com.xuexiang.xui.utils.CountDownButtonHelper
+import com.xuexiang.xui.utils.ResUtils
import com.xuexiang.xui.widget.actionbar.TitleBar
+import com.xuexiang.xutil.XUtil
@Page(name = "SendSms")
-@Suppress("PrivatePropertyName")
-class SendSmsFragment : BaseFragment(), View.OnClickListener {
+@Suppress("PrivatePropertyName", "DEPRECATION")
+class SendSmsFragment : BaseFragment(), View.OnClickListener {
private val TAG: String = SendSmsFragment::class.java.simpleName
var titleBar: TitleBar? = null
@@ -36,8 +52,10 @@ class SendSmsFragment : BaseFragment(), View.OnClickL
@AutoWired(name = KEY_EVENT_DATA_ACTION)
var eventData: String? = null
- private var expression = "* * * * * ? *"
- private var description = "测试描述"
+ private var description = ""
+ private var simSlot = 1
+ private var phoneNumbers = ""
+ private var msgContent = ""
override fun initArgs() {
XRouter.getInstance().inject(this)
@@ -46,18 +64,19 @@ class SendSmsFragment : BaseFragment(), View.OnClickL
override fun viewBindingInflate(
inflater: LayoutInflater,
container: ViewGroup,
- ): FragmentTasksCronBinding {
- return FragmentTasksCronBinding.inflate(inflater, container, false)
+ ): FragmentTasksActionSendSmsBinding {
+ return FragmentTasksActionSendSmsBinding.inflate(inflater, container, false)
}
override fun initTitle(): TitleBar? {
- titleBar = super.initTitle()!!.setImmersive(false).setTitle(R.string.task_cron)
+ titleBar = super.initTitle()!!.setImmersive(false).setTitle(R.string.task_sendsms)
return titleBar
}
/**
* 初始化控件
*/
+ @SuppressLint("SetTextI18n")
override fun initViews() {
//测试按钮增加倒计时,避免重复点击
mCountDownHelper = CountDownButtonHelper(binding!!.btnTest, 3)
@@ -71,11 +90,27 @@ class SendSmsFragment : BaseFragment(), View.OnClickL
}
})
+ //卡槽信息
+ val serverConfigStr = HttpServerUtils.serverConfig
+ if (!TextUtils.isEmpty(serverConfigStr)) {
+ val serverConfig: ConfigData = Gson().fromJson(serverConfigStr, object : TypeToken() {}.type)
+ binding!!.rbSimSlot1.text = "SIM1:" + serverConfig.extraSim1
+ binding!!.rbSimSlot2.text = "SIM2:" + serverConfig.extraSim2
+ }
+
Log.d(TAG, "initViews eventData:$eventData")
if (eventData != null) {
- val settingVo = Gson().fromJson(eventData, CronSetting::class.java)
+ val settingVo = Gson().fromJson(eventData, SmsSetting::class.java)
Log.d(TAG, "initViews settingVo:$settingVo")
+
+ simSlot = settingVo.simSlot
+ phoneNumbers = settingVo.phoneNumbers
+ msgContent = settingVo.msgContent
}
+
+ binding!!.rgSimSlot.check(if (simSlot == 1) R.id.rb_sim_slot_1 else R.id.rb_sim_slot_2)
+ binding!!.etPhoneNumbers.setText(phoneNumbers)
+ binding!!.etMsgContent.setText(msgContent)
}
@SuppressLint("SetTextI18n")
@@ -83,6 +118,12 @@ class SendSmsFragment : BaseFragment(), View.OnClickL
binding!!.btnTest.setOnClickListener(this)
binding!!.btnDel.setOnClickListener(this)
binding!!.btnSave.setOnClickListener(this)
+ LiveEventBus.get(EVENT_KEY_SIM_SLOT, Int::class.java).observeSticky(this) { value: Int ->
+ binding!!.rgSimSlot.check(if (value == 1) R.id.rb_sim_slot_2 else R.id.rb_sim_slot_1)
+ }
+ LiveEventBus.get(EVENT_KEY_PHONE_NUMBERS, String::class.java).observeSticky(this) { value: String ->
+ binding!!.etPhoneNumbers.setText(value)
+ }
LiveEventBus.get(KEY_TEST_ACTION, String::class.java).observe(this) {
mCountDownHelper?.finish()
@@ -100,16 +141,45 @@ class SendSmsFragment : BaseFragment(), View.OnClickL
when (v.id) {
R.id.btn_test -> {
mCountDownHelper?.start()
- Thread {
- try {
- val settingVo = checkSetting()
- Log.d(TAG, settingVo.toString())
- LiveEventBus.get(KEY_TEST_ACTION, String::class.java).post("success")
- } catch (e: Exception) {
- LiveEventBus.get(KEY_TEST_ACTION, String::class.java).post(e.message.toString())
- e.printStackTrace()
- }
- }.start()
+
+ //检查发送短信权限是否获取
+ XXPermissions.with(this)
+ .permission(Permission.SEND_SMS)
+ .request(object : OnPermissionCallback {
+ override fun onGranted(permissions: List, all: Boolean) {
+ Thread {
+ try {
+ val settingVo = checkSetting()
+ Log.d(TAG, settingVo.toString())
+
+ //获取卡槽信息
+ if (App.SimInfoList.isEmpty()) {
+ App.SimInfoList = PhoneUtils.getSimMultiInfo()
+ }
+ Log.d(TAG, App.SimInfoList.toString())
+
+ //发送卡槽: 1=SIM1, 2=SIM2
+ val simSlotIndex = settingVo.simSlot - 1
+ //TODO:取不到卡槽信息时,采用默认卡槽发送
+ val mSubscriptionId: Int = App.SimInfoList[simSlotIndex]?.mSubscriptionId ?: -1
+
+ val msg = if (ActivityCompat.checkSelfPermission(XUtil.getContext(), Manifest.permission.SEND_SMS) != PackageManager.PERMISSION_GRANTED) {
+ ResUtils.getString(R.string.no_sms_sending_permission)
+ } else {
+ PhoneUtils.sendSms(mSubscriptionId, settingVo.phoneNumbers, settingVo.msgContent) ?: "success"
+ }
+ LiveEventBus.get(KEY_TEST_ACTION, String::class.java).post(msg)
+ } catch (e: Exception) {
+ LiveEventBus.get(KEY_TEST_ACTION, String::class.java).post(e.message.toString())
+ e.printStackTrace()
+ }
+ }.start()
+ }
+
+ override fun onDenied(permissions: List, never: Boolean) {
+ LiveEventBus.get(KEY_TEST_ACTION, String::class.java).post(ResUtils.getString(R.string.no_sms_sending_permission))
+ }
+ })
return
}
@@ -135,7 +205,21 @@ class SendSmsFragment : BaseFragment(), View.OnClickL
//检查设置
@SuppressLint("SetTextI18n")
- private fun checkSetting(): CronSetting {
- return CronSetting(expression, description)
+ private fun checkSetting(): SmsSetting {
+ phoneNumbers = binding!!.etPhoneNumbers.text.toString().trim()
+ if (!getString(R.string.phone_numbers_regex).toRegex().matches(phoneNumbers)) {
+ throw Exception(getString(R.string.phone_numbers_error))
+ }
+
+ msgContent = binding!!.etMsgContent.text.toString().trim()
+ if (!getString(R.string.msg_content_regex).toRegex().matches(msgContent)) {
+ throw Exception(getString(R.string.msg_content_error))
+ }
+
+ simSlot = if (binding!!.rgSimSlot.checkedRadioButtonId == R.id.rb_sim_slot_2) 2 else 1
+
+ description = String.format(getString(R.string.send_sms_to), simSlot, phoneNumbers)
+
+ return SmsSetting(description, simSlot, phoneNumbers, msgContent)
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/idormy/sms/forwarder/fragment/condition/BatteryFragment.kt b/app/src/main/java/com/idormy/sms/forwarder/fragment/condition/BatteryFragment.kt
index 67042c60..58347773 100644
--- a/app/src/main/java/com/idormy/sms/forwarder/fragment/condition/BatteryFragment.kt
+++ b/app/src/main/java/com/idormy/sms/forwarder/fragment/condition/BatteryFragment.kt
@@ -9,7 +9,7 @@ import android.view.ViewGroup
import com.google.gson.Gson
import com.idormy.sms.forwarder.R
import com.idormy.sms.forwarder.core.BaseFragment
-import com.idormy.sms.forwarder.databinding.FragmentTasksCronBinding
+import com.idormy.sms.forwarder.databinding.FragmentTasksActionSendSmsBinding
import com.idormy.sms.forwarder.entity.task.CronSetting
import com.idormy.sms.forwarder.utils.KEY_BACK_DATA_CONDITION
import com.idormy.sms.forwarder.utils.KEY_EVENT_DATA_CONDITION
@@ -26,7 +26,7 @@ import com.xuexiang.xui.widget.actionbar.TitleBar
@Page(name = "Battery")
@Suppress("PrivatePropertyName")
-class BatteryFragment : BaseFragment(), View.OnClickListener {
+class BatteryFragment : BaseFragment(), View.OnClickListener {
private val TAG: String = BatteryFragment::class.java.simpleName
var titleBar: TitleBar? = null
@@ -46,8 +46,8 @@ class BatteryFragment : BaseFragment(), View.OnClickL
override fun viewBindingInflate(
inflater: LayoutInflater,
container: ViewGroup,
- ): FragmentTasksCronBinding {
- return FragmentTasksCronBinding.inflate(inflater, container, false)
+ ): FragmentTasksActionSendSmsBinding {
+ return FragmentTasksActionSendSmsBinding.inflate(inflater, container, false)
}
override fun initTitle(): TitleBar? {
diff --git a/app/src/main/java/com/idormy/sms/forwarder/fragment/condition/ChargeFragment.kt b/app/src/main/java/com/idormy/sms/forwarder/fragment/condition/ChargeFragment.kt
index a4361015..fd2f3313 100644
--- a/app/src/main/java/com/idormy/sms/forwarder/fragment/condition/ChargeFragment.kt
+++ b/app/src/main/java/com/idormy/sms/forwarder/fragment/condition/ChargeFragment.kt
@@ -9,7 +9,7 @@ import android.view.ViewGroup
import com.google.gson.Gson
import com.idormy.sms.forwarder.R
import com.idormy.sms.forwarder.core.BaseFragment
-import com.idormy.sms.forwarder.databinding.FragmentTasksCronBinding
+import com.idormy.sms.forwarder.databinding.FragmentTasksActionSendSmsBinding
import com.idormy.sms.forwarder.entity.task.CronSetting
import com.idormy.sms.forwarder.utils.KEY_BACK_DATA_CONDITION
import com.idormy.sms.forwarder.utils.KEY_EVENT_DATA_CONDITION
@@ -26,7 +26,7 @@ import com.xuexiang.xui.widget.actionbar.TitleBar
@Page(name = "Charge")
@Suppress("PrivatePropertyName")
-class ChargeFragment : BaseFragment(), View.OnClickListener {
+class ChargeFragment : BaseFragment(), View.OnClickListener {
private val TAG: String = ChargeFragment::class.java.simpleName
var titleBar: TitleBar? = null
@@ -46,8 +46,8 @@ class ChargeFragment : BaseFragment(), View.OnClickLi
override fun viewBindingInflate(
inflater: LayoutInflater,
container: ViewGroup,
- ): FragmentTasksCronBinding {
- return FragmentTasksCronBinding.inflate(inflater, container, false)
+ ): FragmentTasksActionSendSmsBinding {
+ return FragmentTasksActionSendSmsBinding.inflate(inflater, container, false)
}
override fun initTitle(): TitleBar? {
diff --git a/app/src/main/java/com/idormy/sms/forwarder/fragment/condition/CronFragment.kt b/app/src/main/java/com/idormy/sms/forwarder/fragment/condition/CronFragment.kt
index 9cefea67..c7861282 100644
--- a/app/src/main/java/com/idormy/sms/forwarder/fragment/condition/CronFragment.kt
+++ b/app/src/main/java/com/idormy/sms/forwarder/fragment/condition/CronFragment.kt
@@ -13,7 +13,7 @@ import android.widget.RadioGroup
import com.google.gson.Gson
import com.idormy.sms.forwarder.R
import com.idormy.sms.forwarder.core.BaseFragment
-import com.idormy.sms.forwarder.databinding.FragmentTasksCronBinding
+import com.idormy.sms.forwarder.databinding.FragmentTasksConditionCronBinding
import com.idormy.sms.forwarder.entity.task.CronSetting
import com.idormy.sms.forwarder.utils.KEY_BACK_DATA_CONDITION
import com.idormy.sms.forwarder.utils.KEY_EVENT_DATA_CONDITION
@@ -37,7 +37,7 @@ import java.util.Locale
@Page(name = "Cron")
@Suppress("PrivatePropertyName")
-class CronFragment : BaseFragment(), View.OnClickListener {
+class CronFragment : BaseFragment(), View.OnClickListener {
private val TAG: String = CronFragment::class.java.simpleName
var titleBar: TitleBar? = null
@@ -87,8 +87,8 @@ class CronFragment : BaseFragment(), View.OnClickList
override fun viewBindingInflate(
inflater: LayoutInflater,
container: ViewGroup,
- ): FragmentTasksCronBinding {
- return FragmentTasksCronBinding.inflate(inflater, container, false)
+ ): FragmentTasksConditionCronBinding {
+ return FragmentTasksConditionCronBinding.inflate(inflater, container, false)
}
override fun initTitle(): TitleBar? {
diff --git a/app/src/main/java/com/idormy/sms/forwarder/fragment/condition/NetworkFragment.kt b/app/src/main/java/com/idormy/sms/forwarder/fragment/condition/NetworkFragment.kt
index 3090766c..6c2efc6e 100644
--- a/app/src/main/java/com/idormy/sms/forwarder/fragment/condition/NetworkFragment.kt
+++ b/app/src/main/java/com/idormy/sms/forwarder/fragment/condition/NetworkFragment.kt
@@ -9,7 +9,7 @@ import android.view.ViewGroup
import com.google.gson.Gson
import com.idormy.sms.forwarder.R
import com.idormy.sms.forwarder.core.BaseFragment
-import com.idormy.sms.forwarder.databinding.FragmentTasksCronBinding
+import com.idormy.sms.forwarder.databinding.FragmentTasksActionSendSmsBinding
import com.idormy.sms.forwarder.entity.task.CronSetting
import com.idormy.sms.forwarder.utils.KEY_BACK_DATA_CONDITION
import com.idormy.sms.forwarder.utils.KEY_EVENT_DATA_CONDITION
@@ -26,7 +26,7 @@ import com.xuexiang.xui.widget.actionbar.TitleBar
@Page(name = "Network")
@Suppress("PrivatePropertyName")
-class NetworkFragment : BaseFragment(), View.OnClickListener {
+class NetworkFragment : BaseFragment(), View.OnClickListener {
private val TAG: String = NetworkFragment::class.java.simpleName
var titleBar: TitleBar? = null
@@ -46,8 +46,8 @@ class NetworkFragment : BaseFragment(), View.OnClickL
override fun viewBindingInflate(
inflater: LayoutInflater,
container: ViewGroup,
- ): FragmentTasksCronBinding {
- return FragmentTasksCronBinding.inflate(inflater, container, false)
+ ): FragmentTasksActionSendSmsBinding {
+ return FragmentTasksActionSendSmsBinding.inflate(inflater, container, false)
}
override fun initTitle(): TitleBar? {
diff --git a/app/src/main/java/com/idormy/sms/forwarder/receiver/AlarmReceiver.kt b/app/src/main/java/com/idormy/sms/forwarder/receiver/AlarmReceiver.kt
index a1d6ea23..0ebf02a1 100644
--- a/app/src/main/java/com/idormy/sms/forwarder/receiver/AlarmReceiver.kt
+++ b/app/src/main/java/com/idormy/sms/forwarder/receiver/AlarmReceiver.kt
@@ -4,20 +4,70 @@ import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.util.Log
+import com.google.gson.Gson
+import com.idormy.sms.forwarder.App
+import com.idormy.sms.forwarder.database.AppDatabase
import com.idormy.sms.forwarder.database.entity.Task
+import com.idormy.sms.forwarder.entity.task.CronSetting
+import com.idormy.sms.forwarder.entity.task.TaskSetting
+import com.idormy.sms.forwarder.utils.task.CronUtils
+import gatewayapps.crondroid.CronExpression
+import java.util.Date
-@Suppress("PropertyName")
+@Suppress("PropertyName", "DEPRECATION")
class AlarmReceiver : BroadcastReceiver() {
val TAG: String = AlarmReceiver::class.java.simpleName
override fun onReceive(context: Context, intent: Intent) {
- val task = intent.getParcelableExtra("task")
+ val task = intent.getParcelableExtra("TASK")
+ if (task == null) {
+ Log.d(TAG, "onReceive task is null")
+ return
+ }
+
+ Log.d(TAG, "onReceive task $task")
+ Log.d(TAG, "lastExecTime = ${task.lastExecTime}, nextExecTime = ${task.nextExecTime}")
+ try {
+ //取消旧任务的定时器
+ CronUtils.cancelAlarm(task)
- // 根据任务信息执行相应操作
- if (task != null) {
- Log.d(TAG, "onReceive task $task")
- // 处理任务逻辑,例如执行特定操作或者更新界面
+ // 根据任务信息执行相应操作
+ val conditionList = Gson().fromJson(task.conditions, Array::class.java).toMutableList()
+ if (conditionList.isEmpty()) {
+ Log.d(TAG, "onReceive conditionList is empty")
+ return
+ }
+ val firstCondition = conditionList.firstOrNull()
+ if (firstCondition == null) {
+ Log.d(TAG, "onReceive firstCondition is null")
+ return
+ }
+ val cronSetting = Gson().fromJson(firstCondition.setting, CronSetting::class.java)
+ if (cronSetting == null) {
+ Log.d(TAG, "onReceive cronSetting is null")
+ return
+ }
+ // 更新任务的上次执行时间和下次执行时间
+ val cronExpression = CronExpression(cronSetting.expression)
+ task.lastExecTime = Date()
+ task.nextExecTime = cronExpression.getNextValidTimeAfter(task.lastExecTime)
+ Log.d(TAG, "lastExecTime = ${task.lastExecTime}, nextExecTime = ${task.nextExecTime}")
+ // 自动禁用任务
+ if (task.nextExecTime <= task.lastExecTime) {
+ task.status = 0
+ }
+ // 更新任务信息
+ AppDatabase.getInstance(App.context).taskDao().update(task)
+ if (task.status == 0) {
+ Log.d(TAG, "onReceive task is disabled")
+ return
+ }
+ //设置新的定时器
+ CronUtils.scheduleAlarm(task)
+ } catch (e: Exception) {
+ Log.e(TAG, "onReceive error $e")
}
+
}
}
diff --git a/app/src/main/java/com/idormy/sms/forwarder/service/ForegroundService.kt b/app/src/main/java/com/idormy/sms/forwarder/service/ForegroundService.kt
index f62e0703..15de3bf0 100644
--- a/app/src/main/java/com/idormy/sms/forwarder/service/ForegroundService.kt
+++ b/app/src/main/java/com/idormy/sms/forwarder/service/ForegroundService.kt
@@ -44,7 +44,7 @@ import java.text.SimpleDateFormat
import java.util.Date
@SuppressLint("SimpleDateFormat")
-@Suppress("PrivatePropertyName", "DeferredResultUnused", "OPT_IN_USAGE", "DEPRECATION")
+@Suppress("PrivatePropertyName", "DeferredResultUnused", "OPT_IN_USAGE", "DEPRECATION", "LiftReturnOrAssignment")
class ForegroundService : Service() {
private val TAG: String = "ForegroundService"
diff --git a/app/src/main/java/com/idormy/sms/forwarder/utils/Constants.kt b/app/src/main/java/com/idormy/sms/forwarder/utils/Constants.kt
index 2a3313bf..ba3e57bf 100644
--- a/app/src/main/java/com/idormy/sms/forwarder/utils/Constants.kt
+++ b/app/src/main/java/com/idormy/sms/forwarder/utils/Constants.kt
@@ -482,6 +482,7 @@ var CLIENT_FRAGMENT_LIST = listOf(
)
//自动任务
+const val MAX_SETTING_NUM = 5 //最大条件/动作设置条数
const val KEY_TEST_CONDITION = "key_test_condition"
const val KEY_EVENT_DATA_CONDITION = "event_data_condition"
const val KEY_BACK_CODE_CONDITION = 1000
diff --git a/app/src/main/java/com/idormy/sms/forwarder/utils/task/CronUtils.kt b/app/src/main/java/com/idormy/sms/forwarder/utils/task/CronUtils.kt
index e09842be..3bfda65b 100644
--- a/app/src/main/java/com/idormy/sms/forwarder/utils/task/CronUtils.kt
+++ b/app/src/main/java/com/idormy/sms/forwarder/utils/task/CronUtils.kt
@@ -5,10 +5,10 @@ import android.app.AlarmManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
+import android.os.Build
import com.idormy.sms.forwarder.database.entity.Task
import com.idormy.sms.forwarder.receiver.AlarmReceiver
-@Suppress("unused")
class CronUtils {
companion object {
@@ -19,48 +19,51 @@ class CronUtils {
this.context = context.applicationContext
}
- fun updateTaskAndScheduleAlarm(task: Task) {
- val oldTask = getOldTask(task.id) // 获取旧的任务信息
- cancelAlarm(oldTask) // 取消旧任务的定时器
-
- updateTaskInDatabase(task) // 更新任务信息(例如,更新数据库中的任务信息)
- scheduleAlarm(task) // 设置新的定时器
- }
-
- private fun cancelAlarm(task: Task?) {
+ fun cancelAlarm(task: Task?) {
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
val alarmIntent = Intent(context, AlarmReceiver::class.java)
val requestCode = task?.id?.toInt() ?: -1
- val pendingIntent = PendingIntent.getBroadcast(context, requestCode, alarmIntent, PendingIntent.FLAG_NO_CREATE or PendingIntent.FLAG_IMMUTABLE)
+ val pendingIntent = PendingIntent.getBroadcast(
+ context,
+ requestCode,
+ alarmIntent,
+ PendingIntent.FLAG_NO_CREATE or PendingIntent.FLAG_MUTABLE
+ )
pendingIntent?.let {
alarmManager.cancel(it)
it.cancel()
}
}
- private fun scheduleAlarm(task: Task) {
+ @SuppressLint("ScheduleExactAlarm")
+ fun scheduleAlarm(task: Task) {
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
val alarmIntent = Intent(context, AlarmReceiver::class.java)
- alarmIntent.putExtra("task", task)
val requestCode = task.id.toInt()
- val pendingIntent = PendingIntent.getBroadcast(context, requestCode, alarmIntent, PendingIntent.FLAG_IMMUTABLE)
- //val now = Calendar.getInstance()
- val nextExecutionTime = task.nextExecTime.time
-
- alarmManager.setExact(
- AlarmManager.RTC_WAKEUP, nextExecutionTime, pendingIntent
+ alarmIntent.putExtra("TASK", task)
+ val pendingIntent = PendingIntent.getBroadcast(
+ context,
+ requestCode,
+ alarmIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
)
- }
- private fun getOldTask(taskId: Long): Task {
- // 实现获取旧任务信息的逻辑
- // 返回旧任务信息(Task对象)
- return Task()
+ // TODO:设置闹钟,低电量模式下无法设置精确闹钟
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ alarmManager.setExactAndAllowWhileIdle(
+ AlarmManager.RTC_WAKEUP,
+ task.nextExecTime.time,
+ pendingIntent
+ )
+ } else {
+ alarmManager.setExact(
+ AlarmManager.RTC_WAKEUP,
+ task.nextExecTime.time,
+ pendingIntent
+ )
+ }
}
- private fun updateTaskInDatabase(task: Task) {
- // 实现更新数据库中任务信息的逻辑
- }
}
}
diff --git a/app/src/main/res/layout/fragment_client_sms_send.xml b/app/src/main/res/layout/fragment_client_sms_send.xml
index b5212865..cac963a5 100644
--- a/app/src/main/res/layout/fragment_client_sms_send.xml
+++ b/app/src/main/res/layout/fragment_client_sms_send.xml
@@ -25,7 +25,8 @@
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:contentDescription="@string/api_sms_send"
- app:srcCompat="@drawable/icon_api_sms_send" />
+ app:srcCompat="@drawable/icon_api_sms_send"
+ tools:ignore="ImageContrastCheck" />
+ android:orientation="vertical"
+ android:paddingBottom="@dimen/config_padding_5dp">
+ android:text="@string/sim1"
+ tools:ignore="TouchTargetSizeCheck" />
+ android:text="@string/sim2"
+ tools:ignore="TouchTargetSizeCheck" />
@@ -78,7 +82,8 @@
app:met_clearButton="true"
app:met_errorMessage="@string/phone_numbers_error"
app:met_regexp="@string/phone_numbers_regex"
- app:met_validateOnFocusLost="true" />
+ app:met_validateOnFocusLost="true"
+ tools:ignore="TouchTargetSizeCheck,TextContrastCheck" />
@@ -105,7 +110,8 @@
app:met_maxCharacters="390"
app:met_regexp="@string/msg_content_regex"
app:met_validateOnFocusLost="true"
- app:mlet_hintText="@string/msg_content_hint" />
+ app:mlet_hintText="@string/msg_content_hint"
+ tools:ignore="TextContrastCheck" />
@@ -126,7 +132,7 @@
android:drawableStart="@drawable/ic_send_white"
android:paddingStart="20dp"
android:text="@string/send"
- tools:ignore="RtlSymmetry" />
+ tools:ignore="RtlSymmetry,TouchTargetSizeCheck,TextContrastCheck" />
diff --git a/app/src/main/res/layout/fragment_tasks_action_send_sms.xml b/app/src/main/res/layout/fragment_tasks_action_send_sms.xml
new file mode 100644
index 00000000..85b6eb89
--- /dev/null
+++ b/app/src/main/res/layout/fragment_tasks_action_send_sms.xml
@@ -0,0 +1,152 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_tasks_cron.xml b/app/src/main/res/layout/fragment_tasks_condition_cron.xml
similarity index 100%
rename from app/src/main/res/layout/fragment_tasks_cron.xml
rename to app/src/main/res/layout/fragment_tasks_condition_cron.xml
diff --git a/app/src/main/res/layout/fragment_tasks_edit.xml b/app/src/main/res/layout/fragment_tasks_edit.xml
index 2d578b06..ed0cc722 100644
--- a/app/src/main/res/layout/fragment_tasks_edit.xml
+++ b/app/src/main/res/layout/fragment_tasks_edit.xml
@@ -262,6 +262,7 @@
android:text="@string/test"
android:textColor="#FFFFFF"
android:textSize="11sp"
+ android:visibility="gone"
tools:ignore="RtlSymmetry,TextContrastCheck,TouchTargetSizeCheck" />
diff --git a/app/src/main/res/values-en/strings.xml b/app/src/main/res/values-en/strings.xml
index 9995fc27..84bb2180 100644
--- a/app/src/main/res/values-en/strings.xml
+++ b/app/src/main/res/values-en/strings.xml
@@ -1108,8 +1108,8 @@
Network
Send Sms
Notification
- Frpc
- HttpServer
+ Frpc Setting
+ Server Setting
Second
Minute
@@ -1166,4 +1166,6 @@
Cron Expression Test Result
Cron expression is invalid:\n%s
The next %s execution times:\n%s
+
+ Use SIM-%s to send sms\n%s
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 505a4a50..d1594b13 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1109,8 +1109,8 @@
网络状态
发送短信
通道推送
- Frpc
- HttpServer
+ 设置 Frpc
+ 设置 HttpServer
秒
分
@@ -1167,4 +1167,6 @@
Cron表达式测试结果
Cron表达式无效:\n%s
最近 %s 次运行时间:\n%s
+
+ 通过卡槽 SIM-%s 发送短信到:\n%s
diff --git a/app/src/main/res/values/styles_widget.xml b/app/src/main/res/values/styles_widget.xml
index 4c42df5d..e5a32895 100644
--- a/app/src/main/res/values/styles_widget.xml
+++ b/app/src/main/res/values/styles_widget.xml
@@ -225,6 +225,7 @@