From 5fb9f1b75c676a42950420a0ba88a684e76b005a Mon Sep 17 00:00:00 2001 From: pppscn <35696959@qq.com> Date: Thu, 7 Dec 2023 11:07:00 +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=20Cron=E5=AE=9A=E6=97=B6=E5=8F=91=E9=80=81?= =?UTF-8?q?=E7=9F=AD=E4=BF=A1=20#279=20#344?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/idormy/sms/forwarder/App.kt | 4 +- .../forwarder/adapter/TaskPagingAdapter.kt | 3 + .../forwarder/fragment/SettingsFragment.kt | 16 +++++ .../forwarder/fragment/TasksEditFragment.kt | 28 ++++---- .../sms/forwarder/receiver/AlarmReceiver.kt | 6 +- .../sms/forwarder/utils/SettingUtils.kt | 2 +- .../task/{CronUtils.kt => AlarmUtils.kt} | 2 +- .../forwarder/utils/task/CronJobScheduler.kt | 10 +-- .../sms/forwarder/workers/ActionWorker.kt | 69 +++++++++++++++++++ .../workers/{TaskWorker.kt => CronWorker.kt} | 47 ++++++++++--- .../adapter_tasks_card_view_list_item.xml | 9 ++- app/src/main/res/layout/fragment_settings.xml | 34 +++++++++ app/src/main/res/values-en/strings.xml | 4 ++ app/src/main/res/values/strings.xml | 4 ++ 14 files changed, 198 insertions(+), 40 deletions(-) rename app/src/main/java/com/idormy/sms/forwarder/utils/task/{CronUtils.kt => AlarmUtils.kt} (96%) create mode 100644 app/src/main/java/com/idormy/sms/forwarder/workers/ActionWorker.kt rename app/src/main/java/com/idormy/sms/forwarder/workers/{TaskWorker.kt => CronWorker.kt} (58%) diff --git a/app/src/main/java/com/idormy/sms/forwarder/App.kt b/app/src/main/java/com/idormy/sms/forwarder/App.kt index 620309c0..8ef5b6af 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/App.kt +++ b/app/src/main/java/com/idormy/sms/forwarder/App.kt @@ -28,7 +28,7 @@ import com.idormy.sms.forwarder.utils.* import com.idormy.sms.forwarder.utils.sdkinit.UMengInit import com.idormy.sms.forwarder.utils.sdkinit.XBasicLibInit import com.idormy.sms.forwarder.utils.sdkinit.XUpdateInit -import com.idormy.sms.forwarder.utils.task.CronUtils +import com.idormy.sms.forwarder.utils.task.AlarmUtils import com.idormy.sms.forwarder.utils.tinker.TinkerLoadLibrary import io.reactivex.Observable import io.reactivex.android.schedulers.AndroidSchedulers @@ -208,7 +208,7 @@ class App : Application(), CactusCallback, Configuration.Provider by Core { // 运营统计数据 UMengInit.init(this) // 定时任务初始化 - CronUtils.initialize(this) + AlarmUtils.initialize(this) // 三方时间库初始化 //AndroidThreeTen.init(this) } diff --git a/app/src/main/java/com/idormy/sms/forwarder/adapter/TaskPagingAdapter.kt b/app/src/main/java/com/idormy/sms/forwarder/adapter/TaskPagingAdapter.kt index 51c895d5..f939ce21 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/adapter/TaskPagingAdapter.kt +++ b/app/src/main/java/com/idormy/sms/forwarder/adapter/TaskPagingAdapter.kt @@ -16,6 +16,7 @@ import com.idormy.sms.forwarder.adapter.TaskPagingAdapter.MyViewHolder import com.idormy.sms.forwarder.database.entity.Task import com.idormy.sms.forwarder.databinding.AdapterTasksCardViewListItemBinding import com.idormy.sms.forwarder.entity.task.TaskSetting +import com.xuexiang.xutil.data.DateUtils class TaskPagingAdapter(private val itemClickListener: OnItemClickListener) : PagingDataAdapter(diffCallback) { @@ -31,6 +32,8 @@ class TaskPagingAdapter(private val itemClickListener: OnItemClickListener) : Pa if (item.type >= 1000) { holder.binding.layoutImage.visibility = View.GONE + holder.binding.tvTime.text = DateUtils.getFriendlyTimeSpanByNow(item.lastExecTime.time) + //遍历conditions显示图标 holder.binding.layoutConditionsIcons.removeAllViews() if (item.conditions.isNotEmpty()) { diff --git a/app/src/main/java/com/idormy/sms/forwarder/fragment/SettingsFragment.kt b/app/src/main/java/com/idormy/sms/forwarder/fragment/SettingsFragment.kt index 5829e429..b451742f 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/fragment/SettingsFragment.kt +++ b/app/src/main/java/com/idormy/sms/forwarder/fragment/SettingsFragment.kt @@ -175,6 +175,9 @@ class SettingsFragment : BaseFragment(), View.OnClickL //纯客户端模式 switchDirectlyToClient(binding!!.sbDirectlyToClient) + //纯自动任务模式 + switchDirectlyToTask(binding!!.sbDirectlyToTask) + //启用 {{定位信息}} 标签 switchEnableLocationTag(binding!!.sbEnableLocationTag) } @@ -996,6 +999,19 @@ class SettingsFragment : BaseFragment(), View.OnClickL } } + //纯自动任务模式 + private fun switchDirectlyToTask(@SuppressLint("UseSwitchCompatOrMaterialCode") switchDirectlyToTask: SwitchButton) { + switchDirectlyToTask.isChecked = SettingUtils.enablePureTaskMode + switchDirectlyToTask.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean -> + SettingUtils.enablePureTaskMode = isChecked + if (isChecked) { + MaterialDialog.Builder(requireContext()).content(getString(R.string.enabling_pure_client_mode)).positiveText(R.string.lab_yes).onPositive { _: MaterialDialog?, _: DialogAction? -> + XUtil.exitApp() + }.negativeText(R.string.lab_no).show() + } + } + } + //启用 {{定位信息}} 标签 private fun switchEnableLocationTag(@SuppressLint("UseSwitchCompatOrMaterialCode") switchEnableLocationTag: SwitchButton) { switchEnableLocationTag.isChecked = SettingUtils.enableLocationTag diff --git a/app/src/main/java/com/idormy/sms/forwarder/fragment/TasksEditFragment.kt b/app/src/main/java/com/idormy/sms/forwarder/fragment/TasksEditFragment.kt index 032dfe85..da8ada65 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/fragment/TasksEditFragment.kt +++ b/app/src/main/java/com/idormy/sms/forwarder/fragment/TasksEditFragment.kt @@ -257,7 +257,7 @@ class TasksEditFragment : BaseFragment(), View.OnClic for (condition in conditionList) { itemListConditions.add(condition) } - Log.d(TAG, "initForm: $itemListConditions") + Log.d(TAG, "itemListConditions: $itemListConditions") conditionsAdapter.notifyDataSetChanged() binding!!.layoutAddCondition.visibility = if (itemListConditions.size >= MAX_SETTING_NUM) View.GONE else View.VISIBLE } @@ -266,7 +266,7 @@ class TasksEditFragment : BaseFragment(), View.OnClic for (action in actionList) { itemListActions.add(action) } - Log.d(TAG, "initForm: $itemListActions") + Log.d(TAG, "itemListActions: $itemListActions") actionsAdapter.notifyDataSetChanged() binding!!.layoutAddAction.visibility = if (itemListActions.size >= MAX_SETTING_NUM) View.GONE else View.VISIBLE } @@ -292,7 +292,9 @@ class TasksEditFragment : BaseFragment(), View.OnClic } val lastExecTime = Date() - var nextExecTime = Date() + // 将毫秒部分设置为 0,避免因为毫秒部分不同导致的任务重复执行 + lastExecTime.time = lastExecTime.time / 1000 * 1000 + var nextExecTime = lastExecTime val firstCondition = itemListConditions[0] taskType = firstCondition.type @@ -317,15 +319,7 @@ class TasksEditFragment : BaseFragment(), View.OnClic 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, - lastExecTime, - nextExecTime + taskId, taskType, taskName, description.toString(), Gson().toJson(itemListConditions), Gson().toJson(itemListActions), status, lastExecTime, nextExecTime ) } @@ -339,11 +333,9 @@ class TasksEditFragment : BaseFragment(), View.OnClic //定时任务 TASK_CONDITION_CRON -> { //取消旧任务的定时器 & 设置新的定时器 - //CronUtils.cancelAlarm(task) - //CronUtils.scheduleAlarm(task) + //AlarmUtils.cancelAlarm(task) + //AlarmUtils.scheduleAlarm(task) - //val uuid = App.TaskIdToWorkerIdMap[task.id] - //uuid?.let { CronJobScheduler.cancelTask(it) } CronJobScheduler.cancelTask(task.id) CronJobScheduler.scheduleTask(task) } @@ -355,6 +347,10 @@ class TasksEditFragment : BaseFragment(), View.OnClic try { dialog.dismiss() Log.d(TAG, "onItemClick: $widgetInfo") + if (pos > 0) { + XToastUtils.info("暂不支持,敬请期待……") + return + } //判断点击的是条件还是动作 if (widgetInfo.classPath.contains(".condition.")) { //判断是否已经添加过该类型条件 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 0ebf02a1..856c7e33 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 @@ -10,7 +10,7 @@ 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 com.idormy.sms.forwarder.utils.task.AlarmUtils import gatewayapps.crondroid.CronExpression import java.util.Date @@ -30,7 +30,7 @@ class AlarmReceiver : BroadcastReceiver() { Log.d(TAG, "lastExecTime = ${task.lastExecTime}, nextExecTime = ${task.nextExecTime}") try { //取消旧任务的定时器 - CronUtils.cancelAlarm(task) + AlarmUtils.cancelAlarm(task) // 根据任务信息执行相应操作 val conditionList = Gson().fromJson(task.conditions, Array::class.java).toMutableList() @@ -64,7 +64,7 @@ class AlarmReceiver : BroadcastReceiver() { return } //设置新的定时器 - CronUtils.scheduleAlarm(task) + AlarmUtils.scheduleAlarm(task) } catch (e: Exception) { Log.e(TAG, "onReceive error $e") } diff --git a/app/src/main/java/com/idormy/sms/forwarder/utils/SettingUtils.kt b/app/src/main/java/com/idormy/sms/forwarder/utils/SettingUtils.kt index c9ae704a..ecb74ea5 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/utils/SettingUtils.kt +++ b/app/src/main/java/com/idormy/sms/forwarder/utils/SettingUtils.kt @@ -159,7 +159,7 @@ class SettingUtils private constructor() { var enablePureClientMode: Boolean by SharedPreference(SP_PURE_CLIENT_MODE, false) //是否纯任务模式 - var enablePureTaskMode: Boolean by SharedPreference(SP_PURE_TASK_MODE, true) + var enablePureTaskMode: Boolean by SharedPreference(SP_PURE_TASK_MODE, false) //是否启用定位标签 var enableLocationTag: Boolean by SharedPreference(SP_LOCATION_TAG, false) 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/AlarmUtils.kt similarity index 96% rename from app/src/main/java/com/idormy/sms/forwarder/utils/task/CronUtils.kt rename to app/src/main/java/com/idormy/sms/forwarder/utils/task/AlarmUtils.kt index 3bfda65b..c56c3a4f 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/AlarmUtils.kt @@ -9,7 +9,7 @@ import android.os.Build import com.idormy.sms.forwarder.database.entity.Task import com.idormy.sms.forwarder.receiver.AlarmReceiver -class CronUtils { +class AlarmUtils { companion object { @SuppressLint("StaticFieldLeak") diff --git a/app/src/main/java/com/idormy/sms/forwarder/utils/task/CronJobScheduler.kt b/app/src/main/java/com/idormy/sms/forwarder/utils/task/CronJobScheduler.kt index 388571f7..fbcacf5d 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/utils/task/CronJobScheduler.kt +++ b/app/src/main/java/com/idormy/sms/forwarder/utils/task/CronJobScheduler.kt @@ -6,7 +6,7 @@ import androidx.work.ExistingWorkPolicy import androidx.work.OneTimeWorkRequestBuilder import androidx.work.WorkManager import com.idormy.sms.forwarder.database.entity.Task -import com.idormy.sms.forwarder.workers.TaskWorker +import com.idormy.sms.forwarder.workers.CronWorker import java.util.concurrent.TimeUnit @Suppress("DEPRECATION") @@ -21,13 +21,13 @@ class CronJobScheduler { val delayInMillis = task.nextExecTime.time / 1000 * 1000 - currentTimeMillis val inputData = Data.Builder().putLong("taskId", task.id).build() val taskRequest = if (delayInMillis <= 0L) { - Log.d(TAG, "立即执行任务${task.id},delayInMillis = $delayInMillis") - OneTimeWorkRequestBuilder() + Log.d(TAG, "任务${task.id}:立即执行,delayInMillis = $delayInMillis") + OneTimeWorkRequestBuilder() .setInputData(inputData) .build() } else { - Log.d(TAG, "延迟 $delayInMillis 毫秒执行任务${task.id}") - OneTimeWorkRequestBuilder() + Log.d(TAG, "任务${task.id}:延迟 $delayInMillis 毫秒执行") + OneTimeWorkRequestBuilder() .setInitialDelay(delayInMillis, TimeUnit.MILLISECONDS) .setInputData(inputData) .build() diff --git a/app/src/main/java/com/idormy/sms/forwarder/workers/ActionWorker.kt b/app/src/main/java/com/idormy/sms/forwarder/workers/ActionWorker.kt new file mode 100644 index 00000000..2c87714e --- /dev/null +++ b/app/src/main/java/com/idormy/sms/forwarder/workers/ActionWorker.kt @@ -0,0 +1,69 @@ +package com.idormy.sms.forwarder.workers + +import android.Manifest +import android.content.Context +import android.content.pm.PackageManager +import android.util.Log +import androidx.core.app.ActivityCompat +import androidx.work.CoroutineWorker +import androidx.work.WorkerParameters +import com.google.gson.Gson +import com.idormy.sms.forwarder.App +import com.idormy.sms.forwarder.R +import com.idormy.sms.forwarder.entity.task.SmsSetting +import com.idormy.sms.forwarder.utils.PhoneUtils +import com.idormy.sms.forwarder.utils.TASK_ACTION_SENDSMS +import com.xuexiang.xui.utils.ResUtils +import com.xuexiang.xutil.XUtil + +@Suppress("PrivatePropertyName", "DEPRECATION") +class ActionWorker(context: Context, params: WorkerParameters) : CoroutineWorker(context, params) { + + private val TAG: String = ActionWorker::class.java.simpleName + + override suspend fun doWork(): Result { + val taskId = inputData.getLong("taskId", -1L) + val actionType = inputData.getInt("actionType", -1) + val actionSetting = inputData.getString("actionSetting") + Log.d(TAG, "taskId: $taskId, actionSetting: $actionSetting") + if (taskId == -1L || actionSetting == null) { + Log.d(TAG, "taskId is -1L or actionSetting is null") + return Result.failure() + } + + when (actionType) { + TASK_ACTION_SENDSMS -> { + val smsSetting = Gson().fromJson(actionSetting, SmsSetting::class.java) + if (smsSetting == null) { + Log.d(TAG, "任务$taskId:smsSetting is null") + return Result.failure() + } + //获取卡槽信息 + if (App.SimInfoList.isEmpty()) { + App.SimInfoList = PhoneUtils.getSimMultiInfo() + } + Log.d(TAG, App.SimInfoList.toString()) + + //发送卡槽: 1=SIM1, 2=SIM2 + val simSlotIndex = smsSetting.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, smsSetting.phoneNumbers, smsSetting.msgContent) ?: "success" + } + + Log.d(TAG, "任务$taskId:send sms result: $msg") + return Result.success() + } + + else -> { + Log.d(TAG, "任务$taskId:action.type is $actionType") + return Result.failure() + } + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/idormy/sms/forwarder/workers/TaskWorker.kt b/app/src/main/java/com/idormy/sms/forwarder/workers/CronWorker.kt similarity index 58% rename from app/src/main/java/com/idormy/sms/forwarder/workers/TaskWorker.kt rename to app/src/main/java/com/idormy/sms/forwarder/workers/CronWorker.kt index e1a43ec0..8bad1a48 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/workers/TaskWorker.kt +++ b/app/src/main/java/com/idormy/sms/forwarder/workers/CronWorker.kt @@ -3,6 +3,9 @@ package com.idormy.sms.forwarder.workers import android.content.Context import android.util.Log import androidx.work.CoroutineWorker +import androidx.work.Data +import androidx.work.OneTimeWorkRequestBuilder +import androidx.work.WorkManager import androidx.work.WorkerParameters import com.google.gson.Gson import com.idormy.sms.forwarder.App @@ -11,13 +14,12 @@ import com.idormy.sms.forwarder.entity.task.CronSetting import com.idormy.sms.forwarder.entity.task.TaskSetting import com.idormy.sms.forwarder.utils.task.CronJobScheduler import gatewayapps.crondroid.CronExpression -import kotlinx.coroutines.delay import java.util.Date -@Suppress("PrivatePropertyName") -class TaskWorker(context: Context, params: WorkerParameters) : CoroutineWorker(context, params) { +@Suppress("PrivatePropertyName", "DEPRECATION") +class CronWorker(context: Context, params: WorkerParameters) : CoroutineWorker(context, params) { - private val TAG: String = TaskWorker::class.java.simpleName + private val TAG: String = CronWorker::class.java.simpleName override suspend fun doWork(): Result { val taskId = inputData.getLong("taskId", -1L) @@ -27,20 +29,31 @@ class TaskWorker(context: Context, params: WorkerParameters) : CoroutineWorker(c } val task = AppDatabase.getInstance(App.context).taskDao().getOne(taskId) + if (task.status == 0) { + Log.d(TAG, "任务${task.id}:task is disabled") + return Result.success() + } + // 根据任务信息执行相应操作 val conditionList = Gson().fromJson(task.conditions, Array::class.java).toMutableList() if (conditionList.isEmpty()) { - Log.d(TAG, "onReceive conditionList is empty") + Log.d(TAG, "任务${task.id}:conditionList is empty") return Result.failure() } val firstCondition = conditionList.firstOrNull() if (firstCondition == null) { - Log.d(TAG, "onReceive firstCondition is null") + Log.d(TAG, "任务${task.id}:firstCondition is null") return Result.failure() } val cronSetting = Gson().fromJson(firstCondition.setting, CronSetting::class.java) if (cronSetting == null) { - Log.d(TAG, "onReceive cronSetting is null") + Log.d(TAG, "任务${task.id}:cronSetting is null") + return Result.failure() + } + + // TODO: 判断其他条件是否满足 + if (false) { + Log.d(TAG, "任务${task.id}:其他条件不满足") return Result.failure() } @@ -52,7 +65,7 @@ class TaskWorker(context: Context, params: WorkerParameters) : CoroutineWorker(c // 将 nextExecTime 的毫秒部分设置为 0,避免因为毫秒部分不同导致的任务重复执行 nextExecTime.time = nextExecTime.time / 1000 * 1000 task.nextExecTime = nextExecTime - Log.d(TAG, "lastExecTime = ${task.lastExecTime}, nextExecTime = ${task.nextExecTime}") + Log.d(TAG, "任务${task.id}:lastExecTime = ${task.lastExecTime}, nextExecTime = ${task.nextExecTime}") // 自动禁用任务 if (task.nextExecTime.time / 1000 < now.time / 1000) { @@ -63,13 +76,25 @@ class TaskWorker(context: Context, params: WorkerParameters) : CoroutineWorker(c AppDatabase.getInstance(App.context).taskDao().updateExecTime(task.id, task.lastExecTime, task.nextExecTime, task.status) if (task.status == 0) { - Log.d(TAG, "onReceive task is disabled") + Log.d(TAG, "任务${task.id}:task is disabled") return Result.success() } // TODO: 执行具体任务 - Log.d(TAG, "执行具体任务,耗时 200 毫秒") - delay(200L) + val actionList = Gson().fromJson(task.actions, Array::class.java).toMutableList() + if (actionList.isEmpty()) { + Log.d(TAG, "任务${task.id}:actionsList is empty") + return Result.failure() + } + for (action in actionList) { + val actionData = Data.Builder() + .putLong("taskId", task.id) + .putInt("actionType", action.type) + .putString("actionSetting", action.setting) + .build() + val actionRequest = OneTimeWorkRequestBuilder().setInputData(actionData).build() + WorkManager.getInstance().enqueue(actionRequest) + } // 为新的 nextExecTime 调度下一次任务执行 CronJobScheduler.cancelTask(task.id) diff --git a/app/src/main/res/layout/adapter_tasks_card_view_list_item.xml b/app/src/main/res/layout/adapter_tasks_card_view_list_item.xml index 9911dfa2..b44ec60c 100644 --- a/app/src/main/res/layout/adapter_tasks_card_view_list_item.xml +++ b/app/src/main/res/layout/adapter_tasks_card_view_list_item.xml @@ -39,9 +39,16 @@ + + + android:layout_marginStart="5dp" /> diff --git a/app/src/main/res/layout/fragment_settings.xml b/app/src/main/res/layout/fragment_settings.xml index 763a10bf..fb02d0ce 100644 --- a/app/src/main/res/layout/fragment_settings.xml +++ b/app/src/main/res/layout/fragment_settings.xml @@ -1748,6 +1748,40 @@ + + + + + + + + + + + + + diff --git a/app/src/main/res/values-en/strings.xml b/app/src/main/res/values-en/strings.xml index 84bb2180..68f0e630 100644 --- a/app/src/main/res/values-en/strings.xml +++ b/app/src/main/res/values-en/strings.xml @@ -989,6 +989,10 @@ When starting the APP, it will directly enter the active control client Exit pure client mode Do you want to quit the app immediately and start it manually to take effect in pure client mode? + Directly To Task + When starting the APP, it will directly enter the task center + Exit pure task mode + Do you want to quit the app immediately and start it manually to take effect in pure task mode? Optional: Enable Cactus Keep Alive Dual process foreground service/JobScheduler/WorkManager/1px/silent music diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d1594b13..aadfce49 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -990,6 +990,10 @@ 启动APP时直接进入主动控制·客户端 退出纯客户端模式 是否立即退出App,并手动启动以生效纯客户端模式? + 纯自动任务模式 + 启动APP时直接进入自动任务 + 退出纯自动任务模式 + 是否立即退出App,并手动启动以生效纯自动任务模式? 可选组件: 启用 Cactus 增强保活措施(会增加耗电) 双进程前台服务/JobScheduler/WorkManager/1像素/无声音乐