diff --git a/app/src/main/java/com/idormy/sms/forwarder/database/dao/MsgDao.kt b/app/src/main/java/com/idormy/sms/forwarder/database/dao/MsgDao.kt index 2c8f492c..025581d0 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/database/dao/MsgDao.kt +++ b/app/src/main/java/com/idormy/sms/forwarder/database/dao/MsgDao.kt @@ -1,7 +1,15 @@ package com.idormy.sms.forwarder.database.dao import androidx.paging.PagingSource -import androidx.room.* +import androidx.room.Dao +import androidx.room.Delete +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import androidx.room.RawQuery +import androidx.room.Transaction +import androidx.room.Update +import androidx.sqlite.db.SupportSQLiteQuery import com.idormy.sms.forwarder.database.entity.Msg import com.idormy.sms.forwarder.database.entity.MsgAndLogs import io.reactivex.Completable @@ -22,6 +30,9 @@ interface MsgDao { @Query("DELETE FROM Msg where type=:type") fun deleteAll(type: String): Completable + @RawQuery + fun deleteAll(sql: SupportSQLiteQuery): Int + @Query("DELETE FROM Msg") fun deleteAll() @@ -41,4 +52,8 @@ interface MsgDao { @Query("SELECT * FROM Msg WHERE type = :type ORDER BY id DESC") fun pagingSource(type: String): PagingSource + @Transaction + @RawQuery(observedEntities = [MsgAndLogs::class]) + fun pagingSource(query: SupportSQLiteQuery): PagingSource + } \ No newline at end of file diff --git a/app/src/main/java/com/idormy/sms/forwarder/database/viewmodel/MsgViewModel.kt b/app/src/main/java/com/idormy/sms/forwarder/database/viewmodel/MsgViewModel.kt index 52e60c17..f863231e 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/database/viewmodel/MsgViewModel.kt +++ b/app/src/main/java/com/idormy/sms/forwarder/database/viewmodel/MsgViewModel.kt @@ -6,19 +6,27 @@ import androidx.paging.Pager import androidx.paging.PagingConfig import androidx.paging.PagingData import androidx.paging.cachedIn +import androidx.sqlite.db.SimpleSQLiteQuery import com.idormy.sms.forwarder.database.dao.MsgDao import com.idormy.sms.forwarder.database.entity.MsgAndLogs import com.idormy.sms.forwarder.database.ext.ioThread +import com.xuexiang.xutil.data.DateUtils import kotlinx.coroutines.flow.Flow class MsgViewModel(private val dao: MsgDao) : ViewModel() { private var type: String = "sms" + private var filter: MutableMap = mutableMapOf() fun setType(type: String): MsgViewModel { this.type = type return this } + fun setFilter(filter: MutableMap): MsgViewModel { + this.filter = filter + return this + } + val allMsg: Flow> = Pager( config = PagingConfig( pageSize = 10, @@ -26,11 +34,69 @@ class MsgViewModel(private val dao: MsgDao) : ViewModel() { initialLoadSize = 10 ) ) { - dao.pagingSource(type) + if (filter.isEmpty()) { + dao.pagingSource(type) + } else { + val sb = StringBuilder().apply { + append("SELECT * FROM Msg WHERE type = '$type'") + append(getOtherCondition()) + append(" ORDER BY id DESC") + } + + //Log.d("MsgViewModel", "sql: $sb") + val query = SimpleSQLiteQuery(sb.toString()) + dao.pagingSource(query) + } + }.flow.cachedIn(viewModelScope) fun delete(id: Long) = ioThread { dao.delete(id) } + fun deleteAll() = ioThread { + if (filter.isEmpty()) { + dao.deleteAll(type) + } else { + val sb = StringBuilder().apply { + append("DELETE FROM Msg WHERE type = '$type'") + append(getOtherCondition()) + } + + //Log.d("MsgViewModel", "sql: $sb") + val query = SimpleSQLiteQuery(sb.toString()) + dao.deleteAll(query) + } + } + + private fun getOtherCondition(): String { + return StringBuilder().apply { + filter["from"]?.toString()?.takeIf { it.isNotEmpty() }?.let { append(" AND `from` LIKE '%$it%'") } + filter["content"]?.toString()?.takeIf { it.isNotEmpty() }?.let { append(" AND content LIKE '%$it%'") } + filter["title"]?.toString()?.takeIf { it.isNotEmpty() }?.let { append(" AND sim_info LIKE '%$it%'") } + filter["start_time"]?.toString()?.takeIf { it.isNotEmpty() }?.let { + val date = DateUtils.string2Date(it, DateUtils.yyyyMMddHHmmss.get()) + append(" AND time >= '${date.time}'") + } + filter["end_time"]?.toString()?.takeIf { it.isNotEmpty() }?.let { + val date = DateUtils.string2Date(it, DateUtils.yyyyMMddHHmmss.get()) + append(" AND time <= '${date.time}'") + } + if (filter["sim_slot"] is Int && filter["sim_slot"] != -1) { + append(" AND sim_slot = ${filter["sim_slot"]}") + } + val callTypeFilter = filter["call_type"] as? MutableList<*> + if (!callTypeFilter.isNullOrEmpty()) { + val callTypeString = callTypeFilter.joinToString(",") { it.toString() } + append(" AND call_type IN ($callTypeString)") + } + val forwardStatusFilter = filter["forward_status"] as? MutableList<*> + if (!forwardStatusFilter.isNullOrEmpty()) { + val forwardStatusString = forwardStatusFilter.joinToString(",") { it.toString() } + val subSql = "SELECT DISTINCT msg_id FROM Logs WHERE type = '$type' and forward_status IN ($forwardStatusString)" + append(" AND id in ($subSql)") + } + }.toString() + } + } \ No newline at end of file diff --git a/app/src/main/java/com/idormy/sms/forwarder/entity/MsgInfo.kt b/app/src/main/java/com/idormy/sms/forwarder/entity/MsgInfo.kt index 0eb446e7..469f4909 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/entity/MsgInfo.kt +++ b/app/src/main/java/com/idormy/sms/forwarder/entity/MsgInfo.kt @@ -99,25 +99,32 @@ data class MsgInfo( ) .replaceTag(getString(R.string.tag_device_name), extraDeviceMark.trim(), needJson) .replaceTag(getString(R.string.tag_app_version), AppUtils.getAppVersionName(), needJson) - .replaceTag(getString(R.string.tag_call_type), - CALL_TYPE_MAP[callType.toString()] ?: getString(R.string.unknown_call), needJson) + .replaceTag( + getString(R.string.tag_call_type), + CALL_TYPE_MAP[callType.toString()] ?: getString(R.string.unknown_call), needJson + ) .replaceTag(getString(R.string.tag_ipv4), TaskUtils.ipv4, needJson) .replaceTag(getString(R.string.tag_ipv6), TaskUtils.ipv6, needJson) .replaceTag(getString(R.string.tag_battery_pct), "%.0f%%".format(TaskUtils.batteryPct), needJson) .replaceTag(getString(R.string.tag_battery_status), BatteryUtils.getStatus(TaskUtils.batteryStatus), needJson) .replaceTag(getString(R.string.tag_battery_plugged), BatteryUtils.getPlugged(TaskUtils.batteryPlugged), needJson) .replaceTag(getString(R.string.tag_battery_info), TaskUtils.batteryInfo, needJson) - .replaceTag(getString(R.string.tag_battery_info_simple), + .replaceTag( + getString(R.string.tag_battery_info_simple), "%.0f%%".format(TaskUtils.batteryPct) + with(BatteryUtils.getPlugged(TaskUtils.batteryPlugged)) { if (this == getString(R.string.battery_unknown)) "" else " - $this" - } + }, + needJson + ) + .replaceTag( + getString(R.string.tag_net_type), with(NetworkUtils.getNetStateType()) { + if (this == NetworkUtils.NetState.NET_NO || this == NetworkUtils.NetState.NET_UNKNOWN) + this.name + this.name.removePrefix("NET_") + }, + needJson ) - .replaceTag(getString(R.string.tag_net_type), with(NetworkUtils.getNetStateType()) { - if (this == NetworkUtils.NetState.NET_NO || this == NetworkUtils.NetState.NET_UNKNOWN) - this.name - this.name.removePrefix("NET_") - }) .replaceAppNameTag(from, needJson) .replaceLocationTag(needJson) .regexReplace(regexReplace) diff --git a/app/src/main/java/com/idormy/sms/forwarder/fragment/LogsFragment.kt b/app/src/main/java/com/idormy/sms/forwarder/fragment/LogsFragment.kt index 8094a171..13e87104 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/fragment/LogsFragment.kt +++ b/app/src/main/java/com/idormy/sms/forwarder/fragment/LogsFragment.kt @@ -1,10 +1,14 @@ package com.idormy.sms.forwarder.fragment import android.annotation.SuppressLint +import android.text.InputType import android.text.TextUtils import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.EditText +import android.widget.LinearLayout +import android.widget.RadioGroup import androidx.fragment.app.viewModels import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.RecyclerView.RecycledViewPool @@ -14,7 +18,6 @@ import com.idormy.sms.forwarder.R import com.idormy.sms.forwarder.activity.MainActivity import com.idormy.sms.forwarder.adapter.MsgPagingAdapter import com.idormy.sms.forwarder.core.BaseFragment -import com.idormy.sms.forwarder.core.Core import com.idormy.sms.forwarder.database.entity.LogsDetail import com.idormy.sms.forwarder.database.entity.MsgAndLogs import com.idormy.sms.forwarder.database.entity.Rule @@ -28,20 +31,24 @@ import com.scwang.smartrefresh.layout.api.RefreshLayout import com.xuexiang.xaop.annotation.SingleClick import com.xuexiang.xpage.annotation.Page import com.xuexiang.xui.widget.actionbar.TitleBar +import com.xuexiang.xui.widget.button.SmoothCheckBox import com.xuexiang.xui.widget.dialog.materialdialog.DialogAction import com.xuexiang.xui.widget.dialog.materialdialog.MaterialDialog +import com.xuexiang.xui.widget.picker.widget.TimePickerView +import com.xuexiang.xui.widget.picker.widget.builder.TimePickerBuilder +import com.xuexiang.xui.widget.picker.widget.configure.TimePickerType import com.xuexiang.xutil.data.DateUtils import com.xuexiang.xutil.resource.ResUtils.getColors import com.xuexiang.xutil.resource.ResUtils.getStringArray -import io.reactivex.CompletableObserver -import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.disposables.Disposable -import io.reactivex.schedulers.Schedulers +import com.xuexiang.xutil.tip.ToastUtils import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch import java.text.SimpleDateFormat +import java.util.Calendar +import java.util.Date import java.util.Locale + @Suppress("PrivatePropertyName") @Page(name = "转发日志") class LogsFragment : BaseFragment(), MsgPagingAdapter.OnItemClickListener { @@ -52,6 +59,11 @@ class LogsFragment : BaseFragment(), MsgPagingAdapter.OnIt private val viewModel by viewModels { BaseViewModelFactory(context) } private var currentType: String = "sms" + //日志筛选 + private var currentFilter: MutableMap = mutableMapOf() + private var logsFilterPopup: MaterialDialog? = null + private var timePicker: TimePickerView? = null + override fun viewBindingInflate( inflater: LayoutInflater, container: ViewGroup, @@ -68,27 +80,28 @@ class LogsFragment : BaseFragment(), MsgPagingAdapter.OnIt @SingleClick override fun performAction(view: View) { MaterialDialog.Builder(requireContext()) - .content(R.string.delete_type_log_tips) + .content(if (currentFilter.isEmpty()) R.string.delete_type_log_tips else R.string.delete_filter_log_tips) .positiveText(R.string.lab_yes) .negativeText(R.string.lab_no) .onPositive { _: MaterialDialog?, _: DialogAction? -> - Core.msg.deleteAll(currentType) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : CompletableObserver { - override fun onSubscribe(d: Disposable) {} - override fun onComplete() { - XToastUtils.success(R.string.delete_type_log_toast) - } - - override fun onError(e: Throwable) { - e.message?.let { XToastUtils.error(it) } - } - }) + try { + viewModel.setType(currentType).setFilter(currentFilter).deleteAll() + reloadData() + XToastUtils.success(if (currentFilter.isEmpty()) R.string.delete_type_log_toast else R.string.delete_filter_log_toast) + } catch (e: Exception) { + e.message?.let { XToastUtils.error(it) } + } } .show() } }) + titleBar!!.addAction(object : TitleBar.ImageAction(R.drawable.ic_filter) { + @SingleClick + override fun performAction(view: View) { + initLogsFilterDialog() + logsFilterPopup?.show() + } + }) return titleBar } @@ -115,9 +128,8 @@ class LogsFragment : BaseFragment(), MsgPagingAdapter.OnIt 2 -> "app" else -> "sms" } - viewModel.setType(currentType) - adapter.refresh() - binding!!.recyclerView.scrollToPosition(0) + initLogsFilterDialog(true) + reloadData() } } @@ -128,7 +140,7 @@ class LogsFragment : BaseFragment(), MsgPagingAdapter.OnIt binding!!.refreshLayout.setOnRefreshListener { refreshLayout: RefreshLayout -> //adapter.refresh() lifecycleScope.launch { - viewModel.setType(currentType).allMsg.collectLatest { adapter.submitData(it) } + viewModel.setType(currentType).setFilter(currentFilter).allMsg.collectLatest { adapter.submitData(it) } } refreshLayout.finishRefresh() } @@ -186,11 +198,6 @@ class LogsFragment : BaseFragment(), MsgPagingAdapter.OnIt .title(R.string.details) .content(detailStr.toString()) .cancelable(true) - /*.positiveText(R.string.del) - .onPositive { _: MaterialDialog?, _: DialogAction? -> - viewModel.delete(item.id) - XToastUtils.success(R.string.delete_log_toast) - }*/ .negativeText(R.string.resend) .onNegative { _: MaterialDialog?, _: DialogAction? -> XToastUtils.toast(R.string.resend_toast) @@ -201,4 +208,138 @@ class LogsFragment : BaseFragment(), MsgPagingAdapter.OnIt override fun onItemRemove(view: View?, id: Int) {} + private fun reloadData() { + viewModel.setType(currentType).setFilter(currentFilter) + adapter.refresh() + binding!!.recyclerView.scrollToPosition(0) + } + + private fun initLogsFilterDialog(needInit: Boolean = false) { + if (logsFilterPopup == null || needInit) { + currentFilter = mutableMapOf() + + val logsFilterDialog = View.inflate(requireContext(), R.layout.dialog_logs_filter, null) + val layoutTitle = logsFilterDialog.findViewById(R.id.layout_title) + val layoutSimSlot = logsFilterDialog.findViewById(R.id.layout_sim_slot) + val layoutCallType = logsFilterDialog.findViewById(R.id.layout_call_type) + when (currentType) { + "app" -> { + layoutTitle.visibility = View.VISIBLE + layoutSimSlot.visibility = View.GONE + layoutCallType.visibility = View.GONE + } + + "call" -> { + layoutTitle.visibility = View.GONE + layoutSimSlot.visibility = View.VISIBLE + layoutCallType.visibility = View.VISIBLE + } + + else -> { + layoutTitle.visibility = View.GONE + layoutSimSlot.visibility = View.VISIBLE + layoutCallType.visibility = View.GONE + } + } + + val scbCallType1 = logsFilterDialog.findViewById(R.id.scb_call_type1) + val scbCallType2 = logsFilterDialog.findViewById(R.id.scb_call_type2) + val scbCallType3 = logsFilterDialog.findViewById(R.id.scb_call_type3) + val scbCallType4 = logsFilterDialog.findViewById(R.id.scb_call_type4) + val scbCallType5 = logsFilterDialog.findViewById(R.id.scb_call_type5) + val scbCallType6 = logsFilterDialog.findViewById(R.id.scb_call_type6) + val etFrom = logsFilterDialog.findViewById(R.id.et_from) + val etContent = logsFilterDialog.findViewById(R.id.et_content) + val etTitle = logsFilterDialog.findViewById(R.id.et_title) + val rgSimSlot = logsFilterDialog.findViewById(R.id.rg_sim_slot) + val etStartTime = logsFilterDialog.findViewById(R.id.et_start_time) + val scbForwardStatus0 = logsFilterDialog.findViewById(R.id.scb_forward_status_0) + val scbForwardStatus1 = logsFilterDialog.findViewById(R.id.scb_forward_status_1) + val scbForwardStatus2 = logsFilterDialog.findViewById(R.id.scb_forward_status_2) + etStartTime.setOnFocusChangeListener { _, hasFocus -> + if (hasFocus) { + showTimePicker(etStartTime.text.toString().trim(), getString(R.string.start_time), etStartTime) + } else { + timePicker?.dismiss() + } + } + val etEndTime = logsFilterDialog.findViewById(R.id.et_end_time) + etEndTime.setOnFocusChangeListener { _, hasFocus -> + if (hasFocus) { + showTimePicker(etEndTime.text.toString().trim(), getString(R.string.end_time), etEndTime) + } else { + timePicker?.dismiss() + } + } + + logsFilterPopup = MaterialDialog.Builder(requireContext()) + .iconRes(android.R.drawable.ic_menu_search) + .title(R.string.menu_logs) + .customView(logsFilterDialog, true) + .cancelable(false) + .autoDismiss(false) + .neutralText(R.string.reset) + .neutralColor(getColors(R.color.darkGrey)) + .onNeutral { dialog: MaterialDialog?, _: DialogAction? -> + dialog?.dismiss() + currentFilter = mutableMapOf() + logsFilterPopup = null + reloadData() + }.positiveText(R.string.search).onPositive { dialog: MaterialDialog?, _: DialogAction? -> + currentFilter = mutableMapOf() + currentFilter["from"] = etFrom.text.toString().trim() + currentFilter["content"] = etContent.text.toString().trim() + currentFilter["title"] = etTitle.text.toString().trim() + currentFilter["start_time"] = etStartTime.text.toString().trim() + currentFilter["end_time"] = etEndTime.text.toString().trim() + currentFilter["sim_slot"] = if (currentType == "app") -1 else when (rgSimSlot.checkedRadioButtonId) { + R.id.rb_sim_slot_1 -> 0 + R.id.rb_sim_slot_2 -> 1 + else -> -1 + } + if (currentType == "call") { + currentFilter["call_type"] = mutableListOf().apply { + if (scbCallType1.isChecked) add(1) + if (scbCallType2.isChecked) add(2) + if (scbCallType3.isChecked) add(3) + if (scbCallType4.isChecked) add(4) + if (scbCallType5.isChecked) add(5) + if (scbCallType6.isChecked) add(6) + } + } + currentFilter["forward_status"] = mutableListOf().apply { + if (scbForwardStatus0.isChecked) add(0) + if (scbForwardStatus1.isChecked) add(1) + if (scbForwardStatus2.isChecked) add(2) + } + reloadData() + dialog?.dismiss() + }.build() + } + } + + private fun showTimePicker(time: String, title: String, et: EditText) { + et.inputType = InputType.TYPE_NULL + val calendar: Calendar = Calendar.getInstance() + calendar.time = try { + if (time.isEmpty()) Date() else DateUtils.string2Date(time, DateUtils.yyyyMMddHHmmss.get()) + } catch (e: Exception) { + Date() + } + timePicker = TimePickerBuilder(context) { date, _ -> + ToastUtils.toast(DateUtils.date2String(date, DateUtils.yyyyMMddHHmmss.get())) + et.setText(DateUtils.date2String(date, DateUtils.yyyyMMddHHmmss.get())) + } + .setTimeSelectChangeListener { date -> + Log.i("pvTime", "onTimeSelectChanged") + et.setText(DateUtils.date2String(date, DateUtils.yyyyMMddHHmmss.get())) + } + .setType(TimePickerType.ALL) + .setTitleText(title) + .isDialog(true) + .setOutSideCancelable(false) + .setDate(calendar) + .build() + timePicker?.show(false) + } } \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_filter.xml b/app/src/main/res/drawable/ic_filter.xml new file mode 100644 index 00000000..3e80a44a --- /dev/null +++ b/app/src/main/res/drawable/ic_filter.xml @@ -0,0 +1,11 @@ + + + diff --git a/app/src/main/res/layout/dialog_logs_filter.xml b/app/src/main/res/layout/dialog_logs_filter.xml new file mode 100644 index 00000000..472d70bc --- /dev/null +++ b/app/src/main/res/layout/dialog_logs_filter.xml @@ -0,0 +1,372 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-en/strings.xml b/app/src/main/res/values-en/strings.xml index f3eea489..b96c8df0 100644 --- a/app/src/main/res/values-en/strings.xml +++ b/app/src/main/res/values-en/strings.xml @@ -164,6 +164,8 @@ Quit Delete Save + Reset + Search Submit Send Test @@ -212,6 +214,8 @@ The log entry is deleted. Are you sure you want to delete all log records for this category? The category log record has been cleared! + Are you sure you want to delete all log records for the current filter? + All log records for the current filter have been deleted! Attempting to resend over the original sender Rematching rule sending Details @@ -630,33 +634,10 @@ Time: Result: Ori. Result: + Result: Success Failed Processing - {{FROM}} - {{SMS}} - {{PACKAGE_NAME}} - {{APP_NAME}} - {{MSG}} - {{CARD_SLOT}} - {{CARD_SUBID}} - {{RECEIVE_TIME}} - {{CURRENT_TIME}} - {{DEVICE_NAME}} - {{APP_VERSION}} - {{TITLE}} - {{CALL_TYPE}} - {{LOCATION}} - {{LOCATION_LONGITUDE}} - {{LOCATION_LATITUDE}} - {{LOCATION_ADDRESS}} - {{BATTERY_PCT}} - {{BATTERY_STATUS}} - {{BATTERY_PLUGGED}} - {{BATTERY_INFO}} - {{UID}} - {{IPV4}} - {{IPV6}} SMS CALL APP @@ -1022,6 +1003,7 @@ Matched rule Copied to clipboard:\n%s Search Keyword: %s + Search Condition: %s Export configuration succeeded! Export failed, please check write permission! Export failed: %s @@ -1046,6 +1028,7 @@ Main title Subtitle + Input keywords to fuzzy match Input keywords to fuzzy match SMS content Pure numbers match numbers / non-numbers match names Input keyword to fuzzy match mobile phone number @@ -1279,7 +1262,9 @@ From week To Start + Start Time End + End Time Starting from second, execute every seconds. diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 253ded8c..bd60bdbc 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -165,6 +165,8 @@ 退出 删除 保存 + 重置 + 搜索 提交 发送 测试 @@ -213,6 +215,8 @@ 该条日志记录已经删除! 确定删除该分类的所有日志记录? 该分类日志记录已经清空! + 确定删除当前筛选的所有日志记录? + 当前筛选的所有日志记录已经删除! 正在尝试通过原发送通道重发 正在重新匹配规则发送 详情 @@ -631,33 +635,10 @@ 时间: 转发结果: 原转发状态: + 状态: 成功 失败 处理中 - {{FROM}} - {{SMS}} - {{PACKAGE_NAME}} - {{APP_NAME}} - {{MSG}} - {{CARD_SLOT}} - {{CARD_SUBID}} - {{RECEIVE_TIME}} - {{CURRENT_TIME}} - {{DEVICE_NAME}} - {{APP_VERSION}} - {{TITLE}} - {{CALL_TYPE}} - {{LOCATION}} - {{LOCATION_LONGITUDE}} - {{LOCATION_LATITUDE}} - {{LOCATION_ADDRESS}} - {{BATTERY_PCT}} - {{BATTERY_STATUS}} - {{BATTERY_PLUGGED}} - {{BATTERY_INFO}} - {{UID}} - {{IPV4}} - {{IPV6}} 短信 来电 应用 @@ -1023,6 +1004,7 @@ 匹配中规则 已复制到剪贴板:\n%s 搜索关键字: %s + 搜索条件: %s 导出配置成功! 导出失败,请检查写入权限! 导出失败: %s @@ -1047,6 +1029,7 @@ 主标题 副标题 + 输入关键字模糊匹配 输入关键字模糊匹配短信内容 纯数字匹配号码/非数字匹配姓名 输入关键字模糊匹配手机号码 @@ -1280,7 +1263,9 @@ 从星期 起始 + 起始时间 结束 + 结束时间 秒开始,每 秒钟执行一次 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 70687376..4dcc3c48 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -164,7 +164,9 @@ 丟棄 退出 刪除 - 保存 + 儲存 + 重置 + 搜索 提交 發送 測試 @@ -213,6 +215,8 @@ 該條日誌記錄已刪除! 確定刪除該分類的所有日誌記錄? 該分類日誌記錄已清空! + 確定刪除當前篩選的所有日誌記錄? + 當前篩選的所有日誌記錄已經刪除! 正在嘗試通過原發送通道重發 正在重新匹配規則發送 詳情 @@ -631,33 +635,10 @@ 時間: 轉發結果: 原轉發狀態: + 狀態: 成功 失敗 處理中 - {{FROM}} - {{SMS}} - {{PACKAGE_NAME}} - {{APP_NAME}} - {{MSG}} - {{CARD_SLOT}} - {{CARD_SUBID}} - {{RECEIVE_TIME}} - {{CURRENT_TIME}} - {{DEVICE_NAME}} - {{APP_VERSION}} - {{TITLE}} - {{CALL_TYPE}} - {{LOCATION}} - {{LOCATION_LONGITUDE}} - {{LOCATION_LATITUDE}} - {{LOCATION_ADDRESS}} - {{BATTERY_PCT}} - {{BATTERY_STATUS}} - {{BATTERY_PLUGGED}} - {{BATTERY_INFO}} - {{UID}} - {{IPV4}} - {{IPV6}} 簡訊 來電 應用 @@ -1023,6 +1004,7 @@ 匹配中規則 已複製到剪貼板:\n%s 搜索關鍵字: %s + 搜索條件:%s 導出配置成功! 導出失敗,請檢查寫入權限! 導出失敗: %s @@ -1047,6 +1029,7 @@ 主標題 副標題 + 輸入關鍵字模糊匹配 輸入關鍵字模糊匹配簡訊內容 純數字匹配號碼/非數字匹配姓名 輸入關鍵字模糊匹配手機號碼 @@ -1280,7 +1263,9 @@ 從星期 起始 - 結束 + 起始时间 + 结束 + 结束时间 秒開始,每 秒鐘執行一次 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 49431ad9..e00b2e17 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,4 +1,30 @@ + {{FROM}} + {{SMS}} + {{PACKAGE_NAME}} + {{APP_NAME}} + {{MSG}} + {{CARD_SLOT}} + {{CARD_SUBID}} + {{RECEIVE_TIME}} + {{CURRENT_TIME}} + {{DEVICE_NAME}} + {{APP_VERSION}} + {{TITLE}} + {{CALL_TYPE}} + {{LOCATION}} + {{LOCATION_LONGITUDE}} + {{LOCATION_LATITUDE}} + {{LOCATION_ADDRESS}} + {{BATTERY_PCT}} + {{BATTERY_STATUS}} + {{BATTERY_PLUGGED}} + {{BATTERY_INFO}} + {{BATTERY_INFO_SIMPLE}} + {{UID}} + {{IPV4}} + {{IPV6}} + {{NET_TYPE}} 短信 通话 @@ -165,6 +191,8 @@ 退出 删除 保存 + 重置 + 搜索 提交 发送 测试 @@ -213,6 +241,8 @@ 该条日志记录已经删除! 确定删除该分类的所有日志记录? 该分类日志记录已经清空! + 确定删除当前筛选的所有日志记录? + 当前筛选的所有日志记录已经删除! 正在尝试通过原发送通道重发 正在重新匹配规则发送 详情 @@ -631,34 +661,10 @@ 时间: 转发结果: 原转发状态: + 状态: 成功 失败 处理中 - {{FROM}} - {{SMS}} - {{PACKAGE_NAME}} - {{APP_NAME}} - {{MSG}} - {{CARD_SLOT}} - {{CARD_SUBID}} - {{RECEIVE_TIME}} - {{CURRENT_TIME}} - {{DEVICE_NAME}} - {{APP_VERSION}} - {{TITLE}} - {{CALL_TYPE}} - {{LOCATION}} - {{LOCATION_LONGITUDE}} - {{LOCATION_LATITUDE}} - {{LOCATION_ADDRESS}} - {{BATTERY_PCT}} - {{BATTERY_STATUS}} - {{BATTERY_PLUGGED}} - {{BATTERY_INFO}} - {{BATTERY_INFO_SIMPLE}} - {{UID}} - {{IPV4}} - {{IPV6}} 短信 来电 应用 @@ -1024,6 +1030,7 @@ 匹配中规则 已复制到剪贴板:\n%s 搜索关键字: %s + 搜索条件: %s 导出配置成功! 导出失败,请检查写入权限! 导出失败: %s @@ -1048,6 +1055,7 @@ 主标题 副标题 + 输入关键字模糊匹配 输入关键字模糊匹配短信内容 纯数字匹配号码/非数字匹配姓名 输入关键字模糊匹配手机号码 @@ -1281,7 +1289,9 @@ 从星期 起始 + 起始时间 结束 + 结束时间 秒开始,每 秒钟执行一次 @@ -1451,5 +1461,4 @@ 不支持蓝牙设备 搜索设备 蓝牙设备MAC地址无效,例如:AA:BB:CC:DD:EE:FF - {{NET_TYPE}}