新增:自动消除额外APP通知 #232 #248

This commit is contained in:
pppscn 2023-02-05 15:29:56 +08:00
parent 04d8c9015a
commit 6ef83f131e
9 changed files with 350 additions and 185 deletions

View File

@ -37,10 +37,7 @@ import io.reactivex.SingleObserver
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers import io.reactivex.schedulers.Schedulers
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.*
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import java.util.* import java.util.*
@Page(name = "转发规则·编辑器") @Page(name = "转发规则·编辑器")
@ -411,6 +408,7 @@ class RulesEditFragment : BaseFragment<FragmentRulesEditBinding?>(), View.OnClic
} }
//初始化APP下拉列表 //初始化APP下拉列表
@OptIn(DelicateCoroutinesApi::class)
private fun initAppSpinner() { private fun initAppSpinner() {
if (ruleType != "app") return if (ruleType != "app") return

View File

@ -16,16 +16,15 @@ import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.CompoundButton import android.widget.*
import android.widget.EditText
import android.widget.LinearLayout
import android.widget.TextView
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import com.hjq.permissions.OnPermissionCallback import com.hjq.permissions.OnPermissionCallback
import com.hjq.permissions.Permission import com.hjq.permissions.Permission
import com.hjq.permissions.XXPermissions import com.hjq.permissions.XXPermissions
import com.idormy.sms.forwarder.App import com.idormy.sms.forwarder.App
import com.idormy.sms.forwarder.R import com.idormy.sms.forwarder.R
import com.idormy.sms.forwarder.adapter.spinner.AppListAdapterItem
import com.idormy.sms.forwarder.adapter.spinner.AppListSpinnerAdapter
import com.idormy.sms.forwarder.core.BaseFragment import com.idormy.sms.forwarder.core.BaseFragment
import com.idormy.sms.forwarder.databinding.FragmentSettingsBinding import com.idormy.sms.forwarder.databinding.FragmentSettingsBinding
import com.idormy.sms.forwarder.entity.SimInfo import com.idormy.sms.forwarder.entity.SimInfo
@ -47,8 +46,10 @@ import com.xuexiang.xui.widget.picker.widget.builder.TimePickerBuilder
import com.xuexiang.xui.widget.picker.widget.listener.OnOptionsSelectListener import com.xuexiang.xui.widget.picker.widget.listener.OnOptionsSelectListener
import com.xuexiang.xutil.XUtil import com.xuexiang.xutil.XUtil
import com.xuexiang.xutil.XUtil.getPackageManager import com.xuexiang.xutil.XUtil.getPackageManager
import com.xuexiang.xutil.app.AppUtils
import com.xuexiang.xutil.app.AppUtils.getAppPackageName import com.xuexiang.xutil.app.AppUtils.getAppPackageName
import com.xuexiang.xutil.data.DateUtils import com.xuexiang.xutil.data.DateUtils
import kotlinx.coroutines.*
import java.util.* import java.util.*
@ -59,6 +60,10 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding?>(), View.OnClickL
val TAG: String = SettingsFragment::class.java.simpleName val TAG: String = SettingsFragment::class.java.simpleName
private val mTimeOption = DataProvider.timePeriodOption private val mTimeOption = DataProvider.timePeriodOption
//已安装App信息列表
private val appListSpinnerList = ArrayList<AppListAdapterItem>()
private lateinit var appListSpinnerAdapter: AppListSpinnerAdapter<*>
override fun viewBindingInflate( override fun viewBindingInflate(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup, container: ViewGroup,
@ -86,6 +91,8 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding?>(), View.OnClickL
switchEnableAppNotify( switchEnableAppNotify(
binding!!.sbEnableAppNotify, binding!!.scbCancelAppNotify, binding!!.scbNotUserPresent binding!!.sbEnableAppNotify, binding!!.scbCancelAppNotify, binding!!.scbNotUserPresent
) )
//设置自动消除额外APP通知
editExtraAppList(binding!!.etAppList)
//启动时异步获取已安装App信息 //启动时异步获取已安装App信息
switchEnableLoadAppList( switchEnableLoadAppList(
binding!!.sbEnableLoadAppList, binding!!.scbLoadUserApp, binding!!.scbLoadSystemApp binding!!.sbEnableLoadAppList, binding!!.scbLoadUserApp, binding!!.scbLoadSystemApp
@ -150,6 +157,9 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding?>(), View.OnClickL
//纯客户端模式 //纯客户端模式
switchDirectlyToClient(binding!!.sbDirectlyToClient) switchDirectlyToClient(binding!!.sbDirectlyToClient)
//初始化APP下拉列表
initAppSpinner()
} }
override fun initListeners() { override fun initListeners() {
@ -409,13 +419,17 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding?>(), View.OnClickL
fun switchEnableAppNotify( fun switchEnableAppNotify(
sbEnableAppNotify: SwitchButton, scbCancelAppNotify: SmoothCheckBox, scbNotUserPresent: SmoothCheckBox sbEnableAppNotify: SwitchButton, scbCancelAppNotify: SmoothCheckBox, scbNotUserPresent: SmoothCheckBox
) { ) {
val layoutOptionalAction: LinearLayout = binding!!.layoutOptionalAction
val isEnable: Boolean = SettingUtils.enableAppNotify val isEnable: Boolean = SettingUtils.enableAppNotify
sbEnableAppNotify.isChecked = isEnable sbEnableAppNotify.isChecked = isEnable
val layoutOptionalAction: LinearLayout = binding!!.layoutOptionalAction
layoutOptionalAction.visibility = if (isEnable) View.VISIBLE else View.GONE layoutOptionalAction.visibility = if (isEnable) View.VISIBLE else View.GONE
val layoutAppList: LinearLayout = binding!!.layoutAppList
layoutAppList.visibility = if (isEnable) View.VISIBLE else View.GONE
sbEnableAppNotify.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean -> sbEnableAppNotify.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
layoutOptionalAction.visibility = if (isChecked) View.VISIBLE else View.GONE layoutOptionalAction.visibility = if (isChecked) View.VISIBLE else View.GONE
layoutAppList.visibility = if (isChecked) View.VISIBLE else View.GONE
SettingUtils.enableAppNotify = isChecked SettingUtils.enableAppNotify = isChecked
if (isChecked) { if (isChecked) {
//检查权限是否获取 //检查权限是否获取
@ -454,7 +468,19 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding?>(), View.OnClickL
} }
} }
//启动时异步获取已安装App信息 (binding!!.sbEnableLoadAppList, binding!!.scbLoadUserApp, binding!!.scbLoadSystemApp) //设置自动消除额外APP通知
private fun editExtraAppList(textAppList: EditText) {
textAppList.setText(SettingUtils.cancelExtraAppNotify)
textAppList.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
override fun afterTextChanged(s: Editable) {
SettingUtils.cancelExtraAppNotify = textAppList.text.toString().trim().removeSuffix("\n")
}
})
}
//启动时异步获取已安装App信息
@SuppressLint("UseSwitchCompatOrMaterialCode") @SuppressLint("UseSwitchCompatOrMaterialCode")
fun switchEnableLoadAppList( fun switchEnableLoadAppList(
sbEnableLoadAppList: SwitchButton, scbLoadUserApp: SmoothCheckBox, scbLoadSystemApp: SmoothCheckBox sbEnableLoadAppList: SwitchButton, scbLoadUserApp: SmoothCheckBox, scbLoadSystemApp: SmoothCheckBox
@ -1059,4 +1085,69 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding?>(), View.OnClickL
} }
} }
//初始化APP下拉列表
@OptIn(DelicateCoroutinesApi::class)
private fun initAppSpinner() {
if (!SettingUtils.enableAppNotify) return
//未开启异步获取已安装App信息开关时规则编辑不显示已安装APP下拉框
if (!SettingUtils.enableLoadUserAppList && !SettingUtils.enableLoadSystemAppList) return
val get = GlobalScope.async(Dispatchers.IO) {
if ((SettingUtils.enableLoadUserAppList && App.UserAppList.isEmpty()) || (SettingUtils.enableLoadSystemAppList && App.SystemAppList.isEmpty())) {
App.UserAppList.clear()
App.SystemAppList.clear()
val appInfoList = AppUtils.getAppsInfo()
for (appInfo in appInfoList) {
if (appInfo.isSystem) {
App.SystemAppList.add(appInfo)
} else {
App.UserAppList.add(appInfo)
}
}
App.UserAppList.sortBy { appInfo -> appInfo.name }
App.SystemAppList.sortBy { appInfo -> appInfo.name }
}
}
GlobalScope.launch(Dispatchers.Main) {
runCatching {
get.await()
if (App.UserAppList.isEmpty() && App.SystemAppList.isEmpty()) return@runCatching
appListSpinnerList.clear()
if (SettingUtils.enableLoadUserAppList) {
for (appInfo in App.UserAppList) {
appListSpinnerList.add(AppListAdapterItem(appInfo.name, appInfo.icon, appInfo.packageName))
}
}
if (SettingUtils.enableLoadSystemAppList) {
for (appInfo in App.SystemAppList) {
appListSpinnerList.add(AppListAdapterItem(appInfo.name, appInfo.icon, appInfo.packageName))
}
}
//列表为空也不显示下拉框
if (appListSpinnerList.isEmpty()) return@runCatching
appListSpinnerAdapter = AppListSpinnerAdapter(appListSpinnerList)
//.setTextColor(ResUtils.getColor(R.color.green))
//.setTextSize(12F)
.setIsFilterKey(true).setFilterColor("#EF5362").setBackgroundSelector(R.drawable.selector_custom_spinner_bg)
binding!!.spApp.setAdapter(appListSpinnerAdapter)
binding!!.spApp.setOnItemClickListener { _: AdapterView<*>, _: View, position: Int, _: Long ->
try {
//val appInfo = appListSpinnerList[position]
val appInfo = appListSpinnerAdapter.getItemSource(position) as AppListAdapterItem
CommonUtils.insertOrReplaceText2Cursor(binding!!.etAppList, appInfo.packageName.toString() + "\n")
} catch (e: Exception) {
XToastUtils.error(e.message.toString())
}
}
binding!!.spApp.visibility = View.VISIBLE
}.onFailure {
Log.e("GlobalScope", it.message.toString())
}
}
}
} }

