新增:发送通道 URL Scheme(支持跨应用数据传递)#250
This commit is contained in:
parent
65e861ba62
commit
992fc2eb8c
|
@ -1,70 +1,72 @@
|
|||
package com.idormy.sms.forwarder.database.entity
|
||||
|
||||
import android.os.Parcelable
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.idormy.sms.forwarder.utils.*
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import java.util.*
|
||||
|
||||
@Parcelize
|
||||
@Entity(tableName = "Sender")
|
||||
data class Sender(
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
@ColumnInfo(name = "id") var id: Long,
|
||||
@ColumnInfo(name = "type", defaultValue = "1") var type: Int = 1,
|
||||
@ColumnInfo(name = "name", defaultValue = "") var name: String,
|
||||
@ColumnInfo(name = "json_setting", defaultValue = "") var jsonSetting: String,
|
||||
@ColumnInfo(name = "status", defaultValue = "1") var status: Int = 1,
|
||||
@ColumnInfo(name = "time") var time: Date = Date(),
|
||||
) : Parcelable {
|
||||
companion object {
|
||||
|
||||
fun getImageId(type: Int): Int = when (type) {
|
||||
TYPE_DINGTALK_GROUP_ROBOT -> R.drawable.icon_dingtalk
|
||||
TYPE_EMAIL -> R.drawable.icon_email
|
||||
TYPE_BARK -> R.drawable.icon_bark
|
||||
TYPE_WEBHOOK -> R.drawable.icon_webhook
|
||||
TYPE_WEWORK_ROBOT -> R.drawable.icon_wework_robot
|
||||
TYPE_WEWORK_AGENT -> R.drawable.icon_wework_agent
|
||||
TYPE_SERVERCHAN -> R.drawable.icon_serverchan
|
||||
TYPE_TELEGRAM -> R.drawable.icon_telegram
|
||||
TYPE_FEISHU -> R.drawable.icon_feishu
|
||||
TYPE_PUSHPLUS -> R.drawable.icon_pushplus
|
||||
TYPE_GOTIFY -> R.drawable.icon_gotify
|
||||
TYPE_SMS -> R.drawable.icon_sms
|
||||
TYPE_DINGTALK_INNER_ROBOT -> R.drawable.icon_dingtalk_inner
|
||||
TYPE_FEISHU_APP -> R.drawable.icon_feishu_app
|
||||
else -> R.drawable.icon_sms
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
val imageId: Int
|
||||
get() = when (type) {
|
||||
TYPE_DINGTALK_GROUP_ROBOT -> R.drawable.icon_dingtalk
|
||||
TYPE_EMAIL -> R.drawable.icon_email
|
||||
TYPE_BARK -> R.drawable.icon_bark
|
||||
TYPE_WEBHOOK -> R.drawable.icon_webhook
|
||||
TYPE_WEWORK_ROBOT -> R.drawable.icon_wework_robot
|
||||
TYPE_WEWORK_AGENT -> R.drawable.icon_wework_agent
|
||||
TYPE_SERVERCHAN -> R.drawable.icon_serverchan
|
||||
TYPE_TELEGRAM -> R.drawable.icon_telegram
|
||||
TYPE_FEISHU -> R.drawable.icon_feishu
|
||||
TYPE_PUSHPLUS -> R.drawable.icon_pushplus
|
||||
TYPE_GOTIFY -> R.drawable.icon_gotify
|
||||
TYPE_SMS -> R.drawable.icon_sms
|
||||
TYPE_DINGTALK_INNER_ROBOT -> R.drawable.icon_dingtalk_inner
|
||||
TYPE_FEISHU_APP -> R.drawable.icon_feishu_app
|
||||
else -> R.drawable.icon_sms
|
||||
}
|
||||
|
||||
val statusImageId: Int
|
||||
get() = when (status) {
|
||||
STATUS_OFF -> R.drawable.icon_off
|
||||
else -> R.drawable.icon_on
|
||||
}
|
||||
|
||||
package com.idormy.sms.forwarder.database.entity
|
||||
|
||||
import android.os.Parcelable
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.idormy.sms.forwarder.utils.*
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import java.util.*
|
||||
|
||||
@Parcelize
|
||||
@Entity(tableName = "Sender")
|
||||
data class Sender(
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
@ColumnInfo(name = "id") var id: Long,
|
||||
@ColumnInfo(name = "type", defaultValue = "1") var type: Int = 1,
|
||||
@ColumnInfo(name = "name", defaultValue = "") var name: String,
|
||||
@ColumnInfo(name = "json_setting", defaultValue = "") var jsonSetting: String,
|
||||
@ColumnInfo(name = "status", defaultValue = "1") var status: Int = 1,
|
||||
@ColumnInfo(name = "time") var time: Date = Date(),
|
||||
) : Parcelable {
|
||||
companion object {
|
||||
|
||||
fun getImageId(type: Int): Int = when (type) {
|
||||
TYPE_DINGTALK_GROUP_ROBOT -> R.drawable.icon_dingtalk
|
||||
TYPE_EMAIL -> R.drawable.icon_email
|
||||
TYPE_BARK -> R.drawable.icon_bark
|
||||
TYPE_WEBHOOK -> R.drawable.icon_webhook
|
||||
TYPE_WEWORK_ROBOT -> R.drawable.icon_wework_robot
|
||||
TYPE_WEWORK_AGENT -> R.drawable.icon_wework_agent
|
||||
TYPE_SERVERCHAN -> R.drawable.icon_serverchan
|
||||
TYPE_TELEGRAM -> R.drawable.icon_telegram
|
||||
TYPE_FEISHU -> R.drawable.icon_feishu
|
||||
TYPE_PUSHPLUS -> R.drawable.icon_pushplus
|
||||
TYPE_GOTIFY -> R.drawable.icon_gotify
|
||||
TYPE_SMS -> R.drawable.icon_sms
|
||||
TYPE_DINGTALK_INNER_ROBOT -> R.drawable.icon_dingtalk_inner
|
||||
TYPE_FEISHU_APP -> R.drawable.icon_feishu_app
|
||||
TYPE_URL_SCHEME -> R.drawable.icon_url_scheme
|
||||
else -> R.drawable.icon_sms
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
val imageId: Int
|
||||
get() = when (type) {
|
||||
TYPE_DINGTALK_GROUP_ROBOT -> R.drawable.icon_dingtalk
|
||||
TYPE_EMAIL -> R.drawable.icon_email
|
||||
TYPE_BARK -> R.drawable.icon_bark
|
||||
TYPE_WEBHOOK -> R.drawable.icon_webhook
|
||||
TYPE_WEWORK_ROBOT -> R.drawable.icon_wework_robot
|
||||
TYPE_WEWORK_AGENT -> R.drawable.icon_wework_agent
|
||||
TYPE_SERVERCHAN -> R.drawable.icon_serverchan
|
||||
TYPE_TELEGRAM -> R.drawable.icon_telegram
|
||||
TYPE_FEISHU -> R.drawable.icon_feishu
|
||||
TYPE_PUSHPLUS -> R.drawable.icon_pushplus
|
||||
TYPE_GOTIFY -> R.drawable.icon_gotify
|
||||
TYPE_SMS -> R.drawable.icon_sms
|
||||
TYPE_DINGTALK_INNER_ROBOT -> R.drawable.icon_dingtalk_inner
|
||||
TYPE_FEISHU_APP -> R.drawable.icon_feishu_app
|
||||
TYPE_URL_SCHEME -> R.drawable.icon_url_scheme
|
||||
else -> R.drawable.icon_sms
|
||||
}
|
||||
|
||||
val statusImageId: Int
|
||||
get() = when (status) {
|
||||
STATUS_OFF -> R.drawable.icon_off
|
||||
else -> R.drawable.icon_on
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package com.idormy.sms.forwarder.entity.setting
|
||||
|
||||
import java.io.Serializable
|
||||
|
||||
data class UrlSchemeSetting(
|
||||
var urlScheme: String,
|
||||
) : Serializable
|
|
@ -1,162 +1,164 @@
|
|||
package com.idormy.sms.forwarder.fragment
|
||||
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.RecyclerView.RecycledViewPool
|
||||
import com.alibaba.android.vlayout.VirtualLayoutManager
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.idormy.sms.forwarder.adapter.SenderPagingAdapter
|
||||
import com.idormy.sms.forwarder.core.BaseFragment
|
||||
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.FragmentSendersBinding
|
||||
import com.idormy.sms.forwarder.fragment.senders.*
|
||||
import com.idormy.sms.forwarder.utils.*
|
||||
import com.scwang.smartrefresh.layout.api.RefreshLayout
|
||||
import com.xuexiang.xpage.annotation.Page
|
||||
import com.xuexiang.xpage.core.PageOption
|
||||
import com.xuexiang.xui.utils.ResUtils
|
||||
import com.xuexiang.xui.widget.actionbar.TitleBar
|
||||
import com.xuexiang.xui.widget.dialog.materialdialog.DialogAction
|
||||
import com.xuexiang.xui.widget.dialog.materialdialog.MaterialDialog
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Suppress("PropertyName")
|
||||
@Page(name = "发送通道")
|
||||
class SendersFragment : BaseFragment<FragmentSendersBinding?>(), SenderPagingAdapter.OnItemClickListener {
|
||||
|
||||
val TAG: String = SendersFragment::class.java.simpleName
|
||||
private var adapter = SenderPagingAdapter(this)
|
||||
private val viewModel by viewModels<SenderViewModel> { BaseViewModelFactory(context) }
|
||||
private var currentStatus: Int = 1
|
||||
//private val statusValueArray = ResUtils.getIntArray(R.array.status_param_value)
|
||||
|
||||
override fun viewBindingInflate(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup,
|
||||
): FragmentSendersBinding {
|
||||
return FragmentSendersBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 返回为 null意为不需要导航栏
|
||||
*/
|
||||
override fun initTitle(): TitleBar? {
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化控件
|
||||
*/
|
||||
override fun initViews() {
|
||||
val virtualLayoutManager = VirtualLayoutManager(requireContext())
|
||||
binding!!.recyclerView.layoutManager = virtualLayoutManager
|
||||
val viewPool = RecycledViewPool()
|
||||
binding!!.recyclerView.setRecycledViewPool(viewPool)
|
||||
viewPool.setMaxRecycledViews(0, 10)
|
||||
|
||||
binding!!.tabBar.setTabTitles(ResUtils.getStringArray(R.array.status_param_option))
|
||||
binding!!.tabBar.setOnTabClickListener { _, position ->
|
||||
//XToastUtils.toast("点击了$title--$position")
|
||||
//currentStatus = statusValueArray[position]
|
||||
currentStatus = 1 - position //注意:这里刚好相反,可以取巧
|
||||
viewModel.setStatus(currentStatus)
|
||||
adapter.refresh()
|
||||
binding!!.recyclerView.scrollToPosition(0)
|
||||
}
|
||||
}
|
||||
|
||||
override fun initListeners() {
|
||||
binding!!.recyclerView.adapter = adapter
|
||||
|
||||
//下拉刷新
|
||||
binding!!.refreshLayout.setOnRefreshListener { refreshLayout: RefreshLayout ->
|
||||
refreshLayout.layout.postDelayed({
|
||||
//adapter!!.refresh()
|
||||
lifecycleScope.launch {
|
||||
viewModel.setStatus(currentStatus).allSenders.collectLatest { adapter.submitData(it) }
|
||||
}
|
||||
refreshLayout.finishRefresh()
|
||||
}, 200)
|
||||
}
|
||||
|
||||
binding!!.refreshLayout.autoRefresh()
|
||||
}
|
||||
|
||||
override fun onItemClicked(view: View?, item: Sender) {
|
||||
Log.e(TAG, item.toString())
|
||||
when (view?.id) {
|
||||
R.id.iv_copy -> {
|
||||
PageOption.to(
|
||||
when (item.type) {
|
||||
TYPE_DINGTALK_GROUP_ROBOT -> DingtalkGroupRobotFragment::class.java
|
||||
TYPE_EMAIL -> EmailFragment::class.java
|
||||
TYPE_BARK -> BarkFragment::class.java
|
||||
TYPE_WEBHOOK -> WebhookFragment::class.java
|
||||
TYPE_WEWORK_ROBOT -> WeworkRobotFragment::class.java
|
||||
TYPE_WEWORK_AGENT -> WeworkAgentFragment::class.java
|
||||
TYPE_SERVERCHAN -> ServerchanFragment::class.java
|
||||
TYPE_TELEGRAM -> TelegramFragment::class.java
|
||||
TYPE_SMS -> SmsFragment::class.java
|
||||
TYPE_FEISHU -> FeishuFragment::class.java
|
||||
TYPE_PUSHPLUS -> PushplusFragment::class.java
|
||||
TYPE_GOTIFY -> GotifyFragment::class.java
|
||||
TYPE_DINGTALK_INNER_ROBOT -> DingtalkInnerRobotFragment::class.java
|
||||
TYPE_FEISHU_APP -> FeishuAppFragment::class.java
|
||||
else -> DingtalkGroupRobotFragment::class.java
|
||||
}
|
||||
).setNewActivity(true)
|
||||
.putLong(KEY_SENDER_ID, item.id)
|
||||
.putInt(KEY_SENDER_TYPE, item.type)
|
||||
.putBoolean(KEY_SENDER_CLONE, true)
|
||||
.open(this)
|
||||
}
|
||||
R.id.iv_edit -> {
|
||||
PageOption.to(
|
||||
when (item.type) {
|
||||
TYPE_DINGTALK_GROUP_ROBOT -> DingtalkGroupRobotFragment::class.java
|
||||
TYPE_EMAIL -> EmailFragment::class.java
|
||||
TYPE_BARK -> BarkFragment::class.java
|
||||
TYPE_WEBHOOK -> WebhookFragment::class.java
|
||||
TYPE_WEWORK_ROBOT -> WeworkRobotFragment::class.java
|
||||
TYPE_WEWORK_AGENT -> WeworkAgentFragment::class.java
|
||||
TYPE_SERVERCHAN -> ServerchanFragment::class.java
|
||||
TYPE_TELEGRAM -> TelegramFragment::class.java
|
||||
TYPE_SMS -> SmsFragment::class.java
|
||||
TYPE_FEISHU -> FeishuFragment::class.java
|
||||
TYPE_PUSHPLUS -> PushplusFragment::class.java
|
||||
TYPE_GOTIFY -> GotifyFragment::class.java
|
||||
TYPE_DINGTALK_INNER_ROBOT -> DingtalkInnerRobotFragment::class.java
|
||||
TYPE_FEISHU_APP -> FeishuAppFragment::class.java
|
||||
else -> DingtalkGroupRobotFragment::class.java
|
||||
}
|
||||
).setNewActivity(true)
|
||||
.putLong(KEY_SENDER_ID, item.id)
|
||||
.putInt(KEY_SENDER_TYPE, item.type)
|
||||
.open(this)
|
||||
}
|
||||
R.id.iv_delete -> {
|
||||
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(item.id)
|
||||
XToastUtils.success(R.string.delete_sender_toast)
|
||||
}
|
||||
.show()
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onItemRemove(view: View?, id: Int) {}
|
||||
|
||||
package com.idormy.sms.forwarder.fragment
|
||||
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.RecyclerView.RecycledViewPool
|
||||
import com.alibaba.android.vlayout.VirtualLayoutManager
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.idormy.sms.forwarder.adapter.SenderPagingAdapter
|
||||
import com.idormy.sms.forwarder.core.BaseFragment
|
||||
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.FragmentSendersBinding
|
||||
import com.idormy.sms.forwarder.fragment.senders.*
|
||||
import com.idormy.sms.forwarder.utils.*
|
||||
import com.scwang.smartrefresh.layout.api.RefreshLayout
|
||||
import com.xuexiang.xpage.annotation.Page
|
||||
import com.xuexiang.xpage.core.PageOption
|
||||
import com.xuexiang.xui.utils.ResUtils
|
||||
import com.xuexiang.xui.widget.actionbar.TitleBar
|
||||
import com.xuexiang.xui.widget.dialog.materialdialog.DialogAction
|
||||
import com.xuexiang.xui.widget.dialog.materialdialog.MaterialDialog
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Suppress("PropertyName")
|
||||
@Page(name = "发送通道")
|
||||
class SendersFragment : BaseFragment<FragmentSendersBinding?>(), SenderPagingAdapter.OnItemClickListener {
|
||||
|
||||
val TAG: String = SendersFragment::class.java.simpleName
|
||||
private var adapter = SenderPagingAdapter(this)
|
||||
private val viewModel by viewModels<SenderViewModel> { BaseViewModelFactory(context) }
|
||||
private var currentStatus: Int = 1
|
||||
//private val statusValueArray = ResUtils.getIntArray(R.array.status_param_value)
|
||||
|
||||
override fun viewBindingInflate(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup,
|
||||
): FragmentSendersBinding {
|
||||
return FragmentSendersBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 返回为 null意为不需要导航栏
|
||||
*/
|
||||
override fun initTitle(): TitleBar? {
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化控件
|
||||
*/
|
||||
override fun initViews() {
|
||||
val virtualLayoutManager = VirtualLayoutManager(requireContext())
|
||||
binding!!.recyclerView.layoutManager = virtualLayoutManager
|
||||
val viewPool = RecycledViewPool()
|
||||
binding!!.recyclerView.setRecycledViewPool(viewPool)
|
||||
viewPool.setMaxRecycledViews(0, 10)
|
||||
|
||||
binding!!.tabBar.setTabTitles(ResUtils.getStringArray(R.array.status_param_option))
|
||||
binding!!.tabBar.setOnTabClickListener { _, position ->
|
||||
//XToastUtils.toast("点击了$title--$position")
|
||||
//currentStatus = statusValueArray[position]
|
||||
currentStatus = 1 - position //注意:这里刚好相反,可以取巧
|
||||
viewModel.setStatus(currentStatus)
|
||||
adapter.refresh()
|
||||
binding!!.recyclerView.scrollToPosition(0)
|
||||
}
|
||||
}
|
||||
|
||||
override fun initListeners() {
|
||||
binding!!.recyclerView.adapter = adapter
|
||||
|
||||
//下拉刷新
|
||||
binding!!.refreshLayout.setOnRefreshListener { refreshLayout: RefreshLayout ->
|
||||
refreshLayout.layout.postDelayed({
|
||||
//adapter!!.refresh()
|
||||
lifecycleScope.launch {
|
||||
viewModel.setStatus(currentStatus).allSenders.collectLatest { adapter.submitData(it) }
|
||||
}
|
||||
refreshLayout.finishRefresh()
|
||||
}, 200)
|
||||
}
|
||||
|
||||
binding!!.refreshLayout.autoRefresh()
|
||||
}
|
||||
|
||||
override fun onItemClicked(view: View?, item: Sender) {
|
||||
Log.e(TAG, item.toString())
|
||||
when (view?.id) {
|
||||
R.id.iv_copy -> {
|
||||
PageOption.to(
|
||||
when (item.type) {
|
||||
TYPE_DINGTALK_GROUP_ROBOT -> DingtalkGroupRobotFragment::class.java
|
||||
TYPE_EMAIL -> EmailFragment::class.java
|
||||
TYPE_BARK -> BarkFragment::class.java
|
||||
TYPE_WEBHOOK -> WebhookFragment::class.java
|
||||
TYPE_WEWORK_ROBOT -> WeworkRobotFragment::class.java
|
||||
TYPE_WEWORK_AGENT -> WeworkAgentFragment::class.java
|
||||
TYPE_SERVERCHAN -> ServerchanFragment::class.java
|
||||
TYPE_TELEGRAM -> TelegramFragment::class.java
|
||||
TYPE_SMS -> SmsFragment::class.java
|
||||
TYPE_FEISHU -> FeishuFragment::class.java
|
||||
TYPE_PUSHPLUS -> PushplusFragment::class.java
|
||||
TYPE_GOTIFY -> GotifyFragment::class.java
|
||||
TYPE_DINGTALK_INNER_ROBOT -> DingtalkInnerRobotFragment::class.java
|
||||
TYPE_FEISHU_APP -> FeishuAppFragment::class.java
|
||||
TYPE_URL_SCHEME -> UrlSchemeFragment::class.java
|
||||
else -> DingtalkGroupRobotFragment::class.java
|
||||
}
|
||||
).setNewActivity(true)
|
||||
.putLong(KEY_SENDER_ID, item.id)
|
||||
.putInt(KEY_SENDER_TYPE, item.type)
|
||||
.putBoolean(KEY_SENDER_CLONE, true)
|
||||
.open(this)
|
||||
}
|
||||
R.id.iv_edit -> {
|
||||
PageOption.to(
|
||||
when (item.type) {
|
||||
TYPE_DINGTALK_GROUP_ROBOT -> DingtalkGroupRobotFragment::class.java
|
||||
TYPE_EMAIL -> EmailFragment::class.java
|
||||
TYPE_BARK -> BarkFragment::class.java
|
||||
TYPE_WEBHOOK -> WebhookFragment::class.java
|
||||
TYPE_WEWORK_ROBOT -> WeworkRobotFragment::class.java
|
||||
TYPE_WEWORK_AGENT -> WeworkAgentFragment::class.java
|
||||
TYPE_SERVERCHAN -> ServerchanFragment::class.java
|
||||
TYPE_TELEGRAM -> TelegramFragment::class.java
|
||||
TYPE_SMS -> SmsFragment::class.java
|
||||
TYPE_FEISHU -> FeishuFragment::class.java
|
||||
TYPE_PUSHPLUS -> PushplusFragment::class.java
|
||||
TYPE_GOTIFY -> GotifyFragment::class.java
|
||||
TYPE_DINGTALK_INNER_ROBOT -> DingtalkInnerRobotFragment::class.java
|
||||
TYPE_FEISHU_APP -> FeishuAppFragment::class.java
|
||||
TYPE_URL_SCHEME -> UrlSchemeFragment::class.java
|
||||
else -> DingtalkGroupRobotFragment::class.java
|
||||
}
|
||||
).setNewActivity(true)
|
||||
.putLong(KEY_SENDER_ID, item.id)
|
||||
.putInt(KEY_SENDER_TYPE, item.type)
|
||||
.open(this)
|
||||
}
|
||||
R.id.iv_delete -> {
|
||||
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(item.id)
|
||||
XToastUtils.success(R.string.delete_sender_toast)
|
||||
}
|
||||
.show()
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onItemRemove(view: View?, id: Int) {}
|
||||
|
||||
}
|
|
@ -0,0 +1,227 @@
|
|||
package com.idormy.sms.forwarder.fragment.senders
|
||||
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.pm.ResolveInfo
|
||||
import android.net.Uri
|
||||
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 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.FragmentSendersUrlSchemeBinding
|
||||
import com.idormy.sms.forwarder.entity.MsgInfo
|
||||
import com.idormy.sms.forwarder.entity.setting.UrlSchemeSetting
|
||||
import com.idormy.sms.forwarder.utils.*
|
||||
import com.idormy.sms.forwarder.utils.sender.UrlSchemeUtils
|
||||
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 = "URL Scheme")
|
||||
@Suppress("PrivatePropertyName")
|
||||
class UrlSchemeFragment : BaseFragment<FragmentSendersUrlSchemeBinding?>(), View.OnClickListener {
|
||||
|
||||
private val TAG: String = UrlSchemeFragment::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,
|
||||
): FragmentSendersUrlSchemeBinding {
|
||||
return FragmentSendersUrlSchemeBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
override fun initTitle(): TitleBar? {
|
||||
titleBar = super.initTitle()!!.setImmersive(false).setTitle(R.string.url_scheme)
|
||||
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, UrlSchemeSetting::class.java)
|
||||
Log.d(TAG, settingVo.toString())
|
||||
if (settingVo != null) {
|
||||
binding!!.etUrlScheme.setText(settingVo.urlScheme)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun initListeners() {
|
||||
binding!!.btnTest.setOnClickListener(this)
|
||||
binding!!.btnDel.setOnClickListener(this)
|
||||
binding!!.btnSave.setOnClickListener(this)
|
||||
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))
|
||||
UrlSchemeUtils.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(): UrlSchemeSetting {
|
||||
val urlScheme = binding!!.etUrlScheme.text.toString().trim()
|
||||
if (!CommonUtils.checkUrlScheme(urlScheme, false)) {
|
||||
throw Exception(getString(R.string.invalid_url_scheme))
|
||||
}
|
||||
|
||||
//TODO:判断Sheme是否有效
|
||||
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(urlScheme))
|
||||
val packageManager: PackageManager = requireContext().packageManager
|
||||
val activities: List<ResolveInfo> = packageManager.queryIntentActivities(intent, 0)
|
||||
if (activities.isEmpty()) {
|
||||
throw Exception(getString(R.string.invalid_url_scheme))
|
||||
}
|
||||
|
||||
return UrlSchemeSetting(urlScheme)
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
if (mCountDownHelper != null) mCountDownHelper!!.recycle()
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
}
|
|
@ -1,276 +1,290 @@
|
|||
package com.idormy.sms.forwarder.utils
|
||||
|
||||
import android.app.Dialog
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.Rect
|
||||
import android.text.SpannableString
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.text.Spanned
|
||||
import android.text.TextUtils
|
||||
import android.text.method.LinkMovementMethod
|
||||
import android.text.style.ClickableSpan
|
||||
import android.view.View
|
||||
import android.widget.EditText
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.idormy.sms.forwarder.core.webview.AgentWebActivity
|
||||
import com.idormy.sms.forwarder.core.webview.AgentWebFragment
|
||||
import com.idormy.sms.forwarder.entity.ImageInfo
|
||||
import com.idormy.sms.forwarder.fragment.MarkdownFragment
|
||||
import com.idormy.sms.forwarder.fragment.ServiceProtocolFragment
|
||||
import com.idormy.sms.forwarder.service.NotifyService
|
||||
import com.xuexiang.xpage.base.XPageFragment
|
||||
import com.xuexiang.xpage.core.PageOption
|
||||
import com.xuexiang.xui.utils.ColorUtils
|
||||
import com.xuexiang.xui.utils.ResUtils
|
||||
import com.xuexiang.xui.widget.dialog.DialogLoader
|
||||
import com.xuexiang.xui.widget.dialog.materialdialog.DialogAction
|
||||
import com.xuexiang.xui.widget.dialog.materialdialog.MaterialDialog
|
||||
import com.xuexiang.xui.widget.dialog.materialdialog.MaterialDialog.SingleButtonCallback
|
||||
import com.xuexiang.xui.widget.imageview.preview.PreviewBuilder
|
||||
import com.xuexiang.xutil.XUtil
|
||||
import com.xuexiang.xutil.common.StringUtils
|
||||
import java.util.regex.Pattern
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
/**
|
||||
* 常用工具类
|
||||
*/
|
||||
@Suppress("RegExpRedundantEscape", "unused")
|
||||
class CommonUtils private constructor() {
|
||||
companion object {
|
||||
/**
|
||||
* 这里填写你的应用隐私政策网页地址
|
||||
*/
|
||||
private const val PRIVACY_URL = "https://gitee.com/pp/SmsForwarder/raw/main/PRIVACY"
|
||||
|
||||
/**
|
||||
* 显示隐私政策的提示
|
||||
*
|
||||
* @param context
|
||||
* @param submitListener 同意的监听
|
||||
* @return
|
||||
*/
|
||||
@Suppress("SameParameterValue", "NAME_SHADOWING")
|
||||
@JvmStatic
|
||||
fun showPrivacyDialog(context: Context, submitListener: SingleButtonCallback?): 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? ->
|
||||
if (submitListener != null) {
|
||||
submitListener.onClick(dialog1, which!!)
|
||||
} else {
|
||||
dialog1.dismiss()
|
||||
}
|
||||
}
|
||||
.negativeText(R.string.lab_disagree).onNegative { dialog, _ ->
|
||||
dialog.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)
|
||||
),
|
||||
ResUtils.getString(R.string.lab_look_again),
|
||||
{ dialog, _ ->
|
||||
dialog.dismiss()
|
||||
showPrivacyDialog(context, submitListener)
|
||||
},
|
||||
ResUtils.getString(R.string.lab_still_disagree)
|
||||
) { dialog, _ ->
|
||||
dialog.dismiss()
|
||||
DialogLoader.getInstance().showConfirmDialog(
|
||||
context,
|
||||
ResUtils.getString(R.string.content_think_about_it_again),
|
||||
ResUtils.getString(R.string.lab_look_again),
|
||||
{ dialog, _ ->
|
||||
dialog.dismiss()
|
||||
showPrivacyDialog(context, submitListener)
|
||||
},
|
||||
ResUtils.getString(R.string.lab_exit_app)
|
||||
) { dialog, _ ->
|
||||
dialog.dismiss()
|
||||
XUtil.exitApp()
|
||||
}
|
||||
}
|
||||
}.build()
|
||||
dialog.setContent(getPrivacyContent(context))
|
||||
//开始响应点击事件
|
||||
dialog.contentView!!.movementMethod = LinkMovementMethod.getInstance()
|
||||
dialog.show()
|
||||
return dialog
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 隐私政策说明
|
||||
*/
|
||||
private fun getPrivacyContent(context: Context): 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))
|
||||
}
|
||||
|
||||
/**
|
||||
* @param context 隐私政策的链接
|
||||
* @return
|
||||
*/
|
||||
@Suppress("SameParameterValue")
|
||||
private fun getPrivacyLink(context: Context, privacyUrl: String): SpannableString {
|
||||
val privacyName = String.format(
|
||||
ResUtils.getString(R.string.lab_privacy_name),
|
||||
ResUtils.getString(R.string.app_name)
|
||||
)
|
||||
val spannableString = SpannableString(privacyName)
|
||||
spannableString.setSpan(object : ClickableSpan() {
|
||||
override fun onClick(widget: View) {
|
||||
goWeb(context, privacyUrl)
|
||||
}
|
||||
}, 0, privacyName.length, Spanned.SPAN_MARK_MARK)
|
||||
return spannableString
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求浏览器
|
||||
*
|
||||
* @param url
|
||||
*/
|
||||
@JvmStatic
|
||||
fun goWeb(context: Context, url: String?) {
|
||||
val intent = Intent(context, AgentWebActivity::class.java)
|
||||
intent.putExtra(AgentWebFragment.KEY_URL, url)
|
||||
context.startActivity(intent)
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开用户协议和隐私协议
|
||||
*
|
||||
* @param fragment
|
||||
* @param isPrivacy 是否是隐私协议
|
||||
* @param isImmersive 是否沉浸式
|
||||
*/
|
||||
@JvmStatic
|
||||
fun gotoProtocol(fragment: XPageFragment?, isPrivacy: Boolean, isImmersive: Boolean) {
|
||||
PageOption.to(ServiceProtocolFragment::class.java)
|
||||
.putString(
|
||||
ServiceProtocolFragment.KEY_PROTOCOL_TITLE,
|
||||
if (isPrivacy) ResUtils.getString(R.string.title_privacy_protocol) else ResUtils.getString(
|
||||
R.string.title_user_protocol
|
||||
)
|
||||
)
|
||||
.putBoolean(ServiceProtocolFragment.KEY_IS_IMMERSIVE, isImmersive)
|
||||
.open(fragment!!)
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否是深色的颜色
|
||||
*
|
||||
* @param color
|
||||
* @return
|
||||
*/
|
||||
@JvmStatic
|
||||
fun isColorDark(@ColorInt color: Int): Boolean {
|
||||
return ColorUtils.isColorDark(color, 0.382)
|
||||
}
|
||||
|
||||
//焦点位置插入文本
|
||||
fun insertOrReplaceText2Cursor(editText: EditText, str: String) {
|
||||
editText.isFocusable = true
|
||||
editText.requestFocus()
|
||||
val start = max(editText.selectionStart, 0)
|
||||
val end = max(editText.selectionEnd, 0)
|
||||
editText.text.replace(min(start, end), max(start, end), str, 0, str.length)
|
||||
}
|
||||
|
||||
//==========图片预览===========//
|
||||
/**
|
||||
* 大图预览
|
||||
*
|
||||
* @param fragment
|
||||
* @param url 图片资源
|
||||
* @param view 小图加载控件
|
||||
*/
|
||||
fun previewPicture(fragment: Fragment?, url: String, view: View?) {
|
||||
if (fragment == null || StringUtils.isEmpty(url)) {
|
||||
return
|
||||
}
|
||||
val bounds = Rect()
|
||||
view?.getGlobalVisibleRect(bounds)
|
||||
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()
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开Markdown链接并渲染
|
||||
*
|
||||
* @param fragment
|
||||
* @param url Markdown链接
|
||||
* @param isImmersive 是否沉浸式
|
||||
*/
|
||||
@JvmStatic
|
||||
fun previewMarkdown(fragment: XPageFragment?, title: String, url: String, isImmersive: Boolean) {
|
||||
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!!)
|
||||
}
|
||||
|
||||
//是否合法的url
|
||||
fun checkUrl(urls: String?): Boolean {
|
||||
return checkUrl(urls, false)
|
||||
}
|
||||
|
||||
//是否合法的url
|
||||
fun checkUrl(urls: String?, emptyResult: Boolean): Boolean {
|
||||
if (TextUtils.isEmpty(urls)) return emptyResult
|
||||
val regex = "^(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;\\[\\]]*[-a-zA-Z0-9+&@#/%=~_|\\[\\]]"
|
||||
val pat = Pattern.compile(regex)
|
||||
val mat = pat.matcher(urls?.trim() ?: "")
|
||||
return mat.matches()
|
||||
}
|
||||
|
||||
//是否启用通知监听服务
|
||||
fun isNotificationListenerServiceEnabled(context: Context): Boolean {
|
||||
val packageNames = NotificationManagerCompat.getEnabledListenerPackages(context)
|
||||
return packageNames.contains(context.packageName)
|
||||
}
|
||||
|
||||
//开关通知监听服务
|
||||
fun toggleNotificationListenerService(context: Context) {
|
||||
val pm = context.packageManager
|
||||
pm.setComponentEnabledSetting(
|
||||
ComponentName(context.applicationContext, NotifyService::class.java),
|
||||
PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP
|
||||
)
|
||||
pm.setComponentEnabledSetting(
|
||||
ComponentName(context.applicationContext, NotifyService::class.java),
|
||||
PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
init {
|
||||
throw UnsupportedOperationException("u can't instantiate me...")
|
||||
}
|
||||
package com.idormy.sms.forwarder.utils
|
||||
|
||||
import android.app.Dialog
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.Rect
|
||||
import android.text.SpannableString
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.text.Spanned
|
||||
import android.text.TextUtils
|
||||
import android.text.method.LinkMovementMethod
|
||||
import android.text.style.ClickableSpan
|
||||
import android.view.View
|
||||
import android.widget.EditText
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.idormy.sms.forwarder.core.webview.AgentWebActivity
|
||||
import com.idormy.sms.forwarder.core.webview.AgentWebFragment
|
||||
import com.idormy.sms.forwarder.entity.ImageInfo
|
||||
import com.idormy.sms.forwarder.fragment.MarkdownFragment
|
||||
import com.idormy.sms.forwarder.fragment.ServiceProtocolFragment
|
||||
import com.idormy.sms.forwarder.service.NotifyService
|
||||
import com.xuexiang.xpage.base.XPageFragment
|
||||
import com.xuexiang.xpage.core.PageOption
|
||||
import com.xuexiang.xui.utils.ColorUtils
|
||||
import com.xuexiang.xui.utils.ResUtils
|
||||
import com.xuexiang.xui.widget.dialog.DialogLoader
|
||||
import com.xuexiang.xui.widget.dialog.materialdialog.DialogAction
|
||||
import com.xuexiang.xui.widget.dialog.materialdialog.MaterialDialog
|
||||
import com.xuexiang.xui.widget.dialog.materialdialog.MaterialDialog.SingleButtonCallback
|
||||
import com.xuexiang.xui.widget.imageview.preview.PreviewBuilder
|
||||
import com.xuexiang.xutil.XUtil
|
||||
import com.xuexiang.xutil.common.StringUtils
|
||||
import java.util.regex.Pattern
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
/**
|
||||
* 常用工具类
|
||||
*/
|
||||
@Suppress("RegExpRedundantEscape", "unused")
|
||||
class CommonUtils private constructor() {
|
||||
companion object {
|
||||
/**
|
||||
* 这里填写你的应用隐私政策网页地址
|
||||
*/
|
||||
private const val PRIVACY_URL = "https://gitee.com/pp/SmsForwarder/raw/main/PRIVACY"
|
||||
|
||||
/**
|
||||
* 显示隐私政策的提示
|
||||
*
|
||||
* @param context
|
||||
* @param submitListener 同意的监听
|
||||
* @return
|
||||
*/
|
||||
@Suppress("SameParameterValue", "NAME_SHADOWING")
|
||||
@JvmStatic
|
||||
fun showPrivacyDialog(context: Context, submitListener: SingleButtonCallback?): 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? ->
|
||||
if (submitListener != null) {
|
||||
submitListener.onClick(dialog1, which!!)
|
||||
} else {
|
||||
dialog1.dismiss()
|
||||
}
|
||||
}
|
||||
.negativeText(R.string.lab_disagree).onNegative { dialog, _ ->
|
||||
dialog.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)
|
||||
),
|
||||
ResUtils.getString(R.string.lab_look_again),
|
||||
{ dialog, _ ->
|
||||
dialog.dismiss()
|
||||
showPrivacyDialog(context, submitListener)
|
||||
},
|
||||
ResUtils.getString(R.string.lab_still_disagree)
|
||||
) { dialog, _ ->
|
||||
dialog.dismiss()
|
||||
DialogLoader.getInstance().showConfirmDialog(
|
||||
context,
|
||||
ResUtils.getString(R.string.content_think_about_it_again),
|
||||
ResUtils.getString(R.string.lab_look_again),
|
||||
{ dialog, _ ->
|
||||
dialog.dismiss()
|
||||
showPrivacyDialog(context, submitListener)
|
||||
},
|
||||
ResUtils.getString(R.string.lab_exit_app)
|
||||
) { dialog, _ ->
|
||||
dialog.dismiss()
|
||||
XUtil.exitApp()
|
||||
}
|
||||
}
|
||||
}.build()
|
||||
dialog.setContent(getPrivacyContent(context))
|
||||
//开始响应点击事件
|
||||
dialog.contentView!!.movementMethod = LinkMovementMethod.getInstance()
|
||||
dialog.show()
|
||||
return dialog
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 隐私政策说明
|
||||
*/
|
||||
private fun getPrivacyContent(context: Context): 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))
|
||||
}
|
||||
|
||||
/**
|
||||
* @param context 隐私政策的链接
|
||||
* @return
|
||||
*/
|
||||
@Suppress("SameParameterValue")
|
||||
private fun getPrivacyLink(context: Context, privacyUrl: String): SpannableString {
|
||||
val privacyName = String.format(
|
||||
ResUtils.getString(R.string.lab_privacy_name),
|
||||
ResUtils.getString(R.string.app_name)
|
||||
)
|
||||
val spannableString = SpannableString(privacyName)
|
||||
spannableString.setSpan(object : ClickableSpan() {
|
||||
override fun onClick(widget: View) {
|
||||
goWeb(context, privacyUrl)
|
||||
}
|
||||
}, 0, privacyName.length, Spanned.SPAN_MARK_MARK)
|
||||
return spannableString
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求浏览器
|
||||
*
|
||||
* @param url
|
||||
*/
|
||||
@JvmStatic
|
||||
fun goWeb(context: Context, url: String?) {
|
||||
val intent = Intent(context, AgentWebActivity::class.java)
|
||||
intent.putExtra(AgentWebFragment.KEY_URL, url)
|
||||
context.startActivity(intent)
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开用户协议和隐私协议
|
||||
*
|
||||
* @param fragment
|
||||
* @param isPrivacy 是否是隐私协议
|
||||
* @param isImmersive 是否沉浸式
|
||||
*/
|
||||
@JvmStatic
|
||||
fun gotoProtocol(fragment: XPageFragment?, isPrivacy: Boolean, isImmersive: Boolean) {
|
||||
PageOption.to(ServiceProtocolFragment::class.java)
|
||||
.putString(
|
||||
ServiceProtocolFragment.KEY_PROTOCOL_TITLE,
|
||||
if (isPrivacy) ResUtils.getString(R.string.title_privacy_protocol) else ResUtils.getString(
|
||||
R.string.title_user_protocol
|
||||
)
|
||||
)
|
||||
.putBoolean(ServiceProtocolFragment.KEY_IS_IMMERSIVE, isImmersive)
|
||||
.open(fragment!!)
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否是深色的颜色
|
||||
*
|
||||
* @param color
|
||||
* @return
|
||||
*/
|
||||
@JvmStatic
|
||||
fun isColorDark(@ColorInt color: Int): Boolean {
|
||||
return ColorUtils.isColorDark(color, 0.382)
|
||||
}
|
||||
|
||||
//焦点位置插入文本
|
||||
fun insertOrReplaceText2Cursor(editText: EditText, str: String) {
|
||||
editText.isFocusable = true
|
||||
editText.requestFocus()
|
||||
val start = max(editText.selectionStart, 0)
|
||||
val end = max(editText.selectionEnd, 0)
|
||||
editText.text.replace(min(start, end), max(start, end), str, 0, str.length)
|
||||
}
|
||||
|
||||
//==========图片预览===========//
|
||||
/**
|
||||
* 大图预览
|
||||
*
|
||||
* @param fragment
|
||||
* @param url 图片资源
|
||||
* @param view 小图加载控件
|
||||
*/
|
||||
fun previewPicture(fragment: Fragment?, url: String, view: View?) {
|
||||
if (fragment == null || StringUtils.isEmpty(url)) {
|
||||
return
|
||||
}
|
||||
val bounds = Rect()
|
||||
view?.getGlobalVisibleRect(bounds)
|
||||
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()
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开Markdown链接并渲染
|
||||
*
|
||||
* @param fragment
|
||||
* @param url Markdown链接
|
||||
* @param isImmersive 是否沉浸式
|
||||
*/
|
||||
@JvmStatic
|
||||
fun previewMarkdown(fragment: XPageFragment?, title: String, url: String, isImmersive: Boolean) {
|
||||
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!!)
|
||||
}
|
||||
|
||||
//是否合法的url
|
||||
fun checkUrl(urls: String?): Boolean {
|
||||
return checkUrl(urls, false)
|
||||
}
|
||||
|
||||
//是否合法的url
|
||||
fun checkUrl(urls: String?, emptyResult: Boolean): Boolean {
|
||||
if (TextUtils.isEmpty(urls)) return emptyResult
|
||||
val regex = "^(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;\\[\\]]*[-a-zA-Z0-9+&@#/%=~_|\\[\\]]"
|
||||
val pat = Pattern.compile(regex)
|
||||
val mat = pat.matcher(urls?.trim() ?: "")
|
||||
return mat.matches()
|
||||
}
|
||||
|
||||
//是否合法的URL Scheme
|
||||
fun checkUrlScheme(urls: String?): Boolean {
|
||||
return checkUrlScheme(urls, false)
|
||||
}
|
||||
|
||||
//是否合法的URL Scheme
|
||||
fun checkUrlScheme(urls: String?, emptyResult: Boolean): Boolean {
|
||||
if (TextUtils.isEmpty(urls)) return emptyResult
|
||||
val regex = "^[a-zA-Z0-9]+://[-a-zA-Z0-9+&@#/%?=~_|!:,.;\\[\\]]*[-a-zA-Z0-9+&@#/%=~_|\\[\\]]"
|
||||
val pat = Pattern.compile(regex)
|
||||
val mat = pat.matcher(urls?.trim() ?: "")
|
||||
return mat.matches()
|
||||
}
|
||||
|
||||
//是否启用通知监听服务
|
||||
fun isNotificationListenerServiceEnabled(context: Context): Boolean {
|
||||
val packageNames = NotificationManagerCompat.getEnabledListenerPackages(context)
|
||||
return packageNames.contains(context.packageName)
|
||||
}
|
||||
|
||||
//开关通知监听服务
|
||||
fun toggleNotificationListenerService(context: Context) {
|
||||
val pm = context.packageManager
|
||||
pm.setComponentEnabledSetting(
|
||||
ComponentName(context.applicationContext, NotifyService::class.java),
|
||||
PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP
|
||||
)
|
||||
pm.setComponentEnabledSetting(
|
||||
ComponentName(context.applicationContext, NotifyService::class.java),
|
||||
PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
init {
|
||||
throw UnsupportedOperationException("u can't instantiate me...")
|
||||
}
|
||||
}
|
|
@ -166,6 +166,7 @@ const val TYPE_PUSHPLUS = 10
|
|||
const val TYPE_GOTIFY = 11
|
||||
const val TYPE_DINGTALK_INNER_ROBOT = 12
|
||||
const val TYPE_FEISHU_APP = 13
|
||||
const val TYPE_URL_SCHEME = 14
|
||||
var SENDER_FRAGMENT_LIST = listOf(
|
||||
PageInfo(
|
||||
getString(R.string.dingtalk_robot),
|
||||
|
@ -265,6 +266,13 @@ var SENDER_FRAGMENT_LIST = listOf(
|
|||
CoreAnim.slide,
|
||||
R.drawable.icon_feishu_app
|
||||
),
|
||||
PageInfo(
|
||||
getString(R.string.url_scheme),
|
||||
"com.idormy.sms.forwarder.fragment.senders.UrlSchemeFragment",
|
||||
"{\"\":\"\"}",
|
||||
CoreAnim.slide,
|
||||
R.drawable.icon_url_scheme
|
||||
),
|
||||
)
|
||||
|
||||
//前台服务
|
||||
|
|
|
@ -126,6 +126,10 @@ object SendUtils {
|
|||
val settingVo = Gson().fromJson(sender.jsonSetting, FeishuAppSetting::class.java)
|
||||
FeishuAppUtils.sendMsg(settingVo, msgInfo, rule, logId)
|
||||
}
|
||||
TYPE_URL_SCHEME -> {
|
||||
val settingVo = Gson().fromJson(sender.jsonSetting, UrlSchemeSetting::class.java)
|
||||
UrlSchemeUtils.sendMsg(settingVo, msgInfo, rule, logId)
|
||||
}
|
||||
else -> {
|
||||
updateLogs(logId, 0, "未知发送通道")
|
||||
}
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
package com.idormy.sms.forwarder.utils.sender
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.util.Log
|
||||
import com.idormy.sms.forwarder.database.entity.Rule
|
||||
import com.idormy.sms.forwarder.entity.MsgInfo
|
||||
import com.idormy.sms.forwarder.entity.setting.UrlSchemeSetting
|
||||
import com.idormy.sms.forwarder.utils.SendUtils
|
||||
import com.idormy.sms.forwarder.utils.SettingUtils
|
||||
import com.xuexiang.xutil.XUtil
|
||||
import com.xuexiang.xutil.app.AppUtils
|
||||
import java.net.URLEncoder
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
|
||||
@Suppress("PrivatePropertyName", "UNUSED_PARAMETER")
|
||||
class UrlSchemeUtils private constructor() {
|
||||
companion object {
|
||||
|
||||
private val TAG: String = UrlSchemeUtils::class.java.simpleName
|
||||
|
||||
fun sendMsg(
|
||||
setting: UrlSchemeSetting,
|
||||
msgInfo: MsgInfo,
|
||||
rule: Rule?,
|
||||
logId: Long?,
|
||||
) {
|
||||
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 urlScheme = setting.urlScheme
|
||||
Log.i(TAG, "urlScheme:$urlScheme")
|
||||
|
||||
urlScheme = urlScheme.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("[timestamp]", timestamp.toString())
|
||||
.replace("\n", "%0A")
|
||||
Log.i(TAG, "urlScheme:$urlScheme")
|
||||
|
||||
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(urlScheme))
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
try {
|
||||
XUtil.getContext().startActivity(intent)
|
||||
SendUtils.updateLogs(logId, 2, "调用成功")
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
Log.e(TAG, e.message.toString())
|
||||
SendUtils.updateLogs(logId, 0, e.message.toString())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun sendMsg(setting: UrlSchemeSetting, msgInfo: MsgInfo) {
|
||||
sendMsg(setting, msgInfo, null, null)
|
||||
}
|
||||
}
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 2.3 KiB |
|
@ -0,0 +1,122 @@
|
|||
<?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="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/url_scheme"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/url_scheme_tips"
|
||||
android:textSize="10sp"
|
||||
tools:ignore="SmallSp" />
|
||||
|
||||
<com.xuexiang.xui.widget.edittext.materialedittext.MaterialEditText
|
||||
android:id="@+id/et_url_scheme"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true"
|
||||
app:met_clearButton="true" />
|
||||
|
||||
</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>
|
|
@ -277,6 +277,7 @@
|
|||
<string name="invalid_openid">Multiple openids are separated by ,</string>
|
||||
<string name="invalid_webserver">WebServer is empty or not a valid URL</string>
|
||||
<string name="invalid_webhook">WebHook is empty or not a valid URL</string>
|
||||
<string name="invalid_url_scheme">URL Scheme is empty or invalid</string>
|
||||
<string name="invalid_at_mobiles">toUser/toParty/toTag cannot be empty or select @all</string>
|
||||
<string name="invalid_wework_agent">CoreID, AgentID, and Secret cannot be empty</string>
|
||||
<string name="invalid_dingtalk_inner_robot">AgentId, AppKey, AppSecret, and UserIds cannot be empty</string>
|
||||
|
@ -334,12 +335,12 @@
|
|||
<!--SettingActivity-->
|
||||
<string name="notify_content">Notify Content</string>
|
||||
<string name="device_name">Device Name</string>
|
||||
<string name="sim_sub_id">SIM1 SubId</string>
|
||||
<string name="sim_sub_id">SIM SubId</string>
|
||||
<string name="sim1_remark" tools:ignore="Typos">SIM1 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="tip_number_only_error_message">Number must be greater than 0!</string>
|
||||
<string name="regexp_number_only">^[1-9]?\\d+$</string>
|
||||
<string name="regexp_number_only" tools:ignore="Typos">^[1-9]?\\d+$</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="retry_interval">Retry Interval</string>
|
||||
|
@ -695,6 +696,9 @@
|
|||
<string name="wework_webHook">WebHook</string>
|
||||
<string name="wework_webHook_tips">Example: https://qyapi.weixin.qq.com/cgixx?key=xxx</string>
|
||||
|
||||
<string name="url_scheme">URL Scheme</string>
|
||||
<string name="url_scheme_tips">Example:myapp://api/add?&type=0&msg=[msg]</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_params">Params</string>
|
||||
|
|
|
@ -278,6 +278,7 @@
|
|||
<string name="invalid_openid">多个 openid 用 , 隔开</string>
|
||||
<string name="invalid_webserver">WebServer为空 或 不是有效URL</string>
|
||||
<string name="invalid_webhook">WebHook为空 或 不是有效URL</string>
|
||||
<string name="invalid_url_scheme">URL Scheme 为空 或 无效</string>
|
||||
<string name="invalid_at_mobiles">指定成员/指定部门/指定标签 不能为空 或者 选择@all</string>
|
||||
<string name="invalid_wework_agent">企业ID、AgentID、Secret都不能为空</string>
|
||||
<string name="invalid_dingtalk_inner_robot">AgentId、AppKey、AppSecret、UserIds都不能为空</string>
|
||||
|
@ -340,7 +341,7 @@
|
|||
<string name="sim2_remark" tools:ignore="Typos">SIM2主键/备注</string>
|
||||
<string name="carrier_mobile">序号/运营商_手机号</string>
|
||||
<string name="tip_number_only_error_message">数字必须大于0!</string>
|
||||
<string name="regexp_number_only">^[1-9]?\\d+$</string>
|
||||
<string name="regexp_number_only" tools:ignore="Typos">^[1-9]?\\d+$</string>
|
||||
<string name="low_power_alarm_threshold">安全电量范围(%)</string>
|
||||
<string name="low_power_alarm_threshold_tips">超出安全范围将发出预警</string>
|
||||
<string name="retry_interval">请求重试机制</string>
|
||||
|
@ -696,6 +697,9 @@
|
|||
<string name="wework_webHook">WebHook地址</string>
|
||||
<string name="wework_webHook_tips">示例:https://qyapi.weixin.qq.com/cgixx?key=xxx</string>
|
||||
|
||||
<string name="url_scheme">URL Scheme</string>
|
||||
<string name="url_scheme_tips">示例:myapp://api/add?&type=0&msg=[msg]</string>
|
||||
|
||||
<string name="webhook_server">Webhook Server</string>
|
||||
<string name="webhook_server_tips">例如:https://a.b.com/msg?token=xyz</string>
|
||||
<string name="webhook_params">Params</string>
|
||||
|
|
Loading…
Reference in New Issue