新增:Socket发送通道(支持MQTT/TCP/UDP协议) #252
This commit is contained in:
parent
419766b47a
commit
565795a843
|
@ -188,6 +188,9 @@ dependencies {
|
||||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||||
//frpc
|
//frpc
|
||||||
implementation files('libs/frpclib.aar')
|
implementation files('libs/frpclib.aar')
|
||||||
|
//kmnkt是基于Kotlin Multiplatform的跨平台socket通信统一接口。支持Android目标与JVM目标,支持UDP/TCP/MQTT协议使用同一套接口实现
|
||||||
|
implementation("org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.2.5")
|
||||||
|
implementation files('libs/socket.aar')
|
||||||
|
|
||||||
testImplementation deps.junit
|
testImplementation deps.junit
|
||||||
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
|
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
|
||||||
|
|
Binary file not shown.
|
@ -282,6 +282,8 @@
|
||||||
-keep class javax.activation.** { *;}
|
-keep class javax.activation.** { *;}
|
||||||
-keep class com.smailnet.emailkit.** { *;}
|
-keep class com.smailnet.emailkit.** { *;}
|
||||||
-keep class com.idormy.sms.forwarder.utils.mail.** {*;}
|
-keep class com.idormy.sms.forwarder.utils.mail.** {*;}
|
||||||
|
-keep class com.gitee.xuankaicat.kmnkt.** {*;}
|
||||||
|
-keep class org.eclipse.paho.client.** {*;}
|
||||||
|
|
||||||
-keep public class com.xuexiang.xrouter.routes.**{*;}
|
-keep public class com.xuexiang.xrouter.routes.**{*;}
|
||||||
-keep class * implements com.xuexiang.xrouter.facade.template.ISyringe{*;}
|
-keep class * implements com.xuexiang.xrouter.facade.template.ISyringe{*;}
|
||||||
|
|
|
@ -54,6 +54,7 @@ data class LogsDetail(
|
||||||
TYPE_DINGTALK_INNER_ROBOT -> R.drawable.icon_dingtalk_inner
|
TYPE_DINGTALK_INNER_ROBOT -> R.drawable.icon_dingtalk_inner
|
||||||
TYPE_FEISHU_APP -> R.drawable.icon_feishu_app
|
TYPE_FEISHU_APP -> R.drawable.icon_feishu_app
|
||||||
TYPE_URL_SCHEME -> R.drawable.icon_url_scheme
|
TYPE_URL_SCHEME -> R.drawable.icon_url_scheme
|
||||||
|
TYPE_SOCKET -> R.drawable.icon_socket
|
||||||
else -> R.drawable.icon_sms
|
else -> R.drawable.icon_sms
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,7 @@ data class Sender(
|
||||||
TYPE_DINGTALK_INNER_ROBOT -> R.drawable.icon_dingtalk_inner
|
TYPE_DINGTALK_INNER_ROBOT -> R.drawable.icon_dingtalk_inner
|
||||||
TYPE_FEISHU_APP -> R.drawable.icon_feishu_app
|
TYPE_FEISHU_APP -> R.drawable.icon_feishu_app
|
||||||
TYPE_URL_SCHEME -> R.drawable.icon_url_scheme
|
TYPE_URL_SCHEME -> R.drawable.icon_url_scheme
|
||||||
|
TYPE_SOCKET -> R.drawable.icon_socket
|
||||||
else -> R.drawable.icon_sms
|
else -> R.drawable.icon_sms
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,6 +61,7 @@ data class Sender(
|
||||||
TYPE_DINGTALK_INNER_ROBOT -> R.drawable.icon_dingtalk_inner
|
TYPE_DINGTALK_INNER_ROBOT -> R.drawable.icon_dingtalk_inner
|
||||||
TYPE_FEISHU_APP -> R.drawable.icon_feishu_app
|
TYPE_FEISHU_APP -> R.drawable.icon_feishu_app
|
||||||
TYPE_URL_SCHEME -> R.drawable.icon_url_scheme
|
TYPE_URL_SCHEME -> R.drawable.icon_url_scheme
|
||||||
|
TYPE_SOCKET -> R.drawable.icon_socket
|
||||||
else -> R.drawable.icon_sms
|
else -> R.drawable.icon_sms
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ class ConvertersSenderList {
|
||||||
|
|
||||||
@TypeConverter
|
@TypeConverter
|
||||||
fun stringToObject(value: String): List<Sender> {
|
fun stringToObject(value: String): List<Sender> {
|
||||||
var senderList: MutableList<Sender> = mutableListOf()
|
val senderList: MutableList<Sender> = mutableListOf()
|
||||||
value.split(",").map { it.trim() }.forEach {
|
value.split(",").map { it.trim() }.forEach {
|
||||||
val sender = Core.sender.getOne(it.toLong())
|
val sender = Core.sender.getOne(it.toLong())
|
||||||
senderList.add(sender)
|
senderList.add(sender)
|
||||||
|
@ -20,7 +20,7 @@ class ConvertersSenderList {
|
||||||
|
|
||||||
@TypeConverter
|
@TypeConverter
|
||||||
fun objectToString(list: List<Sender>): String {
|
fun objectToString(list: List<Sender>): String {
|
||||||
var senderList = ArrayList<Long>()
|
val senderList = ArrayList<Long>()
|
||||||
list.forEach {
|
list.forEach {
|
||||||
senderList += it.id
|
senderList += it.id
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
package com.idormy.sms.forwarder.entity.setting
|
||||||
|
|
||||||
|
import com.idormy.sms.forwarder.R
|
||||||
|
import java.io.Serializable
|
||||||
|
|
||||||
|
data class SocketSetting(
|
||||||
|
val method: String? = "MQTT",
|
||||||
|
var address: String = "", //IP地址
|
||||||
|
val port: Int = 0, //端口号
|
||||||
|
val msgTemplate: String = "", //消息模板
|
||||||
|
val secret: String? = "", //签名密钥
|
||||||
|
val username: String = "", //用户名
|
||||||
|
val password: String = "", //密码
|
||||||
|
val inCharset: String = "", //输入编码
|
||||||
|
val outCharset: String = "", //输出编码
|
||||||
|
val inMessageTopic: String = "", //Mqtt专属,输入信息响应主题,即接收对应主题的消息
|
||||||
|
val outMessageTopic: String = "", //Mqtt专属,输出信息响应主题,即发送对应主题的消息
|
||||||
|
val uriType: String = "tcp", //Mqtt专属,通信方式 默认为tcp
|
||||||
|
val path: String = "", //Mqtt专属,通信路径,用于在使用ws进行通信时设置uri,最后的访问结果为"${uriType}://${address}:${port}${path}"
|
||||||
|
val clientId: String = "", //Mqtt专属,客户端ID,如果为空则为随机值
|
||||||
|
) : Serializable {
|
||||||
|
|
||||||
|
fun getMethodCheckId(): Int {
|
||||||
|
return when (method) {
|
||||||
|
null, "MQTT" -> R.id.rb_method_mqtt
|
||||||
|
"TCP" -> R.id.rb_method_tcp
|
||||||
|
"UDP" -> R.id.rb_method_udp
|
||||||
|
else -> R.id.rb_method_mqtt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -110,6 +110,7 @@ class SendersFragment : BaseFragment<FragmentSendersBinding?>(), SenderPagingAda
|
||||||
TYPE_DINGTALK_INNER_ROBOT -> DingtalkInnerRobotFragment::class.java
|
TYPE_DINGTALK_INNER_ROBOT -> DingtalkInnerRobotFragment::class.java
|
||||||
TYPE_FEISHU_APP -> FeishuAppFragment::class.java
|
TYPE_FEISHU_APP -> FeishuAppFragment::class.java
|
||||||
TYPE_URL_SCHEME -> UrlSchemeFragment::class.java
|
TYPE_URL_SCHEME -> UrlSchemeFragment::class.java
|
||||||
|
TYPE_SOCKET -> SocketFragment::class.java
|
||||||
else -> DingtalkGroupRobotFragment::class.java
|
else -> DingtalkGroupRobotFragment::class.java
|
||||||
}
|
}
|
||||||
).setNewActivity(true)
|
).setNewActivity(true)
|
||||||
|
@ -136,6 +137,7 @@ class SendersFragment : BaseFragment<FragmentSendersBinding?>(), SenderPagingAda
|
||||||
TYPE_DINGTALK_INNER_ROBOT -> DingtalkInnerRobotFragment::class.java
|
TYPE_DINGTALK_INNER_ROBOT -> DingtalkInnerRobotFragment::class.java
|
||||||
TYPE_FEISHU_APP -> FeishuAppFragment::class.java
|
TYPE_FEISHU_APP -> FeishuAppFragment::class.java
|
||||||
TYPE_URL_SCHEME -> UrlSchemeFragment::class.java
|
TYPE_URL_SCHEME -> UrlSchemeFragment::class.java
|
||||||
|
TYPE_SOCKET -> SocketFragment::class.java
|
||||||
else -> DingtalkGroupRobotFragment::class.java
|
else -> DingtalkGroupRobotFragment::class.java
|
||||||
}
|
}
|
||||||
).setNewActivity(true)
|
).setNewActivity(true)
|
||||||
|
|
|
@ -0,0 +1,252 @@
|
||||||
|
package com.idormy.sms.forwarder.fragment.senders
|
||||||
|
|
||||||
|
import android.os.Looper
|
||||||
|
import android.text.TextUtils
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.RadioGroup
|
||||||
|
import androidx.fragment.app.viewModels
|
||||||
|
import com.google.gson.Gson
|
||||||
|
import com.idormy.sms.forwarder.R
|
||||||
|
import com.idormy.sms.forwarder.core.BaseFragment
|
||||||
|
import com.idormy.sms.forwarder.database.AppDatabase
|
||||||
|
import com.idormy.sms.forwarder.database.entity.Sender
|
||||||
|
import com.idormy.sms.forwarder.database.viewmodel.BaseViewModelFactory
|
||||||
|
import com.idormy.sms.forwarder.database.viewmodel.SenderViewModel
|
||||||
|
import com.idormy.sms.forwarder.databinding.FragmentSendersSocketBinding
|
||||||
|
import com.idormy.sms.forwarder.entity.MsgInfo
|
||||||
|
import com.idormy.sms.forwarder.entity.setting.SocketSetting
|
||||||
|
import com.idormy.sms.forwarder.utils.*
|
||||||
|
import com.idormy.sms.forwarder.utils.sender.SocketUtils
|
||||||
|
import com.jeremyliao.liveeventbus.LiveEventBus
|
||||||
|
import com.xuexiang.xaop.annotation.SingleClick
|
||||||
|
import com.xuexiang.xpage.annotation.Page
|
||||||
|
import com.xuexiang.xrouter.annotation.AutoWired
|
||||||
|
import com.xuexiang.xrouter.launcher.XRouter
|
||||||
|
import com.xuexiang.xui.utils.CountDownButtonHelper
|
||||||
|
import com.xuexiang.xui.widget.actionbar.TitleBar
|
||||||
|
import com.xuexiang.xui.widget.dialog.materialdialog.DialogAction
|
||||||
|
import com.xuexiang.xui.widget.dialog.materialdialog.MaterialDialog
|
||||||
|
import io.reactivex.SingleObserver
|
||||||
|
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||||
|
import io.reactivex.disposables.Disposable
|
||||||
|
import io.reactivex.schedulers.Schedulers
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
@Page(name = "Socket")
|
||||||
|
@Suppress("PrivatePropertyName")
|
||||||
|
class SocketFragment : BaseFragment<FragmentSendersSocketBinding?>(), View.OnClickListener {
|
||||||
|
|
||||||
|
private val TAG: String = SocketFragment::class.java.simpleName
|
||||||
|
var titleBar: TitleBar? = null
|
||||||
|
private val viewModel by viewModels<SenderViewModel> { BaseViewModelFactory(context) }
|
||||||
|
private var mCountDownHelper: CountDownButtonHelper? = null
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
@AutoWired(name = KEY_SENDER_ID)
|
||||||
|
var senderId: Long = 0
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
@AutoWired(name = KEY_SENDER_TYPE)
|
||||||
|
var senderType: Int = 0
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
@AutoWired(name = KEY_SENDER_CLONE)
|
||||||
|
var isClone: Boolean = false
|
||||||
|
|
||||||
|
override fun initArgs() {
|
||||||
|
XRouter.getInstance().inject(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun viewBindingInflate(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup,
|
||||||
|
): FragmentSendersSocketBinding {
|
||||||
|
return FragmentSendersSocketBinding.inflate(inflater, container, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun initTitle(): TitleBar? {
|
||||||
|
titleBar = super.initTitle()!!.setImmersive(false).setTitle(R.string.socket)
|
||||||
|
return titleBar
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化控件
|
||||||
|
*/
|
||||||
|
override fun initViews() {
|
||||||
|
//测试按钮增加倒计时,避免重复点击
|
||||||
|
mCountDownHelper = CountDownButtonHelper(binding!!.btnTest, SettingUtils.requestTimeout)
|
||||||
|
mCountDownHelper!!.setOnCountDownListener(object : CountDownButtonHelper.OnCountDownListener {
|
||||||
|
override fun onCountDown(time: Int) {
|
||||||
|
binding!!.btnTest.text = String.format(getString(R.string.seconds_n), time)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFinished() {
|
||||||
|
binding!!.btnTest.text = getString(R.string.test)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
//新增
|
||||||
|
if (senderId <= 0) {
|
||||||
|
titleBar?.setSubTitle(getString(R.string.add_sender))
|
||||||
|
binding!!.btnDel.setText(R.string.discard)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//编辑
|
||||||
|
binding!!.btnDel.setText(R.string.del)
|
||||||
|
AppDatabase.getInstance(requireContext()).senderDao().get(senderId).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(object : SingleObserver<Sender> {
|
||||||
|
override fun onSubscribe(d: Disposable) {}
|
||||||
|
|
||||||
|
override fun onError(e: Throwable) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSuccess(sender: Sender) {
|
||||||
|
if (isClone) {
|
||||||
|
titleBar?.setSubTitle(getString(R.string.clone_sender) + ": " + sender.name)
|
||||||
|
binding!!.btnDel.setText(R.string.discard)
|
||||||
|
} else {
|
||||||
|
titleBar?.setSubTitle(getString(R.string.edit_sender) + ": " + sender.name)
|
||||||
|
}
|
||||||
|
binding!!.etName.setText(sender.name)
|
||||||
|
binding!!.sbEnable.isChecked = sender.status == 1
|
||||||
|
val settingVo = Gson().fromJson(sender.jsonSetting, SocketSetting::class.java)
|
||||||
|
Log.d(TAG, settingVo.toString())
|
||||||
|
if (settingVo != null) {
|
||||||
|
val checkedId = settingVo.getMethodCheckId()
|
||||||
|
binding!!.rgMethod.check(checkedId)
|
||||||
|
binding!!.etAddress.setText(settingVo.address)
|
||||||
|
binding!!.etPort.setText(settingVo.port.toString())
|
||||||
|
binding!!.etMsgTemplate.setText(settingVo.msgTemplate)
|
||||||
|
binding!!.etSecret.setText(settingVo.secret)
|
||||||
|
binding!!.etUsername.setText(settingVo.username)
|
||||||
|
binding!!.etPassword.setText(settingVo.password)
|
||||||
|
binding!!.etInCharset.setSelectedItem(settingVo.inCharset)
|
||||||
|
binding!!.etOutCharset.setSelectedItem(settingVo.outCharset)
|
||||||
|
binding!!.etInMessageTopic.setText(settingVo.inMessageTopic)
|
||||||
|
binding!!.etOutMessageTopic.setText(settingVo.outMessageTopic)
|
||||||
|
binding!!.etUriType.setText(settingVo.uriType)
|
||||||
|
binding!!.etPath.setText(settingVo.path)
|
||||||
|
binding!!.etClientId.setText(settingVo.clientId)
|
||||||
|
binding!!.layoutMqtt.visibility = if (checkedId == R.id.rb_method_mqtt) View.VISIBLE else View.GONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun initListeners() {
|
||||||
|
binding!!.btnTest.setOnClickListener(this)
|
||||||
|
binding!!.btnDel.setOnClickListener(this)
|
||||||
|
binding!!.btnSave.setOnClickListener(this)
|
||||||
|
binding!!.rgMethod.setOnCheckedChangeListener { _: RadioGroup?, checkedId: Int ->
|
||||||
|
binding!!.layoutMqtt.visibility = if (checkedId == R.id.rb_method_mqtt) View.VISIBLE else View.GONE
|
||||||
|
}
|
||||||
|
LiveEventBus.get(KEY_SENDER_TEST, String::class.java).observe(this) { mCountDownHelper?.finish() }
|
||||||
|
}
|
||||||
|
|
||||||
|
@SingleClick
|
||||||
|
override fun onClick(v: View) {
|
||||||
|
try {
|
||||||
|
when (v.id) {
|
||||||
|
R.id.btn_test -> {
|
||||||
|
mCountDownHelper?.start()
|
||||||
|
Thread {
|
||||||
|
try {
|
||||||
|
val settingVo = checkSetting()
|
||||||
|
Log.d(TAG, settingVo.toString())
|
||||||
|
val msgInfo = MsgInfo(
|
||||||
|
"sms", getString(R.string.test_phone_num), getString(R.string.test_sender_sms), Date(), getString(R.string.test_sim_info)
|
||||||
|
)
|
||||||
|
SocketUtils.sendMsg(settingVo, msgInfo)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
if (Looper.myLooper() == null) Looper.prepare()
|
||||||
|
XToastUtils.error(e.message.toString())
|
||||||
|
Looper.loop()
|
||||||
|
}
|
||||||
|
LiveEventBus.get(KEY_SENDER_TEST, String::class.java).post("finish")
|
||||||
|
}.start()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
R.id.btn_del -> {
|
||||||
|
if (senderId <= 0 || isClone) {
|
||||||
|
popToBack()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialDialog.Builder(requireContext()).title(R.string.delete_sender_title).content(R.string.delete_sender_tips).positiveText(R.string.lab_yes).negativeText(R.string.lab_no).onPositive { _: MaterialDialog?, _: DialogAction? ->
|
||||||
|
viewModel.delete(senderId)
|
||||||
|
XToastUtils.success(R.string.delete_sender_toast)
|
||||||
|
popToBack()
|
||||||
|
}.show()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
R.id.btn_save -> {
|
||||||
|
val name = binding!!.etName.text.toString().trim()
|
||||||
|
if (TextUtils.isEmpty(name)) {
|
||||||
|
throw Exception(getString(R.string.invalid_name))
|
||||||
|
}
|
||||||
|
|
||||||
|
val status = if (binding!!.sbEnable.isChecked) 1 else 0
|
||||||
|
val settingVo = checkSetting()
|
||||||
|
if (isClone) senderId = 0
|
||||||
|
val senderNew = Sender(senderId, senderType, name, Gson().toJson(settingVo), status)
|
||||||
|
Log.d(TAG, senderNew.toString())
|
||||||
|
|
||||||
|
viewModel.insertOrUpdate(senderNew)
|
||||||
|
XToastUtils.success(R.string.tipSaveSuccess)
|
||||||
|
popToBack()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
XToastUtils.error(e.message.toString())
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkSetting(): SocketSetting {
|
||||||
|
val address = binding!!.etAddress.text.toString().trim()
|
||||||
|
if (CommonUtils.checkIP(address) == "Neither" && !CommonUtils.checkDomain(address)) {
|
||||||
|
throw Exception(getString(R.string.invalid_ip))
|
||||||
|
}
|
||||||
|
|
||||||
|
val port = binding!!.etPort.text.toString().trim()
|
||||||
|
if (!CommonUtils.checkPort(port)) {
|
||||||
|
throw Exception(getString(R.string.invalid_port))
|
||||||
|
}
|
||||||
|
|
||||||
|
val method = when (binding!!.rgMethod.checkedRadioButtonId) {
|
||||||
|
R.id.rb_method_tcp -> "TCP"
|
||||||
|
R.id.rb_method_udp -> "UDP"
|
||||||
|
else -> "MQTT"
|
||||||
|
}
|
||||||
|
|
||||||
|
val msgTemplate = binding!!.etMsgTemplate.text.toString().trim()
|
||||||
|
val secret = binding!!.etSecret.text.toString().trim()
|
||||||
|
val username = binding!!.etUsername.text.toString().trim()
|
||||||
|
val password = binding!!.etPassword.text.toString().trim()
|
||||||
|
val inCharset = binding!!.etInCharset.text.toString().trim()
|
||||||
|
val outCharset = binding!!.etOutCharset.text.toString().trim()
|
||||||
|
val inMessageTopic = binding!!.etInMessageTopic.text.toString().trim()
|
||||||
|
val outMessageTopic = binding!!.etOutMessageTopic.text.toString().trim()
|
||||||
|
val uriType = binding!!.etUriType.text.toString().trim()
|
||||||
|
val path = binding!!.etPath.text.toString().trim()
|
||||||
|
val clientId = binding!!.etClientId.text.toString().trim()
|
||||||
|
|
||||||
|
if (method == "MQTT" && (TextUtils.isEmpty(inMessageTopic) || TextUtils.isEmpty(outMessageTopic))) {
|
||||||
|
throw Exception(getString(R.string.invalid_mqtt_message_topic))
|
||||||
|
}
|
||||||
|
|
||||||
|
return SocketSetting(method, address, port.toInt(), msgTemplate, secret, username, password, inCharset, outCharset, inMessageTopic, outMessageTopic, uriType, path, clientId)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
if (mCountDownHelper != null) mCountDownHelper!!.recycle()
|
||||||
|
super.onDestroyView()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -60,49 +60,34 @@ class CommonUtils private constructor() {
|
||||||
@Suppress("SameParameterValue", "NAME_SHADOWING")
|
@Suppress("SameParameterValue", "NAME_SHADOWING")
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun showPrivacyDialog(context: Context, submitListener: SingleButtonCallback?): Dialog {
|
fun showPrivacyDialog(context: Context, submitListener: SingleButtonCallback?): Dialog {
|
||||||
val dialog =
|
val dialog = MaterialDialog.Builder(context).title(R.string.title_reminder).autoDismiss(false).cancelable(false).positiveText(R.string.lab_agree).onPositive { dialog1: MaterialDialog, which: DialogAction? ->
|
||||||
MaterialDialog.Builder(context).title(R.string.title_reminder).autoDismiss(false)
|
if (submitListener != null) {
|
||||||
.cancelable(false)
|
submitListener.onClick(dialog1, which!!)
|
||||||
.positiveText(R.string.lab_agree)
|
} else {
|
||||||
.onPositive { dialog1: MaterialDialog, which: DialogAction? ->
|
dialog1.dismiss()
|
||||||
if (submitListener != null) {
|
}
|
||||||
submitListener.onClick(dialog1, which!!)
|
}.negativeText(R.string.lab_disagree).onNegative { dialog, _ ->
|
||||||
} else {
|
dialog.dismiss()
|
||||||
dialog1.dismiss()
|
DialogLoader.getInstance().showConfirmDialog(
|
||||||
}
|
context, ResUtils.getString(R.string.title_reminder), String.format(
|
||||||
}
|
ResUtils.getString(R.string.content_privacy_explain_again), ResUtils.getString(R.string.app_name)
|
||||||
.negativeText(R.string.lab_disagree).onNegative { dialog, _ ->
|
), ResUtils.getString(R.string.lab_look_again), { dialog, _ ->
|
||||||
dialog.dismiss()
|
dialog.dismiss()
|
||||||
DialogLoader.getInstance().showConfirmDialog(
|
showPrivacyDialog(context, submitListener)
|
||||||
context,
|
}, ResUtils.getString(R.string.lab_still_disagree)
|
||||||
ResUtils.getString(R.string.title_reminder),
|
) { dialog, _ ->
|
||||||
String.format(
|
dialog.dismiss()
|
||||||
ResUtils.getString(R.string.content_privacy_explain_again),
|
DialogLoader.getInstance().showConfirmDialog(
|
||||||
ResUtils.getString(R.string.app_name)
|
context, ResUtils.getString(R.string.content_think_about_it_again), ResUtils.getString(R.string.lab_look_again), { dialog, _ ->
|
||||||
),
|
|
||||||
ResUtils.getString(R.string.lab_look_again),
|
|
||||||
{ dialog, _ ->
|
|
||||||
dialog.dismiss()
|
|
||||||
showPrivacyDialog(context, submitListener)
|
|
||||||
},
|
|
||||||
ResUtils.getString(R.string.lab_still_disagree)
|
|
||||||
) { dialog, _ ->
|
|
||||||
dialog.dismiss()
|
dialog.dismiss()
|
||||||
DialogLoader.getInstance().showConfirmDialog(
|
showPrivacyDialog(context, submitListener)
|
||||||
context,
|
}, ResUtils.getString(R.string.lab_exit_app)
|
||||||
ResUtils.getString(R.string.content_think_about_it_again),
|
) { dialog, _ ->
|
||||||
ResUtils.getString(R.string.lab_look_again),
|
dialog.dismiss()
|
||||||
{ dialog, _ ->
|
XUtil.exitApp()
|
||||||
dialog.dismiss()
|
}
|
||||||
showPrivacyDialog(context, submitListener)
|
}
|
||||||
},
|
}.build()
|
||||||
ResUtils.getString(R.string.lab_exit_app)
|
|
||||||
) { dialog, _ ->
|
|
||||||
dialog.dismiss()
|
|
||||||
XUtil.exitApp()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}.build()
|
|
||||||
dialog.setContent(getPrivacyContent(context))
|
dialog.setContent(getPrivacyContent(context))
|
||||||
//开始响应点击事件
|
//开始响应点击事件
|
||||||
dialog.contentView!!.movementMethod = LinkMovementMethod.getInstance()
|
dialog.contentView!!.movementMethod = LinkMovementMethod.getInstance()
|
||||||
|
@ -114,15 +99,7 @@ class CommonUtils private constructor() {
|
||||||
* @return 隐私政策说明
|
* @return 隐私政策说明
|
||||||
*/
|
*/
|
||||||
private fun getPrivacyContent(context: Context): SpannableStringBuilder {
|
private fun getPrivacyContent(context: Context): SpannableStringBuilder {
|
||||||
return SpannableStringBuilder()
|
return SpannableStringBuilder().append(" ").append(ResUtils.getString(R.string.privacy_content_1)).append(" ").append(ResUtils.getString(R.string.app_name)).append("!\n").append(" ").append(ResUtils.getString(R.string.privacy_content_2)).append(" ").append(ResUtils.getString(R.string.privacy_content_3)).append(getPrivacyLink(context, PRIVACY_URL)).append(ResUtils.getString(R.string.privacy_content_4)).append(" ").append(ResUtils.getString(R.string.privacy_content_5)).append(getPrivacyLink(context, PRIVACY_URL)).append(ResUtils.getString(R.string.privacy_content_6))
|
||||||
.append(" ").append(ResUtils.getString(R.string.privacy_content_1)).append(" ").append(ResUtils.getString(R.string.app_name)).append("!\n")
|
|
||||||
.append(" ").append(ResUtils.getString(R.string.privacy_content_2))
|
|
||||||
.append(" ").append(ResUtils.getString(R.string.privacy_content_3))
|
|
||||||
.append(getPrivacyLink(context, PRIVACY_URL))
|
|
||||||
.append(ResUtils.getString(R.string.privacy_content_4))
|
|
||||||
.append(" ").append(ResUtils.getString(R.string.privacy_content_5))
|
|
||||||
.append(getPrivacyLink(context, PRIVACY_URL))
|
|
||||||
.append(ResUtils.getString(R.string.privacy_content_6))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -132,8 +109,7 @@ class CommonUtils private constructor() {
|
||||||
@Suppress("SameParameterValue")
|
@Suppress("SameParameterValue")
|
||||||
private fun getPrivacyLink(context: Context, privacyUrl: String): SpannableString {
|
private fun getPrivacyLink(context: Context, privacyUrl: String): SpannableString {
|
||||||
val privacyName = String.format(
|
val privacyName = String.format(
|
||||||
ResUtils.getString(R.string.lab_privacy_name),
|
ResUtils.getString(R.string.lab_privacy_name), ResUtils.getString(R.string.app_name)
|
||||||
ResUtils.getString(R.string.app_name)
|
|
||||||
)
|
)
|
||||||
val spannableString = SpannableString(privacyName)
|
val spannableString = SpannableString(privacyName)
|
||||||
spannableString.setSpan(object : ClickableSpan() {
|
spannableString.setSpan(object : ClickableSpan() {
|
||||||
|
@ -165,15 +141,11 @@ class CommonUtils private constructor() {
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun gotoProtocol(fragment: XPageFragment?, isPrivacy: Boolean, isImmersive: Boolean) {
|
fun gotoProtocol(fragment: XPageFragment?, isPrivacy: Boolean, isImmersive: Boolean) {
|
||||||
PageOption.to(ServiceProtocolFragment::class.java)
|
PageOption.to(ServiceProtocolFragment::class.java).putString(
|
||||||
.putString(
|
ServiceProtocolFragment.KEY_PROTOCOL_TITLE, if (isPrivacy) ResUtils.getString(R.string.title_privacy_protocol) else ResUtils.getString(
|
||||||
ServiceProtocolFragment.KEY_PROTOCOL_TITLE,
|
R.string.title_user_protocol
|
||||||
if (isPrivacy) ResUtils.getString(R.string.title_privacy_protocol) else ResUtils.getString(
|
|
||||||
R.string.title_user_protocol
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
.putBoolean(ServiceProtocolFragment.KEY_IS_IMMERSIVE, isImmersive)
|
).putBoolean(ServiceProtocolFragment.KEY_IS_IMMERSIVE, isImmersive).open(fragment!!)
|
||||||
.open(fragment!!)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -210,13 +182,7 @@ class CommonUtils private constructor() {
|
||||||
}
|
}
|
||||||
val bounds = Rect()
|
val bounds = Rect()
|
||||||
view?.getGlobalVisibleRect(bounds)
|
view?.getGlobalVisibleRect(bounds)
|
||||||
PreviewBuilder.from(fragment)
|
PreviewBuilder.from(fragment).setImgs(ImageInfo.newInstance(url, bounds)).setCurrentIndex(0).setSingleFling(true).setProgressColor(R.color.xui_config_color_main_theme).setType(PreviewBuilder.IndicatorType.Number).start()
|
||||||
.setImgs(ImageInfo.newInstance(url, bounds))
|
|
||||||
.setCurrentIndex(0)
|
|
||||||
.setSingleFling(true)
|
|
||||||
.setProgressColor(R.color.xui_config_color_main_theme)
|
|
||||||
.setType(PreviewBuilder.IndicatorType.Number)
|
|
||||||
.start()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -228,11 +194,7 @@ class CommonUtils private constructor() {
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun previewMarkdown(fragment: XPageFragment?, title: String, url: String, isImmersive: Boolean) {
|
fun previewMarkdown(fragment: XPageFragment?, title: String, url: String, isImmersive: Boolean) {
|
||||||
PageOption.to(MarkdownFragment::class.java)
|
PageOption.to(MarkdownFragment::class.java).putString(MarkdownFragment.KEY_MD_TITLE, title).putString(MarkdownFragment.KEY_MD_URL, url).putBoolean(MarkdownFragment.KEY_IS_IMMERSIVE, isImmersive).open(fragment!!)
|
||||||
.putString(MarkdownFragment.KEY_MD_TITLE, title)
|
|
||||||
.putString(MarkdownFragment.KEY_MD_URL, url)
|
|
||||||
.putBoolean(MarkdownFragment.KEY_IS_IMMERSIVE, isImmersive)
|
|
||||||
.open(fragment!!)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//是否合法的url
|
//是否合法的url
|
||||||
|
@ -243,7 +205,7 @@ class CommonUtils private constructor() {
|
||||||
//是否合法的url
|
//是否合法的url
|
||||||
fun checkUrl(urls: String?, emptyResult: Boolean): Boolean {
|
fun checkUrl(urls: String?, emptyResult: Boolean): Boolean {
|
||||||
if (TextUtils.isEmpty(urls)) return emptyResult
|
if (TextUtils.isEmpty(urls)) return emptyResult
|
||||||
val regex = "^(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;\\[\\]]*[-a-zA-Z0-9+&@#/%=~_|\\[\\]]"
|
val regex = "^(https?|ftp|file)://[-a-zA-Z\\d+&@#/%?=~_|!:,.;\\[\\]]*[-a-zA-Z\\d+&@#/%=~_|\\[\\]]"
|
||||||
val pat = Pattern.compile(regex)
|
val pat = Pattern.compile(regex)
|
||||||
val mat = pat.matcher(urls?.trim() ?: "")
|
val mat = pat.matcher(urls?.trim() ?: "")
|
||||||
return mat.matches()
|
return mat.matches()
|
||||||
|
@ -257,12 +219,41 @@ class CommonUtils private constructor() {
|
||||||
//是否合法的URL Scheme
|
//是否合法的URL Scheme
|
||||||
fun checkUrlScheme(urls: String?, emptyResult: Boolean): Boolean {
|
fun checkUrlScheme(urls: String?, emptyResult: Boolean): Boolean {
|
||||||
if (TextUtils.isEmpty(urls)) return emptyResult
|
if (TextUtils.isEmpty(urls)) return emptyResult
|
||||||
val regex = "^[a-zA-Z0-9]+://[-a-zA-Z0-9+&@#/%?=~_|!:,.;\\[\\]]*[-a-zA-Z0-9+&@#/%=~_|\\[\\]]"
|
val regex = "^[a-zA-Z\\d]+://[-a-zA-Z\\d+&@#/%?=~_|!:,.;\\[\\]]*[-a-zA-Z\\d+&@#/%=~_|\\[\\]]"
|
||||||
val pat = Pattern.compile(regex)
|
val pat = Pattern.compile(regex)
|
||||||
val mat = pat.matcher(urls?.trim() ?: "")
|
val mat = pat.matcher(urls?.trim() ?: "")
|
||||||
return mat.matches()
|
return mat.matches()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//是否合法的IP地址
|
||||||
|
fun checkIP(IP: String): String {
|
||||||
|
if (TextUtils.isEmpty(IP)) return "Neither"
|
||||||
|
|
||||||
|
if (IP.contains(".")) {
|
||||||
|
val chunkIPv4 = "([\\d]|[1-9][\\d]|1[\\d][\\d]|2[0-4][\\d]|25[0-5])"
|
||||||
|
val pattenIPv4 = Pattern.compile("^($chunkIPv4\\.){3}$chunkIPv4$")
|
||||||
|
return if (pattenIPv4.matcher(IP).matches()) "IPv4" else "Neither"
|
||||||
|
} else if (IP.contains(":")) {
|
||||||
|
val chunkIPv6 = "([\\da-fA-F]{1,4})"
|
||||||
|
val pattenIPv6 = Pattern.compile("^($chunkIPv6\\:){7}$chunkIPv6$")
|
||||||
|
return if (pattenIPv6.matcher(IP).matches()) "IPv6" else "Neither"
|
||||||
|
}
|
||||||
|
return "Neither"
|
||||||
|
}
|
||||||
|
|
||||||
|
//是否合法的域名
|
||||||
|
fun checkDomain(domain: String): Boolean {
|
||||||
|
val pattenDomain = Pattern.compile("^(?=^.{3,255}$)(?:(?:(?:[a-zA-Z\\d]|[a-zA-Z\\d][a-zA-Z\\d\\-]*[a-zA-Z\\d])\\.){1,126}(?:[A-Za-z\\d]|[A-Za-z\\d][A-Za-z\\d\\-]*[A-Za-z\\d]))$")
|
||||||
|
return pattenDomain.matcher(domain).matches()
|
||||||
|
}
|
||||||
|
|
||||||
|
//是否合法的端口号
|
||||||
|
fun checkPort(port: String): Boolean {
|
||||||
|
if (TextUtils.isEmpty(port)) return false
|
||||||
|
val pattenPort = Pattern.compile("^((6[0-4]\\d{3}|65[0-4]\\d{2}|655[0-2]\\d|6553[0-5])|[0-5]?\\d{0,4})$")
|
||||||
|
return pattenPort.matcher(port).matches()
|
||||||
|
}
|
||||||
|
|
||||||
//是否启用通知监听服务
|
//是否启用通知监听服务
|
||||||
fun isNotificationListenerServiceEnabled(context: Context): Boolean {
|
fun isNotificationListenerServiceEnabled(context: Context): Boolean {
|
||||||
val packageNames = NotificationManagerCompat.getEnabledListenerPackages(context)
|
val packageNames = NotificationManagerCompat.getEnabledListenerPackages(context)
|
||||||
|
@ -273,12 +264,10 @@ class CommonUtils private constructor() {
|
||||||
fun toggleNotificationListenerService(context: Context) {
|
fun toggleNotificationListenerService(context: Context) {
|
||||||
val pm = context.packageManager
|
val pm = context.packageManager
|
||||||
pm.setComponentEnabledSetting(
|
pm.setComponentEnabledSetting(
|
||||||
ComponentName(context.applicationContext, NotifyService::class.java),
|
ComponentName(context.applicationContext, NotifyService::class.java), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP
|
||||||
PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP
|
|
||||||
)
|
)
|
||||||
pm.setComponentEnabledSetting(
|
pm.setComponentEnabledSetting(
|
||||||
ComponentName(context.applicationContext, NotifyService::class.java),
|
ComponentName(context.applicationContext, NotifyService::class.java), PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP
|
||||||
PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -179,6 +179,7 @@ const val TYPE_GOTIFY = 11
|
||||||
const val TYPE_DINGTALK_INNER_ROBOT = 12
|
const val TYPE_DINGTALK_INNER_ROBOT = 12
|
||||||
const val TYPE_FEISHU_APP = 13
|
const val TYPE_FEISHU_APP = 13
|
||||||
const val TYPE_URL_SCHEME = 14
|
const val TYPE_URL_SCHEME = 14
|
||||||
|
const val TYPE_SOCKET = 15
|
||||||
var SENDER_FRAGMENT_LIST = listOf(
|
var SENDER_FRAGMENT_LIST = listOf(
|
||||||
PageInfo(
|
PageInfo(
|
||||||
getString(R.string.dingtalk_robot),
|
getString(R.string.dingtalk_robot),
|
||||||
|
@ -285,6 +286,13 @@ var SENDER_FRAGMENT_LIST = listOf(
|
||||||
CoreAnim.slide,
|
CoreAnim.slide,
|
||||||
R.drawable.icon_url_scheme
|
R.drawable.icon_url_scheme
|
||||||
),
|
),
|
||||||
|
PageInfo(
|
||||||
|
getString(R.string.socket),
|
||||||
|
"com.idormy.sms.forwarder.fragment.senders.SocketFragment",
|
||||||
|
"{\"\":\"\"}",
|
||||||
|
CoreAnim.slide,
|
||||||
|
R.drawable.icon_socket
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
//前台服务
|
//前台服务
|
||||||
|
|
|
@ -54,7 +54,7 @@ object SendUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var rule = item.rule
|
val rule = item.rule
|
||||||
rule.senderLogic = SENDER_LOGIC_RETRY
|
rule.senderLogic = SENDER_LOGIC_RETRY
|
||||||
sendMsgSender(msgInfo, rule, senderIndex, logId, item.msg.id)
|
sendMsgSender(msgInfo, rule, senderIndex, logId, item.msg.id)
|
||||||
}
|
}
|
||||||
|
@ -124,6 +124,10 @@ object SendUtils {
|
||||||
val settingVo = Gson().fromJson(sender.jsonSetting, UrlSchemeSetting::class.java)
|
val settingVo = Gson().fromJson(sender.jsonSetting, UrlSchemeSetting::class.java)
|
||||||
UrlSchemeUtils.sendMsg(settingVo, msgInfo, rule, senderIndex, logId, msgId)
|
UrlSchemeUtils.sendMsg(settingVo, msgInfo, rule, senderIndex, logId, msgId)
|
||||||
}
|
}
|
||||||
|
TYPE_SOCKET -> {
|
||||||
|
val settingVo = Gson().fromJson(sender.jsonSetting, SocketSetting::class.java)
|
||||||
|
SocketUtils.sendMsg(settingVo, msgInfo, rule, senderIndex, logId, msgId)
|
||||||
|
}
|
||||||
else -> {
|
else -> {
|
||||||
updateLogs(logId, 0, "未知发送通道")
|
updateLogs(logId, 0, "未知发送通道")
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,182 @@
|
||||||
|
package com.idormy.sms.forwarder.utils.sender
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.text.TextUtils
|
||||||
|
import android.util.Base64
|
||||||
|
import com.gitee.xuankaicat.kmnkt.socket.MqttQuality
|
||||||
|
import com.gitee.xuankaicat.kmnkt.socket.dsl.mqtt
|
||||||
|
import com.gitee.xuankaicat.kmnkt.socket.dsl.tcp
|
||||||
|
import com.gitee.xuankaicat.kmnkt.socket.dsl.udp
|
||||||
|
import com.gitee.xuankaicat.kmnkt.socket.open
|
||||||
|
import com.gitee.xuankaicat.kmnkt.socket.utils.Charset
|
||||||
|
import com.google.gson.Gson
|
||||||
|
import com.idormy.sms.forwarder.database.entity.Rule
|
||||||
|
import com.idormy.sms.forwarder.entity.MsgInfo
|
||||||
|
import com.idormy.sms.forwarder.entity.setting.SocketSetting
|
||||||
|
import com.idormy.sms.forwarder.utils.SendUtils
|
||||||
|
import com.idormy.sms.forwarder.utils.SettingUtils
|
||||||
|
import com.xuexiang.xutil.app.AppUtils
|
||||||
|
import java.net.URLEncoder
|
||||||
|
import java.nio.charset.StandardCharsets
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.*
|
||||||
|
import javax.crypto.Mac
|
||||||
|
import javax.crypto.spec.SecretKeySpec
|
||||||
|
|
||||||
|
@Suppress("PrivatePropertyName", "UNUSED_PARAMETER", "unused")
|
||||||
|
class SocketUtils {
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
private val TAG: String = SocketUtils::class.java.simpleName
|
||||||
|
|
||||||
|
fun sendMsg(
|
||||||
|
setting: SocketSetting, msgInfo: MsgInfo, rule: Rule? = null, senderIndex: Int = 0, logId: Long = 0L, msgId: Long = 0L
|
||||||
|
) {
|
||||||
|
val from: String = msgInfo.from
|
||||||
|
val content: String = if (rule != null) {
|
||||||
|
msgInfo.getContentForSend(rule.smsTemplate, rule.regexReplace)
|
||||||
|
} else {
|
||||||
|
msgInfo.getContentForSend(SettingUtils.smsTemplate)
|
||||||
|
}
|
||||||
|
|
||||||
|
val timestamp = System.currentTimeMillis()
|
||||||
|
val orgContent: String = msgInfo.content
|
||||||
|
val deviceMark: String = SettingUtils.extraDeviceMark
|
||||||
|
val appVersion: String = AppUtils.getAppVersionName()
|
||||||
|
val simInfo: String = msgInfo.simInfo
|
||||||
|
@SuppressLint("SimpleDateFormat") val receiveTime = SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(Date()) //smsVo.getDate()
|
||||||
|
var sign = ""
|
||||||
|
if (!TextUtils.isEmpty(setting.secret)) {
|
||||||
|
val stringToSign = "$timestamp\n" + setting.secret
|
||||||
|
val mac = Mac.getInstance("HmacSHA256")
|
||||||
|
mac.init(SecretKeySpec(setting.secret?.toByteArray(StandardCharsets.UTF_8), "HmacSHA256"))
|
||||||
|
val signData = mac.doFinal(stringToSign.toByteArray(StandardCharsets.UTF_8))
|
||||||
|
sign = URLEncoder.encode(String(Base64.encode(signData, Base64.NO_WRAP)), "UTF-8")
|
||||||
|
}
|
||||||
|
|
||||||
|
var message = if (TextUtils.isEmpty(setting.msgTemplate)) "{\"msg\": \"[msg]\"}" else setting.msgTemplate
|
||||||
|
message = if (message.startsWith("{")) {
|
||||||
|
message.replace("[from]", from).replace("[content]", escapeJson(content)).replace("[msg]", escapeJson(content)).replace("[org_content]", escapeJson(orgContent)).replace("[device_mark]", escapeJson(deviceMark)).replace("[app_version]", appVersion).replace("[title]", escapeJson(simInfo)).replace("[card_slot]", escapeJson(simInfo)).replace("[receive_time]", receiveTime).replace("[timestamp]", timestamp.toString()).replace("[sign]", sign)
|
||||||
|
} else {
|
||||||
|
message.replace("[from]", URLEncoder.encode(from, "UTF-8")).replace("[content]", URLEncoder.encode(content, "UTF-8")).replace("[msg]", URLEncoder.encode(content, "UTF-8")).replace("[org_content]", URLEncoder.encode(orgContent, "UTF-8")).replace("[device_mark]", URLEncoder.encode(deviceMark, "UTF-8")).replace("[app_version]", URLEncoder.encode(appVersion, "UTF-8")).replace("[title]", URLEncoder.encode(simInfo, "UTF-8")).replace("[card_slot]", URLEncoder.encode(simInfo, "UTF-8")).replace("[receive_time]", URLEncoder.encode(receiveTime, "UTF-8")).replace("\n", "%0A").replace("[timestamp]", timestamp.toString()).replace("[sign]", sign)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setting.method == "TCP" || setting.method == "UDP") {
|
||||||
|
var isReceived = false
|
||||||
|
var isConnected = false
|
||||||
|
val socket = if (setting.method == "TCP") {
|
||||||
|
tcp {
|
||||||
|
address = setting.address//设置ip地址
|
||||||
|
port = setting.port//设置端口号
|
||||||
|
if (!TextUtils.isEmpty(setting.inCharset)) inCharset = Charset.forName(setting.inCharset)//设置输入编码
|
||||||
|
if (!TextUtils.isEmpty(setting.outCharset)) outCharset = Charset.forName(setting.outCharset)//设置输出编码
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
udp {
|
||||||
|
address = setting.address//设置ip地址
|
||||||
|
port = setting.port//设置端口号
|
||||||
|
if (!TextUtils.isEmpty(setting.inCharset)) inCharset = Charset.forName(setting.inCharset)//设置输入编码
|
||||||
|
if (!TextUtils.isEmpty(setting.outCharset)) outCharset = Charset.forName(setting.outCharset)//设置输出编码
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
socket.open {
|
||||||
|
success {
|
||||||
|
//开启连接成功时执行
|
||||||
|
isConnected = true
|
||||||
|
SendUtils.updateLogs(logId, 1, "TCP连接成功")
|
||||||
|
socket.send(message)
|
||||||
|
socket.startReceive { str, data ->
|
||||||
|
isReceived = true
|
||||||
|
android.util.Log.d(TAG, "str=$str,data=$data")
|
||||||
|
SendUtils.updateLogs(logId, 2, "收到订阅消息:str=$str,data=$data")
|
||||||
|
SendUtils.senderLogic(2, msgInfo, rule, senderIndex, msgId)
|
||||||
|
return@startReceive false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
failure {
|
||||||
|
//开启连接失败时执行
|
||||||
|
val status = 0
|
||||||
|
SendUtils.updateLogs(logId, status, "TCP连接失败")
|
||||||
|
SendUtils.senderLogic(status, msgInfo, rule, senderIndex, msgId)
|
||||||
|
return@failure false//是否继续尝试连接
|
||||||
|
}
|
||||||
|
loss {
|
||||||
|
//失去连接时执行
|
||||||
|
return@loss false//是否尝试重连
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//延时5秒关闭连接
|
||||||
|
if (isConnected) {
|
||||||
|
Thread.sleep(5000)
|
||||||
|
socket.stopReceive()
|
||||||
|
socket.close()
|
||||||
|
if (!isReceived) {
|
||||||
|
SendUtils.updateLogs(logId, 0, "未收到订阅消息")
|
||||||
|
SendUtils.senderLogic(0, msgInfo, rule, senderIndex, msgId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
|
||||||
|
} else if (setting.method == "MQTT") {
|
||||||
|
val mqtt = mqtt {
|
||||||
|
address = setting.address//设置ip地址
|
||||||
|
port = setting.port//设置端口号
|
||||||
|
if (!TextUtils.isEmpty(setting.inCharset)) inCharset = Charset.forName(setting.inCharset)//设置输入编码
|
||||||
|
if (!TextUtils.isEmpty(setting.outCharset)) outCharset = Charset.forName(setting.outCharset)//设置输出编码
|
||||||
|
if (!TextUtils.isEmpty(setting.username)) username = setting.username
|
||||||
|
if (!TextUtils.isEmpty(setting.password)) password = setting.password
|
||||||
|
if (!TextUtils.isEmpty(setting.inMessageTopic)) inMessageTopic = setting.inMessageTopic
|
||||||
|
if (!TextUtils.isEmpty(setting.outMessageTopic)) outMessageTopic = setting.outMessageTopic
|
||||||
|
//自定义配置
|
||||||
|
qos = MqttQuality.ExactlyOnce // 服务质量 详见MqttQuality
|
||||||
|
if (!TextUtils.isEmpty(setting.uriType)) uriType = setting.uriType //通信方式 默认为tcp
|
||||||
|
if (!TextUtils.isEmpty(setting.clientId)) clientId = setting.clientId //客户端ID,如果为空则为随机值
|
||||||
|
timeOut = 10 //设置超时时间
|
||||||
|
cleanSession = true //断开连接后是否清楚缓存,如果清除缓存则在重连后需要手动恢复订阅。
|
||||||
|
keepAliveInterval = 20 //检测连接是否中断的间隔
|
||||||
|
//行为配置
|
||||||
|
threadLock = false //是否启用线程同步锁 默认false
|
||||||
|
}
|
||||||
|
|
||||||
|
mqtt.open {
|
||||||
|
success {
|
||||||
|
//开启连接成功时执行
|
||||||
|
SendUtils.updateLogs(logId, 1, "MQTT连接成功")
|
||||||
|
// 订阅并发布后等待至拿到响应消息并赋值给result
|
||||||
|
// 如果超过10秒没有收到消息则将result设为"消息响应超时",并取消订阅topic
|
||||||
|
val response = mqtt.sendAndReceiveSync(setting.outMessageTopic, setting.inMessageTopic, message, 10000L) ?: "消息响应超时"
|
||||||
|
mqtt.close()
|
||||||
|
|
||||||
|
val status = if (response == "消息响应超时") 0 else 2
|
||||||
|
SendUtils.updateLogs(logId, status, "收到订阅消息:$response")
|
||||||
|
SendUtils.senderLogic(status, msgInfo, rule, senderIndex, msgId)
|
||||||
|
return@success
|
||||||
|
}
|
||||||
|
failure {
|
||||||
|
//开启连接失败时执行
|
||||||
|
val status = 0
|
||||||
|
SendUtils.updateLogs(logId, status, "MQTT连接失败")
|
||||||
|
SendUtils.senderLogic(status, msgInfo, rule, senderIndex, msgId)
|
||||||
|
return@failure false//是否继续尝试连接
|
||||||
|
}
|
||||||
|
loss {
|
||||||
|
//失去连接时执行
|
||||||
|
return@loss false//是否尝试重连
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//JSON需要转义的字符
|
||||||
|
private fun escapeJson(str: String?): String {
|
||||||
|
if (str == null) return "null"
|
||||||
|
val jsonStr: String = Gson().toJson(str)
|
||||||
|
return if (jsonStr.length >= 2) jsonStr.substring(1, jsonStr.length - 1) else jsonStr
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 1.9 KiB |
|
@ -16,70 +16,62 @@
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="center_vertical"
|
android:orientation="vertical"
|
||||||
android:orientation="horizontal">
|
tools:ignore="DisableBaselineAlignment">
|
||||||
|
|
||||||
<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:gravity="center_vertical"
|
||||||
android:orientation="vertical">
|
android:orientation="horizontal">
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/tv_from"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_weight="1" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/tv_time"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="5dp" />
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/iv_sim_image"
|
|
||||||
android:layout_width="16dp"
|
|
||||||
android:layout_height="16dp"
|
|
||||||
android:layout_marginStart="5dp"
|
|
||||||
tools:ignore="ContentDescription" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/tv_content"
|
android:id="@+id/tv_from"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="start"
|
android:layout_weight="1" />
|
||||||
android:layout_marginTop="3dp"
|
|
||||||
android:ellipsize="end"
|
|
||||||
android:gravity="start"
|
|
||||||
android:maxEms="8"
|
|
||||||
android:maxLines="3"
|
|
||||||
android:textSize="11sp" />
|
|
||||||
|
|
||||||
<View
|
<TextView
|
||||||
android:layout_width="match_parent"
|
android:id="@+id/tv_time"
|
||||||
android:layout_height="1dp"
|
android:layout_width="wrap_content"
|
||||||
android:layout_marginTop="3dp"
|
|
||||||
android:layout_marginBottom="3dp"
|
|
||||||
android:background="?attr/xui_config_color_separator_light" />
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/layout_Logs"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="center_vertical"
|
android:layout_marginStart="5dp" />
|
||||||
android:orientation="horizontal"></LinearLayout>
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/iv_sim_image"
|
||||||
|
android:layout_width="16dp"
|
||||||
|
android:layout_height="16dp"
|
||||||
|
android:layout_marginStart="5dp"
|
||||||
|
tools:ignore="ContentDescription" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tv_content"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="start"
|
||||||
|
android:layout_marginTop="3dp"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:gravity="start"
|
||||||
|
android:maxEms="8"
|
||||||
|
android:maxLines="3"
|
||||||
|
android:textSize="11sp" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:layout_marginTop="3dp"
|
||||||
|
android:layout_marginBottom="3dp"
|
||||||
|
android:background="?attr/xui_config_color_separator_light" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/layout_Logs"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</com.xuexiang.xui.widget.layout.XUIFrameLayout>
|
</com.xuexiang.xui.widget.layout.XUIFrameLayout>
|
|
@ -65,7 +65,7 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="5dp"
|
android:layout_marginStart="5dp"
|
||||||
android:orientation="horizontal"></LinearLayout>
|
android:orientation="horizontal" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
|
@ -59,7 +59,7 @@
|
||||||
android:id="@+id/layout_Senders"
|
android:id="@+id/layout_Senders"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical"></LinearLayout>
|
android:orientation="vertical" />
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/layout_sender_logic"
|
android:id="@+id/layout_sender_logic"
|
||||||
|
|
|
@ -0,0 +1,440 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="?attr/xui_config_color_background"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<androidx.core.widget.NestedScrollView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:overScrollMode="never">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="5dp"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
style="@style/senderBarStyleWithSwitch"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/sender_name_status"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<com.xuexiang.xui.widget.edittext.materialedittext.MaterialEditText
|
||||||
|
android:id="@+id/et_name"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="5dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:singleLine="true"
|
||||||
|
app:met_clearButton="true" />
|
||||||
|
|
||||||
|
<com.xuexiang.xui.widget.button.switchbutton.SwitchButton
|
||||||
|
android:id="@+id/sb_enable"
|
||||||
|
style="@style/SwitchButtonStyle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:checked="true" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
style="@style/senderBarStyle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/Method"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<RadioGroup
|
||||||
|
android:id="@+id/rg_method"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="5dp"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<RadioButton
|
||||||
|
android:id="@+id/rb_method_mqtt"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:checked="true"
|
||||||
|
android:text="@string/mqtt"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
<RadioButton
|
||||||
|
android:id="@+id/rb_method_tcp"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/tcp"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
<RadioButton
|
||||||
|
android:id="@+id/rb_method_udp"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/udp"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
</RadioGroup>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
style="@style/senderBarStyle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/server_ip"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<com.xuexiang.xui.widget.edittext.materialedittext.MaterialEditText
|
||||||
|
android:id="@+id/et_address"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="5dp"
|
||||||
|
android:layout_weight="3"
|
||||||
|
android:singleLine="true"
|
||||||
|
app:met_clearButton="true" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="5dp"
|
||||||
|
android:text="@string/server_port"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<com.xuexiang.xui.widget.edittext.materialedittext.MaterialEditText
|
||||||
|
android:id="@+id/et_port"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="5dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:digits="0123456789"
|
||||||
|
android:inputType="number"
|
||||||
|
android:maxLength="5"
|
||||||
|
android:singleLine="true"
|
||||||
|
app:met_clearButton="true" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
style="@style/senderBarStyle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/username"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<com.xuexiang.xui.widget.edittext.materialedittext.MaterialEditText
|
||||||
|
android:id="@+id/et_username"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="5dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:hint="@string/optional"
|
||||||
|
android:singleLine="true"
|
||||||
|
app:met_clearButton="true" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="5dp"
|
||||||
|
android:text="@string/password"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<com.xuexiang.xui.widget.edittext.materialedittext.MaterialEditText
|
||||||
|
android:id="@+id/et_password"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="5dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:hint="@string/optional"
|
||||||
|
android:singleLine="true"
|
||||||
|
app:met_passWordButton="true" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
style="@style/senderBarStyle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/in_charset"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<com.xuexiang.xui.widget.spinner.materialspinner.MaterialSpinner
|
||||||
|
android:id="@+id/et_inCharset"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="3dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:singleLine="true"
|
||||||
|
app:ms_entries="@array/charsets" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="5dp"
|
||||||
|
android:text="@string/out_charset"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<com.xuexiang.xui.widget.spinner.materialspinner.MaterialSpinner
|
||||||
|
android:id="@+id/et_outCharset"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="3dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:singleLine="true"
|
||||||
|
app:ms_entries="@array/charsets" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
style="@style/senderBarStyle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text='@string/webhook_params'
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/webhook_params_tips"
|
||||||
|
android:textSize="10sp"
|
||||||
|
tools:ignore="SmallSp" />
|
||||||
|
|
||||||
|
<com.xuexiang.xui.widget.edittext.materialedittext.MaterialEditText
|
||||||
|
android:id="@+id/et_msgTemplate"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/optional"
|
||||||
|
android:singleLine="true"
|
||||||
|
app:met_clearButton="true" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
style="@style/senderBarStyle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/webhook_secret"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<com.xuexiang.xui.widget.edittext.materialedittext.MaterialEditText
|
||||||
|
android:id="@+id/et_Secret"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/optional"
|
||||||
|
android:singleLine="true"
|
||||||
|
app:met_passWordButton="true" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/layout_mqtt"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
style="@style/senderBarStyle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/in_message_topic"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<com.xuexiang.xui.widget.edittext.materialedittext.MaterialEditText
|
||||||
|
android:id="@+id/et_inMessageTopic"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="5dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:hint="@string/in_message_topic_hint"
|
||||||
|
android:singleLine="true"
|
||||||
|
app:met_clearButton="true" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
style="@style/senderBarStyle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/out_message_topic"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<com.xuexiang.xui.widget.edittext.materialedittext.MaterialEditText
|
||||||
|
android:id="@+id/et_outMessageTopic"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="5dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:hint="@string/out_message_topic_hint"
|
||||||
|
android:singleLine="true"
|
||||||
|
app:met_clearButton="true" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
style="@style/senderBarStyle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/uri_type"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<com.xuexiang.xui.widget.edittext.materialedittext.MaterialEditText
|
||||||
|
android:id="@+id/et_uriType"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="5dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:hint="@string/uri_type_hint"
|
||||||
|
android:singleLine="true"
|
||||||
|
app:met_clearButton="true" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
style="@style/senderBarStyle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/client_id"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<com.xuexiang.xui.widget.edittext.materialedittext.MaterialEditText
|
||||||
|
android:id="@+id/et_clientId"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="5dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:hint="@string/client_id_hint"
|
||||||
|
android:singleLine="true"
|
||||||
|
app:met_clearButton="true" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
style="@style/senderBarStyle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/path"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<com.xuexiang.xui.widget.edittext.materialedittext.MaterialEditText
|
||||||
|
android:id="@+id/et_path"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="5dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:hint="@string/path_hint"
|
||||||
|
android:singleLine="true"
|
||||||
|
app:met_clearButton="true" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</androidx.core.widget.NestedScrollView>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:padding="10dp">
|
||||||
|
|
||||||
|
<com.xuexiang.xui.widget.textview.supertextview.SuperButton
|
||||||
|
android:id="@+id/btn_del"
|
||||||
|
style="@style/SuperButton.Gray.Icon"
|
||||||
|
android:drawableStart="@drawable/icon_delete"
|
||||||
|
android:paddingStart="15dp"
|
||||||
|
android:text="@string/del"
|
||||||
|
android:textSize="11sp"
|
||||||
|
tools:ignore="RtlSymmetry" />
|
||||||
|
|
||||||
|
<com.xuexiang.xui.widget.textview.supertextview.SuperButton
|
||||||
|
android:id="@+id/btn_save"
|
||||||
|
style="@style/SuperButton.Blue.Icon"
|
||||||
|
android:layout_marginStart="10dp"
|
||||||
|
android:drawableStart="@drawable/icon_save"
|
||||||
|
android:paddingStart="15dp"
|
||||||
|
android:text="@string/save"
|
||||||
|
android:textSize="11sp"
|
||||||
|
tools:ignore="RtlSymmetry" />
|
||||||
|
|
||||||
|
<com.xuexiang.xui.widget.textview.supertextview.SuperButton
|
||||||
|
android:id="@+id/btn_test"
|
||||||
|
style="@style/SuperButton.Green.Icon"
|
||||||
|
android:layout_marginStart="10dp"
|
||||||
|
android:drawableStart="@drawable/icon_test"
|
||||||
|
android:paddingStart="15dp"
|
||||||
|
android:text="@string/test"
|
||||||
|
android:textSize="11sp"
|
||||||
|
tools:ignore="RtlSymmetry" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
|
@ -664,7 +664,8 @@
|
||||||
<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"
|
||||||
|
tools:ignore="TooManyViews">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
|
|
|
@ -331,7 +331,8 @@
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:paddingEnd="10dp"
|
android:paddingEnd="10dp"
|
||||||
android:visibility="gone">
|
android:visibility="gone"
|
||||||
|
tools:ignore="RtlSymmetry">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
@ -1056,6 +1057,7 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:autofillHints=""
|
android:autofillHints=""
|
||||||
|
android:digits="0123456789"
|
||||||
android:inputType="number"
|
android:inputType="number"
|
||||||
android:maxLength="2"
|
android:maxLength="2"
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
|
@ -1085,6 +1087,7 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:autofillHints=""
|
android:autofillHints=""
|
||||||
|
android:digits="0123456789"
|
||||||
android:inputType="number"
|
android:inputType="number"
|
||||||
android:maxLength="2"
|
android:maxLength="2"
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
|
@ -1114,6 +1117,7 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:autofillHints=""
|
android:autofillHints=""
|
||||||
|
android:digits="0123456789"
|
||||||
android:inputType="number"
|
android:inputType="number"
|
||||||
android:maxLength="2"
|
android:maxLength="2"
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
|
|
|
@ -143,4 +143,16 @@
|
||||||
<item>Beagle</item>
|
<item>Beagle</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
|
<string-array name="charsets">
|
||||||
|
<item>UTF-8</item>
|
||||||
|
<item>US-ASCII</item>
|
||||||
|
<item>ISO-8859-1</item>
|
||||||
|
<item>UTF-16</item>
|
||||||
|
<item>UTF-16BE</item>
|
||||||
|
<item>UTF-16LE</item>
|
||||||
|
<item>UTF-32</item>
|
||||||
|
<item>UTF-32LE</item>
|
||||||
|
<item>UTF-32BE</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
|
@ -398,6 +398,9 @@
|
||||||
<string name="get">GET</string>
|
<string name="get">GET</string>
|
||||||
<string name="put">PUT</string>
|
<string name="put">PUT</string>
|
||||||
<string name="patch">PATCH</string>
|
<string name="patch">PATCH</string>
|
||||||
|
<string name="udp">UDP</string>
|
||||||
|
<string name="tcp">TCP</string>
|
||||||
|
<string name="mqtt">MQTT</string>
|
||||||
<!--CloneActivity-->
|
<!--CloneActivity-->
|
||||||
<string name="local_ip">Local IP: </string>
|
<string name="local_ip">Local IP: </string>
|
||||||
<string name="operating_instruction">Instructions: \n[Note] The APP version of the sender and receiver must be the same!\n1. Please keep the SOURCE and DESTINATION phones in the same Wi-Fi network, and do not turn on isolation. \n2. Tap "Send" on SOURCE mobile phone, and get "server IP" \n3. After filling in "Server IP" on DESTINATION phone, tap "Receive". \n [NOTE:] sender(s), forwarding rule(s) and log(s) will be overwritten after cloning!</string> <!-- 原文是“新旧手机”,英文翻译中处理为“源”手机和“目标”手机,因为担心“新旧”的表述引起混淆(有没一种可能就是用户就是用从新手机的设备复制到旧手机上去呢?)。 -->
|
<string name="operating_instruction">Instructions: \n[Note] The APP version of the sender and receiver must be the same!\n1. Please keep the SOURCE and DESTINATION phones in the same Wi-Fi network, and do not turn on isolation. \n2. Tap "Send" on SOURCE mobile phone, and get "server IP" \n3. After filling in "Server IP" on DESTINATION phone, tap "Receive". \n [NOTE:] sender(s), forwarding rule(s) and log(s) will be overwritten after cloning!</string> <!-- 原文是“新旧手机”,英文翻译中处理为“源”手机和“目标”手机,因为担心“新旧”的表述引起混淆(有没一种可能就是用户就是用从新手机的设备复制到旧手机上去呢?)。 -->
|
||||||
|
@ -410,8 +413,12 @@
|
||||||
<string name="old_mobile_phone">Old Phone</string>
|
<string name="old_mobile_phone">Old Phone</string>
|
||||||
<string name="new_mobile_phone">New Phone</string>
|
<string name="new_mobile_phone">New Phone</string>
|
||||||
<string name="server_ip">Server IP: </string>
|
<string name="server_ip">Server IP: </string>
|
||||||
|
<string name="server_address">Host:</string>
|
||||||
|
<string name="server_port">Port: </string>
|
||||||
<string name="point">.</string>
|
<string name="point">.</string>
|
||||||
<string name="invalid_ip">Please enter a valid IP address</string>
|
<string name="invalid_ip">Please enter a valid IP or domain</string>
|
||||||
|
<string name="invalid_mqtt_message_topic">Please enter a valid message topic</string>
|
||||||
|
<string name="invalid_port">Please enter a valid port</string>
|
||||||
<string name="server_has_started">The server is started successfully</string>
|
<string name="server_has_started">The server is started successfully</string>
|
||||||
<string name="server_has_stopped">The server has been stopped</string>
|
<string name="server_has_stopped">The server has been stopped</string>
|
||||||
<string name="sender_cannot_receive">This mobile phone is the SOURCE and cannot receive files.</string>
|
<string name="sender_cannot_receive">This mobile phone is the SOURCE and cannot receive files.</string>
|
||||||
|
@ -467,6 +474,18 @@
|
||||||
<string name="proxy_authenticator">Proxy Authenticator</string>
|
<string name="proxy_authenticator">Proxy Authenticator</string>
|
||||||
<string name="username">Username</string>
|
<string name="username">Username</string>
|
||||||
<string name="password">Password</string>
|
<string name="password">Password</string>
|
||||||
|
<string name="in_charset">In Charset</string>
|
||||||
|
<string name="out_charset">Out Charset</string>
|
||||||
|
<string name="in_message_topic">In Message Topic</string>
|
||||||
|
<string name="in_message_topic_hint">Receive messages on the corresponding topic</string>
|
||||||
|
<string name="out_message_topic">Out Message Topic</string>
|
||||||
|
<string name="out_message_topic_hint">Send a message on the corresponding topic</string>
|
||||||
|
<string name="uri_type">Uri Type</string>
|
||||||
|
<string name="uri_type_hint">Optional, default is tcp</string>
|
||||||
|
<string name="path">Path</string>
|
||||||
|
<string name="path_hint">Used to set the uri when communicating using ws</string>
|
||||||
|
<string name="client_id">Client Id</string>
|
||||||
|
<string name="client_id_hint">Random value if empty</string>
|
||||||
<string name="privacy_policy">Privacy Policy</string>
|
<string name="privacy_policy">Privacy Policy</string>
|
||||||
<string name="agree">Agree</string>
|
<string name="agree">Agree</string>
|
||||||
<string name="refuse">Refuse</string>
|
<string name="refuse">Refuse</string>
|
||||||
|
@ -706,10 +725,18 @@
|
||||||
<string name="url_scheme">URL Scheme</string>
|
<string name="url_scheme">URL Scheme</string>
|
||||||
<string name="url_scheme_tips">Example:myapp://api/add?&type=0&msg=[msg]</string>
|
<string name="url_scheme_tips">Example:myapp://api/add?&type=0&msg=[msg]</string>
|
||||||
|
|
||||||
|
<string name="socket">Socket</string>
|
||||||
|
<string name="socket_tips">Example:myapp://api/add?&type=0&msg=[msg]</string>
|
||||||
|
<string name="socket_address">服务地址</string>
|
||||||
|
<string name="socket_address_tips">IP 或 域名</string>
|
||||||
|
<string name="socket_port">端口</string>
|
||||||
|
<string name="socket_port_tips">取值范围:1-65535</string>
|
||||||
|
|
||||||
<string name="webhook_server">Webhook Server</string>
|
<string name="webhook_server">Webhook Server</string>
|
||||||
<string name="webhook_server_tips">For example: https://a.b.com/msg?token=xyz</string>
|
<string name="webhook_server_tips">For example: https://a.b.com/msg?token=xyz</string>
|
||||||
<string name="webhook_params">Params</string>
|
<string name="webhook_params">Params</string>
|
||||||
<string name="webhook_params_tips" formatted="false">For example: payload=%7B%22text%22%3A%22[msg]%22%7D [msg] will be replaced with SMS content.\nJson format is supported, for example: {\"text\":\"[msg]\"}.\nNote: msg is automatically UTF-8 encoded except in JSON format</string>
|
<string name="msg_template">Msg Template</string>
|
||||||
|
<string name="webhook_params_tips" formatted="false">For example: payload=%7B%22text%22%3A%22[msg]%22%7D [msg] will be replaced with SMS content.\nJson format is supported, for example: {\"text\":\"[msg]\"}.\nNote: msg is automatically URLEncoder except in JSON format</string>
|
||||||
<string name="webhook_secret">Secret: If it is empty, the sign will not be calculated</string>
|
<string name="webhook_secret">Secret: If it is empty, the sign will not be calculated</string>
|
||||||
<string name="headers">Headers</string>
|
<string name="headers">Headers</string>
|
||||||
<string name="header_key">Key</string>
|
<string name="header_key">Key</string>
|
||||||
|
|
|
@ -143,4 +143,16 @@
|
||||||
<item>Beagle</item>
|
<item>Beagle</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
|
<string-array name="charsets">
|
||||||
|
<item>UTF-8</item>
|
||||||
|
<item>US-ASCII</item>
|
||||||
|
<item>ISO-8859-1</item>
|
||||||
|
<item>UTF-16</item>
|
||||||
|
<item>UTF-16BE</item>
|
||||||
|
<item>UTF-16LE</item>
|
||||||
|
<item>UTF-32</item>
|
||||||
|
<item>UTF-32LE</item>
|
||||||
|
<item>UTF-32BE</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
|
@ -399,6 +399,9 @@
|
||||||
<string name="get">GET</string>
|
<string name="get">GET</string>
|
||||||
<string name="put">PUT</string>
|
<string name="put">PUT</string>
|
||||||
<string name="patch">PATCH</string>
|
<string name="patch">PATCH</string>
|
||||||
|
<string name="udp">UDP</string>
|
||||||
|
<string name="tcp">TCP</string>
|
||||||
|
<string name="mqtt">MQTT</string>
|
||||||
<!--CloneActivity-->
|
<!--CloneActivity-->
|
||||||
<string name="local_ip">本机IP:</string>
|
<string name="local_ip">本机IP:</string>
|
||||||
<string name="operating_instruction">严正声明:\n该功能仅限个人新旧手机切换使用,用于非法用途后果自负!\n\n操作说明:\n1.新旧手机连接同一个WiFi网络(禁用AP隔离),如需穿透内网请先配置Frpc\n2.【二选一】旧手机点【推送】按钮,将本机的配置推送到服务端\n3.【二选一】新手机点【拉取】按钮,将拉取服务端的配置到本机\n\n注意事项:\n1.客户端与服务端的APP版本必须一致,才能克隆!\n2.导入成功后,发送通道、转发规则将完全被覆盖,清空历史记录!\n3.主动请求、保活措施、个性设置不在克隆范围</string>
|
<string name="operating_instruction">严正声明:\n该功能仅限个人新旧手机切换使用,用于非法用途后果自负!\n\n操作说明:\n1.新旧手机连接同一个WiFi网络(禁用AP隔离),如需穿透内网请先配置Frpc\n2.【二选一】旧手机点【推送】按钮,将本机的配置推送到服务端\n3.【二选一】新手机点【拉取】按钮,将拉取服务端的配置到本机\n\n注意事项:\n1.客户端与服务端的APP版本必须一致,才能克隆!\n2.导入成功后,发送通道、转发规则将完全被覆盖,清空历史记录!\n3.主动请求、保活措施、个性设置不在克隆范围</string>
|
||||||
|
@ -411,8 +414,12 @@
|
||||||
<string name="old_mobile_phone">我是旧手机</string>
|
<string name="old_mobile_phone">我是旧手机</string>
|
||||||
<string name="new_mobile_phone">我是新手机</string>
|
<string name="new_mobile_phone">我是新手机</string>
|
||||||
<string name="server_ip">服务端IP:</string>
|
<string name="server_ip">服务端IP:</string>
|
||||||
|
<string name="server_address">主机:</string>
|
||||||
|
<string name="server_port">端口:</string>
|
||||||
<string name="point">.</string>
|
<string name="point">.</string>
|
||||||
<string name="invalid_ip">请输入合法的IP地址</string>
|
<string name="invalid_ip">请输入合法的IP或域名</string>
|
||||||
|
<string name="invalid_mqtt_message_topic">请输入合法的输入/输出信息主题</string>
|
||||||
|
<string name="invalid_port">请输入合法的端口号</string>
|
||||||
<string name="server_has_started">服务端已启动</string>
|
<string name="server_has_started">服务端已启动</string>
|
||||||
<string name="server_has_stopped">服务端已停止</string>
|
<string name="server_has_stopped">服务端已停止</string>
|
||||||
<string name="sender_cannot_receive">本手机是发送端,不可接收文件,请先停止服务端!</string>
|
<string name="sender_cannot_receive">本手机是发送端,不可接收文件,请先停止服务端!</string>
|
||||||
|
@ -468,6 +475,18 @@
|
||||||
<string name="proxy_authenticator">代理身份验证</string>
|
<string name="proxy_authenticator">代理身份验证</string>
|
||||||
<string name="username">用户</string>
|
<string name="username">用户</string>
|
||||||
<string name="password">密码</string>
|
<string name="password">密码</string>
|
||||||
|
<string name="in_charset">输入编码</string>
|
||||||
|
<string name="out_charset">输出编码</string>
|
||||||
|
<string name="in_message_topic">输入消息主题</string>
|
||||||
|
<string name="in_message_topic_hint">接收对应主题的消息</string>
|
||||||
|
<string name="out_message_topic">输出消息主题</string>
|
||||||
|
<string name="out_message_topic_hint">发送对应主题的消息</string>
|
||||||
|
<string name="uri_type">通信方式</string>
|
||||||
|
<string name="uri_type_hint">可选,默认为tcp</string>
|
||||||
|
<string name="path">通信路径</string>
|
||||||
|
<string name="path_hint">用于在使用ws进行通信时设置uri</string>
|
||||||
|
<string name="client_id">客户端ID</string>
|
||||||
|
<string name="client_id_hint">如果为空则为随机值</string>
|
||||||
<string name="privacy_policy">隐私政策</string>
|
<string name="privacy_policy">隐私政策</string>
|
||||||
<string name="agree">同意</string>
|
<string name="agree">同意</string>
|
||||||
<string name="refuse">拒绝</string>
|
<string name="refuse">拒绝</string>
|
||||||
|
@ -707,10 +726,18 @@
|
||||||
<string name="url_scheme">URL Scheme</string>
|
<string name="url_scheme">URL Scheme</string>
|
||||||
<string name="url_scheme_tips">示例:myapp://api/add?&type=0&msg=[msg]</string>
|
<string name="url_scheme_tips">示例:myapp://api/add?&type=0&msg=[msg]</string>
|
||||||
|
|
||||||
|
<string name="socket">Socket</string>
|
||||||
|
<string name="socket_tips">示例:myapp://api/add?&type=0&msg=[msg]</string>
|
||||||
|
<string name="socket_address">服务地址</string>
|
||||||
|
<string name="socket_address_tips">IP 或 域名</string>
|
||||||
|
<string name="socket_port">端口</string>
|
||||||
|
<string name="socket_port_tips">取值范围:1-65535</string>
|
||||||
|
|
||||||
<string name="webhook_server">Webhook Server</string>
|
<string name="webhook_server">Webhook Server</string>
|
||||||
<string name="webhook_server_tips">例如:https://a.b.com/msg?token=xyz</string>
|
<string name="webhook_server_tips">例如:https://a.b.com/msg?token=xyz</string>
|
||||||
<string name="webhook_params">Params</string>
|
<string name="webhook_params">消息模板</string>
|
||||||
<string name="webhook_params_tips" formatted="false">例如:payload=%7B%22text%22%3A%22[msg]%22%7D [msg]将被替换成短信内容。\n支持Json格式,例如:{\"text\":\"[msg]\"}。\n注意:除JSON格式外,msg会自动进行UTF-8编码</string>
|
<string name="msg_template">Params</string>
|
||||||
|
<string name="webhook_params_tips" formatted="false">例如:payload=%7B%22text%22%3A%22[msg]%22%7D [msg]将被替换成短信内容。\n支持Json格式,例如:{\"text\":\"[msg]\"}。\n注意:除JSON格式外,msg会自动进行URLEncoder</string>
|
||||||
<string name="webhook_secret">Secret:置空则不计算sign</string>
|
<string name="webhook_secret">Secret:置空则不计算sign</string>
|
||||||
<string name="headers">Headers</string>
|
<string name="headers">Headers</string>
|
||||||
<string name="header_key">Key</string>
|
<string name="header_key">Key</string>
|
||||||
|
|
Loading…
Reference in New Issue