View File

@ -1,119 +1,132 @@
package com.idormy.sms.forwarder.service package com.idormy.sms.forwarder.service
import android.content.ComponentName import android.content.ComponentName
import android.os.Build import android.os.Build
import android.service.notification.NotificationListenerService import android.service.notification.NotificationListenerService
import android.service.notification.StatusBarNotification import android.service.notification.StatusBarNotification
import android.util.Log import android.util.Log
import androidx.work.OneTimeWorkRequestBuilder import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.WorkManager import androidx.work.WorkManager
import androidx.work.workDataOf import androidx.work.workDataOf
import com.google.gson.Gson import com.google.gson.Gson
import com.idormy.sms.forwarder.core.Core import com.idormy.sms.forwarder.core.Core
import com.idormy.sms.forwarder.database.entity.Rule import com.idormy.sms.forwarder.database.entity.Rule
import com.idormy.sms.forwarder.entity.MsgInfo import com.idormy.sms.forwarder.entity.MsgInfo
import com.idormy.sms.forwarder.utils.PACKAGE_NAME import com.idormy.sms.forwarder.utils.PACKAGE_NAME
import com.idormy.sms.forwarder.utils.SettingUtils import com.idormy.sms.forwarder.utils.SettingUtils
import com.idormy.sms.forwarder.utils.Worker import com.idormy.sms.forwarder.utils.Worker
import com.idormy.sms.forwarder.workers.SendWorker import com.idormy.sms.forwarder.workers.SendWorker
import com.xuexiang.xrouter.utils.TextUtils import com.xuexiang.xrouter.utils.TextUtils
import com.xuexiang.xutil.display.ScreenUtils import com.xuexiang.xutil.display.ScreenUtils
import java.util.* import java.util.*
@Suppress("PrivatePropertyName", "DEPRECATION") @Suppress("PrivatePropertyName", "DEPRECATION")
class NotifyService : NotificationListenerService() { class NotifyService : NotificationListenerService() {
private val TAG: String = "NotifyService" private val TAG: String = "NotifyService"
override fun onListenerConnected() { override fun onListenerConnected() {
Log.d(TAG, "onListenerConnected") Log.d(TAG, "onListenerConnected")
} }
override fun onListenerDisconnected() { override fun onListenerDisconnected() {
//纯客户端模式 //纯客户端模式
if (SettingUtils.enablePureClientMode) return if (SettingUtils.enablePureClientMode) return
//总开关 //总开关
if (!SettingUtils.enableAppNotify) return if (!SettingUtils.enableAppNotify) return
Log.d(TAG, "通知侦听器断开连接 - 请求重新绑定") Log.d(TAG, "通知侦听器断开连接 - 请求重新绑定")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
requestRebind(ComponentName(this, NotificationListenerService::class.java)) requestRebind(ComponentName(this, NotificationListenerService::class.java))
} }
} }
override fun onNotificationPosted(sbn: StatusBarNotification?) { override fun onNotificationPosted(sbn: StatusBarNotification?) {
try { try {
//纯客户端模式 //纯客户端模式
if (SettingUtils.enablePureClientMode) return if (SettingUtils.enablePureClientMode) return
//总开关 //总开关
if (!SettingUtils.enableAppNotify) return if (!SettingUtils.enableAppNotify) return
//异常通知跳过 //异常通知跳过
if (sbn!!.notification == null) return if (sbn!!.notification == null) return
if (sbn.notification.extras == null) return if (sbn.notification.extras == null) return
//仅锁屏状态转发APP通知 //自动消除额外APP通知
if (SettingUtils.enableNotUserPresent && !ScreenUtils.isScreenLock()) return if (!TextUtils.isEmpty(SettingUtils.cancelExtraAppNotify)) {
for (app in SettingUtils.cancelExtraAppNotify.split("\n")) {
val from = sbn.packageName if (sbn.packageName == app.trim()) {
//自身通知跳过 Log.d(TAG, "自动消除额外APP通知$app")
if (PACKAGE_NAME == sbn.packageName) return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
cancelNotification(sbn.key)
//通知标题 } else {
var title = "" cancelNotification(sbn.packageName, sbn.tag, sbn.id)
if (sbn.notification.extras["android.title"] != null) { }
title = sbn.notification.extras["android.title"].toString() break
} }
//通知内容 }
var text = "" }
if (sbn.notification.extras["android.text"] != null) {
text = sbn.notification.extras["android.text"].toString() //仅锁屏状态转发APP通知
} if (SettingUtils.enableNotUserPresent && !ScreenUtils.isScreenLock()) return
if (text.isEmpty() && sbn.notification.tickerText != null) {
text = sbn.notification.tickerText.toString() val from = sbn.packageName
} //自身通知跳过
if (PACKAGE_NAME == sbn.packageName) return
//不处理空消息(标题跟内容都为空)
if (TextUtils.isEmpty(title) && TextUtils.isEmpty(text)) return //通知标题
var title = ""
val msgInfo = MsgInfo("app", from, text, Date(), title, -1) if (sbn.notification.extras["android.title"] != null) {
title = sbn.notification.extras["android.title"].toString()
//TODO自动消除通知临时方案重复查询换取准确性 }
if (SettingUtils.enableCancelAppNotify) { //通知内容
val ruleList: List<Rule> = Core.rule.getRuleList(msgInfo.type, 1, "SIM0") var text = ""
for (rule in ruleList) { if (sbn.notification.extras["android.text"] != null) {
if (rule.checkMsg(msgInfo)) { text = sbn.notification.extras["android.text"].toString()
Log.d(TAG, "自动消除通知") }
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { if (text.isEmpty() && sbn.notification.tickerText != null) {
cancelNotification(sbn.key) text = sbn.notification.tickerText.toString()
} else { }
cancelNotification(sbn.packageName, sbn.tag, sbn.id)
} //不处理空消息(标题跟内容都为空)
break if (TextUtils.isEmpty(title) && TextUtils.isEmpty(text)) return
}
} val msgInfo = MsgInfo("app", from, text, Date(), title, -1)
}
//TODO自动消除通知临时方案重复查询换取准确性
val request = OneTimeWorkRequestBuilder<SendWorker>() if (SettingUtils.enableCancelAppNotify) {
.setInputData( val ruleList: List<Rule> = Core.rule.getRuleList(msgInfo.type, 1, "SIM0")
workDataOf( for (rule in ruleList) {
Worker.sendMsgInfo to Gson().toJson(msgInfo), if (rule.checkMsg(msgInfo)) {
) Log.d(TAG, "自动消除通知")
) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
.build() cancelNotification(sbn.key)
WorkManager.getInstance(applicationContext).enqueue(request) } else {
cancelNotification(sbn.packageName, sbn.tag, sbn.id)
} catch (e: Exception) { }
Log.e(TAG, "Parsing Notification failed: " + e.message.toString()) break
} }
}
} }
override fun onNotificationRemoved(sbn: StatusBarNotification?) { val request = OneTimeWorkRequestBuilder<SendWorker>().setInputData(
Log.d(TAG, "Removed Package Name : ${sbn?.packageName}") workDataOf(
} Worker.sendMsgInfo to Gson().toJson(msgInfo),
)
).build()
WorkManager.getInstance(applicationContext).enqueue(request)
} catch (e: Exception) {
Log.e(TAG, "Parsing Notification failed: " + e.message.toString())
}
}
override fun onNotificationRemoved(sbn: StatusBarNotification?) {
Log.d(TAG, "Removed Package Name : ${sbn?.packageName}")
}
} }

