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