新增:转发规则可以设置免打扰(禁用转发)时间段 #318

This commit is contained in:
pppscn 2023-07-21 14:38:23 +08:00
parent 3c0bead575
commit 2bee1dad87
5 changed files with 157 additions and 5 deletions

View File

@ -15,7 +15,7 @@ import com.idormy.sms.forwarder.utils.DATABASE_NAME
@Database(
entities = [Frpc::class, Msg::class, Logs::class, Rule::class, Sender::class],
views = [LogsDetail::class],
version = 15,
version = 16,
exportSchema = false
)
@TypeConverters(ConvertersDate::class)
@ -96,6 +96,7 @@ custom_domains = smsf.demo.com
MIGRATION_12_13,
MIGRATION_13_14,
MIGRATION_14_15,
MIGRATION_15_16,
)
/*if (BuildConfig.DEBUG) {
@ -364,6 +365,14 @@ CREATE TABLE "Logs" (
}
}
//免打扰(禁用转发)时间段
private val MIGRATION_15_16 = object : Migration(15, 16) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("Alter table rule add column silent_period_start INTEGER NOT NULL DEFAULT 0 ")
database.execSQL("Alter table rule add column silent_period_end INTEGER NOT NULL DEFAULT 0 ")
}
}
}
}

View File

@ -47,6 +47,9 @@ data class Rule(
@ColumnInfo(name = "time") var time: Date = Date(),
@ColumnInfo(name = "sender_list", defaultValue = "") var senderList: List<Sender>,
@ColumnInfo(name = "sender_logic", defaultValue = "ALL") var senderLogic: String = "ALL",
//免打扰(禁用转发)时间段
@ColumnInfo(name = "silent_period_start", defaultValue = "0") var silentPeriodStart: Int = 0,
@ColumnInfo(name = "silent_period_end", defaultValue = "0") var silentPeriodEnd: Int = 0,
) : Parcelable {
companion object {

View File

@ -37,6 +37,8 @@ import com.xuexiang.xui.utils.ResUtils
import com.xuexiang.xui.widget.actionbar.TitleBar
import com.xuexiang.xui.widget.dialog.materialdialog.DialogAction
import com.xuexiang.xui.widget.dialog.materialdialog.MaterialDialog
import com.xuexiang.xui.widget.picker.widget.builder.OptionsPickerBuilder
import com.xuexiang.xui.widget.picker.widget.listener.OnOptionsSelectListener
import com.xuexiang.xutil.XUtil
import io.reactivex.SingleObserver
import io.reactivex.android.schedulers.AndroidSchedulers
@ -46,13 +48,18 @@ import kotlinx.coroutines.*
import java.util.*
@Page(name = "转发规则·编辑器")
@Suppress("PrivatePropertyName")
@Suppress("PrivatePropertyName", "DEPRECATION")
class RulesEditFragment : BaseFragment<FragmentRulesEditBinding?>(), View.OnClickListener, CompoundButton.OnCheckedChangeListener {
private val TAG: String = RulesEditFragment::class.java.simpleName
var titleBar: TitleBar? = null
private val viewModel by viewModels<RuleViewModel> { BaseViewModelFactory(context) }
//免打扰(禁用转发)时间段
private val mTimeOption = DataProvider.timePeriodOption
private var silentPeriodStart = 0
private var silentPeriodEnd = 0
//当前发送通道
var senderId = 0L
var senderListSelected: MutableList<Sender> = mutableListOf()
@ -119,6 +126,7 @@ class RulesEditFragment : BaseFragment<FragmentRulesEditBinding?>(), View.OnClic
//监听已安装App信息列表加载完成事件
LiveEventBus.get(EVENT_LOAD_APP_LIST, String::class.java).observeStickyForever(appListObserver)
}
"call" -> {
titleBar?.setTitle(R.string.call_rule)
binding!!.rbContent.visibility = View.GONE
@ -130,6 +138,7 @@ class RulesEditFragment : BaseFragment<FragmentRulesEditBinding?>(), View.OnClic
binding!!.btInsertTitleApp.visibility = View.GONE
binding!!.btInsertContentApp.visibility = View.GONE
}
else -> {
titleBar?.setTitle(R.string.sms_rule)
binding!!.rbPackageName.visibility = View.GONE
@ -152,6 +161,7 @@ class RulesEditFragment : BaseFragment<FragmentRulesEditBinding?>(), View.OnClic
}
override fun initListeners() {
binding!!.btnSilentPeriod.setOnClickListener(this)
binding!!.btInsertSender.setOnClickListener(this)
binding!!.btInsertContent.setOnClickListener(this)
binding!!.btInsertSenderApp.setOnClickListener(this)
@ -164,6 +174,7 @@ class RulesEditFragment : BaseFragment<FragmentRulesEditBinding?>(), View.OnClic
binding!!.btnDel.setOnClickListener(this)
binding!!.btnSave.setOnClickListener(this)
binding!!.sbStatus.setOnCheckedChangeListener(this)
binding!!.sbSmsTemplate.setOnCheckedChangeListener(this)
binding!!.sbRegexReplace.setOnCheckedChangeListener(this)
@ -178,12 +189,14 @@ class RulesEditFragment : BaseFragment<FragmentRulesEditBinding?>(), View.OnClic
binding!!.layoutMatchType.visibility = View.GONE
binding!!.layoutMatchValue.visibility = View.GONE
}
R.id.rb_multi_match -> {
binding!!.rgCheck.check(R.id.rb_is)
binding!!.tvMuRuleTips.visibility = View.VISIBLE
binding!!.layoutMatchType.visibility = View.GONE
binding!!.layoutMatchValue.visibility = View.VISIBLE
}
else -> {
binding!!.tvMuRuleTips.visibility = View.GONE
binding!!.layoutMatchType.visibility = View.VISIBLE
@ -209,6 +222,10 @@ class RulesEditFragment : BaseFragment<FragmentRulesEditBinding?>(), View.OnClic
override fun onCheckedChanged(buttonView: CompoundButton?, isChecked: Boolean) {
when (buttonView?.id) {
R.id.sb_status -> {
binding!!.layoutSilentPeriod.visibility = if (isChecked) View.VISIBLE else View.GONE
}
R.id.sb_sms_template -> {
if (isChecked) {
binding!!.layoutSmsTemplate.visibility = View.VISIBLE
@ -217,6 +234,7 @@ class RulesEditFragment : BaseFragment<FragmentRulesEditBinding?>(), View.OnClic
binding!!.etSmsTemplate.setText("")
}
}
R.id.sb_regex_replace -> {
if (isChecked) {
binding!!.layoutRegexReplace.visibility = View.VISIBLE
@ -225,6 +243,7 @@ class RulesEditFragment : BaseFragment<FragmentRulesEditBinding?>(), View.OnClic
binding!!.etRegexReplace.setText("")
}
}
else -> {}
}
}
@ -234,43 +253,66 @@ class RulesEditFragment : BaseFragment<FragmentRulesEditBinding?>(), View.OnClic
try {
val etSmsTemplate: EditText = binding!!.etSmsTemplate
when (v.id) {
R.id.btn_silent_period -> {
OptionsPickerBuilder(context, OnOptionsSelectListener { _: View?, options1: Int, options2: Int, _: Int ->
silentPeriodStart = options1
silentPeriodEnd = options2
val txt = mTimeOption[options1] + " ~ " + mTimeOption[options2]
binding!!.tvSilentPeriod.text = txt
XToastUtils.toast(txt)
return@OnOptionsSelectListener false
}).setTitleText(getString(R.string.select_time_period)).setSelectOptions(silentPeriodStart, silentPeriodEnd).build<Any>().also {
it.setNPicker(mTimeOption, mTimeOption)
it.show()
}
}
R.id.bt_insert_sender -> {
CommonUtils.insertOrReplaceText2Cursor(etSmsTemplate, getString(R.string.tag_from))
return
}
R.id.bt_insert_content -> {
CommonUtils.insertOrReplaceText2Cursor(etSmsTemplate, getString(R.string.tag_sms))
return
}
R.id.bt_insert_sender_app -> {
CommonUtils.insertOrReplaceText2Cursor(etSmsTemplate, getString(R.string.tag_package_name))
return
}
R.id.bt_insert_title_app -> {
CommonUtils.insertOrReplaceText2Cursor(etSmsTemplate, getString(R.string.tag_title))
return
}
R.id.bt_insert_content_app -> {
CommonUtils.insertOrReplaceText2Cursor(etSmsTemplate, getString(R.string.tag_msg))
return
}
R.id.bt_insert_extra -> {
CommonUtils.insertOrReplaceText2Cursor(etSmsTemplate, getString(R.string.tag_card_slot))
return
}
R.id.bt_insert_time -> {
CommonUtils.insertOrReplaceText2Cursor(etSmsTemplate, getString(R.string.tag_receive_time))
return
}
R.id.bt_insert_device_name -> {
CommonUtils.insertOrReplaceText2Cursor(etSmsTemplate, getString(R.string.tag_device_name))
return
}
R.id.btn_test -> {
val ruleNew = checkForm()
testRule(ruleNew)
return
}
R.id.btn_del -> {
if (ruleId <= 0 || isClone) {
popToBack()
@ -284,6 +326,7 @@ class RulesEditFragment : BaseFragment<FragmentRulesEditBinding?>(), View.OnClic
}.show()
return
}
R.id.btn_save -> {
val ruleNew = checkForm()
if (isClone) ruleNew.id = 0
@ -301,7 +344,11 @@ class RulesEditFragment : BaseFragment<FragmentRulesEditBinding?>(), View.OnClic
}
//初始化发送通道下拉框
@SuppressLint("SetTextI18n")
private fun initSenderSpinner() {
//免打扰(禁用转发)时间段
binding!!.tvSilentPeriod.text = mTimeOption[silentPeriodStart] + " ~ " + mTimeOption[silentPeriodEnd]
AppDatabase.getInstance(requireContext()).senderDao().getAll().subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(object : SingleObserver<List<Sender>> {
override fun onSubscribe(d: Disposable) {}
@ -503,6 +550,8 @@ class RulesEditFragment : BaseFragment<FragmentRulesEditBinding?>(), View.OnClic
binding!!.sbRegexReplace.isChecked = !TextUtils.isEmpty(rule.regexReplace.trim())
binding!!.etRegexReplace.setText(rule.regexReplace.trim())
binding!!.sbStatus.isChecked = rule.statusChecked
silentPeriodStart = rule.silentPeriodStart
silentPeriodEnd = rule.silentPeriodEnd
//初始化发送通道下拉框
initSenderSpinner()
@ -565,7 +614,7 @@ class RulesEditFragment : BaseFragment<FragmentRulesEditBinding?>(), View.OnClic
// throw Exception(getString(R.string.invalid_rule_status))
//}
return Rule(ruleId, ruleType, filed, check, value, senderId, smsTemplate, regexReplace, simSlot, status, Date(), senderListSelected, senderLogic)
return Rule(ruleId, ruleType, filed, check, value, senderId, smsTemplate, regexReplace, simSlot, status, Date(), senderListSelected, senderLogic, silentPeriodStart, silentPeriodEnd)
}
//检查多重匹配规则是否正确
@ -635,7 +684,7 @@ class RulesEditFragment : BaseFragment<FragmentRulesEditBinding?>(), View.OnClic
val testSim = "SIM" + (simSlot + 1)
val ruleSim: String = rule.simSlot
if (ruleSim != "ALL" && ruleSim != testSim) {
throw java.lang.Exception(getString(R.string.card_slot_does_not_match))
throw Exception(getString(R.string.card_slot_does_not_match))
}
//获取卡槽信息
@ -647,7 +696,7 @@ class RulesEditFragment : BaseFragment<FragmentRulesEditBinding?>(), View.OnClic
val msgInfo = MsgInfo(ruleType, etFrom.text.toString(), etContent.text.toString(), Date(), simInfo, simSlot)
if (!rule.checkMsg(msgInfo)) {
throw java.lang.Exception(getString(R.string.unmatched_rule))
throw Exception(getString(R.string.unmatched_rule))
}
Thread {

View File

@ -1,5 +1,6 @@
package com.idormy.sms.forwarder.utils
import android.annotation.SuppressLint
import android.os.Looper
import android.util.Log
import androidx.work.OneTimeWorkRequestBuilder
@ -19,7 +20,12 @@ import com.idormy.sms.forwarder.workers.SendWorker
import com.idormy.sms.forwarder.workers.UpdateLogsWorker
import com.xuexiang.xui.utils.ResUtils
import com.xuexiang.xutil.XUtil
import java.text.ParsePosition
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Date
@Suppress("DEPRECATION")
object SendUtils {
private const val TAG = "SendUtils"
@ -58,6 +64,7 @@ object SendUtils {
}
//匹配发送通道发送消息
@SuppressLint("SimpleDateFormat")
fun sendMsgSender(msgInfo: MsgInfo, rule: Rule, senderIndex: Int = 0, logId: Long = 0L, msgId: Long = 0L) {
try {
val sender = rule.senderList[senderIndex]
@ -67,6 +74,35 @@ object SendUtils {
senderLogic(0, msgInfo, rule, senderIndex, msgId)
return
}
//免打扰(禁用转发)时间段
if (rule.silentPeriodStart != rule.silentPeriodEnd) {
val periodStartDay = Date()
var periodStartEnd = Date()
//跨天了
if (rule.silentPeriodStart > rule.silentPeriodEnd) {
val c: Calendar = Calendar.getInstance()
c.time = periodStartEnd
c.add(Calendar.DAY_OF_MONTH, 1)
periodStartEnd = c.time
}
val dateFmt = SimpleDateFormat("yyyy-MM-dd")
val mTimeOption = DataProvider.timePeriodOption
val periodStartStr = dateFmt.format(periodStartDay) + " " + mTimeOption[rule.silentPeriodStart] + ":00"
val periodEndStr = dateFmt.format(periodStartEnd) + " " + mTimeOption[rule.silentPeriodEnd] + ":00"
val timeFmt = SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
val periodStart = timeFmt.parse(periodStartStr, ParsePosition(0))?.time
val periodEnd = timeFmt.parse(periodEndStr, ParsePosition(0))?.time
val now = System.currentTimeMillis()
if (periodStart != null && periodEnd != null && now in periodStart..periodEnd) {
Log.d(TAG, "免打扰(禁用转发)时间段")
updateLogs(logId, 0, ResUtils.getString(R.string.silent_time_period))
senderLogic(0, msgInfo, rule, senderIndex, msgId)
return
}
}
when (sender.type) {
TYPE_DINGTALK_GROUP_ROBOT -> {
val settingVo = Gson().fromJson(sender.jsonSetting, DingtalkGroupRobotSetting::class.java)

View File

@ -481,6 +481,61 @@
</LinearLayout>
<LinearLayout
android:id="@+id/layout_silent_period"
style="@style/ruleBarStyleWithSwitch"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingEnd="15dp"
tools:ignore="RtlSymmetry">
<LinearLayout
android:layout_width="180dp"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/silent_time_period"
android:textStyle="bold"
tools:ignore="RelativeOverlap" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/silent_time_period_tips"
android:textSize="9sp"
tools:ignore="SmallSp" />
</LinearLayout>
<TextView
android:id="@+id/tv_silent_period"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_weight="1"
android:gravity="center" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/btn_silent_period"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:gravity="center"
android:padding="5dp"
android:text="@string/select"
android:textColor="@color/white"
android:textSize="10sp"
app:sb_color_unpressed="@color/colorPrimary"
app:sb_ripple_color="@color/white"
app:sb_ripple_duration="500"
app:sb_shape_type="rectangle"
tools:ignore="SmallSp" />
</LinearLayout>
</LinearLayout>
</androidx.core.widget.NestedScrollView>