View File

@ -38,6 +38,7 @@ const val SP_ENABLE_CALL_TYPE_6 = "enable_call_type_6"
const val SP_ENABLE_APP_NOTIFY = "enable_app_notify" const val SP_ENABLE_APP_NOTIFY = "enable_app_notify"
const val SP_ENABLE_CANCEL_APP_NOTIFY = "enable_cancel_app_notify" const val SP_ENABLE_CANCEL_APP_NOTIFY = "enable_cancel_app_notify"
const val SP_CANCEL_EXTRA_APP_NOTIFY = "cancel_extra_app_notify"
const val SP_ENABLE_NOT_USER_PRESENT = "enable_not_user_present" const val SP_ENABLE_NOT_USER_PRESENT = "enable_not_user_present"
const val ENABLE_LOAD_APP_LIST = "enable_load_app_list" const val ENABLE_LOAD_APP_LIST = "enable_load_app_list"

View File

@ -40,7 +40,10 @@ class SettingUtils private constructor() {
var enableAppNotify: Boolean by SharedPreference(SP_ENABLE_APP_NOTIFY, false) var enableAppNotify: Boolean by SharedPreference(SP_ENABLE_APP_NOTIFY, false)
//是否转发应用通知——自动消除通知 //是否转发应用通知——自动消除通知
var enableCancelAppNotify: Boolean by SharedPreference(SP_ENABLE_CANCEL_APP_NOTIFY, false) var enableCancelAppNotify: Boolean by SharedPreference(SP_CANCEL_EXTRA_APP_NOTIFY, false)
//是否转发应用通知——自动消除额外APP通知
var cancelExtraAppNotify: String by SharedPreference(SP_BATTERY_CRON_START_TIME, "")
//是否转发应用通知——仅锁屏状态 //是否转发应用通知——仅锁屏状态
var enableNotUserPresent: Boolean by SharedPreference(SP_ENABLE_NOT_USER_PRESENT, false) var enableNotUserPresent: Boolean by SharedPreference(SP_ENABLE_NOT_USER_PRESENT, false)

View File

@ -101,6 +101,7 @@
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="3dp"
android:gravity="center_vertical" android:gravity="center_vertical"
android:orientation="horizontal"> android:orientation="horizontal">
@ -240,81 +241,135 @@
<LinearLayout <LinearLayout
style="@style/settingBarStyle" style="@style/settingBarStyle"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout <LinearLayout
android:layout_width="0dp" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:orientation="horizontal">
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/forward_app_notify"
android:textStyle="bold"
tools:ignore="RelativeOverlap" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/forward_app_notify_tips"
android:textSize="9sp"
tools:ignore="SmallSp" />
<LinearLayout <LinearLayout
android:id="@+id/layout_optional_action" android:layout_width="0dp"
android:layout_width="match_parent" android:layout_height="wrap_content"
android:layout_height="25dp" android:layout_weight="1"
android:gravity="center_vertical" android:orientation="vertical">
android:orientation="horizontal"
android:visibility="gone">
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/optional_action" android:text="@string/forward_app_notify"
android:textSize="10sp"
android:textStyle="bold" android:textStyle="bold"
tools:ignore="SmallSp" /> tools:ignore="RelativeOverlap" />
<com.xuexiang.xui.widget.button.SmoothCheckBox
android:id="@+id/scb_cancel_app_notify"
android:layout_width="15dp"
android:layout_height="15dp"
app:scb_color_checked="@color/colorPrimary" />
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/cancel_app_notify" android:text="@string/forward_app_notify_tips"
android:textSize="10sp" android:textSize="9sp"
tools:ignore="SmallSp" /> tools:ignore="SmallSp" />
<com.xuexiang.xui.widget.button.SmoothCheckBox <LinearLayout
android:id="@+id/scb_not_user_present" android:id="@+id/layout_optional_action"
android:layout_width="15dp" android:layout_width="match_parent"
android:layout_height="15dp" android:layout_height="25dp"
android:layout_marginStart="5dp" android:gravity="center_vertical"
app:scb_color_checked="@color/colorPrimary" /> android:orientation="horizontal"
android:visibility="gone">
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/optional_action"
android:textSize="10sp"
android:textStyle="bold"
tools:ignore="SmallSp" />
<com.xuexiang.xui.widget.button.SmoothCheckBox
android:id="@+id/scb_cancel_app_notify"
android:layout_width="15dp"
android:layout_height="15dp"
app:scb_color_checked="@color/colorPrimary" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/cancel_app_notify"
android:textSize="10sp"
tools:ignore="SmallSp" />
<com.xuexiang.xui.widget.button.SmoothCheckBox
android:id="@+id/scb_not_user_present"
android:layout_width="15dp"
android:layout_height="15dp"
android:layout_marginStart="5dp"
app:scb_color_checked="@color/colorPrimary" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/not_user_present"
android:textSize="10sp"
tools:ignore="SmallSp" />
</LinearLayout>
</LinearLayout>
<com.xuexiang.xui.widget.button.switchbutton.SwitchButton
android:id="@+id/sb_enable_app_notify"
style="@style/SwitchButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
android:id="@+id/layout_app_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingEnd="10dp"
android:visibility="gone">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="2"
android:text="@string/extra_app"
android:textSize="12sp"
android:textStyle="bold" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_weight="1"
android:orientation="vertical">
<com.xuexiang.xui.widget.edittext.materialedittext.MaterialEditText
android:id="@+id/et_app_list"
android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/not_user_present" android:hint="@string/extra_app_hint"
android:textSize="10sp" android:inputType="textMultiLine"
tools:ignore="SmallSp" /> app:met_clearButton="true" />
<com.xuexiang.xui.widget.spinner.editspinner.EditSpinner
android:id="@+id/sp_app"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="3dp"
android:visibility="gone"
app:es_hint="@string/choose_app_hint"
app:es_maxLength="20"
app:es_maxLine="1" />
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
<com.xuexiang.xui.widget.button.switchbutton.SwitchButton
android:id="@+id/sb_enable_app_notify"
style="@style/SwitchButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout> </LinearLayout>
<LinearLayout <LinearLayout

View File

@ -721,7 +721,9 @@
<string name="feishu_secret_hint">Group Robot → Security Settings → Signature Verification</string> <string name="feishu_secret_hint">Group Robot → Security Settings → Signature Verification</string>
<string name="pushplus_token_hint">Please go to the corresponding official website to obtain</string> <string name="pushplus_token_hint">Please go to the corresponding official website to obtain</string>
<string name="choose_sender">Drop-down selection, keyword fuzzy match</string> <string name="choose_sender">Drop-down selection, keyword fuzzy match</string>
<string name="choose_app">Installed apps</string> <string name="choose_app">Installed Apps</string>
<string name="extra_app">Extra Apps</string>
<string name="extra_app_hint">One package name per line\nEnable async loading of the App list for selection.</string>
<string name="choose_app_hint">Drop-down selection to get package name, keyword fuzzy matching APP name</string> <string name="choose_app_hint">Drop-down selection to get package name, keyword fuzzy matching APP name</string>
<string name="regex_multi_match" tools:ignore="TypographyDashes">^\\s*(AND|OR)\\s(IS|NOTIS)\\s(PHONE_NUM|PACKAGE_NAME|MSG_CONTENT|INFORM_CONTENT|INFORM_TITLE|CARD_SLOT)\\s(EQUALS|CONTAIN|NOTCONTAIN|STARTWITH|ENDWITH|REGEX)\\s(.*)$</string> <string name="regex_multi_match" tools:ignore="TypographyDashes">^\\s*(AND|OR)\\s(IS|NOTIS)\\s(PHONE_NUM|PACKAGE_NAME|MSG_CONTENT|INFORM_CONTENT|INFORM_TITLE|CARD_SLOT)\\s(EQUALS|CONTAIN|NOTCONTAIN|STARTWITH|ENDWITH|REGEX)\\s(.*)$</string>
<string name="privacy_content_1">Welcome to</string> <string name="privacy_content_1">Welcome to</string>

View File

@ -723,6 +723,8 @@
<string name="pushplus_token_hint">请前往对应的官网地址获取</string> <string name="pushplus_token_hint">请前往对应的官网地址获取</string>
<string name="choose_sender">下拉选择,关键字模糊匹配</string> <string name="choose_sender">下拉选择,关键字模糊匹配</string>
<string name="choose_app">已装APP列表</string> <string name="choose_app">已装APP列表</string>
<string name="extra_app">额外消除应用通知</string>
<string name="extra_app_hint">一行一个包名\n开启异步加载App列表以便选择</string>
<string name="choose_app_hint">下拉选择获取包名关键字模糊匹配APP名称</string> <string name="choose_app_hint">下拉选择获取包名关键字模糊匹配APP名称</string>
<string name="regex_multi_match" tools:ignore="TypographyDashes">^\\s*(并且|或者)\\s(是|不是)\\s(手机号|APP包名|短信内容|通知内容|通知标题|卡槽信息)\\s(相等|包含|不包含|开头|结尾|正则匹配)\\s(.*)$</string> <string name="regex_multi_match" tools:ignore="TypographyDashes">^\\s*(并且|或者)\\s(是|不是)\\s(手机号|APP包名|短信内容|通知内容|通知标题|卡槽信息)\\s(相等|包含|不包含|开头|结尾|正则匹配)\\s(.*)$</string>
<string name="privacy_content_1">欢迎使用</string> <string name="privacy_content_1">欢迎使用</string>

View File

@ -18,4 +18,4 @@ isUseBooster=false
android.precompileDependenciesResources=false android.precompileDependenciesResources=false
android.useAndroidX=true android.useAndroidX=true
android.enableJetifier=true android.enableJetifier=true
android.enableD8=true #android.enableD8=true