mirror of
https://github.com/pppscn/SmsForwarder
synced 2025-08-03 09:27:41 +08:00
整理:code review
This commit is contained in:
parent
992fc2eb8c
commit
e1660fe9bb
@ -1,174 +1,174 @@
|
|||||||
package com.idormy.sms.forwarder.service
|
package com.idormy.sms.forwarder.service
|
||||||
|
|
||||||
import android.app.*
|
import android.app.*
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.graphics.BitmapFactory
|
import android.graphics.BitmapFactory
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.IBinder
|
import android.os.IBinder
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
import androidx.lifecycle.Observer
|
import androidx.lifecycle.Observer
|
||||||
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.activity.MainActivity
|
import com.idormy.sms.forwarder.activity.MainActivity
|
||||||
import com.idormy.sms.forwarder.database.AppDatabase
|
import com.idormy.sms.forwarder.database.AppDatabase
|
||||||
import com.idormy.sms.forwarder.utils.*
|
import com.idormy.sms.forwarder.utils.*
|
||||||
import com.jeremyliao.liveeventbus.LiveEventBus
|
import com.jeremyliao.liveeventbus.LiveEventBus
|
||||||
import com.xuexiang.xutil.file.FileUtils
|
import com.xuexiang.xutil.file.FileUtils
|
||||||
import frpclib.Frpclib
|
import frpclib.Frpclib
|
||||||
import io.reactivex.Single
|
import io.reactivex.Single
|
||||||
import io.reactivex.SingleObserver
|
import io.reactivex.SingleObserver
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||||
import io.reactivex.disposables.CompositeDisposable
|
import io.reactivex.disposables.CompositeDisposable
|
||||||
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.Dispatchers
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.async
|
import kotlinx.coroutines.async
|
||||||
|
|
||||||
@Suppress("PrivatePropertyName", "DeferredResultUnused", "OPT_IN_USAGE")
|
@Suppress("PrivatePropertyName", "DeferredResultUnused", "OPT_IN_USAGE")
|
||||||
class ForegroundService : Service() {
|
class ForegroundService : Service() {
|
||||||
private val TAG: String = "ForegroundService"
|
private val TAG: String = "ForegroundService"
|
||||||
private val compositeDisposable = CompositeDisposable()
|
private val compositeDisposable = CompositeDisposable()
|
||||||
private val frpcObserver = Observer { uid: String ->
|
private val frpcObserver = Observer { uid: String ->
|
||||||
if (Frpclib.isRunning(uid)) {
|
if (Frpclib.isRunning(uid)) {
|
||||||
return@Observer
|
return@Observer
|
||||||
}
|
}
|
||||||
AppDatabase.getInstance(App.context)
|
AppDatabase.getInstance(App.context)
|
||||||
.frpcDao()
|
.frpcDao()
|
||||||
.get(uid)
|
.get(uid)
|
||||||
.flatMap { (uid1, _, config) ->
|
.flatMap { (uid1, _, config) ->
|
||||||
val error = Frpclib.runContent(uid1, config)
|
val error = Frpclib.runContent(uid1, config)
|
||||||
Single.just(error)
|
Single.just(error)
|
||||||
}
|
}
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe(object : SingleObserver<String> {
|
.subscribe(object : SingleObserver<String> {
|
||||||
override fun onSubscribe(d: Disposable) {
|
override fun onSubscribe(d: Disposable) {
|
||||||
compositeDisposable.add(d)
|
compositeDisposable.add(d)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onError(e: Throwable) {
|
override fun onError(e: Throwable) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
LiveEventBus.get(EVENT_FRPC_RUNNING_ERROR, String::class.java).post(uid)
|
LiveEventBus.get(EVENT_FRPC_RUNNING_ERROR, String::class.java).post(uid)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSuccess(msg: String) {
|
override fun onSuccess(msg: String) {
|
||||||
if (!TextUtils.isEmpty(msg)) {
|
if (!TextUtils.isEmpty(msg)) {
|
||||||
Log.e(TAG, msg)
|
Log.e(TAG, msg)
|
||||||
LiveEventBus.get(EVENT_FRPC_RUNNING_ERROR, String::class.java).post(uid)
|
LiveEventBus.get(EVENT_FRPC_RUNNING_ERROR, String::class.java).post(uid)
|
||||||
} else {
|
} else {
|
||||||
LiveEventBus.get(EVENT_FRPC_RUNNING_SUCCESS, String::class.java).post(uid)
|
LiveEventBus.get(EVENT_FRPC_RUNNING_SUCCESS, String::class.java).post(uid)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
private var notificationManager: NotificationManager? = null
|
private var notificationManager: NotificationManager? = null
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
var isRunning = false
|
var isRunning = false
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
//纯客户端模式
|
//纯客户端模式
|
||||||
if (SettingUtils.enablePureClientMode) return
|
if (SettingUtils.enablePureClientMode) return
|
||||||
|
|
||||||
notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
|
notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
|
||||||
startForeground(FRONT_NOTIFY_ID, createForegroundNotification())
|
startForeground(FRONT_NOTIFY_ID, createForegroundNotification())
|
||||||
|
|
||||||
//开关通知监听服务
|
//开关通知监听服务
|
||||||
if (SettingUtils.enableAppNotify && CommonUtils.isNotificationListenerServiceEnabled(this)) {
|
if (SettingUtils.enableAppNotify && CommonUtils.isNotificationListenerServiceEnabled(this)) {
|
||||||
CommonUtils.toggleNotificationListenerService(this)
|
CommonUtils.toggleNotificationListenerService(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FileUtils.isFileExists(filesDir.absolutePath + "/libs/libgojni.so")) {
|
if (FileUtils.isFileExists(filesDir.absolutePath + "/libs/libgojni.so")) {
|
||||||
//监听Frpc启动指令
|
//监听Frpc启动指令
|
||||||
LiveEventBus.get(INTENT_FRPC_APPLY_FILE, String::class.java).observeStickyForever(frpcObserver)
|
LiveEventBus.get(INTENT_FRPC_APPLY_FILE, String::class.java).observeStickyForever(frpcObserver)
|
||||||
//自启动的Frpc
|
//自启动的Frpc
|
||||||
GlobalScope.async(Dispatchers.IO) {
|
GlobalScope.async(Dispatchers.IO) {
|
||||||
val frpcList = AppDatabase.getInstance(App.context).frpcDao().getAutorun()
|
val frpcList = AppDatabase.getInstance(App.context).frpcDao().getAutorun()
|
||||||
|
|
||||||
if (frpcList.isEmpty()) {
|
if (frpcList.isEmpty()) {
|
||||||
Log.d(TAG, "没有自启动的Frpc")
|
Log.d(TAG, "没有自启动的Frpc")
|
||||||
return@async
|
return@async
|
||||||
}
|
}
|
||||||
|
|
||||||
for (frpc in frpcList) {
|
for (frpc in frpcList) {
|
||||||
val error = Frpclib.runContent(frpc.uid, frpc.config)
|
val error = Frpclib.runContent(frpc.uid, frpc.config)
|
||||||
if (!TextUtils.isEmpty(error)) {
|
if (!TextUtils.isEmpty(error)) {
|
||||||
Log.e(TAG, error)
|
Log.e(TAG, error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
isRunning = true
|
isRunning = true
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
isRunning = false
|
isRunning = false
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
|
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
|
||||||
isRunning = true
|
isRunning = true
|
||||||
return START_STICKY
|
return START_STICKY
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
//纯客户端模式
|
//纯客户端模式
|
||||||
if (SettingUtils.enablePureClientMode) {
|
if (SettingUtils.enablePureClientMode) {
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
stopForeground(true)
|
stopForeground(true)
|
||||||
compositeDisposable.dispose()
|
compositeDisposable.dispose()
|
||||||
isRunning = false
|
isRunning = false
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBind(intent: Intent): IBinder? {
|
override fun onBind(intent: Intent): IBinder? {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createForegroundNotification(): Notification {
|
private fun createForegroundNotification(): Notification {
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
val importance = NotificationManager.IMPORTANCE_HIGH
|
val importance = NotificationManager.IMPORTANCE_HIGH
|
||||||
val notificationChannel = NotificationChannel(FRONT_CHANNEL_ID, FRONT_CHANNEL_NAME, importance)
|
val notificationChannel = NotificationChannel(FRONT_CHANNEL_ID, FRONT_CHANNEL_NAME, importance)
|
||||||
notificationChannel.description = "Frpc Foreground Service"
|
notificationChannel.description = "Frpc Foreground Service"
|
||||||
notificationChannel.enableLights(true)
|
notificationChannel.enableLights(true)
|
||||||
notificationChannel.lightColor = Color.GREEN
|
notificationChannel.lightColor = Color.GREEN
|
||||||
notificationChannel.vibrationPattern = longArrayOf(0, 1000, 500, 1000)
|
notificationChannel.vibrationPattern = longArrayOf(0, 1000, 500, 1000)
|
||||||
notificationChannel.enableVibration(true)
|
notificationChannel.enableVibration(true)
|
||||||
if (notificationManager != null) {
|
if (notificationManager != null) {
|
||||||
notificationManager!!.createNotificationChannel(notificationChannel)
|
notificationManager!!.createNotificationChannel(notificationChannel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val builder = NotificationCompat.Builder(this, FRONT_CHANNEL_ID)
|
val builder = NotificationCompat.Builder(this, FRONT_CHANNEL_ID)
|
||||||
builder.setSmallIcon(R.drawable.ic_forwarder)
|
builder.setSmallIcon(R.drawable.ic_forwarder)
|
||||||
builder.setLargeIcon(BitmapFactory.decodeResource(resources, R.drawable.ic_menu_frpc))
|
builder.setLargeIcon(BitmapFactory.decodeResource(resources, R.drawable.ic_menu_frpc))
|
||||||
// TODO: 部分机型标题会重复待排除
|
// TODO: 部分机型标题会重复待排除
|
||||||
// if (DeviceUtils.getDeviceBrand().contains("Xiaomi")) {
|
// if (DeviceUtils.getDeviceBrand().contains("Xiaomi")) {
|
||||||
builder.setContentTitle(getString(R.string.app_name))
|
builder.setContentTitle(getString(R.string.app_name))
|
||||||
//}
|
//}
|
||||||
builder.setContentText(SettingUtils.notifyContent.toString())
|
builder.setContentText(SettingUtils.notifyContent)
|
||||||
builder.setWhen(System.currentTimeMillis())
|
builder.setWhen(System.currentTimeMillis())
|
||||||
val activityIntent = Intent(this, MainActivity::class.java)
|
val activityIntent = Intent(this, MainActivity::class.java)
|
||||||
val flags = if (Build.VERSION.SDK_INT >= 30) PendingIntent.FLAG_IMMUTABLE else PendingIntent.FLAG_UPDATE_CURRENT
|
val flags = if (Build.VERSION.SDK_INT >= 30) PendingIntent.FLAG_IMMUTABLE else PendingIntent.FLAG_UPDATE_CURRENT
|
||||||
val pendingIntent = PendingIntent.getActivity(this, 0, activityIntent, flags)
|
val pendingIntent = PendingIntent.getActivity(this, 0, activityIntent, flags)
|
||||||
builder.setContentIntent(pendingIntent)
|
builder.setContentIntent(pendingIntent)
|
||||||
return builder.build()
|
return builder.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -35,7 +35,7 @@ class BarkUtils {
|
|||||||
val content: String = if (rule != null) {
|
val content: String = if (rule != null) {
|
||||||
msgInfo.getContentForSend(rule.smsTemplate, rule.regexReplace)
|
msgInfo.getContentForSend(rule.smsTemplate, rule.regexReplace)
|
||||||
} else {
|
} else {
|
||||||
msgInfo.getContentForSend(SettingUtils.smsTemplate.toString())
|
msgInfo.getContentForSend(SettingUtils.smsTemplate)
|
||||||
}
|
}
|
||||||
|
|
||||||
val requestUrl: String = setting.server //推送地址
|
val requestUrl: String = setting.server //推送地址
|
||||||
|
@ -35,7 +35,7 @@ class DingtalkGroupRobotUtils private constructor() {
|
|||||||
val content: String = if (rule != null) {
|
val content: String = if (rule != null) {
|
||||||
msgInfo.getContentForSend(rule.smsTemplate, rule.regexReplace)
|
msgInfo.getContentForSend(rule.smsTemplate, rule.regexReplace)
|
||||||
} else {
|
} else {
|
||||||
msgInfo.getContentForSend(SettingUtils.smsTemplate.toString())
|
msgInfo.getContentForSend(SettingUtils.smsTemplate)
|
||||||
}
|
}
|
||||||
|
|
||||||
var requestUrl = if (setting.token.startsWith("http")) setting.token else "https://oapi.dingtalk.com/robot/send?access_token=" + setting.token
|
var requestUrl = if (setting.token.startsWith("http")) setting.token else "https://oapi.dingtalk.com/robot/send?access_token=" + setting.token
|
||||||
|
@ -126,7 +126,7 @@ class DingtalkInnerRobotUtils private constructor() {
|
|||||||
val content: String = if (rule != null) {
|
val content: String = if (rule != null) {
|
||||||
msgInfo.getContentForSend(rule.smsTemplate, rule.regexReplace)
|
msgInfo.getContentForSend(rule.smsTemplate, rule.regexReplace)
|
||||||
} else {
|
} else {
|
||||||
msgInfo.getContentForSend(SettingUtils.smsTemplate.toString())
|
msgInfo.getContentForSend(SettingUtils.smsTemplate)
|
||||||
}
|
}
|
||||||
|
|
||||||
val msgParam: MutableMap<String, Any> = mutableMapOf()
|
val msgParam: MutableMap<String, Any> = mutableMapOf()
|
||||||
|
@ -1,151 +1,151 @@
|
|||||||
package com.idormy.sms.forwarder.utils.sender
|
package com.idormy.sms.forwarder.utils.sender
|
||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.idormy.sms.forwarder.R
|
import com.idormy.sms.forwarder.R
|
||||||
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.entity.setting.EmailSetting
|
import com.idormy.sms.forwarder.entity.setting.EmailSetting
|
||||||
import com.idormy.sms.forwarder.utils.SendUtils
|
import com.idormy.sms.forwarder.utils.SendUtils
|
||||||
import com.idormy.sms.forwarder.utils.SettingUtils
|
import com.idormy.sms.forwarder.utils.SettingUtils
|
||||||
import com.idormy.sms.forwarder.utils.mail.Mail
|
import com.idormy.sms.forwarder.utils.mail.Mail
|
||||||
import com.idormy.sms.forwarder.utils.mail.MailSender
|
import com.idormy.sms.forwarder.utils.mail.MailSender
|
||||||
import com.xuexiang.xui.utils.ResUtils
|
import com.xuexiang.xui.utils.ResUtils
|
||||||
|
|
||||||
@Suppress("PrivatePropertyName", "UNUSED_PARAMETER", "unused")
|
@Suppress("PrivatePropertyName", "UNUSED_PARAMETER", "unused")
|
||||||
class EmailUtils {
|
class EmailUtils {
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
private val TAG: String = EmailUtils::class.java.simpleName
|
private val TAG: String = EmailUtils::class.java.simpleName
|
||||||
|
|
||||||
fun sendMsg(
|
fun sendMsg(
|
||||||
setting: EmailSetting,
|
setting: EmailSetting,
|
||||||
msgInfo: MsgInfo,
|
msgInfo: MsgInfo,
|
||||||
rule: Rule?,
|
rule: Rule?,
|
||||||
logId: Long?,
|
logId: Long?,
|
||||||
) {
|
) {
|
||||||
val title: String = if (rule != null) {
|
val title: String = if (rule != null) {
|
||||||
msgInfo.getTitleForSend(setting.title.toString(), rule.regexReplace)
|
msgInfo.getTitleForSend(setting.title.toString(), rule.regexReplace)
|
||||||
} else {
|
} else {
|
||||||
msgInfo.getTitleForSend(setting.title.toString())
|
msgInfo.getTitleForSend(setting.title.toString())
|
||||||
}
|
}
|
||||||
val message: String = if (rule != null) {
|
val message: String = if (rule != null) {
|
||||||
msgInfo.getContentForSend(rule.smsTemplate, rule.regexReplace)
|
msgInfo.getContentForSend(rule.smsTemplate, rule.regexReplace)
|
||||||
} else {
|
} else {
|
||||||
msgInfo.getContentForSend(SettingUtils.smsTemplate.toString())
|
msgInfo.getContentForSend(SettingUtils.smsTemplate)
|
||||||
}
|
}
|
||||||
|
|
||||||
//常用邮箱类型的转换
|
//常用邮箱类型的转换
|
||||||
when (setting.mailType) {
|
when (setting.mailType) {
|
||||||
"@qq.com", "@foxmail.com" -> {
|
"@qq.com", "@foxmail.com" -> {
|
||||||
setting.host = "smtp.qq.com"
|
setting.host = "smtp.qq.com"
|
||||||
setting.port = "465"
|
setting.port = "465"
|
||||||
setting.ssl = true
|
setting.ssl = true
|
||||||
setting.fromEmail += setting.mailType
|
setting.fromEmail += setting.mailType
|
||||||
}
|
}
|
||||||
"@exmail.qq.com" -> {
|
"@exmail.qq.com" -> {
|
||||||
setting.host = "smtp.exmail.qq.com"
|
setting.host = "smtp.exmail.qq.com"
|
||||||
setting.port = "465"
|
setting.port = "465"
|
||||||
setting.ssl = true
|
setting.ssl = true
|
||||||
setting.fromEmail += setting.mailType
|
setting.fromEmail += setting.mailType
|
||||||
}
|
}
|
||||||
"@msn.com" -> {
|
"@msn.com" -> {
|
||||||
setting.host = "smtp-mail.outlook.com"
|
setting.host = "smtp-mail.outlook.com"
|
||||||
setting.port = "587"
|
setting.port = "587"
|
||||||
setting.ssl = false
|
setting.ssl = false
|
||||||
setting.startTls = true
|
setting.startTls = true
|
||||||
setting.fromEmail += setting.mailType
|
setting.fromEmail += setting.mailType
|
||||||
}
|
}
|
||||||
"@outlook.com", "@office365.com", "@live.com", "@hotmail.com" -> {
|
"@outlook.com", "@office365.com", "@live.com", "@hotmail.com" -> {
|
||||||
setting.host = "smtp.office365.com"
|
setting.host = "smtp.office365.com"
|
||||||
setting.port = "587"
|
setting.port = "587"
|
||||||
setting.ssl = false
|
setting.ssl = false
|
||||||
setting.startTls = true
|
setting.startTls = true
|
||||||
setting.fromEmail += setting.mailType
|
setting.fromEmail += setting.mailType
|
||||||
}
|
}
|
||||||
"@gmail.com" -> {
|
"@gmail.com" -> {
|
||||||
setting.host = "smtp.gmail.com"
|
setting.host = "smtp.gmail.com"
|
||||||
setting.port = "587"
|
setting.port = "587"
|
||||||
setting.ssl = true
|
setting.ssl = true
|
||||||
setting.startTls = true
|
setting.startTls = true
|
||||||
setting.fromEmail += setting.mailType
|
setting.fromEmail += setting.mailType
|
||||||
}
|
}
|
||||||
"@yeah.net" -> {
|
"@yeah.net" -> {
|
||||||
setting.host = "smtp.yeah.net"
|
setting.host = "smtp.yeah.net"
|
||||||
setting.port = "465"
|
setting.port = "465"
|
||||||
setting.ssl = true
|
setting.ssl = true
|
||||||
setting.fromEmail += setting.mailType
|
setting.fromEmail += setting.mailType
|
||||||
}
|
}
|
||||||
"@163.com" -> {
|
"@163.com" -> {
|
||||||
setting.host = "smtp.163.com"
|
setting.host = "smtp.163.com"
|
||||||
setting.port = "465"
|
setting.port = "465"
|
||||||
setting.ssl = true
|
setting.ssl = true
|
||||||
setting.fromEmail += setting.mailType
|
setting.fromEmail += setting.mailType
|
||||||
}
|
}
|
||||||
"@126.com" -> {
|
"@126.com" -> {
|
||||||
setting.host = "smtp.126.com"
|
setting.host = "smtp.126.com"
|
||||||
setting.port = "465"
|
setting.port = "465"
|
||||||
setting.ssl = true
|
setting.ssl = true
|
||||||
setting.fromEmail += setting.mailType
|
setting.fromEmail += setting.mailType
|
||||||
}
|
}
|
||||||
"@sina.com" -> {
|
"@sina.com" -> {
|
||||||
setting.host = "smtp.sina.com"
|
setting.host = "smtp.sina.com"
|
||||||
setting.port = "465"
|
setting.port = "465"
|
||||||
setting.ssl = true
|
setting.ssl = true
|
||||||
setting.fromEmail += setting.mailType
|
setting.fromEmail += setting.mailType
|
||||||
}
|
}
|
||||||
"@sina.cn" -> {
|
"@sina.cn" -> {
|
||||||
setting.host = "smtp.sina.cn"
|
setting.host = "smtp.sina.cn"
|
||||||
setting.port = "465"
|
setting.port = "465"
|
||||||
setting.ssl = true
|
setting.ssl = true
|
||||||
setting.fromEmail += setting.mailType
|
setting.fromEmail += setting.mailType
|
||||||
}
|
}
|
||||||
"@139.com" -> {
|
"@139.com" -> {
|
||||||
setting.host = "smtp.139.com"
|
setting.host = "smtp.139.com"
|
||||||
setting.port = "465"
|
setting.port = "465"
|
||||||
setting.ssl = true
|
setting.ssl = true
|
||||||
setting.fromEmail += setting.mailType
|
setting.fromEmail += setting.mailType
|
||||||
}
|
}
|
||||||
"@189.cn" -> {
|
"@189.cn" -> {
|
||||||
setting.host = "smtp.189.cn"
|
setting.host = "smtp.189.cn"
|
||||||
setting.port = "465"
|
setting.port = "465"
|
||||||
setting.ssl = true
|
setting.ssl = true
|
||||||
setting.fromEmail += setting.mailType
|
setting.fromEmail += setting.mailType
|
||||||
}
|
}
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
|
|
||||||
//收件地址
|
//收件地址
|
||||||
val toAddressList = setting.toEmail.toString().replace("[,,;;]".toRegex(), ",").trim(',').split(',')
|
val toAddressList = setting.toEmail.toString().replace("[,,;;]".toRegex(), ",").trim(',').split(',')
|
||||||
|
|
||||||
//创建邮箱
|
//创建邮箱
|
||||||
val mail = Mail().apply {
|
val mail = Mail().apply {
|
||||||
mailServerHost = setting.host.toString()
|
mailServerHost = setting.host.toString()
|
||||||
mailServerPort = setting.port.toString()
|
mailServerPort = setting.port.toString()
|
||||||
fromAddress = setting.fromEmail.toString()
|
fromAddress = setting.fromEmail.toString()
|
||||||
fromNickname = msgInfo.getTitleForSend(setting.nickname.toString())
|
fromNickname = msgInfo.getTitleForSend(setting.nickname.toString())
|
||||||
password = setting.pwd.toString()
|
password = setting.pwd.toString()
|
||||||
toAddress = toAddressList
|
toAddress = toAddressList
|
||||||
subject = title
|
subject = title
|
||||||
content = message.replace("\n", "<br>")
|
content = message.replace("\n", "<br>")
|
||||||
openSSL = setting.ssl == true
|
openSSL = setting.ssl == true
|
||||||
startTls = setting.startTls == true
|
startTls = setting.startTls == true
|
||||||
}
|
}
|
||||||
|
|
||||||
MailSender.getInstance().sendMail(mail, object : MailSender.OnMailSendListener {
|
MailSender.getInstance().sendMail(mail, object : MailSender.OnMailSendListener {
|
||||||
override fun onError(e: Throwable) {
|
override fun onError(e: Throwable) {
|
||||||
Log.e("MailSender", e.message.toString())
|
Log.e("MailSender", e.message.toString())
|
||||||
SendUtils.updateLogs(logId, 0, e.message.toString())
|
SendUtils.updateLogs(logId, 0, e.message.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSuccess() {
|
override fun onSuccess() {
|
||||||
SendUtils.updateLogs(logId, 2, ResUtils.getString(R.string.request_succeeded))
|
SendUtils.updateLogs(logId, 2, ResUtils.getString(R.string.request_succeeded))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun sendMsg(setting: EmailSetting, msgInfo: MsgInfo) {
|
fun sendMsg(setting: EmailSetting, msgInfo: MsgInfo) {
|
||||||
sendMsg(setting, msgInfo, null, null)
|
sendMsg(setting, msgInfo, null, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -84,7 +84,7 @@ class FeishuAppUtils private constructor() {
|
|||||||
val content: String = if (rule != null) {
|
val content: String = if (rule != null) {
|
||||||
msgInfo.getContentForSend(rule.smsTemplate, rule.regexReplace)
|
msgInfo.getContentForSend(rule.smsTemplate, rule.regexReplace)
|
||||||
} else {
|
} else {
|
||||||
msgInfo.getContentForSend(SettingUtils.smsTemplate.toString())
|
msgInfo.getContentForSend(SettingUtils.smsTemplate)
|
||||||
}
|
}
|
||||||
|
|
||||||
val msgContent = if ("interactive" == setting.msgType) {
|
val msgContent = if ("interactive" == setting.msgType) {
|
||||||
|
@ -94,7 +94,7 @@ class FeishuUtils private constructor() {
|
|||||||
val content: String = if (rule != null) {
|
val content: String = if (rule != null) {
|
||||||
msgInfo.getContentForSend(rule.smsTemplate, rule.regexReplace)
|
msgInfo.getContentForSend(rule.smsTemplate, rule.regexReplace)
|
||||||
} else {
|
} else {
|
||||||
msgInfo.getContentForSend(SettingUtils.smsTemplate.toString())
|
msgInfo.getContentForSend(SettingUtils.smsTemplate)
|
||||||
}
|
}
|
||||||
|
|
||||||
val requestUrl = setting.webhook
|
val requestUrl = setting.webhook
|
||||||
|
@ -1,89 +1,89 @@
|
|||||||
package com.idormy.sms.forwarder.utils.sender
|
package com.idormy.sms.forwarder.utils.sender
|
||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
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.entity.result.GotifyResult
|
import com.idormy.sms.forwarder.entity.result.GotifyResult
|
||||||
import com.idormy.sms.forwarder.entity.setting.GotifySetting
|
import com.idormy.sms.forwarder.entity.setting.GotifySetting
|
||||||
import com.idormy.sms.forwarder.utils.SendUtils
|
import com.idormy.sms.forwarder.utils.SendUtils
|
||||||
import com.idormy.sms.forwarder.utils.SettingUtils
|
import com.idormy.sms.forwarder.utils.SettingUtils
|
||||||
import com.xuexiang.xhttp2.XHttp
|
import com.xuexiang.xhttp2.XHttp
|
||||||
import com.xuexiang.xhttp2.cache.model.CacheMode
|
import com.xuexiang.xhttp2.cache.model.CacheMode
|
||||||
import com.xuexiang.xhttp2.callback.SimpleCallBack
|
import com.xuexiang.xhttp2.callback.SimpleCallBack
|
||||||
import com.xuexiang.xhttp2.exception.ApiException
|
import com.xuexiang.xhttp2.exception.ApiException
|
||||||
|
|
||||||
@Suppress("PrivatePropertyName", "UNUSED_PARAMETER", "unused")
|
@Suppress("PrivatePropertyName", "UNUSED_PARAMETER", "unused")
|
||||||
class GotifyUtils {
|
class GotifyUtils {
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
private val TAG: String = GotifyUtils::class.java.simpleName
|
private val TAG: String = GotifyUtils::class.java.simpleName
|
||||||
|
|
||||||
fun sendMsg(
|
fun sendMsg(
|
||||||
setting: GotifySetting,
|
setting: GotifySetting,
|
||||||
msgInfo: MsgInfo,
|
msgInfo: MsgInfo,
|
||||||
rule: Rule?,
|
rule: Rule?,
|
||||||
logId: Long?,
|
logId: Long?,
|
||||||
) {
|
) {
|
||||||
val title: String = if (rule != null) {
|
val title: String = if (rule != null) {
|
||||||
msgInfo.getTitleForSend(setting.title.toString(), rule.regexReplace)
|
msgInfo.getTitleForSend(setting.title.toString(), rule.regexReplace)
|
||||||
} else {
|
} else {
|
||||||
msgInfo.getTitleForSend(setting.title.toString())
|
msgInfo.getTitleForSend(setting.title.toString())
|
||||||
}
|
}
|
||||||
val content: String = if (rule != null) {
|
val content: String = if (rule != null) {
|
||||||
msgInfo.getContentForSend(rule.smsTemplate, rule.regexReplace)
|
msgInfo.getContentForSend(rule.smsTemplate, rule.regexReplace)
|
||||||
} else {
|
} else {
|
||||||
msgInfo.getContentForSend(SettingUtils.smsTemplate.toString())
|
msgInfo.getContentForSend(SettingUtils.smsTemplate)
|
||||||
}
|
}
|
||||||
|
|
||||||
val requestUrl: String = setting.webServer //推送地址
|
val requestUrl: String = setting.webServer //推送地址
|
||||||
Log.i(TAG, "requestUrl:$requestUrl")
|
Log.i(TAG, "requestUrl:$requestUrl")
|
||||||
|
|
||||||
//支持HTTP基本认证(Basic Authentication)
|
//支持HTTP基本认证(Basic Authentication)
|
||||||
val regex = "^(https?://)([^:]+):([^@]+)@(.+)"
|
val regex = "^(https?://)([^:]+):([^@]+)@(.+)"
|
||||||
val matches = Regex(regex, RegexOption.IGNORE_CASE).findAll(requestUrl).toList().flatMap(MatchResult::groupValues)
|
val matches = Regex(regex, RegexOption.IGNORE_CASE).findAll(requestUrl).toList().flatMap(MatchResult::groupValues)
|
||||||
Log.i(TAG, "matches = $matches")
|
Log.i(TAG, "matches = $matches")
|
||||||
val request = if (matches.isNotEmpty()) {
|
val request = if (matches.isNotEmpty()) {
|
||||||
XHttp.post(matches[1] + matches[4]).addInterceptor(BasicAuthInterceptor(matches[2], matches[3]))
|
XHttp.post(matches[1] + matches[4]).addInterceptor(BasicAuthInterceptor(matches[2], matches[3]))
|
||||||
} else {
|
} else {
|
||||||
XHttp.post(requestUrl)
|
XHttp.post(requestUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
request.params("title", title)
|
request.params("title", title)
|
||||||
.params("message", content)
|
.params("message", content)
|
||||||
.params("priority", setting.priority)
|
.params("priority", setting.priority)
|
||||||
.ignoreHttpsCert() //忽略https证书
|
.ignoreHttpsCert() //忽略https证书
|
||||||
.keepJson(true)
|
.keepJson(true)
|
||||||
.timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
|
.timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
|
||||||
.cacheMode(CacheMode.NO_CACHE)
|
.cacheMode(CacheMode.NO_CACHE)
|
||||||
.retryCount(SettingUtils.requestRetryTimes) //超时重试的次数
|
.retryCount(SettingUtils.requestRetryTimes) //超时重试的次数
|
||||||
.retryDelay(SettingUtils.requestDelayTime) //超时重试的延迟时间
|
.retryDelay(SettingUtils.requestDelayTime) //超时重试的延迟时间
|
||||||
.retryIncreaseDelay(SettingUtils.requestDelayTime) //超时重试叠加延时
|
.retryIncreaseDelay(SettingUtils.requestDelayTime) //超时重试叠加延时
|
||||||
.timeStamp(true)
|
.timeStamp(true)
|
||||||
.execute(object : SimpleCallBack<String>() {
|
.execute(object : SimpleCallBack<String>() {
|
||||||
|
|
||||||
override fun onError(e: ApiException) {
|
override fun onError(e: ApiException) {
|
||||||
Log.e(TAG, e.detailMessage)
|
Log.e(TAG, e.detailMessage)
|
||||||
SendUtils.updateLogs(logId, 0, e.displayMessage)
|
SendUtils.updateLogs(logId, 0, e.displayMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSuccess(response: String) {
|
override fun onSuccess(response: String) {
|
||||||
Log.i(TAG, response)
|
Log.i(TAG, response)
|
||||||
|
|
||||||
val resp = Gson().fromJson(response, GotifyResult::class.java)
|
val resp = Gson().fromJson(response, GotifyResult::class.java)
|
||||||
if (resp?.id != null) {
|
if (resp?.id != null) {
|
||||||
SendUtils.updateLogs(logId, 2, response)
|
SendUtils.updateLogs(logId, 2, response)
|
||||||
} else {
|
} else {
|
||||||
SendUtils.updateLogs(logId, 0, response)
|
SendUtils.updateLogs(logId, 0, response)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun sendMsg(setting: GotifySetting, msgInfo: MsgInfo) {
|
fun sendMsg(setting: GotifySetting, msgInfo: MsgInfo) {
|
||||||
sendMsg(setting, msgInfo, null, null)
|
sendMsg(setting, msgInfo, null, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -37,7 +37,7 @@ class PushplusUtils private constructor() {
|
|||||||
val content: String = if (rule != null) {
|
val content: String = if (rule != null) {
|
||||||
msgInfo.getContentForSend(rule.smsTemplate, rule.regexReplace)
|
msgInfo.getContentForSend(rule.smsTemplate, rule.regexReplace)
|
||||||
} else {
|
} else {
|
||||||
msgInfo.getContentForSend(SettingUtils.smsTemplate.toString())
|
msgInfo.getContentForSend(SettingUtils.smsTemplate)
|
||||||
}
|
}
|
||||||
|
|
||||||
val requestUrl = "https://" + setting.website + "/send"
|
val requestUrl = "https://" + setting.website + "/send"
|
||||||
@ -63,7 +63,6 @@ class PushplusUtils private constructor() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
val requestMsg: String = Gson().toJson(msgMap)
|
val requestMsg: String = Gson().toJson(msgMap)
|
||||||
Log.i(TAG, "requestMsg:$requestMsg")
|
Log.i(TAG, "requestMsg:$requestMsg")
|
||||||
|
|
||||||
|
@ -1,83 +1,83 @@
|
|||||||
package com.idormy.sms.forwarder.utils.sender
|
package com.idormy.sms.forwarder.utils.sender
|
||||||
|
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
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.entity.result.ServerchanResult
|
import com.idormy.sms.forwarder.entity.result.ServerchanResult
|
||||||
import com.idormy.sms.forwarder.entity.setting.ServerchanSetting
|
import com.idormy.sms.forwarder.entity.setting.ServerchanSetting
|
||||||
import com.idormy.sms.forwarder.utils.SendUtils
|
import com.idormy.sms.forwarder.utils.SendUtils
|
||||||
import com.idormy.sms.forwarder.utils.SettingUtils
|
import com.idormy.sms.forwarder.utils.SettingUtils
|
||||||
import com.xuexiang.xhttp2.XHttp
|
import com.xuexiang.xhttp2.XHttp
|
||||||
import com.xuexiang.xhttp2.cache.model.CacheMode
|
import com.xuexiang.xhttp2.cache.model.CacheMode
|
||||||
import com.xuexiang.xhttp2.callback.SimpleCallBack
|
import com.xuexiang.xhttp2.callback.SimpleCallBack
|
||||||
import com.xuexiang.xhttp2.exception.ApiException
|
import com.xuexiang.xhttp2.exception.ApiException
|
||||||
|
|
||||||
@Suppress("PrivatePropertyName", "UNUSED_PARAMETER", "unused")
|
@Suppress("PrivatePropertyName", "UNUSED_PARAMETER", "unused")
|
||||||
class ServerchanUtils {
|
class ServerchanUtils {
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
private val TAG: String = ServerchanUtils::class.java.simpleName
|
private val TAG: String = ServerchanUtils::class.java.simpleName
|
||||||
|
|
||||||
fun sendMsg(
|
fun sendMsg(
|
||||||
setting: ServerchanSetting,
|
setting: ServerchanSetting,
|
||||||
msgInfo: MsgInfo,
|
msgInfo: MsgInfo,
|
||||||
rule: Rule?,
|
rule: Rule?,
|
||||||
logId: Long?,
|
logId: Long?,
|
||||||
) {
|
) {
|
||||||
val title: String = if (rule != null) {
|
val title: String = if (rule != null) {
|
||||||
msgInfo.getTitleForSend(setting.titleTemplate.toString(), rule.regexReplace)
|
msgInfo.getTitleForSend(setting.titleTemplate.toString(), rule.regexReplace)
|
||||||
} else {
|
} else {
|
||||||
msgInfo.getTitleForSend(setting.titleTemplate.toString())
|
msgInfo.getTitleForSend(setting.titleTemplate.toString())
|
||||||
}
|
}
|
||||||
val content: String = if (rule != null) {
|
val content: String = if (rule != null) {
|
||||||
msgInfo.getContentForSend(rule.smsTemplate, rule.regexReplace)
|
msgInfo.getContentForSend(rule.smsTemplate, rule.regexReplace)
|
||||||
} else {
|
} else {
|
||||||
msgInfo.getContentForSend(SettingUtils.smsTemplate.toString())
|
msgInfo.getContentForSend(SettingUtils.smsTemplate)
|
||||||
}
|
}
|
||||||
|
|
||||||
val requestUrl: String = String.format("https://sctapi.ftqq.com/%s.send", setting.sendKey) //推送地址
|
val requestUrl: String = String.format("https://sctapi.ftqq.com/%s.send", setting.sendKey) //推送地址
|
||||||
Log.i(TAG, "requestUrl:$requestUrl")
|
Log.i(TAG, "requestUrl:$requestUrl")
|
||||||
|
|
||||||
val request = XHttp.post(requestUrl)
|
val request = XHttp.post(requestUrl)
|
||||||
.params("title", title)
|
.params("title", title)
|
||||||
.params("desp", content)
|
.params("desp", content)
|
||||||
|
|
||||||
if (!TextUtils.isEmpty(setting.channel)) request.params("channel", setting.channel)
|
if (!TextUtils.isEmpty(setting.channel)) request.params("channel", setting.channel)
|
||||||
if (!TextUtils.isEmpty(setting.openid)) request.params("group", setting.openid)
|
if (!TextUtils.isEmpty(setting.openid)) request.params("group", setting.openid)
|
||||||
|
|
||||||
request.keepJson(true)
|
request.keepJson(true)
|
||||||
.timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
|
.timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
|
||||||
.cacheMode(CacheMode.NO_CACHE)
|
.cacheMode(CacheMode.NO_CACHE)
|
||||||
.retryCount(SettingUtils.requestRetryTimes) //超时重试的次数
|
.retryCount(SettingUtils.requestRetryTimes) //超时重试的次数
|
||||||
.retryDelay(SettingUtils.requestDelayTime) //超时重试的延迟时间
|
.retryDelay(SettingUtils.requestDelayTime) //超时重试的延迟时间
|
||||||
.retryIncreaseDelay(SettingUtils.requestDelayTime) //超时重试叠加延时
|
.retryIncreaseDelay(SettingUtils.requestDelayTime) //超时重试叠加延时
|
||||||
.timeStamp(true)
|
.timeStamp(true)
|
||||||
.execute(object : SimpleCallBack<String>() {
|
.execute(object : SimpleCallBack<String>() {
|
||||||
|
|
||||||
override fun onError(e: ApiException) {
|
override fun onError(e: ApiException) {
|
||||||
Log.e(TAG, e.detailMessage)
|
Log.e(TAG, e.detailMessage)
|
||||||
SendUtils.updateLogs(logId, 0, e.displayMessage)
|
SendUtils.updateLogs(logId, 0, e.displayMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSuccess(response: String) {
|
override fun onSuccess(response: String) {
|
||||||
Log.i(TAG, response)
|
Log.i(TAG, response)
|
||||||
|
|
||||||
val resp = Gson().fromJson(response, ServerchanResult::class.java)
|
val resp = Gson().fromJson(response, ServerchanResult::class.java)
|
||||||
if (resp?.code == 0L) {
|
if (resp?.code == 0L) {
|
||||||
SendUtils.updateLogs(logId, 2, response)
|
SendUtils.updateLogs(logId, 2, response)
|
||||||
} else {
|
} else {
|
||||||
SendUtils.updateLogs(logId, 0, response)
|
SendUtils.updateLogs(logId, 0, response)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun sendMsg(setting: ServerchanSetting, msgInfo: MsgInfo) {
|
fun sendMsg(setting: ServerchanSetting, msgInfo: MsgInfo) {
|
||||||
sendMsg(setting, msgInfo, null, null)
|
sendMsg(setting, msgInfo, null, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -40,7 +40,7 @@ class TelegramUtils private constructor() {
|
|||||||
val content: String = if (rule != null) {
|
val content: String = if (rule != null) {
|
||||||
msgInfo.getContentForSend(rule.smsTemplate, rule.regexReplace)
|
msgInfo.getContentForSend(rule.smsTemplate, rule.regexReplace)
|
||||||
} else {
|
} else {
|
||||||
msgInfo.getContentForSend(SettingUtils.smsTemplate.toString())
|
msgInfo.getContentForSend(SettingUtils.smsTemplate)
|
||||||
}
|
}
|
||||||
|
|
||||||
var requestUrl = if (setting.apiToken.startsWith("http")) {
|
var requestUrl = if (setting.apiToken.startsWith("http")) {
|
||||||
|
@ -1,209 +1,209 @@
|
|||||||
package com.idormy.sms.forwarder.utils.sender
|
package com.idormy.sms.forwarder.utils.sender
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
import android.util.Base64
|
import android.util.Base64
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
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.entity.setting.WebhookSetting
|
import com.idormy.sms.forwarder.entity.setting.WebhookSetting
|
||||||
import com.idormy.sms.forwarder.utils.SendUtils
|
import com.idormy.sms.forwarder.utils.SendUtils
|
||||||
import com.idormy.sms.forwarder.utils.SettingUtils
|
import com.idormy.sms.forwarder.utils.SettingUtils
|
||||||
import com.xuexiang.xhttp2.XHttp
|
import com.xuexiang.xhttp2.XHttp
|
||||||
import com.xuexiang.xhttp2.cache.model.CacheMode
|
import com.xuexiang.xhttp2.cache.model.CacheMode
|
||||||
import com.xuexiang.xhttp2.callback.SimpleCallBack
|
import com.xuexiang.xhttp2.callback.SimpleCallBack
|
||||||
import com.xuexiang.xhttp2.exception.ApiException
|
import com.xuexiang.xhttp2.exception.ApiException
|
||||||
import com.xuexiang.xutil.app.AppUtils
|
import com.xuexiang.xutil.app.AppUtils
|
||||||
import java.net.URLEncoder
|
import java.net.URLEncoder
|
||||||
import java.nio.charset.StandardCharsets
|
import java.nio.charset.StandardCharsets
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import javax.crypto.Mac
|
import javax.crypto.Mac
|
||||||
import javax.crypto.spec.SecretKeySpec
|
import javax.crypto.spec.SecretKeySpec
|
||||||
|
|
||||||
@Suppress("PrivatePropertyName", "UNUSED_PARAMETER", "unused")
|
@Suppress("PrivatePropertyName", "UNUSED_PARAMETER", "unused")
|
||||||
class WebhookUtils {
|
class WebhookUtils {
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
private val TAG: String = WebhookUtils::class.java.simpleName
|
private val TAG: String = WebhookUtils::class.java.simpleName
|
||||||
|
|
||||||
fun sendMsg(
|
fun sendMsg(
|
||||||
setting: WebhookSetting,
|
setting: WebhookSetting,
|
||||||
msgInfo: MsgInfo,
|
msgInfo: MsgInfo,
|
||||||
rule: Rule?,
|
rule: Rule?,
|
||||||
logId: Long?,
|
logId: Long?,
|
||||||
) {
|
) {
|
||||||
val from: String = msgInfo.from
|
val from: String = msgInfo.from
|
||||||
val content: String = if (rule != null) {
|
val content: String = if (rule != null) {
|
||||||
msgInfo.getContentForSend(rule.smsTemplate, rule.regexReplace)
|
msgInfo.getContentForSend(rule.smsTemplate, rule.regexReplace)
|
||||||
} else {
|
} else {
|
||||||
msgInfo.getContentForSend(SettingUtils.smsTemplate.toString())
|
msgInfo.getContentForSend(SettingUtils.smsTemplate)
|
||||||
}
|
}
|
||||||
|
|
||||||
var requestUrl: String = setting.webServer //推送地址
|
var requestUrl: String = setting.webServer //推送地址
|
||||||
Log.i(TAG, "requestUrl:$requestUrl")
|
Log.i(TAG, "requestUrl:$requestUrl")
|
||||||
|
|
||||||
val timestamp = System.currentTimeMillis()
|
val timestamp = System.currentTimeMillis()
|
||||||
val orgContent: String = msgInfo.content
|
val orgContent: String = msgInfo.content
|
||||||
val deviceMark: String = SettingUtils.extraDeviceMark ?: ""
|
val deviceMark: String = SettingUtils.extraDeviceMark
|
||||||
val appVersion: String = AppUtils.getAppVersionName()
|
val appVersion: String = AppUtils.getAppVersionName()
|
||||||
val simInfo: String = msgInfo.simInfo
|
val simInfo: String = msgInfo.simInfo
|
||||||
@SuppressLint("SimpleDateFormat") val receiveTime =
|
@SuppressLint("SimpleDateFormat") val receiveTime =
|
||||||
SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(Date()) //smsVo.getDate()
|
SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(Date()) //smsVo.getDate()
|
||||||
|
|
||||||
var sign = ""
|
var sign = ""
|
||||||
if (!TextUtils.isEmpty(setting.secret)) {
|
if (!TextUtils.isEmpty(setting.secret)) {
|
||||||
val stringToSign = "$timestamp\n" + setting.secret
|
val stringToSign = "$timestamp\n" + setting.secret
|
||||||
val mac = Mac.getInstance("HmacSHA256")
|
val mac = Mac.getInstance("HmacSHA256")
|
||||||
mac.init(
|
mac.init(
|
||||||
SecretKeySpec(
|
SecretKeySpec(
|
||||||
setting.secret?.toByteArray(StandardCharsets.UTF_8),
|
setting.secret?.toByteArray(StandardCharsets.UTF_8),
|
||||||
"HmacSHA256"
|
"HmacSHA256"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
val signData = mac.doFinal(stringToSign.toByteArray(StandardCharsets.UTF_8))
|
val signData = mac.doFinal(stringToSign.toByteArray(StandardCharsets.UTF_8))
|
||||||
sign = URLEncoder.encode(String(Base64.encode(signData, Base64.NO_WRAP)), "UTF-8")
|
sign = URLEncoder.encode(String(Base64.encode(signData, Base64.NO_WRAP)), "UTF-8")
|
||||||
}
|
}
|
||||||
|
|
||||||
var webParams = setting.webParams?.trim()
|
var webParams = setting.webParams?.trim()
|
||||||
|
|
||||||
//支持HTTP基本认证(Basic Authentication)
|
//支持HTTP基本认证(Basic Authentication)
|
||||||
val regex = "^(https?://)([^:]+):([^@]+)@(.+)"
|
val regex = "^(https?://)([^:]+):([^@]+)@(.+)"
|
||||||
val matches = Regex(regex, RegexOption.IGNORE_CASE).findAll(requestUrl).toList()
|
val matches = Regex(regex, RegexOption.IGNORE_CASE).findAll(requestUrl).toList()
|
||||||
.flatMap(MatchResult::groupValues)
|
.flatMap(MatchResult::groupValues)
|
||||||
Log.i(TAG, "matches = $matches")
|
Log.i(TAG, "matches = $matches")
|
||||||
if (matches.isNotEmpty()) {
|
if (matches.isNotEmpty()) {
|
||||||
requestUrl = matches[1] + matches[4]
|
requestUrl = matches[1] + matches[4]
|
||||||
Log.i(TAG, "requestUrl:$requestUrl")
|
Log.i(TAG, "requestUrl:$requestUrl")
|
||||||
}
|
}
|
||||||
|
|
||||||
val request = if (setting.method == "GET" && TextUtils.isEmpty(webParams)) {
|
val request = if (setting.method == "GET" && TextUtils.isEmpty(webParams)) {
|
||||||
setting.webServer += (if (setting.webServer.contains("?")) "&" else "?") + "from=" + URLEncoder.encode(
|
setting.webServer += (if (setting.webServer.contains("?")) "&" else "?") + "from=" + URLEncoder.encode(
|
||||||
from,
|
from,
|
||||||
"UTF-8"
|
"UTF-8"
|
||||||
)
|
)
|
||||||
requestUrl += "&content=" + URLEncoder.encode(content, "UTF-8")
|
requestUrl += "&content=" + URLEncoder.encode(content, "UTF-8")
|
||||||
if (!TextUtils.isEmpty(sign)) {
|
if (!TextUtils.isEmpty(sign)) {
|
||||||
requestUrl += "×tamp=$timestamp"
|
requestUrl += "×tamp=$timestamp"
|
||||||
requestUrl += "&sign=$sign"
|
requestUrl += "&sign=$sign"
|
||||||
}
|
}
|
||||||
Log.d(TAG, "method = GET, Url = $requestUrl")
|
Log.d(TAG, "method = GET, Url = $requestUrl")
|
||||||
XHttp.get(requestUrl).keepJson(true)
|
XHttp.get(requestUrl).keepJson(true)
|
||||||
} else if (setting.method == "GET" && !TextUtils.isEmpty(webParams)) {
|
} else if (setting.method == "GET" && !TextUtils.isEmpty(webParams)) {
|
||||||
webParams = webParams.toString().replace("[from]", URLEncoder.encode(from, "UTF-8"))
|
webParams = webParams.toString().replace("[from]", URLEncoder.encode(from, "UTF-8"))
|
||||||
.replace("[content]", URLEncoder.encode(content, "UTF-8"))
|
.replace("[content]", URLEncoder.encode(content, "UTF-8"))
|
||||||
.replace("[msg]", URLEncoder.encode(content, "UTF-8"))
|
.replace("[msg]", URLEncoder.encode(content, "UTF-8"))
|
||||||
.replace("[org_content]", URLEncoder.encode(orgContent, "UTF-8"))
|
.replace("[org_content]", URLEncoder.encode(orgContent, "UTF-8"))
|
||||||
.replace("[device_mark]", URLEncoder.encode(deviceMark, "UTF-8"))
|
.replace("[device_mark]", URLEncoder.encode(deviceMark, "UTF-8"))
|
||||||
.replace("[app_version]", URLEncoder.encode(appVersion, "UTF-8"))
|
.replace("[app_version]", URLEncoder.encode(appVersion, "UTF-8"))
|
||||||
.replace("[title]", URLEncoder.encode(simInfo, "UTF-8"))
|
.replace("[title]", URLEncoder.encode(simInfo, "UTF-8"))
|
||||||
.replace("[card_slot]", URLEncoder.encode(simInfo, "UTF-8"))
|
.replace("[card_slot]", URLEncoder.encode(simInfo, "UTF-8"))
|
||||||
.replace("[receive_time]", URLEncoder.encode(receiveTime, "UTF-8"))
|
.replace("[receive_time]", URLEncoder.encode(receiveTime, "UTF-8"))
|
||||||
.replace("\n", "%0A")
|
.replace("\n", "%0A")
|
||||||
if (!TextUtils.isEmpty(setting.secret)) {
|
if (!TextUtils.isEmpty(setting.secret)) {
|
||||||
webParams = webParams.replace("[timestamp]", timestamp.toString())
|
webParams = webParams.replace("[timestamp]", timestamp.toString())
|
||||||
.replace("[sign]", URLEncoder.encode(sign, "UTF-8"))
|
.replace("[sign]", URLEncoder.encode(sign, "UTF-8"))
|
||||||
}
|
}
|
||||||
requestUrl += if (webParams.startsWith("/")) {
|
requestUrl += if (webParams.startsWith("/")) {
|
||||||
webParams
|
webParams
|
||||||
} else {
|
} else {
|
||||||
(if (requestUrl.contains("?")) "&" else "?") + webParams
|
(if (requestUrl.contains("?")) "&" else "?") + webParams
|
||||||
}
|
}
|
||||||
Log.d(TAG, "method = GET, Url = $requestUrl")
|
Log.d(TAG, "method = GET, Url = $requestUrl")
|
||||||
XHttp.get(requestUrl).keepJson(true)
|
XHttp.get(requestUrl).keepJson(true)
|
||||||
} else if (webParams != null && webParams.isNotEmpty() && webParams.startsWith("{")) {
|
} else if (webParams != null && webParams.isNotEmpty() && webParams.startsWith("{")) {
|
||||||
val bodyMsg = webParams.replace("[from]", from)
|
val bodyMsg = webParams.replace("[from]", from)
|
||||||
.replace("[content]", escapeJson(content))
|
.replace("[content]", escapeJson(content))
|
||||||
.replace("[msg]", escapeJson(content))
|
.replace("[msg]", escapeJson(content))
|
||||||
.replace("[org_content]", escapeJson(orgContent))
|
.replace("[org_content]", escapeJson(orgContent))
|
||||||
.replace("[device_mark]", escapeJson(deviceMark))
|
.replace("[device_mark]", escapeJson(deviceMark))
|
||||||
.replace("[app_version]", appVersion)
|
.replace("[app_version]", appVersion)
|
||||||
.replace("[title]", escapeJson(simInfo))
|
.replace("[title]", escapeJson(simInfo))
|
||||||
.replace("[card_slot]", escapeJson(simInfo))
|
.replace("[card_slot]", escapeJson(simInfo))
|
||||||
.replace("[receive_time]", receiveTime)
|
.replace("[receive_time]", receiveTime)
|
||||||
.replace("[timestamp]", timestamp.toString())
|
.replace("[timestamp]", timestamp.toString())
|
||||||
.replace("[sign]", sign)
|
.replace("[sign]", sign)
|
||||||
Log.d(TAG, "method = ${setting.method}, Url = $requestUrl, bodyMsg = $bodyMsg")
|
Log.d(TAG, "method = ${setting.method}, Url = $requestUrl, bodyMsg = $bodyMsg")
|
||||||
when (setting.method) {
|
when (setting.method) {
|
||||||
"PUT" -> XHttp.put(requestUrl).keepJson(true).upJson(bodyMsg)
|
"PUT" -> XHttp.put(requestUrl).keepJson(true).upJson(bodyMsg)
|
||||||
"PATCH" -> XHttp.patch(requestUrl).keepJson(true).upJson(bodyMsg)
|
"PATCH" -> XHttp.patch(requestUrl).keepJson(true).upJson(bodyMsg)
|
||||||
else -> XHttp.post(requestUrl).keepJson(true).upJson(bodyMsg)
|
else -> XHttp.post(requestUrl).keepJson(true).upJson(bodyMsg)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (webParams == null || webParams.isEmpty()) {
|
if (webParams == null || webParams.isEmpty()) {
|
||||||
webParams = "from=[from]&content=[content]×tamp=[timestamp]"
|
webParams = "from=[from]&content=[content]×tamp=[timestamp]"
|
||||||
if (!TextUtils.isEmpty(sign)) webParams += "&sign=[sign]"
|
if (!TextUtils.isEmpty(sign)) webParams += "&sign=[sign]"
|
||||||
}
|
}
|
||||||
Log.d(TAG, "method = ${setting.method}, Url = $requestUrl")
|
Log.d(TAG, "method = ${setting.method}, Url = $requestUrl")
|
||||||
val postRequest = when (setting.method) {
|
val postRequest = when (setting.method) {
|
||||||
"PUT" -> XHttp.put(requestUrl).keepJson(true)
|
"PUT" -> XHttp.put(requestUrl).keepJson(true)
|
||||||
"PATCH" -> XHttp.patch(requestUrl).keepJson(true)
|
"PATCH" -> XHttp.patch(requestUrl).keepJson(true)
|
||||||
else -> XHttp.post(requestUrl).keepJson(true)
|
else -> XHttp.post(requestUrl).keepJson(true)
|
||||||
}
|
}
|
||||||
webParams.trim('&').split("&").forEach {
|
webParams.trim('&').split("&").forEach {
|
||||||
val param = it.split("=")
|
val param = it.split("=")
|
||||||
if (param.size == 2) {
|
if (param.size == 2) {
|
||||||
postRequest.params(
|
postRequest.params(
|
||||||
param[0], param[1].replace("[from]", from)
|
param[0], param[1].replace("[from]", from)
|
||||||
.replace("[content]", content)
|
.replace("[content]", content)
|
||||||
.replace("[msg]", content)
|
.replace("[msg]", content)
|
||||||
.replace("[org_content]", orgContent)
|
.replace("[org_content]", orgContent)
|
||||||
.replace("[device_mark]", deviceMark)
|
.replace("[device_mark]", deviceMark)
|
||||||
.replace("[app_version]", appVersion)
|
.replace("[app_version]", appVersion)
|
||||||
.replace("[title]", simInfo)
|
.replace("[title]", simInfo)
|
||||||
.replace("[card_slot]", simInfo)
|
.replace("[card_slot]", simInfo)
|
||||||
.replace("[receive_time]", receiveTime)
|
.replace("[receive_time]", receiveTime)
|
||||||
.replace("[timestamp]", timestamp.toString())
|
.replace("[timestamp]", timestamp.toString())
|
||||||
.replace("[sign]", sign)
|
.replace("[sign]", sign)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
postRequest
|
postRequest
|
||||||
}
|
}
|
||||||
|
|
||||||
//添加headers
|
//添加headers
|
||||||
for ((key, value) in setting.headers?.entries!!) {
|
for ((key, value) in setting.headers?.entries!!) {
|
||||||
request.headers(key, value)
|
request.headers(key, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
//支持HTTP基本认证(Basic Authentication)
|
//支持HTTP基本认证(Basic Authentication)
|
||||||
if (matches.isNotEmpty()) {
|
if (matches.isNotEmpty()) {
|
||||||
request.addInterceptor(BasicAuthInterceptor(matches[2], matches[3]))
|
request.addInterceptor(BasicAuthInterceptor(matches[2], matches[3]))
|
||||||
}
|
}
|
||||||
|
|
||||||
request.ignoreHttpsCert() //忽略https证书
|
request.ignoreHttpsCert() //忽略https证书
|
||||||
.timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
|
.timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
|
||||||
.cacheMode(CacheMode.NO_CACHE)
|
.cacheMode(CacheMode.NO_CACHE)
|
||||||
.retryCount(SettingUtils.requestRetryTimes) //超时重试的次数
|
.retryCount(SettingUtils.requestRetryTimes) //超时重试的次数
|
||||||
.retryDelay(SettingUtils.requestDelayTime) //超时重试的延迟时间
|
.retryDelay(SettingUtils.requestDelayTime) //超时重试的延迟时间
|
||||||
.retryIncreaseDelay(SettingUtils.requestDelayTime) //超时重试叠加延时
|
.retryIncreaseDelay(SettingUtils.requestDelayTime) //超时重试叠加延时
|
||||||
.timeStamp(true)
|
.timeStamp(true)
|
||||||
.execute(object : SimpleCallBack<String>() {
|
.execute(object : SimpleCallBack<String>() {
|
||||||
|
|
||||||
override fun onError(e: ApiException) {
|
override fun onError(e: ApiException) {
|
||||||
Log.e(TAG, e.detailMessage)
|
Log.e(TAG, e.detailMessage)
|
||||||
SendUtils.updateLogs(logId, 0, e.displayMessage)
|
SendUtils.updateLogs(logId, 0, e.displayMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSuccess(response: String) {
|
override fun onSuccess(response: String) {
|
||||||
Log.i(TAG, response)
|
Log.i(TAG, response)
|
||||||
SendUtils.updateLogs(logId, 2, response)
|
SendUtils.updateLogs(logId, 2, response)
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//JSON需要转义的字符
|
//JSON需要转义的字符
|
||||||
private fun escapeJson(str: String?): String {
|
private fun escapeJson(str: String?): String {
|
||||||
if (str == null) return "null"
|
if (str == null) return "null"
|
||||||
val jsonStr: String = Gson().toJson(str)
|
val jsonStr: String = Gson().toJson(str)
|
||||||
return if (jsonStr.length >= 2) jsonStr.substring(1, jsonStr.length - 1) else jsonStr
|
return if (jsonStr.length >= 2) jsonStr.substring(1, jsonStr.length - 1) else jsonStr
|
||||||
}
|
}
|
||||||
|
|
||||||
fun sendMsg(setting: WebhookSetting, msgInfo: MsgInfo) {
|
fun sendMsg(setting: WebhookSetting, msgInfo: MsgInfo) {
|
||||||
sendMsg(setting, msgInfo, null, null)
|
sendMsg(setting, msgInfo, null, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -120,7 +120,7 @@ class WeworkAgentUtils private constructor() {
|
|||||||
val content: String = if (rule != null) {
|
val content: String = if (rule != null) {
|
||||||
msgInfo.getContentForSend(rule.smsTemplate, rule.regexReplace)
|
msgInfo.getContentForSend(rule.smsTemplate, rule.regexReplace)
|
||||||
} else {
|
} else {
|
||||||
msgInfo.getContentForSend(SettingUtils.smsTemplate.toString())
|
msgInfo.getContentForSend(SettingUtils.smsTemplate)
|
||||||
}
|
}
|
||||||
|
|
||||||
val textMsgMap: MutableMap<String, Any> = mutableMapOf()
|
val textMsgMap: MutableMap<String, Any> = mutableMapOf()
|
||||||
@ -132,7 +132,7 @@ class WeworkAgentUtils private constructor() {
|
|||||||
val textText: MutableMap<String, Any> = mutableMapOf()
|
val textText: MutableMap<String, Any> = mutableMapOf()
|
||||||
textText["content"] = content
|
textText["content"] = content
|
||||||
textMsgMap["text"] = textText
|
textMsgMap["text"] = textText
|
||||||
var accessToken: String by SharedPreference("access_token_" + setting.agentID, "")
|
val accessToken: String by SharedPreference("access_token_" + setting.agentID, "")
|
||||||
val requestUrl = "https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=$accessToken"
|
val requestUrl = "https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=$accessToken"
|
||||||
Log.i(TAG, "requestUrl:$requestUrl")
|
Log.i(TAG, "requestUrl:$requestUrl")
|
||||||
val requestMsg: String = Gson().toJson(textMsgMap)
|
val requestMsg: String = Gson().toJson(textMsgMap)
|
||||||
|
@ -28,7 +28,7 @@ class WeworkRobotUtils private constructor() {
|
|||||||
val content: String = if (rule != null) {
|
val content: String = if (rule != null) {
|
||||||
msgInfo.getContentForSend(rule.smsTemplate, rule.regexReplace)
|
msgInfo.getContentForSend(rule.smsTemplate, rule.regexReplace)
|
||||||
} else {
|
} else {
|
||||||
msgInfo.getContentForSend(SettingUtils.smsTemplate.toString())
|
msgInfo.getContentForSend(SettingUtils.smsTemplate)
|
||||||
}
|
}
|
||||||
|
|
||||||
val requestUrl = setting.webHook
|
val requestUrl = setting.webHook
|
||||||
|
@ -340,7 +340,7 @@
|
|||||||
<string name="sim2_remark" tools:ignore="Typos">SIM2 SubId/Label</string>
|
<string name="sim2_remark" tools:ignore="Typos">SIM2 SubId/Label</string>
|
||||||
<string name="carrier_mobile" tools:ignore="Typos">Label of SIM,\neg. AT&T_88888888</string>
|
<string name="carrier_mobile" tools:ignore="Typos">Label of SIM,\neg. AT&T_88888888</string>
|
||||||
<string name="tip_number_only_error_message">Number must be greater than 0!</string>
|
<string name="tip_number_only_error_message">Number must be greater than 0!</string>
|
||||||
<string name="regexp_number_only" tools:ignore="Typos">^[1-9]?\\d+$</string>
|
<string name="regexp_number_only" tools:ignore="TypographyDashes,Typos">^[1-9]?\\d+$</string>
|
||||||
<string name="low_power_alarm_threshold">Low Power Alarm</string>
|
<string name="low_power_alarm_threshold">Low Power Alarm</string>
|
||||||
<string name="low_power_alarm_threshold_tips">Value range: 0–99.\nLeft blank or 0 is disabled</string>
|
<string name="low_power_alarm_threshold_tips">Value range: 0–99.\nLeft blank or 0 is disabled</string>
|
||||||
<string name="retry_interval">Retry Interval</string>
|
<string name="retry_interval">Retry Interval</string>
|
||||||
|
@ -341,7 +341,7 @@
|
|||||||
<string name="sim2_remark" tools:ignore="Typos">SIM2主键/备注</string>
|
<string name="sim2_remark" tools:ignore="Typos">SIM2主键/备注</string>
|
||||||
<string name="carrier_mobile">序号/运营商_手机号</string>
|
<string name="carrier_mobile">序号/运营商_手机号</string>
|
||||||
<string name="tip_number_only_error_message">数字必须大于0!</string>
|
<string name="tip_number_only_error_message">数字必须大于0!</string>
|
||||||
<string name="regexp_number_only" tools:ignore="Typos">^[1-9]?\\d+$</string>
|
<string name="regexp_number_only" tools:ignore="TypographyDashes,Typos">^[1-9]?\\d+$</string>
|
||||||
<string name="low_power_alarm_threshold">安全电量范围(%)</string>
|
<string name="low_power_alarm_threshold">安全电量范围(%)</string>
|
||||||
<string name="low_power_alarm_threshold_tips">超出安全范围将发出预警</string>
|
<string name="low_power_alarm_threshold_tips">超出安全范围将发出预警</string>
|
||||||
<string name="retry_interval">请求重试机制</string>
|
<string name="retry_interval">请求重试机制</string>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user