You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
SmsForwarder/app/src/main/java/com/idormy/sms/forwarder/fragment/condition/CronFragment.kt

1601 lines
68 KiB
Kotlin

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

package com.idormy.sms.forwarder.fragment.condition
import android.annotation.SuppressLint
import android.content.Intent
import android.text.Editable
import android.text.TextWatcher
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
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.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_BACK_DESCRIPTION_CONDITION
import com.idormy.sms.forwarder.utils.KEY_EVENT_DATA_CONDITION
import com.idormy.sms.forwarder.utils.KEY_TEST_CONDITION
import com.idormy.sms.forwarder.utils.TASK_CONDITION_CRON
import com.idormy.sms.forwarder.utils.XToastUtils
import com.jeremyliao.liveeventbus.LiveEventBus
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.xui.utils.CountDownButtonHelper
import com.xuexiang.xui.widget.actionbar.TitleBar
import com.xuexiang.xui.widget.flowlayout.FlowTagLayout
import gatewayapps.crondroid.CronExpression
import net.redhogs.cronparser.CronExpressionDescriptor
import net.redhogs.cronparser.Options
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
@Page(name = "Cron")
@Suppress("PrivatePropertyName")
class CronFragment : BaseFragment<FragmentTasksConditionCronBinding?>(), View.OnClickListener {
private val TAG: String = CronFragment::class.java.simpleName
private var titleBar: TitleBar? = null
private var mCountDownHelper: CountDownButtonHelper? = null
@JvmField
@AutoWired(name = KEY_EVENT_DATA_CONDITION)
var eventData: String? = null
private val secondsList: List<String> = (0..59).map { String.format("%02d", it) }
private var selectedSecondList = ""
private val minutesList: List<String> = (0..59).map { String.format("%02d", it) }
private var selectedMinuteList = ""
private val hoursList: List<String> = (0..23).map { String.format("%02d", it) }
private var selectedHourList = ""
private val dayList: List<String> = (1..31).map { String.format("%d", it) }
private var selectedDayList = ""
//private val monthList = listOf("JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC")
private val monthList: List<String> = (1..12).map { String.format("%d", it) }
private var selectedMonthList = ""
//private val weekList = listOf("MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN")
private val weekList: List<String> = (1..7).map { String.format("%d", it) }
private var selectedWeekList = ""
private val yearList: List<String> = (2020..2099).map { String.format("%d", it) }
private var selectedYearList = ""
private var second = "*"
private var minute = "*"
private var hour = "*"
private var day = "*"
private var month = "*"
private var week = "?"
private var year = "*"
private var expression = "$second $minute $hour $day $month $week $year"
private var description = ""
override fun initArgs() {
XRouter.getInstance().inject(this)
}
override fun viewBindingInflate(
inflater: LayoutInflater,
container: ViewGroup,
): FragmentTasksConditionCronBinding {
return FragmentTasksConditionCronBinding.inflate(inflater, container, false)
}
override fun initTitle(): TitleBar? {
titleBar = super.initTitle()!!.setImmersive(false).setTitle(R.string.task_cron)
return titleBar
}
/**
* 初始化控件
*/
override fun initViews() {
//测试按钮增加倒计时,避免重复点击
mCountDownHelper = CountDownButtonHelper(binding!!.btnTest, 3)
mCountDownHelper!!.setOnCountDownListener(object : CountDownButtonHelper.OnCountDownListener {
override fun onCountDown(time: Int) {
binding!!.btnTest.text = String.format(getString(R.string.seconds_n), time)
}
override fun onFinished() {
binding!!.btnTest.text = getString(R.string.test)
}
})
Log.d(TAG, "initViews eventData:$eventData")
if (eventData != null) {
val settingVo = Gson().fromJson(eventData, CronSetting::class.java)
binding!!.tvDescription.text = settingVo.description
expression = settingVo.expression
Log.d(TAG, "initViews expression:$expression")
val fields = expression.split(" ")
second = fields.getOrNull(0) ?: "*"
minute = fields.getOrNull(1) ?: "*"
hour = fields.getOrNull(2) ?: "*"
day = fields.getOrNull(3) ?: "*"
month = fields.getOrNull(4) ?: "*"
week = fields.getOrNull(5) ?: "?"
year = fields.getOrNull(6) ?: "*"
}
//初始化输入提示
initSecondInputHelper()
initMinuteInputHelper()
initHourInputHelper()
initDayInputHelper()
initMonthInputHelper()
initWeekInputHelper()
initYearInputHelper()
}
@SuppressLint("SetTextI18n")
override fun initListeners() {
binding!!.btnTest.setOnClickListener(this)
binding!!.btnDel.setOnClickListener(this)
binding!!.btnSave.setOnClickListener(this)
LiveEventBus.get(KEY_TEST_CONDITION, String::class.java).observe(this) {
mCountDownHelper?.finish()
switchInputHelper(binding!!.layoutCronExpressionCheck)
if (it == "success") {
//生成最近10次运行时间
val nextTimeList = mutableListOf<String>()
val dateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault())
val cronExpression = CronExpression(expression)
var nextDate = Date()
var times = 0
for (i in 0 until 10) {
nextDate = cronExpression.getNextValidTimeAfter(nextDate) ?: break
nextTimeList.add(dateFormat.format(nextDate))
times++
}
binding!!.tvDescription.text = description
binding!!.tvCronExpressionCheckTips.text = "$expression\n$description"
binding!!.tvNextTimeList.text = String.format(getString(R.string.next_execution_times), times.toString(), nextTimeList.joinToString("\n"))
binding!!.tvNextTimeList.visibility = View.VISIBLE
binding!!.separatorCronExpressionCheck.visibility = View.VISIBLE
} else {
binding!!.tvCronExpressionCheckTips.text = String.format(getString(R.string.invalid_cron_expression), it.toString())
binding!!.tvNextTimeList.text = ""
binding!!.tvNextTimeList.visibility = View.GONE
binding!!.separatorCronExpressionCheck.visibility = View.GONE
}
}
}
@SingleClick
override fun onClick(v: View) {
try {
when (v.id) {
R.id.btn_test -> {
mCountDownHelper?.start()
Thread {
try {
val settingVo = checkSetting()
Log.d(TAG, settingVo.toString())
LiveEventBus.get(KEY_TEST_CONDITION, String::class.java).post("success")
} catch (e: Exception) {
LiveEventBus.get(KEY_TEST_CONDITION, String::class.java).post(e.message.toString())
e.printStackTrace()
}
}.start()
return
}
R.id.btn_del -> {
popToBack()
return
}
R.id.btn_save -> {
val settingVo = checkSetting()
val intent = Intent()
intent.putExtra(KEY_BACK_DESCRIPTION_CONDITION, description)
intent.putExtra(KEY_BACK_DATA_CONDITION, Gson().toJson(settingVo))
setFragmentResult(TASK_CONDITION_CRON, intent)
popToBack()
return
}
}
} catch (e: Exception) {
XToastUtils.error(e.message.toString(), 30000)
e.printStackTrace()
}
}
//切换输入提示
private fun switchInputHelper(layout: LinearLayout) {
binding!!.layoutSecondType.visibility = View.GONE
binding!!.layoutMinuteType.visibility = View.GONE
binding!!.layoutHourType.visibility = View.GONE
binding!!.layoutDayType.visibility = View.GONE
binding!!.layoutMonthType.visibility = View.GONE
binding!!.layoutWeekType.visibility = View.GONE
binding!!.layoutYearType.visibility = View.GONE
binding!!.layoutCronExpressionCheck.visibility = View.GONE
layout.visibility = View.VISIBLE
}
//初始化输入提示--秒
@SuppressLint("SetTextI18n")
private fun initSecondInputHelper() {
binding!!.etSecond.setOnFocusChangeListener { _, hasFocus ->
if (hasFocus) {
switchInputHelper(binding!!.layoutSecondType)
} else {
afterSecondChanged()
}
}
binding!!.etSecond.setText(second)
afterSecondChanged()
//秒类型
binding!!.rgSecondType.setOnCheckedChangeListener { _: RadioGroup?, checkedId: Int ->
when (checkedId) {
R.id.rb_second_type_cyclic -> {
var secondCyclicFrom = binding!!.etSecondCyclicFrom.text.toString().trim()
if (secondCyclicFrom.isEmpty()) {
secondCyclicFrom = "00"
binding!!.etSecondCyclicFrom.setText(secondCyclicFrom)
}
var secondCyclicTo = binding!!.etSecondCyclicTo.text.toString().trim()
if (secondCyclicTo.isEmpty()) {
secondCyclicTo = "59"
binding!!.etSecondCyclicTo.setText(secondCyclicTo)
}
second = "$secondCyclicFrom-$secondCyclicTo"
}
R.id.rb_second_type_interval -> {
var secondIntervalStart = binding!!.etSecondIntervalStart.text.toString().trim()
if (secondIntervalStart.isEmpty()) {
secondIntervalStart = "0"
binding!!.etSecondIntervalStart.setText(secondIntervalStart)
}
var secondInterval = binding!!.etSecondInterval.text.toString().trim()
if (secondInterval.isEmpty()) {
secondInterval = "2"
binding!!.etSecondInterval.setText(secondInterval)
}
second = "$secondIntervalStart/$secondInterval"
}
R.id.rb_second_type_assigned -> {
if (selectedSecondList.isEmpty()) {
selectedSecondList = "00"
binding!!.flowlayoutMultiSelectSecond.setSelectedItems("00")
}
second = selectedSecondList
}
else -> {
second = "*"
}
}
binding!!.etSecond.setText(second)
}
//初始化输入提示--秒--周期
val secondCyclicWatcher = object : TextWatcher {
override fun afterTextChanged(s: Editable) {
val secondCyclicFrom = binding!!.etSecondCyclicFrom.text.toString().trim()
val secondCyclicTo = binding!!.etSecondCyclicTo.text.toString().trim()
if (secondCyclicFrom.isNotEmpty() && secondCyclicTo.isNotEmpty()) {
second = "$secondCyclicFrom-$secondCyclicTo"
binding!!.etSecond.setText(second)
binding!!.rbSecondTypeCyclic.isChecked = true
}
}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
}
binding!!.etSecondCyclicFrom.addTextChangedListener(secondCyclicWatcher)
binding!!.etSecondCyclicTo.addTextChangedListener(secondCyclicWatcher)
//初始化输入提示--秒--间隔
val secondIntervalWatcher = object : TextWatcher {
override fun afterTextChanged(s: Editable) {
val secondIntervalStart = binding!!.etSecondIntervalStart.text.toString().trim()
val secondInterval = binding!!.etSecondInterval.text.toString().trim()
if (secondIntervalStart.isNotEmpty() && secondInterval.isNotEmpty()) {
second = "$secondIntervalStart/$secondInterval"
binding!!.etSecond.setText(second)
binding!!.rbSecondTypeInterval.isChecked = true
}
}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
}
binding!!.etSecondIntervalStart.addTextChangedListener(secondIntervalWatcher)
binding!!.etSecondInterval.addTextChangedListener(secondIntervalWatcher)
//初始化输入提示--秒--指定
binding!!.flowlayoutMultiSelectSecond.setTagCheckedMode(FlowTagLayout.FLOW_TAG_CHECKED_MULTI)
binding!!.flowlayoutMultiSelectSecond.setItems(secondsList)
binding!!.flowlayoutMultiSelectSecond.setOnTagSelectListener { parent, position, selectedList ->
selectedSecondList = getSelectedItems(parent, selectedList, 1)
Log.d(TAG, "position:$position, selectedSecondList:$selectedSecondList")
if (selectedSecondList.isEmpty()) {
binding!!.rbSecondTypeAll.isChecked = true
second = "*"
} else {
binding!!.rbSecondTypeAssigned.isChecked = true
second = selectedSecondList
}
binding!!.etSecond.setText(second)
}
}
private fun afterSecondChanged() {
second = binding!!.etSecond.text.toString().trim()
try {
//判断cronExpression是否有效
expression = "$second $minute $hour $day $month $week $year"
Log.d(TAG, "afterSecondChanged expression:$expression")
CronExpression.validateExpression(expression)
} catch (e: Exception) {
XToastUtils.error("Cron表达式无效" + e.message, 30000)
return
}
when {
second == "*" -> {
binding!!.rbSecondTypeAll.isChecked = true
}
second.contains("/") -> {
val secondsArray = second.split("/")
binding!!.etSecondIntervalStart.setText(secondsArray.getOrNull(0) ?: "0")
binding!!.etSecondInterval.setText(secondsArray.getOrNull(1) ?: "1")
binding!!.rbSecondTypeInterval.isChecked = true
}
second.contains(",") -> {
val secondsList = restoreMergedItems(second, "%02d")
Log.d(TAG, "secondsList:$secondsList")
binding!!.flowlayoutMultiSelectSecond.setSelectedItems(secondsList)
binding!!.rbSecondTypeAssigned.isChecked = true
selectedSecondList = secondsList.joinToString(",")
}
second.contains("-") -> {
val secondsArray = second.split("-")
binding!!.etSecondCyclicFrom.setText(secondsArray.getOrNull(0) ?: "00")
binding!!.etSecondCyclicTo.setText(secondsArray.getOrNull(1) ?: "59")
binding!!.rbSecondTypeCyclic.isChecked = true
}
secondsList.indexOf(String.format("%02d", second.toInt())) != -1 -> {
binding!!.flowlayoutMultiSelectSecond.setSelectedItems(String.format("%02d", second.toInt()))
binding!!.rbSecondTypeAssigned.isChecked = true
selectedSecondList = second
}
else -> {
binding!!.rbSecondTypeAll.isChecked = true
}
}
}
//初始化输入提示--分
@SuppressLint("SetTextI18n")
private fun initMinuteInputHelper() {
binding!!.etMinute.setOnFocusChangeListener { _, hasFocus ->
if (hasFocus) {
switchInputHelper(binding!!.layoutMinuteType)
} else {
afterMinuteChanged()
}
}
binding!!.etMinute.setText(minute)
afterMinuteChanged()
//分类型
binding!!.rgMinuteType.setOnCheckedChangeListener { _: RadioGroup?, checkedId: Int ->
when (checkedId) {
R.id.rb_minute_type_cyclic -> {
var minuteCyclicFrom = binding!!.etMinuteCyclicFrom.text.toString().trim()
if (minuteCyclicFrom.isEmpty()) {
minuteCyclicFrom = "00"
binding!!.etMinuteCyclicFrom.setText(minuteCyclicFrom)
}
var minuteCyclicTo = binding!!.etMinuteCyclicTo.text.toString().trim()
if (minuteCyclicTo.isEmpty()) {
minuteCyclicTo = "59"
binding!!.etMinuteCyclicTo.setText(minuteCyclicTo)
}
minute = "$minuteCyclicFrom-$minuteCyclicTo"
}
R.id.rb_minute_type_interval -> {
var minuteIntervalStart = binding!!.etMinuteIntervalStart.text.toString().trim()
if (minuteIntervalStart.isEmpty()) {
minuteIntervalStart = "0"
binding!!.etMinuteIntervalStart.setText(minuteIntervalStart)
}
var minuteInterval = binding!!.etMinuteInterval.text.toString().trim()
if (minuteInterval.isEmpty()) {
minuteInterval = "2"
binding!!.etMinuteInterval.setText(minuteInterval)
}
minute = "$minuteIntervalStart/$minuteInterval"
}
R.id.rb_minute_type_assigned -> {
if (selectedMinuteList.isEmpty()) {
selectedMinuteList = "00"
binding!!.flowlayoutMultiSelectMinute.setSelectedItems("00")
}
minute = selectedMinuteList
}
else -> {
minute = "*"
}
}
binding!!.etMinute.setText(minute)
}
//初始化输入提示--分--周期
val minuteCyclicWatcher = object : TextWatcher {
override fun afterTextChanged(s: Editable) {
val minuteCyclicFrom = binding!!.etMinuteCyclicFrom.text.toString().trim()
val minuteCyclicTo = binding!!.etMinuteCyclicTo.text.toString().trim()
if (minuteCyclicFrom.isNotEmpty() && minuteCyclicTo.isNotEmpty()) {
minute = "$minuteCyclicFrom-$minuteCyclicTo"
binding!!.etMinute.setText(minute)
binding!!.rbMinuteTypeCyclic.isChecked = true
}
}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
}
binding!!.etMinuteCyclicFrom.addTextChangedListener(minuteCyclicWatcher)
binding!!.etMinuteCyclicTo.addTextChangedListener(minuteCyclicWatcher)
//初始化输入提示--分--间隔
val minuteIntervalWatcher = object : TextWatcher {
override fun afterTextChanged(s: Editable) {
val minuteIntervalStart = binding!!.etMinuteIntervalStart.text.toString().trim()
val minuteInterval = binding!!.etMinuteInterval.text.toString().trim()
if (minuteIntervalStart.isNotEmpty() && minuteInterval.isNotEmpty()) {
minute = "$minuteIntervalStart/$minuteInterval"
binding!!.etMinute.setText(minute)
binding!!.rbMinuteTypeInterval.isChecked = true
}
}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
}
binding!!.etMinuteIntervalStart.addTextChangedListener(minuteIntervalWatcher)
binding!!.etMinuteInterval.addTextChangedListener(minuteIntervalWatcher)
//初始化输入提示--分--指定
binding!!.flowlayoutMultiSelectMinute.setTagCheckedMode(FlowTagLayout.FLOW_TAG_CHECKED_MULTI)
binding!!.flowlayoutMultiSelectMinute.setItems(minutesList)
binding!!.flowlayoutMultiSelectMinute.setOnTagSelectListener { parent, position, selectedList ->
selectedMinuteList = getSelectedItems(parent, selectedList, 1)
Log.d(TAG, "position:$position, selectedMinutesList:$selectedMinuteList")
if (selectedMinuteList.isEmpty()) {
binding!!.rbMinuteTypeAll.isChecked = true
minute = "*"
} else {
binding!!.rbMinuteTypeAssigned.isChecked = true
minute = selectedMinuteList
}
binding!!.etMinute.setText(minute)
}
}
private fun afterMinuteChanged() {
minute = binding!!.etMinute.text.toString().trim()
try {
//判断cronExpression是否有效
expression = "$second $minute $hour $day $month $week $year"
Log.d(TAG, "afterMinuteChanged expression:$expression")
CronExpression.validateExpression(expression)
} catch (e: Exception) {
XToastUtils.error("Cron表达式无效" + e.message, 30000)
return
}
when {
minute == "*" -> {
binding!!.rbMinuteTypeAll.isChecked = true
}
minute.contains("/") -> {
val minutesArray = minute.split("/")
binding!!.etMinuteIntervalStart.setText(minutesArray.getOrNull(0) ?: "0")
binding!!.etMinuteInterval.setText(minutesArray.getOrNull(1) ?: "1")
binding!!.rbMinuteTypeInterval.isChecked = true
}
minute.contains(",") -> {
val minutesList = restoreMergedItems(minute, "%02d")
Log.d(TAG, "minutesList:$minutesList")
binding!!.flowlayoutMultiSelectMinute.setSelectedItems(minutesList)
binding!!.rbMinuteTypeAssigned.isChecked = true
selectedMinuteList = minutesList.joinToString(",")
}
minute.contains("-") -> {
val minutesArray = minute.split("-")
binding!!.etMinuteCyclicFrom.setText(minutesArray.getOrNull(0) ?: "00")
binding!!.etMinuteCyclicTo.setText(minutesArray.getOrNull(1) ?: "59")
binding!!.rbMinuteTypeCyclic.isChecked = true
}
minutesList.indexOf(String.format("%02d", minute.toInt())) != -1 -> {
binding!!.flowlayoutMultiSelectMinute.setSelectedItems(String.format("%02d", minute.toInt()))
binding!!.rbMinuteTypeAssigned.isChecked = true
selectedMinuteList = minute
}
else -> {
binding!!.rbMinuteTypeAll.isChecked = true
}
}
}
//初始化输入提示--时
@SuppressLint("SetTextI18n")
private fun initHourInputHelper() {
binding!!.etHour.setOnFocusChangeListener { _, hasFocus ->
if (hasFocus) {
switchInputHelper(binding!!.layoutHourType)
} else {
afterHourChanged()
}
}
binding!!.etHour.setText(hour)
afterHourChanged()
//时类型
binding!!.rgHourType.setOnCheckedChangeListener { _: RadioGroup?, checkedId: Int ->
when (checkedId) {
R.id.rb_hour_type_cyclic -> {
var hourCyclicFrom = binding!!.etHourCyclicFrom.text.toString().trim()
if (hourCyclicFrom.isEmpty()) {
hourCyclicFrom = "00"
binding!!.etHourCyclicFrom.setText(hourCyclicFrom)
}
var hourCyclicTo = binding!!.etHourCyclicTo.text.toString().trim()
if (hourCyclicTo.isEmpty()) {
hourCyclicTo = "23"
binding!!.etHourCyclicTo.setText(hourCyclicTo)
}
hour = "$hourCyclicFrom-$hourCyclicTo"
}
R.id.rb_hour_type_interval -> {
var hourIntervalStart = binding!!.etHourIntervalStart.text.toString().trim()
if (hourIntervalStart.isEmpty()) {
hourIntervalStart = "0"
binding!!.etHourIntervalStart.setText(hourIntervalStart)
}
var hourInterval = binding!!.etHourInterval.text.toString().trim()
if (hourInterval.isEmpty()) {
hourInterval = "2"
binding!!.etHourInterval.setText(hourInterval)
}
hour = "$hourIntervalStart/$hourInterval"
}
R.id.rb_hour_type_assigned -> {
if (selectedHourList.isEmpty()) {
selectedHourList = "00"
binding!!.flowlayoutMultiSelectHour.setSelectedItems("00")
}
hour = selectedHourList
}
else -> {
hour = "*"
}
}
binding!!.etHour.setText(hour)
}
//初始化输入提示--时--周期
val hourCyclicWatcher = object : TextWatcher {
override fun afterTextChanged(s: Editable) {
val hourCyclicFrom = binding!!.etHourCyclicFrom.text.toString().trim()
val hourCyclicTo = binding!!.etHourCyclicTo.text.toString().trim()
if (hourCyclicFrom.isNotEmpty() && hourCyclicTo.isNotEmpty()) {
hour = "$hourCyclicFrom-$hourCyclicTo"
binding!!.etHour.setText(hour)
binding!!.rbHourTypeCyclic.isChecked = true
}
}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
}
binding!!.etHourCyclicFrom.addTextChangedListener(hourCyclicWatcher)
binding!!.etHourCyclicTo.addTextChangedListener(hourCyclicWatcher)
//初始化输入提示--时--间隔
val hourIntervalWatcher = object : TextWatcher {
override fun afterTextChanged(s: Editable) {
val hourIntervalStart = binding!!.etHourIntervalStart.text.toString().trim()
val hourInterval = binding!!.etHourInterval.text.toString().trim()
if (hourIntervalStart.isNotEmpty() && hourInterval.isNotEmpty()) {
hour = "$hourIntervalStart/$hourInterval"
binding!!.etHour.setText(hour)
binding!!.rbHourTypeInterval.isChecked = true
}
}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
}
binding!!.etHourIntervalStart.addTextChangedListener(hourIntervalWatcher)
binding!!.etHourInterval.addTextChangedListener(hourIntervalWatcher)
//初始化输入提示--时--指定
binding!!.flowlayoutMultiSelectHour.setTagCheckedMode(FlowTagLayout.FLOW_TAG_CHECKED_MULTI)
binding!!.flowlayoutMultiSelectHour.setItems(hoursList)
binding!!.flowlayoutMultiSelectHour.setOnTagSelectListener { parent, position, selectedList ->
selectedHourList = getSelectedItems(parent, selectedList, 1)
Log.d(TAG, "position:$position, selectedHoursList:$selectedHourList")
if (selectedHourList.isEmpty()) {
binding!!.rbHourTypeAll.isChecked = true
hour = "*"
} else {
binding!!.rbHourTypeAssigned.isChecked = true
hour = selectedHourList
}
binding!!.etHour.setText(hour)
}
}
private fun afterHourChanged() {
hour = binding!!.etHour.text.toString().trim()
try {
//判断cronExpression是否有效
expression = "$second $minute $hour $day $month $week $year"
Log.d(TAG, "afterHourChanged expression:$expression")
CronExpression.validateExpression(expression)
} catch (e: Exception) {
XToastUtils.error("Cron表达式无效" + e.message, 30000)
return
}
when {
hour == "*" -> {
binding!!.rbHourTypeAll.isChecked = true
}
hour.contains("/") -> {
val hoursArray = hour.split("/")
binding!!.etHourIntervalStart.setText(hoursArray.getOrNull(0) ?: "0")
binding!!.etHourInterval.setText(hoursArray.getOrNull(1) ?: "1")
binding!!.rbHourTypeInterval.isChecked = true
}
hour.contains(",") -> {
val hoursList = restoreMergedItems(hour, "%02d")
Log.d(TAG, "hoursList:$hoursList")
binding!!.flowlayoutMultiSelectHour.setSelectedItems(hoursList)
binding!!.rbHourTypeAssigned.isChecked = true
selectedHourList = hoursList.joinToString(",")
}
hour.contains("-") -> {
val hoursArray = hour.split("-")
binding!!.etHourCyclicFrom.setText(hoursArray.getOrNull(0) ?: "00")
binding!!.etHourCyclicTo.setText(hoursArray.getOrNull(1) ?: "23")
binding!!.rbHourTypeCyclic.isChecked = true
}
hoursList.indexOf(String.format("%02d", hour.toInt())) != -1 -> {
binding!!.flowlayoutMultiSelectHour.setSelectedItems(String.format("%02d", hour.toInt()))
binding!!.rbHourTypeAssigned.isChecked = true
selectedHourList = hour
}
else -> {
binding!!.rbHourTypeAll.isChecked = true
}
}
}
//初始化输入提示--日
@SuppressLint("SetTextI18n")
private fun initDayInputHelper() {
binding!!.etDay.setOnFocusChangeListener { _, hasFocus ->
if (hasFocus) {
switchInputHelper(binding!!.layoutDayType)
} else {
afterDayChanged()
}
}
binding!!.etDay.setText(day)
afterDayChanged()
//日类型
binding!!.rgDayType.setOnCheckedChangeListener { _: RadioGroup?, checkedId: Int ->
when (checkedId) {
R.id.rb_day_type_cyclic -> {
var dayCyclicFrom = binding!!.etDayCyclicFrom.text.toString().trim()
if (dayCyclicFrom.isEmpty()) {
dayCyclicFrom = "1"
binding!!.etDayCyclicFrom.setText(dayCyclicFrom)
}
var dayCyclicTo = binding!!.etDayCyclicTo.text.toString().trim()
if (dayCyclicTo.isEmpty()) {
dayCyclicTo = "31"
binding!!.etDayCyclicTo.setText(dayCyclicTo)
}
day = "$dayCyclicFrom-$dayCyclicTo"
}
R.id.rb_day_type_interval -> {
var dayIntervalStart = binding!!.etDayIntervalStart.text.toString().trim()
if (dayIntervalStart.isEmpty()) {
dayIntervalStart = "1"
binding!!.etDayIntervalStart.setText(dayIntervalStart)
}
var dayInterval = binding!!.etDayInterval.text.toString().trim()
if (dayInterval.isEmpty()) {
dayInterval = "2"
binding!!.etDayInterval.setText(dayInterval)
}
day = "$dayIntervalStart/$dayInterval"
}
R.id.rb_day_type_assigned -> {
if (selectedDayList.isEmpty()) {
selectedDayList = "1"
binding!!.flowlayoutMultiSelectDay.setSelectedItems("1")
}
day = selectedDayList
}
R.id.rb_day_type_last_day_of_month -> {
day = "L"
}
R.id.rb_day_type_last_day_of_month_recent_day -> {
day = "LW"
}
R.id.rb_day_type_recent_day -> {
var recentDay = binding!!.etRecentDay.text.toString().trim()
if (recentDay.isEmpty()) {
recentDay = "1"
binding!!.etRecentDay.setText(recentDay)
}
day = recentDay + "W"
}
R.id.rb_day_type_not_assigned -> {
day = "?"
}
else -> {
day = "*"
}
}
binding!!.etDay.setText(day)
}
//初始化输入提示--日--周期
val dayCyclicWatcher = object : TextWatcher {
override fun afterTextChanged(s: Editable) {
val dayCyclicFrom = binding!!.etDayCyclicFrom.text.toString().trim()
val dayCyclicTo = binding!!.etDayCyclicTo.text.toString().trim()
if (dayCyclicFrom.isNotEmpty() && dayCyclicTo.isNotEmpty()) {
day = "$dayCyclicFrom-$dayCyclicTo"
binding!!.etDay.setText(day)
binding!!.rbDayTypeCyclic.isChecked = true
}
}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
}
binding!!.etDayCyclicFrom.addTextChangedListener(dayCyclicWatcher)
binding!!.etDayCyclicTo.addTextChangedListener(dayCyclicWatcher)
//初始化输入提示--日--间隔
val dayIntervalWatcher = object : TextWatcher {
override fun afterTextChanged(s: Editable) {
val dayIntervalStart = binding!!.etDayIntervalStart.text.toString().trim()
val dayInterval = binding!!.etDayInterval.text.toString().trim()
if (dayIntervalStart.isNotEmpty() && dayInterval.isNotEmpty()) {
day = "$dayIntervalStart/$dayInterval"
binding!!.etDay.setText(day)
binding!!.rbDayTypeInterval.isChecked = true
}
}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
}
binding!!.etDayIntervalStart.addTextChangedListener(dayIntervalWatcher)
binding!!.etDayInterval.addTextChangedListener(dayIntervalWatcher)
//初始化输入提示--日--指定
binding!!.flowlayoutMultiSelectDay.setTagCheckedMode(FlowTagLayout.FLOW_TAG_CHECKED_MULTI)
binding!!.flowlayoutMultiSelectDay.setItems(dayList)
binding!!.flowlayoutMultiSelectDay.setOnTagSelectListener { parent, position, selectedList ->
selectedDayList = getSelectedItems(parent, selectedList)
Log.d(TAG, "position:$position, selectedDayList:$selectedDayList")
if (selectedDayList.isEmpty()) {
binding!!.rbDayTypeAll.isChecked = true
day = "*"
} else {
binding!!.rbDayTypeAssigned.isChecked = true
day = selectedDayList
}
binding!!.etDay.setText(day)
}
}
private fun afterDayChanged() {
//周和日不能同时设置
day = binding!!.etDay.text.toString().trim()
if (day != "?" && week != "?") {
week = "?"
binding!!.etWeek.setText(week)
}
try {
//判断cronExpression是否有效
expression = "$second $minute $hour $day $month $week $year"
Log.d(TAG, "afterDayChanged expression:$expression")
CronExpression.validateExpression(expression)
} catch (e: Exception) {
XToastUtils.error("Cron表达式无效" + e.message, 30000)
return
}
when {
day == "*" -> {
binding!!.rbDayTypeAll.isChecked = true
}
day == "?" -> {
binding!!.rbDayTypeNotAssigned.isChecked = true
}
day == "L" -> {
binding!!.rbDayTypeLastDayOfMonth.isChecked = true
}
day == "LW" -> {
binding!!.rbDayTypeLastDayOfMonthRecentDay.isChecked = true
return
}
day.endsWith("W") -> {
binding!!.rbDayTypeRecentDay.isChecked = true
binding!!.etRecentDay.setText(day.removeSuffix("W"))
return
}
day.contains("/") -> {
val dayArray = day.split("/")
binding!!.etDayIntervalStart.setText(dayArray.getOrNull(0) ?: "0")
binding!!.etDayInterval.setText(dayArray.getOrNull(1) ?: "1")
binding!!.rbDayTypeInterval.isChecked = true
}
day.contains(",") -> {
val dayList = restoreMergedItems(day, "%d")
Log.d(TAG, "dayList:$dayList")
binding!!.flowlayoutMultiSelectDay.setSelectedItems(dayList)
binding!!.rbDayTypeAssigned.isChecked = true
selectedDayList = dayList.joinToString(",")
}
day.contains("-") -> {
val dayArray = day.split("-")
binding!!.etDayCyclicFrom.setText(dayArray.getOrNull(0) ?: "1")
binding!!.etDayCyclicTo.setText(dayArray.getOrNull(1) ?: "31")
binding!!.rbDayTypeCyclic.isChecked = true
}
dayList.indexOf(String.format("%d", day.toInt())) != -1 -> {
binding!!.flowlayoutMultiSelectDay.setSelectedItems(String.format("%d", day.toInt()))
binding!!.rbDayTypeAssigned.isChecked = true
selectedDayList = day
}
else -> {
binding!!.rbDayTypeAll.isChecked = true
}
}
}
//初始化输入提示--月
@SuppressLint("SetTextI18n")
private fun initMonthInputHelper() {
binding!!.etMonth.setOnFocusChangeListener { _, hasFocus ->
if (hasFocus) {
switchInputHelper(binding!!.layoutMonthType)
} else {
afterMonthChanged()
}
}
binding!!.etMonth.setText(month)
afterMonthChanged()
//月类型
binding!!.rgMonthType.setOnCheckedChangeListener { _: RadioGroup?, checkedId: Int ->
when (checkedId) {
R.id.rb_month_type_cyclic -> {
var monthCyclicFrom = binding!!.etMonthCyclicFrom.text.toString().trim()
if (monthCyclicFrom.isEmpty()) {
monthCyclicFrom = "1"
binding!!.etMonthCyclicFrom.setText(monthCyclicFrom)
}
var monthCyclicTo = binding!!.etMonthCyclicTo.text.toString().trim()
if (monthCyclicTo.isEmpty()) {
monthCyclicTo = "12"
binding!!.etMonthCyclicTo.setText(monthCyclicTo)
}
month = "$monthCyclicFrom-$monthCyclicTo"
}
R.id.rb_month_type_interval -> {
var monthIntervalStart = binding!!.etMonthIntervalStart.text.toString().trim()
if (monthIntervalStart.isEmpty()) {
monthIntervalStart = "1"
binding!!.etMonthIntervalStart.setText(monthIntervalStart)
}
var monthInterval = binding!!.etMonthInterval.text.toString().trim()
if (monthInterval.isEmpty()) {
monthInterval = "2"
binding!!.etMonthInterval.setText(monthInterval)
}
month = "$monthIntervalStart/$monthInterval"
}
R.id.rb_month_type_assigned -> {
if (selectedMonthList.isEmpty()) {
selectedMonthList = "1"
binding!!.flowlayoutMultiSelectMonth.setSelectedItems("1")
}
month = selectedMonthList
}
else -> {
month = "*"
}
}
binding!!.etMonth.setText(month)
}
//初始化输入提示--月--周期
val monthCyclicWatcher = object : TextWatcher {
override fun afterTextChanged(s: Editable) {
val monthCyclicFrom = binding!!.etMonthCyclicFrom.text.toString().trim()
val monthCyclicTo = binding!!.etMonthCyclicTo.text.toString().trim()
if (monthCyclicFrom.isNotEmpty() && monthCyclicTo.isNotEmpty()) {
month = "$monthCyclicFrom-$monthCyclicTo"
binding!!.etMonth.setText(month)
binding!!.rbMonthTypeCyclic.isChecked = true
}
}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
}
binding!!.etMonthCyclicFrom.addTextChangedListener(monthCyclicWatcher)
binding!!.etMonthCyclicTo.addTextChangedListener(monthCyclicWatcher)
//初始化输入提示--月--间隔
val monthIntervalWatcher = object : TextWatcher {
override fun afterTextChanged(s: Editable) {
val monthIntervalStart = binding!!.etMonthIntervalStart.text.toString().trim()
val monthInterval = binding!!.etMonthInterval.text.toString().trim()
if (monthIntervalStart.isNotEmpty() && monthInterval.isNotEmpty()) {
month = "$monthIntervalStart/$monthInterval"
binding!!.etMonth.setText(month)
binding!!.rbMonthTypeInterval.isChecked = true
}
}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
}
binding!!.etMonthIntervalStart.addTextChangedListener(monthIntervalWatcher)
binding!!.etMonthInterval.addTextChangedListener(monthIntervalWatcher)
//初始化输入提示--月--指定
binding!!.flowlayoutMultiSelectMonth.setTagCheckedMode(FlowTagLayout.FLOW_TAG_CHECKED_MULTI)
binding!!.flowlayoutMultiSelectMonth.setItems(monthList)
binding!!.flowlayoutMultiSelectMonth.setOnTagSelectListener { parent, position, selectedList ->
selectedMonthList = getSelectedItems(parent, selectedList)
Log.d(TAG, "position:$position, selectedMonthList:$selectedMonthList")
if (selectedMonthList.isEmpty()) {
binding!!.rbMonthTypeAll.isChecked = true
month = "*"
} else {
binding!!.rbMonthTypeAssigned.isChecked = true
month = selectedMonthList
}
binding!!.etMonth.setText(month)
}
}
private fun afterMonthChanged() {
month = binding!!.etMonth.text.toString().trim()
try {
//判断cronExpression是否有效
expression = "$second $minute $hour $day $month $week $year"
Log.d(TAG, "afterMonthChanged expression:$expression")
CronExpression.validateExpression(expression)
} catch (e: Exception) {
XToastUtils.error("Cron表达式无效" + e.message, 30000)
return
}
when {
month == "*" -> {
binding!!.rbMonthTypeAll.isChecked = true
}
month.contains("/") -> {
val monthArray = month.split("/")
binding!!.etMonthIntervalStart.setText(monthArray.getOrNull(0) ?: "0")
binding!!.etMonthInterval.setText(monthArray.getOrNull(1) ?: "1")
binding!!.rbMonthTypeInterval.isChecked = true
}
month.contains(",") -> {
val monthList = restoreMergedItems(month, "%d")
Log.d(TAG, "monthList:$monthList")
binding!!.flowlayoutMultiSelectMonth.setSelectedItems(monthList)
binding!!.rbMonthTypeAssigned.isChecked = true
selectedMonthList = monthList.joinToString(",")
}
month.contains("-") -> {
val monthArray = month.split("-")
binding!!.etMonthCyclicFrom.setText(monthArray.getOrNull(0) ?: "1")
binding!!.etMonthCyclicTo.setText(monthArray.getOrNull(1) ?: "31")
binding!!.rbMonthTypeCyclic.isChecked = true
}
monthList.indexOf(month) != -1 -> {
binding!!.flowlayoutMultiSelectMonth.setSelectedItems(month)
binding!!.rbMonthTypeAssigned.isChecked = true
selectedMonthList = month
}
else -> {
binding!!.rbMonthTypeAll.isChecked = true
}
}
}
//初始化输入提示--周
private fun initWeekInputHelper() {
binding!!.etWeek.setOnFocusChangeListener { _, hasFocus ->
if (hasFocus) {
switchInputHelper(binding!!.layoutWeekType)
} else {
afterWeekChanged()
}
}
binding!!.etWeek.setText(week)
afterWeekChanged()
//周类型
binding!!.rgWeekType.setOnCheckedChangeListener { _: RadioGroup?, checkedId: Int ->
when (checkedId) {
R.id.rb_week_type_cyclic -> {
var weekCyclicFrom = binding!!.etWeekCyclicFrom.text.toString().trim()
if (weekCyclicFrom.isEmpty()) {
weekCyclicFrom = "1"
binding!!.etWeekCyclicFrom.setText(weekCyclicFrom)
}
var weekCyclicTo = binding!!.etWeekCyclicTo.text.toString().trim()
if (weekCyclicTo.isEmpty()) {
weekCyclicTo = "7"
binding!!.etWeekCyclicTo.setText(weekCyclicTo)
}
week = "$weekCyclicFrom-$weekCyclicTo"
}
R.id.rb_week_type_weeks_of_week -> {
var whichWeekOfMonth = binding!!.etWhichWeekOfMonth.text.toString().trim()
if (whichWeekOfMonth.isEmpty()) {
whichWeekOfMonth = "1"
binding!!.etWhichWeekOfMonth.setText(whichWeekOfMonth)
}
var whichDayOfWeek = binding!!.etWhichDayOfWeek.text.toString().trim()
if (whichDayOfWeek.isEmpty()) {
whichDayOfWeek = "1"
binding!!.etWhichDayOfWeek.setText(whichDayOfWeek)
}
week = "$whichWeekOfMonth#$whichDayOfWeek"
}
R.id.rb_week_type_assigned -> {
if (selectedWeekList.isEmpty()) {
selectedWeekList = "1"
binding!!.flowlayoutMultiSelectWeek.setSelectedItems("1")
}
week = selectedWeekList
}
R.id.rb_week_type_last_week_of_month -> {
var lastWeekOfMonth = binding!!.etLastWeekOfMonth.text.toString().trim()
if (lastWeekOfMonth.isEmpty()) {
lastWeekOfMonth = "1"
binding!!.etLastWeekOfMonth.setText(lastWeekOfMonth)
}
week = lastWeekOfMonth + "L"
}
R.id.rb_week_type_all -> {
week = "*"
}
else -> {
week = "?"
}
}
binding!!.etWeek.setText(week)
}
//初始化输入提示--周--周期
val weekCyclicWatcher = object : TextWatcher {
override fun afterTextChanged(s: Editable) {
val weekCyclicFrom = binding!!.etWeekCyclicFrom.text.toString().trim()
val weekCyclicTo = binding!!.etWeekCyclicTo.text.toString().trim()
if (weekCyclicFrom.isNotEmpty() && weekCyclicTo.isNotEmpty()) {
week = "$weekCyclicFrom-$weekCyclicTo"
binding!!.etWeek.setText(week)
binding!!.rbWeekTypeCyclic.isChecked = true
}
}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
}
binding!!.etWeekCyclicFrom.addTextChangedListener(weekCyclicWatcher)
binding!!.etWeekCyclicTo.addTextChangedListener(weekCyclicWatcher)
//初始化输入提示--周--间隔
val weeksOfWeekWatcher = object : TextWatcher {
override fun afterTextChanged(s: Editable) {
val whichWeekOfMonth = binding!!.etWhichWeekOfMonth.text.toString().trim()
val whichDayOfWeek = binding!!.etWhichDayOfWeek.text.toString().trim()
if (whichWeekOfMonth.isNotEmpty() && whichDayOfWeek.isNotEmpty()) {
week = "$whichWeekOfMonth#$whichDayOfWeek"
binding!!.etWeek.setText(week)
binding!!.rbWeekTypeWeeksOfWeek.isChecked = true
}
}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
}
binding!!.etWhichWeekOfMonth.addTextChangedListener(weeksOfWeekWatcher)
binding!!.etWhichDayOfWeek.addTextChangedListener(weeksOfWeekWatcher)
//初始化输入提示--周--指定
binding!!.flowlayoutMultiSelectWeek.setTagCheckedMode(FlowTagLayout.FLOW_TAG_CHECKED_MULTI)
binding!!.flowlayoutMultiSelectWeek.setItems(weekList)
binding!!.flowlayoutMultiSelectWeek.setOnTagSelectListener { parent, position, selectedList ->
selectedWeekList = getSelectedItems(parent, selectedList)
Log.d(TAG, "position:$position, selectedWeekList:$selectedWeekList")
if (selectedWeekList.isEmpty()) {
binding!!.rbWeekTypeAll.isChecked = true
week = "?"
} else {
binding!!.rbWeekTypeAssigned.isChecked = true
week = selectedWeekList
}
binding!!.etWeek.setText(week)
}
//初始化输入提示--周--本月最后
binding!!.etLastWeekOfMonth.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable) {
val lastWeekOfMonth = binding!!.etLastWeekOfMonth.text.toString().trim()
if (lastWeekOfMonth.isNotEmpty()) {
week = lastWeekOfMonth + "L"
binding!!.etWeek.setText(week)
binding!!.rbWeekTypeLastWeekOfMonth.isChecked = true
} else {
week = "*"
binding!!.rbWeekTypeAll.isChecked = true
}
}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
})
}
private fun afterWeekChanged() {
//周和日不能同时设置
week = binding!!.etWeek.text.toString().trim()
if (day != "?" && week != "?") {
day = "?"
binding!!.etDay.setText(day)
}
try {
//判断cronExpression是否有效
expression = "$second $minute $hour $day $month $week $year"
Log.d(TAG, "afterWeekChanged expression:$expression")
CronExpression.validateExpression(expression)
} catch (e: Exception) {
XToastUtils.error("Cron表达式无效" + e.message, 30000)
return
}
when {
week == "*" -> {
binding!!.rbWeekTypeAll.isChecked = true
}
week == "?" -> {
binding!!.rbWeekTypeNotAssigned.isChecked = true
}
week.contains(",") -> {
val weekList = restoreMergedItems(week, "%d")
Log.d(TAG, "weekList:$weekList")
binding!!.flowlayoutMultiSelectWeek.setSelectedItems(weekList)
binding!!.rbWeekTypeAssigned.isChecked = true
selectedWeekList = weekList.joinToString(",")
}
week.contains("-") -> {
val weekArray = week.split("-")
binding!!.etWeekCyclicFrom.setText(weekArray.getOrNull(0) ?: "1")
binding!!.etWeekCyclicTo.setText(weekArray.getOrNull(1) ?: "31")
binding!!.rbWeekTypeCyclic.isChecked = true
}
week.contains("#") -> {
val weekArray = week.split("#")
binding!!.etWhichWeekOfMonth.setText(weekArray.getOrNull(0) ?: "1")
binding!!.etWhichDayOfWeek.setText(weekArray.getOrNull(1) ?: "1")
binding!!.rbWeekTypeWeeksOfWeek.isChecked = true
}
weekList.indexOf(week) != -1 -> {
binding!!.flowlayoutMultiSelectWeek.setSelectedItems(week)
binding!!.rbWeekTypeAssigned.isChecked = true
selectedWeekList = week
}
week.endsWith("L") -> {
binding!!.rbWeekTypeLastWeekOfMonth.isChecked = true
binding!!.etLastWeekOfMonth.setText(week.removeSuffix("L"))
}
else -> {
binding!!.rbWeekTypeAll.isChecked = true
}
}
}
//初始化输入提示--年
@SuppressLint("SetTextI18n")
private fun initYearInputHelper() {
binding!!.etYear.setOnFocusChangeListener { _, hasFocus ->
if (hasFocus) {
switchInputHelper(binding!!.layoutYearType)
} else {
afterYearChanged()
}
}
binding!!.etYear.setText(year)
afterYearChanged()
//年类型
binding!!.rgYearType.setOnCheckedChangeListener { _: RadioGroup?, checkedId: Int ->
when (checkedId) {
R.id.rb_year_type_cyclic -> {
var yearCyclicFrom = binding!!.etYearCyclicFrom.text.toString().trim()
if (yearCyclicFrom.isEmpty()) {
yearCyclicFrom = "2023"
binding!!.etYearCyclicFrom.setText(yearCyclicFrom)
}
var yearCyclicTo = binding!!.etYearCyclicTo.text.toString().trim()
if (yearCyclicTo.isEmpty()) {
yearCyclicTo = "2058"
binding!!.etYearCyclicTo.setText(yearCyclicTo)
}
year = "$yearCyclicFrom-$yearCyclicTo"
}
R.id.rb_year_type_interval -> {
var yearIntervalStart = binding!!.etYearIntervalStart.text.toString().trim()
if (yearIntervalStart.isEmpty()) {
yearIntervalStart = "2023"
binding!!.etYearIntervalStart.setText(yearIntervalStart)
}
var yearInterval = binding!!.etYearInterval.text.toString().trim()
if (yearInterval.isEmpty()) {
yearInterval = "2"
binding!!.etYearInterval.setText(yearInterval)
}
year = "$yearIntervalStart/$yearInterval"
}
R.id.rb_year_type_assigned -> {
if (selectedYearList.isEmpty()) {
selectedYearList = "1"
binding!!.flowlayoutMultiSelectYear.setSelectedItems("1")
}
year = selectedYearList
}
else -> {
year = "*"
}
}
binding!!.etYear.setText(year)
}
//初始化输入提示--年--周期
val yearCyclicWatcher = object : TextWatcher {
override fun afterTextChanged(s: Editable) {
val yearCyclicFrom = binding!!.etYearCyclicFrom.text.toString().trim()
val yearCyclicTo = binding!!.etYearCyclicTo.text.toString().trim()
if (yearCyclicFrom.isNotEmpty() && yearCyclicTo.isNotEmpty()) {
year = "$yearCyclicFrom-$yearCyclicTo"
binding!!.etYear.setText(year)
binding!!.rbYearTypeCyclic.isChecked = true
}
}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
}
binding!!.etYearCyclicFrom.addTextChangedListener(yearCyclicWatcher)
binding!!.etYearCyclicTo.addTextChangedListener(yearCyclicWatcher)
//初始化输入提示--年--间隔
val yearIntervalWatcher = object : TextWatcher {
override fun afterTextChanged(s: Editable) {
val yearIntervalStart = binding!!.etYearIntervalStart.text.toString().trim()
val yearInterval = binding!!.etYearInterval.text.toString().trim()
if (yearIntervalStart.isNotEmpty() && yearInterval.isNotEmpty()) {
year = "$yearIntervalStart/$yearInterval"
binding!!.etYear.setText(year)
binding!!.rbYearTypeInterval.isChecked = true
}
}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
}
binding!!.etYearIntervalStart.addTextChangedListener(yearIntervalWatcher)
binding!!.etYearInterval.addTextChangedListener(yearIntervalWatcher)
//初始化输入提示--年--指定
binding!!.flowlayoutMultiSelectYear.setTagCheckedMode(FlowTagLayout.FLOW_TAG_CHECKED_MULTI)
binding!!.flowlayoutMultiSelectYear.setItems(yearList)
binding!!.flowlayoutMultiSelectYear.setOnTagSelectListener { parent, position, selectedList ->
selectedYearList = getSelectedItems(parent, selectedList)
Log.d(TAG, "position:$position, selectedYearList:$selectedYearList")
if (selectedYearList.isEmpty()) {
binding!!.rbYearTypeAll.isChecked = true
year = "*"
} else {
binding!!.rbYearTypeAssigned.isChecked = true
year = selectedYearList
}
binding!!.etYear.setText(year)
}
}
private fun afterYearChanged() {
year = binding!!.etYear.text.toString().trim()
try {
//判断cronExpression是否有效
expression = "$second $minute $hour $day $month $week $year"
Log.d(TAG, "afterYearChanged expression:$expression")
CronExpression.validateExpression(expression)
} catch (e: Exception) {
XToastUtils.error("Cron表达式无效" + e.message, 30000)
return
}
when {
year == "*" -> {
binding!!.rbYearTypeAll.isChecked = true
}
year == "?" -> {
binding!!.rbYearTypeNotAssigned.isChecked = true
}
year.contains("/") -> {
val yearArray = year.split("/")
binding!!.etYearIntervalStart.setText(yearArray.getOrNull(0) ?: "2023")
binding!!.etYearInterval.setText(yearArray.getOrNull(1) ?: "2")
binding!!.rbYearTypeInterval.isChecked = true
}
year.contains(",") -> {
val yearList = restoreMergedItems(year, "%d")
Log.d(TAG, "yearList:$yearList")
binding!!.flowlayoutMultiSelectYear.setSelectedItems(yearList)
binding!!.rbYearTypeAssigned.isChecked = true
selectedYearList = yearList.joinToString(",")
}
year.contains("-") -> {
val yearArray = year.split("-")
binding!!.etYearCyclicFrom.setText(yearArray.getOrNull(0) ?: "1970")
binding!!.etYearCyclicTo.setText(yearArray.getOrNull(1) ?: "2099")
binding!!.rbYearTypeCyclic.isChecked = true
}
yearList.indexOf(year) != -1 -> {
binding!!.flowlayoutMultiSelectYear.setSelectedItems(year)
binding!!.rbYearTypeAssigned.isChecked = true
selectedYearList = year
}
else -> {
binding!!.rbYearTypeAll.isChecked = true
}
}
}
//获取选中的项目
private fun getSelectedItems(parent: FlowTagLayout, selectedList: List<Int>, dataType: Int = 0): String {
if (selectedList.isEmpty()) return ""
val selectedNumList = mutableListOf<String>()
for (index in selectedList) {
selectedNumList.add(parent.adapter.getItem(index).toString())
}
val mergedList = when (dataType) {
2 -> mergeContinuousEnum(selectedNumList)
1 -> mergeContinuousItems(selectedNumList, "%02d")
else -> mergeContinuousItems(selectedNumList)
}
return mergedList.joinToString(",")
}
//合并连续的枚举值
private fun mergeContinuousEnum(input: List<String>): List<String> {
if (input.isEmpty()) return emptyList()
val result = mutableListOf<String>()
val enumValues = if (input.firstOrNull() in monthList) monthList else weekList
var start = enumValues.indexOf(input[0])
var end = enumValues.indexOf(input[0])
for (i in 1 until input.size) {
val currentIndex = enumValues.indexOf(input[i])
if (currentIndex == end + 1) {
end = currentIndex
} else {
if (start == end) {
result.add(enumValues[start])
} else {
result.add("${enumValues[start]}-${enumValues[end]}")
}
start = currentIndex
end = currentIndex
}
}
if (start == end) {
result.add(enumValues[start])
} else {
result.add("${enumValues[start]}-${enumValues[end]}")
}
return result
}
//合并连续的数字
private fun mergeContinuousItems(input: List<String>, stringFormat: String = "%d"): List<String> {
if (input.isEmpty()) return emptyList()
val items = input.map { it.toInt() }.sorted()
val result = mutableListOf<String>()
var start = items[0]
var end = items[0]
for (i in 1 until items.size) {
if (items[i] == end + 1) {
end = items[i]
} else {
if (start == end) {
result.add(String.format(stringFormat, start))
} else {
result.add(String.format(stringFormat, start) + "-" + String.format(stringFormat, end))
}
start = items[i]
end = items[i]
}
}
if (start == end) {
result.add(String.format(stringFormat, start))
} else {
result.add(String.format(stringFormat, start) + "-" + String.format(stringFormat, end))
}
return result
}
//还原被合并的连续数字
private fun restoreMergedItems(mergedString: String, stringFormat: String = "%d"): List<String> {
if (mergedString.isEmpty()) return emptyList()
val items = mutableListOf<String>()
val ranges = mergedString.split(",")
for (range in ranges) {
val rangeParts = range.split("-")
if (rangeParts.size == 1) {
items.add(rangeParts[0])
} else if (rangeParts.size == 2) {
val start = rangeParts[0].toInt()
val end = rangeParts[1].toInt()
for (i in start..end) {
items.add(String.format(stringFormat, i))
}
}
}
return items
}
//检查设置
@SuppressLint("SetTextI18n")
private fun checkSetting(): CronSetting {
second = binding!!.etSecond.text.toString().trim()
minute = binding!!.etMinute.text.toString().trim()
hour = binding!!.etHour.text.toString().trim()
day = binding!!.etDay.text.toString().trim()
month = binding!!.etMonth.text.toString().trim()
week = binding!!.etWeek.text.toString().trim()
year = binding!!.etYear.text.toString().trim()
expression = "$second $minute $hour $day $month $week $year"
description = ""
Log.d(TAG, "checkSetting, expression:$expression")
//判断cronExpression是否有效
CronExpression.validateExpression(expression)
//生成cron表达式描述
val options = Options()
options.isTwentyFourHourTime = true
//TODO支持多语言
val locale = Locale.getDefault()
//Chinese, Japanese, Korean and other East Asian languages have no spaces between words
options.isNeedSpaceBetweenWords = locale == Locale("zh") || locale == Locale("ja") || locale == Locale("ko")
description = CronExpressionDescriptor.getDescription(expression, options, locale)
return CronSetting(description, expression)
}
}