新增:发送通道 URL Scheme(支持跨应用数据传递)#250
parent
65e861ba62
commit
992fc2eb8c
@ -0,0 +1,7 @@
|
|||||||
|
package com.idormy.sms.forwarder.entity.setting
|
||||||
|
|
||||||
|
import java.io.Serializable
|
||||||
|
|
||||||
|
data class UrlSchemeSetting(
|
||||||
|
var urlScheme: String,
|
||||||
|
) : Serializable
|
@ -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()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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>
|
Loading…
Reference in New Issue