parent
4d7146bd7b
commit
5f55d20c83
@ -1,151 +1,151 @@
|
||||
package com.idormy.sms.forwarder.fragment.client
|
||||
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.idormy.sms.forwarder.core.BaseFragment
|
||||
import com.idormy.sms.forwarder.databinding.FragmentClientBatteryQueryBinding
|
||||
import com.idormy.sms.forwarder.entity.BatteryInfo
|
||||
import com.idormy.sms.forwarder.server.model.BaseResponse
|
||||
import com.idormy.sms.forwarder.utils.*
|
||||
import com.xuexiang.xhttp2.XHttp
|
||||
import com.xuexiang.xhttp2.cache.model.CacheMode
|
||||
import com.xuexiang.xhttp2.callback.SimpleCallBack
|
||||
import com.xuexiang.xhttp2.exception.ApiException
|
||||
import com.xuexiang.xpage.annotation.Page
|
||||
import com.xuexiang.xrouter.utils.TextUtils
|
||||
import com.xuexiang.xui.utils.ResUtils
|
||||
import com.xuexiang.xui.widget.actionbar.TitleBar
|
||||
import com.xuexiang.xui.widget.grouplist.XUIGroupListView
|
||||
import com.xuexiang.xutil.data.ConvertTools
|
||||
|
||||
@Suppress("PropertyName")
|
||||
@Page(name = "远程查电量")
|
||||
class BatteryQueryFragment : BaseFragment<FragmentClientBatteryQueryBinding?>() {
|
||||
|
||||
val TAG: String = BatteryQueryFragment::class.java.simpleName
|
||||
|
||||
override fun viewBindingInflate(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup,
|
||||
): FragmentClientBatteryQueryBinding {
|
||||
return FragmentClientBatteryQueryBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
override fun initTitle(): TitleBar? {
|
||||
val titleBar = super.initTitle()!!.setImmersive(false)
|
||||
titleBar.setTitle(R.string.api_battery_query)
|
||||
return titleBar
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化控件
|
||||
*/
|
||||
override fun initViews() {
|
||||
|
||||
val requestUrl: String = HttpServerUtils.serverAddress + "/battery/query"
|
||||
Log.i(TAG, "requestUrl:$requestUrl")
|
||||
|
||||
val msgMap: MutableMap<String, Any> = mutableMapOf()
|
||||
val timestamp = System.currentTimeMillis()
|
||||
msgMap["timestamp"] = timestamp
|
||||
val clientSignKey = HttpServerUtils.clientSignKey
|
||||
if (!TextUtils.isEmpty(clientSignKey)) {
|
||||
msgMap["sign"] = HttpServerUtils.calcSign(timestamp.toString(), clientSignKey.toString())
|
||||
}
|
||||
val dataMap: MutableMap<String, Any> = mutableMapOf()
|
||||
msgMap["data"] = dataMap
|
||||
|
||||
var requestMsg: String = Gson().toJson(msgMap)
|
||||
Log.i(TAG, "requestMsg:$requestMsg")
|
||||
|
||||
val postRequest = XHttp.post(requestUrl)
|
||||
.keepJson(true)
|
||||
.timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
|
||||
.cacheMode(CacheMode.NO_CACHE)
|
||||
.timeStamp(true)
|
||||
|
||||
when (HttpServerUtils.clientSafetyMeasures) {
|
||||
2 -> {
|
||||
try {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey.toString())
|
||||
requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
requestMsg = RSACrypt.encryptByPublicKey(requestMsg, publicKey)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
3 -> {
|
||||
try {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey.toString())
|
||||
//requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
val encryptCBC = SM4Crypt.encrypt(requestMsg.toByteArray(), sm4Key)
|
||||
requestMsg = ConvertTools.bytes2HexString(encryptCBC)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
else -> {
|
||||
postRequest.upJson(requestMsg)
|
||||
}
|
||||
}
|
||||
|
||||
postRequest.execute(object : SimpleCallBack<String>() {
|
||||
override fun onError(e: ApiException) {
|
||||
XToastUtils.error(e.displayMessage)
|
||||
}
|
||||
|
||||
override fun onSuccess(response: String) {
|
||||
Log.i(TAG, response)
|
||||
try {
|
||||
var json = response
|
||||
if (HttpServerUtils.clientSafetyMeasures == 2) {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey.toString())
|
||||
json = RSACrypt.decryptByPublicKey(json, publicKey)
|
||||
json = String(Base64.decode(json))
|
||||
} else if (HttpServerUtils.clientSafetyMeasures == 3) {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey.toString())
|
||||
val encryptCBC = ConvertTools.hexStringToByteArray(json)
|
||||
val decryptCBC = SM4Crypt.decrypt(encryptCBC, sm4Key)
|
||||
json = String(decryptCBC)
|
||||
}
|
||||
val resp: BaseResponse<BatteryInfo> = Gson().fromJson(json, object : TypeToken<BaseResponse<BatteryInfo>>() {}.type)
|
||||
if (resp.code == 200) {
|
||||
XToastUtils.success(ResUtils.getString(R.string.request_succeeded))
|
||||
val batteryInfo = resp.data ?: return
|
||||
|
||||
val groupListView = binding!!.infoList
|
||||
val section = XUIGroupListView.newSection(context)
|
||||
section.addItemView(groupListView.createItemView(String.format(ResUtils.getString(R.string.battery_level), batteryInfo.level))) {}
|
||||
if (batteryInfo.scale != "") section.addItemView(groupListView.createItemView(String.format(ResUtils.getString(R.string.battery_scale), batteryInfo.scale))) {}
|
||||
if (batteryInfo.voltage != "") section.addItemView(groupListView.createItemView(String.format(ResUtils.getString(R.string.battery_voltage), batteryInfo.voltage))) {}
|
||||
if (batteryInfo.temperature != "") section.addItemView(groupListView.createItemView(String.format(ResUtils.getString(R.string.battery_temperature), batteryInfo.temperature))) {}
|
||||
section.addItemView(groupListView.createItemView(String.format(ResUtils.getString(R.string.battery_status), batteryInfo.status))) {}
|
||||
section.addItemView(groupListView.createItemView(String.format(ResUtils.getString(R.string.battery_health), batteryInfo.health))) {}
|
||||
section.addItemView(groupListView.createItemView(String.format(ResUtils.getString(R.string.battery_plugged), batteryInfo.plugged))) {}
|
||||
section.addTo(groupListView)
|
||||
|
||||
} else {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + resp.msg)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + response)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
package com.idormy.sms.forwarder.fragment.client
|
||||
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.idormy.sms.forwarder.core.BaseFragment
|
||||
import com.idormy.sms.forwarder.databinding.FragmentClientBatteryQueryBinding
|
||||
import com.idormy.sms.forwarder.entity.BatteryInfo
|
||||
import com.idormy.sms.forwarder.server.model.BaseResponse
|
||||
import com.idormy.sms.forwarder.utils.*
|
||||
import com.xuexiang.xhttp2.XHttp
|
||||
import com.xuexiang.xhttp2.cache.model.CacheMode
|
||||
import com.xuexiang.xhttp2.callback.SimpleCallBack
|
||||
import com.xuexiang.xhttp2.exception.ApiException
|
||||
import com.xuexiang.xpage.annotation.Page
|
||||
import com.xuexiang.xrouter.utils.TextUtils
|
||||
import com.xuexiang.xui.utils.ResUtils
|
||||
import com.xuexiang.xui.widget.actionbar.TitleBar
|
||||
import com.xuexiang.xui.widget.grouplist.XUIGroupListView
|
||||
import com.xuexiang.xutil.data.ConvertTools
|
||||
|
||||
@Suppress("PropertyName")
|
||||
@Page(name = "远程查电量")
|
||||
class BatteryQueryFragment : BaseFragment<FragmentClientBatteryQueryBinding?>() {
|
||||
|
||||
val TAG: String = BatteryQueryFragment::class.java.simpleName
|
||||
|
||||
override fun viewBindingInflate(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup,
|
||||
): FragmentClientBatteryQueryBinding {
|
||||
return FragmentClientBatteryQueryBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
override fun initTitle(): TitleBar? {
|
||||
val titleBar = super.initTitle()!!.setImmersive(false)
|
||||
titleBar.setTitle(R.string.api_battery_query)
|
||||
return titleBar
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化控件
|
||||
*/
|
||||
override fun initViews() {
|
||||
|
||||
val requestUrl: String = HttpServerUtils.serverAddress + "/battery/query"
|
||||
Log.i(TAG, "requestUrl:$requestUrl")
|
||||
|
||||
val msgMap: MutableMap<String, Any> = mutableMapOf()
|
||||
val timestamp = System.currentTimeMillis()
|
||||
msgMap["timestamp"] = timestamp
|
||||
val clientSignKey = HttpServerUtils.clientSignKey
|
||||
if (!TextUtils.isEmpty(clientSignKey)) {
|
||||
msgMap["sign"] = HttpServerUtils.calcSign(timestamp.toString(), clientSignKey)
|
||||
}
|
||||
val dataMap: MutableMap<String, Any> = mutableMapOf()
|
||||
msgMap["data"] = dataMap
|
||||
|
||||
var requestMsg: String = Gson().toJson(msgMap)
|
||||
Log.i(TAG, "requestMsg:$requestMsg")
|
||||
|
||||
val postRequest = XHttp.post(requestUrl)
|
||||
.keepJson(true)
|
||||
.timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
|
||||
.cacheMode(CacheMode.NO_CACHE)
|
||||
.timeStamp(true)
|
||||
|
||||
when (HttpServerUtils.clientSafetyMeasures) {
|
||||
2 -> {
|
||||
try {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey)
|
||||
requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
requestMsg = RSACrypt.encryptByPublicKey(requestMsg, publicKey)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
3 -> {
|
||||
try {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey)
|
||||
//requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
val encryptCBC = SM4Crypt.encrypt(requestMsg.toByteArray(), sm4Key)
|
||||
requestMsg = ConvertTools.bytes2HexString(encryptCBC)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
else -> {
|
||||
postRequest.upJson(requestMsg)
|
||||
}
|
||||
}
|
||||
|
||||
postRequest.execute(object : SimpleCallBack<String>() {
|
||||
override fun onError(e: ApiException) {
|
||||
XToastUtils.error(e.displayMessage)
|
||||
}
|
||||
|
||||
override fun onSuccess(response: String) {
|
||||
Log.i(TAG, response)
|
||||
try {
|
||||
var json = response
|
||||
if (HttpServerUtils.clientSafetyMeasures == 2) {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey)
|
||||
json = RSACrypt.decryptByPublicKey(json, publicKey)
|
||||
json = String(Base64.decode(json))
|
||||
} else if (HttpServerUtils.clientSafetyMeasures == 3) {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey)
|
||||
val encryptCBC = ConvertTools.hexStringToByteArray(json)
|
||||
val decryptCBC = SM4Crypt.decrypt(encryptCBC, sm4Key)
|
||||
json = String(decryptCBC)
|
||||
}
|
||||
val resp: BaseResponse<BatteryInfo> = Gson().fromJson(json, object : TypeToken<BaseResponse<BatteryInfo>>() {}.type)
|
||||
if (resp.code == 200) {
|
||||
XToastUtils.success(ResUtils.getString(R.string.request_succeeded))
|
||||
val batteryInfo = resp.data ?: return
|
||||
|
||||
val groupListView = binding!!.infoList
|
||||
val section = XUIGroupListView.newSection(context)
|
||||
section.addItemView(groupListView.createItemView(String.format(ResUtils.getString(R.string.battery_level), batteryInfo.level))) {}
|
||||
if (batteryInfo.scale != "") section.addItemView(groupListView.createItemView(String.format(ResUtils.getString(R.string.battery_scale), batteryInfo.scale))) {}
|
||||
if (batteryInfo.voltage != "") section.addItemView(groupListView.createItemView(String.format(ResUtils.getString(R.string.battery_voltage), batteryInfo.voltage))) {}
|
||||
if (batteryInfo.temperature != "") section.addItemView(groupListView.createItemView(String.format(ResUtils.getString(R.string.battery_temperature), batteryInfo.temperature))) {}
|
||||
section.addItemView(groupListView.createItemView(String.format(ResUtils.getString(R.string.battery_status), batteryInfo.status))) {}
|
||||
section.addItemView(groupListView.createItemView(String.format(ResUtils.getString(R.string.battery_health), batteryInfo.health))) {}
|
||||
section.addItemView(groupListView.createItemView(String.format(ResUtils.getString(R.string.battery_plugged), batteryInfo.plugged))) {}
|
||||
section.addTo(groupListView)
|
||||
|
||||
} else {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + resp.msg)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + response)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,295 +1,295 @@
|
||||
package com.idormy.sms.forwarder.fragment.client
|
||||
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.alibaba.android.vlayout.DelegateAdapter
|
||||
import com.alibaba.android.vlayout.VirtualLayoutManager
|
||||
import com.alibaba.android.vlayout.layout.LinearLayoutHelper
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.idormy.sms.forwarder.adapter.base.broccoli.BroccoliSimpleDelegateAdapter
|
||||
import com.idormy.sms.forwarder.adapter.base.delegate.SimpleDelegateAdapter
|
||||
import com.idormy.sms.forwarder.core.BaseFragment
|
||||
import com.idormy.sms.forwarder.databinding.FragmentClientCallQueryBinding
|
||||
import com.idormy.sms.forwarder.entity.CallInfo
|
||||
import com.idormy.sms.forwarder.server.model.BaseResponse
|
||||
import com.idormy.sms.forwarder.server.model.CallQueryData
|
||||
import com.idormy.sms.forwarder.utils.*
|
||||
import com.jeremyliao.liveeventbus.LiveEventBus
|
||||
import com.scwang.smartrefresh.layout.api.RefreshLayout
|
||||
import com.xuexiang.xaop.annotation.SingleClick
|
||||
import com.xuexiang.xhttp2.XHttp
|
||||
import com.xuexiang.xhttp2.cache.model.CacheMode
|
||||
import com.xuexiang.xhttp2.callback.SimpleCallBack
|
||||
import com.xuexiang.xhttp2.exception.ApiException
|
||||
import com.xuexiang.xpage.annotation.Page
|
||||
import com.xuexiang.xpage.base.XPageActivity
|
||||
import com.xuexiang.xpage.core.PageOption
|
||||
import com.xuexiang.xrouter.utils.TextUtils
|
||||
import com.xuexiang.xui.adapter.recyclerview.RecyclerViewHolder
|
||||
import com.xuexiang.xui.utils.ResUtils
|
||||
import com.xuexiang.xui.utils.SnackbarUtils
|
||||
import com.xuexiang.xui.widget.actionbar.TitleBar
|
||||
import com.xuexiang.xui.widget.searchview.MaterialSearchView
|
||||
import com.xuexiang.xutil.data.ConvertTools
|
||||
import com.xuexiang.xutil.data.DateUtils
|
||||
import com.xuexiang.xutil.system.ClipboardUtils
|
||||
import me.samlss.broccoli.Broccoli
|
||||
|
||||
@Suppress("PropertyName")
|
||||
@Page(name = "远程查通话")
|
||||
class CallQueryFragment : BaseFragment<FragmentClientCallQueryBinding?>() {
|
||||
|
||||
val TAG: String = CallQueryFragment::class.java.simpleName
|
||||
private var mAdapter: SimpleDelegateAdapter<CallInfo>? = null
|
||||
private var callType: Int = 3
|
||||
private var pageNum: Int = 1
|
||||
private val pageSize: Int = 20
|
||||
private var keyword: String = ""
|
||||
|
||||
override fun viewBindingInflate(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup,
|
||||
): FragmentClientCallQueryBinding {
|
||||
return FragmentClientCallQueryBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
override fun initTitle(): TitleBar {
|
||||
val titleBar = super.initTitle()!!.setImmersive(false)
|
||||
titleBar.setTitle(R.string.api_call_query)
|
||||
titleBar!!.addAction(object : TitleBar.ImageAction(R.drawable.ic_query) {
|
||||
@SingleClick
|
||||
override fun performAction(view: View) {
|
||||
binding!!.searchView.showSearch()
|
||||
}
|
||||
})
|
||||
return titleBar
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化控件
|
||||
*/
|
||||
override fun initViews() {
|
||||
val virtualLayoutManager = VirtualLayoutManager(requireContext())
|
||||
binding!!.recyclerView.layoutManager = virtualLayoutManager
|
||||
val viewPool = RecyclerView.RecycledViewPool()
|
||||
binding!!.recyclerView.setRecycledViewPool(viewPool)
|
||||
viewPool.setMaxRecycledViews(0, 10)
|
||||
|
||||
mAdapter = object : BroccoliSimpleDelegateAdapter<CallInfo>(
|
||||
R.layout.adapter_call_card_view_list_item,
|
||||
LinearLayoutHelper(),
|
||||
DataProvider.emptyCallInfo
|
||||
) {
|
||||
override fun onBindData(
|
||||
holder: RecyclerViewHolder,
|
||||
model: CallInfo,
|
||||
position: Int,
|
||||
) {
|
||||
val from = if (TextUtils.isEmpty(model.name)) model.number else model.number + " | " + model.name
|
||||
holder.text(R.id.tv_from, from)
|
||||
holder.text(R.id.tv_time, DateUtils.getFriendlyTimeSpanByNow(model.dateLong))
|
||||
holder.image(R.id.iv_image, model.typeImageId)
|
||||
holder.image(R.id.iv_sim_image, model.simImageId)
|
||||
holder.text(R.id.tv_duration, ResUtils.getString(R.string.call_duration) + model.duration + ResUtils.getString(R.string.seconds))
|
||||
holder.image(R.id.iv_copy, R.drawable.ic_copy)
|
||||
holder.image(R.id.iv_call, R.drawable.ic_phone_out)
|
||||
holder.image(R.id.iv_reply, R.drawable.ic_reply)
|
||||
holder.click(R.id.iv_copy) {
|
||||
XToastUtils.info(String.format(getString(R.string.copied_to_clipboard), from))
|
||||
ClipboardUtils.copyText(from)
|
||||
}
|
||||
holder.click(R.id.iv_call) {
|
||||
XToastUtils.info(getString(R.string.local_call) + model.number)
|
||||
PhoneUtils.dial(model.number)
|
||||
}
|
||||
holder.click(R.id.iv_reply) {
|
||||
XToastUtils.info(getString(R.string.remote_sms) + model.number)
|
||||
LiveEventBus.get<Int>(EVENT_KEY_SIM_SLOT).post(model.simId)
|
||||
LiveEventBus.get<String>(EVENT_KEY_PHONE_NUMBERS).post(model.number)
|
||||
PageOption.to(SmsSendFragment::class.java).setNewActivity(true).open((context as XPageActivity?)!!)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindBroccoli(holder: RecyclerViewHolder, broccoli: Broccoli) {
|
||||
broccoli.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.tv_from)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.tv_time)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.iv_sim_image)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.tv_duration)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.iv_image)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.iv_copy)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.iv_call)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.iv_reply)))
|
||||
}
|
||||
}
|
||||
|
||||
val delegateAdapter = DelegateAdapter(virtualLayoutManager)
|
||||
delegateAdapter.addAdapter(mAdapter)
|
||||
binding!!.recyclerView.adapter = delegateAdapter
|
||||
|
||||
binding!!.tabBar.setTabTitles(ResUtils.getStringArray(R.array.call_type_option))
|
||||
binding!!.tabBar.setOnTabClickListener { _, position ->
|
||||
//XToastUtils.toast("点击了$title--$position")
|
||||
callType = 3 - position
|
||||
loadRemoteData(true)
|
||||
binding!!.recyclerView.scrollToPosition(0)
|
||||
}
|
||||
|
||||
//搜索框
|
||||
binding!!.searchView.findViewById<View>(com.xuexiang.xui.R.id.search_layout).visibility = View.GONE
|
||||
binding!!.searchView.setVoiceSearch(true)
|
||||
binding!!.searchView.setEllipsize(true)
|
||||
binding!!.searchView.setSuggestions(resources.getStringArray(R.array.query_suggestions))
|
||||
binding!!.searchView.setOnQueryTextListener(object : MaterialSearchView.OnQueryTextListener {
|
||||
override fun onQueryTextSubmit(query: String): Boolean {
|
||||
SnackbarUtils.Indefinite(view, String.format(getString(R.string.search_keyword), query)).info()
|
||||
.actionColor(ResUtils.getColor(R.color.xui_config_color_white))
|
||||
.setAction(getString(R.string.clear)) {
|
||||
keyword = ""
|
||||
loadRemoteData(true)
|
||||
}.show()
|
||||
if (keyword != query) {
|
||||
keyword = query
|
||||
loadRemoteData(true)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onQueryTextChange(newText: String): Boolean {
|
||||
//Do some magic
|
||||
return false
|
||||
}
|
||||
})
|
||||
binding!!.searchView.setOnSearchViewListener(object : MaterialSearchView.SearchViewListener {
|
||||
override fun onSearchViewShown() {
|
||||
//Do some magic
|
||||
}
|
||||
|
||||
override fun onSearchViewClosed() {
|
||||
//Do some magic
|
||||
}
|
||||
})
|
||||
binding!!.searchView.setSubmitOnClick(true)
|
||||
}
|
||||
|
||||
override fun initListeners() {
|
||||
//下拉刷新
|
||||
binding!!.refreshLayout.setOnRefreshListener { refreshLayout: RefreshLayout ->
|
||||
refreshLayout.layout.postDelayed({
|
||||
loadRemoteData(true)
|
||||
}, 1000)
|
||||
}
|
||||
//上拉加载
|
||||
binding!!.refreshLayout.setOnLoadMoreListener { refreshLayout: RefreshLayout ->
|
||||
refreshLayout.layout.postDelayed({
|
||||
loadRemoteData(false)
|
||||
}, 1000)
|
||||
}
|
||||
binding!!.refreshLayout.autoRefresh() //第一次进入触发自动刷新,演示效果
|
||||
}
|
||||
|
||||
private fun loadRemoteData(refresh: Boolean) {
|
||||
|
||||
val requestUrl: String = HttpServerUtils.serverAddress + "/call/query"
|
||||
Log.i(TAG, "requestUrl:$requestUrl")
|
||||
|
||||
val msgMap: MutableMap<String, Any> = mutableMapOf()
|
||||
val timestamp = System.currentTimeMillis()
|
||||
msgMap["timestamp"] = timestamp
|
||||
val clientSignKey = HttpServerUtils.clientSignKey
|
||||
if (!TextUtils.isEmpty(clientSignKey)) {
|
||||
msgMap["sign"] = HttpServerUtils.calcSign(timestamp.toString(), clientSignKey.toString())
|
||||
}
|
||||
|
||||
if (refresh) pageNum = 1
|
||||
msgMap["data"] = CallQueryData(callType, pageNum, pageSize, keyword)
|
||||
|
||||
var requestMsg: String = Gson().toJson(msgMap)
|
||||
Log.i(TAG, "requestMsg:$requestMsg")
|
||||
|
||||
val postRequest = XHttp.post(requestUrl)
|
||||
.keepJson(true)
|
||||
.timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
|
||||
.cacheMode(CacheMode.NO_CACHE)
|
||||
.timeStamp(true)
|
||||
|
||||
when (HttpServerUtils.clientSafetyMeasures) {
|
||||
2 -> {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey.toString())
|
||||
try {
|
||||
requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
requestMsg = RSACrypt.encryptByPublicKey(requestMsg, publicKey)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
3 -> {
|
||||
try {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey.toString())
|
||||
//requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
val encryptCBC = SM4Crypt.encrypt(requestMsg.toByteArray(), sm4Key)
|
||||
requestMsg = ConvertTools.bytes2HexString(encryptCBC)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
else -> {
|
||||
postRequest.upJson(requestMsg)
|
||||
}
|
||||
}
|
||||
|
||||
postRequest.execute(object : SimpleCallBack<String>() {
|
||||
override fun onError(e: ApiException) {
|
||||
XToastUtils.error(e.displayMessage)
|
||||
}
|
||||
|
||||
override fun onSuccess(response: String) {
|
||||
Log.i(TAG, response)
|
||||
try {
|
||||
var json = response
|
||||
if (HttpServerUtils.clientSafetyMeasures == 2) {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey.toString())
|
||||
json = RSACrypt.decryptByPublicKey(json, publicKey)
|
||||
json = String(Base64.decode(json))
|
||||
} else if (HttpServerUtils.clientSafetyMeasures == 3) {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey.toString())
|
||||
val encryptCBC = ConvertTools.hexStringToByteArray(json)
|
||||
val decryptCBC = SM4Crypt.decrypt(encryptCBC, sm4Key)
|
||||
json = String(decryptCBC)
|
||||
}
|
||||
val resp: BaseResponse<List<CallInfo>?> = Gson().fromJson(json, object : TypeToken<BaseResponse<List<CallInfo>?>>() {}.type)
|
||||
if (resp.code == 200) {
|
||||
pageNum++
|
||||
if (refresh) {
|
||||
mAdapter!!.refresh(resp.data)
|
||||
binding!!.refreshLayout.finishRefresh()
|
||||
binding!!.recyclerView.scrollToPosition(0)
|
||||
} else {
|
||||
mAdapter!!.loadMore(resp.data)
|
||||
binding!!.refreshLayout.finishLoadMore()
|
||||
}
|
||||
} else {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + resp.msg)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + response)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
package com.idormy.sms.forwarder.fragment.client
|
||||
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.alibaba.android.vlayout.DelegateAdapter
|
||||
import com.alibaba.android.vlayout.VirtualLayoutManager
|
||||
import com.alibaba.android.vlayout.layout.LinearLayoutHelper
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.idormy.sms.forwarder.adapter.base.broccoli.BroccoliSimpleDelegateAdapter
|
||||
import com.idormy.sms.forwarder.adapter.base.delegate.SimpleDelegateAdapter
|
||||
import com.idormy.sms.forwarder.core.BaseFragment
|
||||
import com.idormy.sms.forwarder.databinding.FragmentClientCallQueryBinding
|
||||
import com.idormy.sms.forwarder.entity.CallInfo
|
||||
import com.idormy.sms.forwarder.server.model.BaseResponse
|
||||
import com.idormy.sms.forwarder.server.model.CallQueryData
|
||||
import com.idormy.sms.forwarder.utils.*
|
||||
import com.jeremyliao.liveeventbus.LiveEventBus
|
||||
import com.scwang.smartrefresh.layout.api.RefreshLayout
|
||||
import com.xuexiang.xaop.annotation.SingleClick
|
||||
import com.xuexiang.xhttp2.XHttp
|
||||
import com.xuexiang.xhttp2.cache.model.CacheMode
|
||||
import com.xuexiang.xhttp2.callback.SimpleCallBack
|
||||
import com.xuexiang.xhttp2.exception.ApiException
|
||||
import com.xuexiang.xpage.annotation.Page
|
||||
import com.xuexiang.xpage.base.XPageActivity
|
||||
import com.xuexiang.xpage.core.PageOption
|
||||
import com.xuexiang.xrouter.utils.TextUtils
|
||||
import com.xuexiang.xui.adapter.recyclerview.RecyclerViewHolder
|
||||
import com.xuexiang.xui.utils.ResUtils
|
||||
import com.xuexiang.xui.utils.SnackbarUtils
|
||||
import com.xuexiang.xui.widget.actionbar.TitleBar
|
||||
import com.xuexiang.xui.widget.searchview.MaterialSearchView
|
||||
import com.xuexiang.xutil.data.ConvertTools
|
||||
import com.xuexiang.xutil.data.DateUtils
|
||||
import com.xuexiang.xutil.system.ClipboardUtils
|
||||
import me.samlss.broccoli.Broccoli
|
||||
|
||||
@Suppress("PropertyName")
|
||||
@Page(name = "远程查通话")
|
||||
class CallQueryFragment : BaseFragment<FragmentClientCallQueryBinding?>() {
|
||||
|
||||
val TAG: String = CallQueryFragment::class.java.simpleName
|
||||
private var mAdapter: SimpleDelegateAdapter<CallInfo>? = null
|
||||
private var callType: Int = 3
|
||||
private var pageNum: Int = 1
|
||||
private val pageSize: Int = 20
|
||||
private var keyword: String = ""
|
||||
|
||||
override fun viewBindingInflate(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup,
|
||||
): FragmentClientCallQueryBinding {
|
||||
return FragmentClientCallQueryBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
override fun initTitle(): TitleBar {
|
||||
val titleBar = super.initTitle()!!.setImmersive(false)
|
||||
titleBar.setTitle(R.string.api_call_query)
|
||||
titleBar!!.addAction(object : TitleBar.ImageAction(R.drawable.ic_query) {
|
||||
@SingleClick
|
||||
override fun performAction(view: View) {
|
||||
binding!!.searchView.showSearch()
|
||||
}
|
||||
})
|
||||
return titleBar
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化控件
|
||||
*/
|
||||
override fun initViews() {
|
||||
val virtualLayoutManager = VirtualLayoutManager(requireContext())
|
||||
binding!!.recyclerView.layoutManager = virtualLayoutManager
|
||||
val viewPool = RecyclerView.RecycledViewPool()
|
||||
binding!!.recyclerView.setRecycledViewPool(viewPool)
|
||||
viewPool.setMaxRecycledViews(0, 10)
|
||||
|
||||
mAdapter = object : BroccoliSimpleDelegateAdapter<CallInfo>(
|
||||
R.layout.adapter_call_card_view_list_item,
|
||||
LinearLayoutHelper(),
|
||||
DataProvider.emptyCallInfo
|
||||
) {
|
||||
override fun onBindData(
|
||||
holder: RecyclerViewHolder,
|
||||
model: CallInfo,
|
||||
position: Int,
|
||||
) {
|
||||
val from = if (TextUtils.isEmpty(model.name)) model.number else model.number + " | " + model.name
|
||||
holder.text(R.id.tv_from, from)
|
||||
holder.text(R.id.tv_time, DateUtils.getFriendlyTimeSpanByNow(model.dateLong))
|
||||
holder.image(R.id.iv_image, model.typeImageId)
|
||||
holder.image(R.id.iv_sim_image, model.simImageId)
|
||||
holder.text(R.id.tv_duration, ResUtils.getString(R.string.call_duration) + model.duration + ResUtils.getString(R.string.seconds))
|
||||
holder.image(R.id.iv_copy, R.drawable.ic_copy)
|
||||
holder.image(R.id.iv_call, R.drawable.ic_phone_out)
|
||||
holder.image(R.id.iv_reply, R.drawable.ic_reply)
|
||||
holder.click(R.id.iv_copy) {
|
||||
XToastUtils.info(String.format(getString(R.string.copied_to_clipboard), from))
|
||||
ClipboardUtils.copyText(from)
|
||||
}
|
||||
holder.click(R.id.iv_call) {
|
||||
XToastUtils.info(getString(R.string.local_call) + model.number)
|
||||
PhoneUtils.dial(model.number)
|
||||
}
|
||||
holder.click(R.id.iv_reply) {
|
||||
XToastUtils.info(getString(R.string.remote_sms) + model.number)
|
||||
LiveEventBus.get<Int>(EVENT_KEY_SIM_SLOT).post(model.simId)
|
||||
LiveEventBus.get<String>(EVENT_KEY_PHONE_NUMBERS).post(model.number)
|
||||
PageOption.to(SmsSendFragment::class.java).setNewActivity(true).open((context as XPageActivity?)!!)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindBroccoli(holder: RecyclerViewHolder, broccoli: Broccoli) {
|
||||
broccoli.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.tv_from)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.tv_time)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.iv_sim_image)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.tv_duration)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.iv_image)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.iv_copy)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.iv_call)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.iv_reply)))
|
||||
}
|
||||
}
|
||||
|
||||
val delegateAdapter = DelegateAdapter(virtualLayoutManager)
|
||||
delegateAdapter.addAdapter(mAdapter)
|
||||
binding!!.recyclerView.adapter = delegateAdapter
|
||||
|
||||
binding!!.tabBar.setTabTitles(ResUtils.getStringArray(R.array.call_type_option))
|
||||
binding!!.tabBar.setOnTabClickListener { _, position ->
|
||||
//XToastUtils.toast("点击了$title--$position")
|
||||
callType = 3 - position
|
||||
loadRemoteData(true)
|
||||
binding!!.recyclerView.scrollToPosition(0)
|
||||
}
|
||||
|
||||
//搜索框
|
||||
binding!!.searchView.findViewById<View>(com.xuexiang.xui.R.id.search_layout).visibility = View.GONE
|
||||
binding!!.searchView.setVoiceSearch(true)
|
||||
binding!!.searchView.setEllipsize(true)
|
||||
binding!!.searchView.setSuggestions(resources.getStringArray(R.array.query_suggestions))
|
||||
binding!!.searchView.setOnQueryTextListener(object : MaterialSearchView.OnQueryTextListener {
|
||||
override fun onQueryTextSubmit(query: String): Boolean {
|
||||
SnackbarUtils.Indefinite(view, String.format(getString(R.string.search_keyword), query)).info()
|
||||
.actionColor(ResUtils.getColor(R.color.xui_config_color_white))
|
||||
.setAction(getString(R.string.clear)) {
|
||||
keyword = ""
|
||||
loadRemoteData(true)
|
||||
}.show()
|
||||
if (keyword != query) {
|
||||
keyword = query
|
||||
loadRemoteData(true)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onQueryTextChange(newText: String): Boolean {
|
||||
//Do some magic
|
||||
return false
|
||||
}
|
||||
})
|
||||
binding!!.searchView.setOnSearchViewListener(object : MaterialSearchView.SearchViewListener {
|
||||
override fun onSearchViewShown() {
|
||||
//Do some magic
|
||||
}
|
||||
|
||||
override fun onSearchViewClosed() {
|
||||
//Do some magic
|
||||
}
|
||||
})
|
||||
binding!!.searchView.setSubmitOnClick(true)
|
||||
}
|
||||
|
||||
override fun initListeners() {
|
||||
//下拉刷新
|
||||
binding!!.refreshLayout.setOnRefreshListener { refreshLayout: RefreshLayout ->
|
||||
refreshLayout.layout.postDelayed({
|
||||
loadRemoteData(true)
|
||||
}, 1000)
|
||||
}
|
||||
//上拉加载
|
||||
binding!!.refreshLayout.setOnLoadMoreListener { refreshLayout: RefreshLayout ->
|
||||
refreshLayout.layout.postDelayed({
|
||||
loadRemoteData(false)
|
||||
}, 1000)
|
||||
}
|
||||
binding!!.refreshLayout.autoRefresh() //第一次进入触发自动刷新,演示效果
|
||||
}
|
||||
|
||||
private fun loadRemoteData(refresh: Boolean) {
|
||||
|
||||
val requestUrl: String = HttpServerUtils.serverAddress + "/call/query"
|
||||
Log.i(TAG, "requestUrl:$requestUrl")
|
||||
|
||||
val msgMap: MutableMap<String, Any> = mutableMapOf()
|
||||
val timestamp = System.currentTimeMillis()
|
||||
msgMap["timestamp"] = timestamp
|
||||
val clientSignKey = HttpServerUtils.clientSignKey
|
||||
if (!TextUtils.isEmpty(clientSignKey)) {
|
||||
msgMap["sign"] = HttpServerUtils.calcSign(timestamp.toString(), clientSignKey)
|
||||
}
|
||||
|
||||
if (refresh) pageNum = 1
|
||||
msgMap["data"] = CallQueryData(callType, pageNum, pageSize, keyword)
|
||||
|
||||
var requestMsg: String = Gson().toJson(msgMap)
|
||||
Log.i(TAG, "requestMsg:$requestMsg")
|
||||
|
||||
val postRequest = XHttp.post(requestUrl)
|
||||
.keepJson(true)
|
||||
.timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
|
||||
.cacheMode(CacheMode.NO_CACHE)
|
||||
.timeStamp(true)
|
||||
|
||||
when (HttpServerUtils.clientSafetyMeasures) {
|
||||
2 -> {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey)
|
||||
try {
|
||||
requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
requestMsg = RSACrypt.encryptByPublicKey(requestMsg, publicKey)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
3 -> {
|
||||
try {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey)
|
||||
//requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
val encryptCBC = SM4Crypt.encrypt(requestMsg.toByteArray(), sm4Key)
|
||||
requestMsg = ConvertTools.bytes2HexString(encryptCBC)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
else -> {
|
||||
postRequest.upJson(requestMsg)
|
||||
}
|
||||
}
|
||||
|
||||
postRequest.execute(object : SimpleCallBack<String>() {
|
||||
override fun onError(e: ApiException) {
|
||||
XToastUtils.error(e.displayMessage)
|
||||
}
|
||||
|
||||
override fun onSuccess(response: String) {
|
||||
Log.i(TAG, response)
|
||||
try {
|
||||
var json = response
|
||||
if (HttpServerUtils.clientSafetyMeasures == 2) {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey)
|
||||
json = RSACrypt.decryptByPublicKey(json, publicKey)
|
||||
json = String(Base64.decode(json))
|
||||
} else if (HttpServerUtils.clientSafetyMeasures == 3) {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey)
|
||||
val encryptCBC = ConvertTools.hexStringToByteArray(json)
|
||||
val decryptCBC = SM4Crypt.decrypt(encryptCBC, sm4Key)
|
||||
json = String(decryptCBC)
|
||||
}
|
||||
val resp: BaseResponse<List<CallInfo>?> = Gson().fromJson(json, object : TypeToken<BaseResponse<List<CallInfo>?>>() {}.type)
|
||||
if (resp.code == 200) {
|
||||
pageNum++
|
||||
if (refresh) {
|
||||
mAdapter!!.refresh(resp.data)
|
||||
binding!!.refreshLayout.finishRefresh()
|
||||
binding!!.recyclerView.scrollToPosition(0)
|
||||
} else {
|
||||
mAdapter!!.loadMore(resp.data)
|
||||
binding!!.refreshLayout.finishLoadMore()
|
||||
}
|
||||
} else {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + resp.msg)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + response)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,459 +1,459 @@
|
||||
package com.idormy.sms.forwarder.fragment.client
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.os.Environment
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.JsonDeserializer
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.hjq.permissions.OnPermissionCallback
|
||||
import com.hjq.permissions.Permission
|
||||
import com.hjq.permissions.XXPermissions
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.idormy.sms.forwarder.core.BaseFragment
|
||||
import com.idormy.sms.forwarder.databinding.FragmentClientCloneBinding
|
||||
import com.idormy.sms.forwarder.entity.CloneInfo
|
||||
import com.idormy.sms.forwarder.server.model.BaseResponse
|
||||
import com.idormy.sms.forwarder.utils.*
|
||||
import com.idormy.sms.forwarder.utils.Base64
|
||||
import com.xuexiang.xaop.annotation.SingleClick
|
||||
import com.xuexiang.xhttp2.XHttp
|
||||
import com.xuexiang.xhttp2.cache.model.CacheMode
|
||||
import com.xuexiang.xhttp2.callback.SimpleCallBack
|
||||
import com.xuexiang.xhttp2.exception.ApiException
|
||||
import com.xuexiang.xpage.annotation.Page
|
||||
import com.xuexiang.xrouter.utils.TextUtils
|
||||
import com.xuexiang.xui.utils.CountDownButtonHelper
|
||||
import com.xuexiang.xui.utils.ResUtils
|
||||
import com.xuexiang.xui.widget.actionbar.TitleBar
|
||||
import com.xuexiang.xutil.app.AppUtils
|
||||
import com.xuexiang.xutil.data.ConvertTools
|
||||
import com.xuexiang.xutil.file.FileIOUtils
|
||||
import com.xuexiang.xutil.file.FileUtils
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
|
||||
|
||||
@Suppress("PropertyName")
|
||||
@Page(name = "一键换新机")
|
||||
class CloneFragment : BaseFragment<FragmentClientCloneBinding?>(), View.OnClickListener {
|
||||
|
||||
val TAG: String = SmsQueryFragment::class.java.simpleName
|
||||
private var backupPath: String? = null
|
||||
private val backupFile = "SmsForwarder.json"
|
||||
private var pushCountDownHelper: CountDownButtonHelper? = null
|
||||
private var pullCountDownHelper: CountDownButtonHelper? = null
|
||||
private var exportCountDownHelper: CountDownButtonHelper? = null
|
||||
private var importCountDownHelper: CountDownButtonHelper? = null
|
||||
|
||||
override fun viewBindingInflate(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup,
|
||||
): FragmentClientCloneBinding {
|
||||
return FragmentClientCloneBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
override fun initTitle(): TitleBar? {
|
||||
val titleBar = super.initTitle()!!.setImmersive(false)
|
||||
titleBar.setTitle(R.string.api_clone)
|
||||
return titleBar
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化控件
|
||||
*/
|
||||
override fun initViews() {
|
||||
// 申请储存权限
|
||||
XXPermissions.with(this)
|
||||
//.permission(*Permission.Group.STORAGE)
|
||||
.permission(Permission.MANAGE_EXTERNAL_STORAGE)
|
||||
.request(object : OnPermissionCallback {
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun onGranted(permissions: List<String>, all: Boolean) {
|
||||
backupPath =
|
||||
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).path
|
||||
binding!!.tvBackupPath.text = backupPath + File.separator + backupFile
|
||||
}
|
||||
|
||||
override fun onDenied(permissions: List<String>, never: Boolean) {
|
||||
if (never) {
|
||||
XToastUtils.error(R.string.toast_denied_never)
|
||||
// 如果是被永久拒绝就跳转到应用权限系统设置页面
|
||||
XXPermissions.startPermissionActivity(requireContext(), permissions)
|
||||
} else {
|
||||
XToastUtils.error(R.string.toast_denied)
|
||||
}
|
||||
binding!!.tvBackupPath.text = getString(R.string.storage_permission_tips)
|
||||
}
|
||||
})
|
||||
|
||||
binding!!.tabBar.setTabTitles(ResUtils.getStringArray(R.array.clone_type_option))
|
||||
binding!!.tabBar.setOnTabClickListener { _, position ->
|
||||
//XToastUtils.toast("点击了$title--$position")
|
||||
if (position == 1) {
|
||||
binding!!.layoutNetwork.visibility = View.GONE
|
||||
binding!!.layoutOffline.visibility = View.VISIBLE
|
||||
} else {
|
||||
binding!!.layoutNetwork.visibility = View.VISIBLE
|
||||
binding!!.layoutOffline.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
//按钮增加倒计时,避免重复点击
|
||||
pushCountDownHelper = CountDownButtonHelper(binding!!.btnPush, SettingUtils.requestTimeout)
|
||||
pushCountDownHelper!!.setOnCountDownListener(object :
|
||||
CountDownButtonHelper.OnCountDownListener {
|
||||
override fun onCountDown(time: Int) {
|
||||
binding!!.btnPush.text = String.format(getString(R.string.seconds_n), time)
|
||||
}
|
||||
|
||||
override fun onFinished() {
|
||||
binding!!.btnPush.text = getString(R.string.push)
|
||||
}
|
||||
})
|
||||
pullCountDownHelper = CountDownButtonHelper(binding!!.btnPull, SettingUtils.requestTimeout)
|
||||
pullCountDownHelper!!.setOnCountDownListener(object :
|
||||
CountDownButtonHelper.OnCountDownListener {
|
||||
override fun onCountDown(time: Int) {
|
||||
binding!!.btnPull.text = String.format(getString(R.string.seconds_n), time)
|
||||
}
|
||||
|
||||
override fun onFinished() {
|
||||
binding!!.btnPull.text = getString(R.string.pull)
|
||||
}
|
||||
})
|
||||
exportCountDownHelper = CountDownButtonHelper(binding!!.btnExport, 3)
|
||||
exportCountDownHelper!!.setOnCountDownListener(object :
|
||||
CountDownButtonHelper.OnCountDownListener {
|
||||
override fun onCountDown(time: Int) {
|
||||
binding!!.btnExport.text = String.format(getString(R.string.seconds_n), time)
|
||||
}
|
||||
|
||||
override fun onFinished() {
|
||||
binding!!.btnExport.text = getString(R.string.export)
|
||||
}
|
||||
})
|
||||
importCountDownHelper = CountDownButtonHelper(binding!!.btnImport, 3)
|
||||
importCountDownHelper!!.setOnCountDownListener(object :
|
||||
CountDownButtonHelper.OnCountDownListener {
|
||||
override fun onCountDown(time: Int) {
|
||||
binding!!.btnImport.text = String.format(getString(R.string.seconds_n), time)
|
||||
}
|
||||
|
||||
override fun onFinished() {
|
||||
binding!!.btnImport.text = getString(R.string.imports)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun initListeners() {
|
||||
binding!!.btnPush.setOnClickListener(this)
|
||||
binding!!.btnPull.setOnClickListener(this)
|
||||
binding!!.btnExport.setOnClickListener(this)
|
||||
binding!!.btnImport.setOnClickListener(this)
|
||||
}
|
||||
|
||||
@SingleClick
|
||||
override fun onClick(v: View) {
|
||||
when (v.id) {
|
||||
//推送配置
|
||||
R.id.btn_push -> pushData()
|
||||
//拉取配置
|
||||
R.id.btn_pull -> pullData()
|
||||
//导出配置
|
||||
R.id.btn_export -> {
|
||||
try {
|
||||
exportCountDownHelper?.start()
|
||||
val file = File(backupPath + File.separator + backupFile)
|
||||
//判断文件是否存在,存在则在创建之前删除
|
||||
FileUtils.createFileByDeleteOldFile(file)
|
||||
val cloneInfo = HttpServerUtils.exportSettings()
|
||||
val jsonStr = Gson().toJson(cloneInfo)
|
||||
if (FileIOUtils.writeFileFromString(file, jsonStr)) {
|
||||
XToastUtils.success(getString(R.string.export_succeeded))
|
||||
} else {
|
||||
binding!!.tvExport.text = getString(R.string.export_failed)
|
||||
XToastUtils.error(getString(R.string.export_failed))
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(String.format(getString(R.string.export_failed_tips), e.message))
|
||||
}
|
||||
}
|
||||
//导入配置
|
||||
R.id.btn_import -> {
|
||||
try {
|
||||
importCountDownHelper?.start()
|
||||
val file = File(backupPath + File.separator + backupFile)
|
||||
//判断文件是否存在
|
||||
if (!FileUtils.isFileExists(file)) {
|
||||
XToastUtils.error(getString(R.string.import_failed_file_not_exist))
|
||||
return
|
||||
}
|
||||
|
||||
val jsonStr = FileIOUtils.readFile2String(file)
|
||||
Log.d(TAG, "jsonStr = $jsonStr")
|
||||
if (TextUtils.isEmpty(jsonStr)) {
|
||||
XToastUtils.error(getString(R.string.import_failed))
|
||||
return
|
||||
}
|
||||
|
||||
//替换Date字段为当前时间
|
||||
val builder = GsonBuilder()
|
||||
builder.registerTypeAdapter(
|
||||
Date::class.java,
|
||||
JsonDeserializer<Any?> { _, _, _ -> Date() })
|
||||
val gson = builder.create()
|
||||
val cloneInfo = gson.fromJson(jsonStr, CloneInfo::class.java)
|
||||
Log.d(TAG, "cloneInfo = $cloneInfo")
|
||||
|
||||
//判断版本是否一致
|
||||
HttpServerUtils.compareVersion(cloneInfo)
|
||||
|
||||
if (HttpServerUtils.restoreSettings(cloneInfo)) {
|
||||
XToastUtils.success(getString(R.string.import_succeeded))
|
||||
} else {
|
||||
XToastUtils.error(getString(R.string.import_failed))
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(String.format(getString(R.string.import_failed_tips), e.message))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//推送配置
|
||||
private fun pushData() {
|
||||
if (!CommonUtils.checkUrl(HttpServerUtils.serverAddress)) {
|
||||
XToastUtils.error(getString(R.string.invalid_service_address))
|
||||
return
|
||||
}
|
||||
|
||||
pushCountDownHelper?.start()
|
||||
|
||||
val requestUrl: String = HttpServerUtils.serverAddress + "/clone/push"
|
||||
Log.i(TAG, "requestUrl:$requestUrl")
|
||||
|
||||
val msgMap: MutableMap<String, Any> = mutableMapOf()
|
||||
val timestamp = System.currentTimeMillis()
|
||||
msgMap["timestamp"] = timestamp
|
||||
val clientSignKey = HttpServerUtils.clientSignKey
|
||||
if (!TextUtils.isEmpty(clientSignKey)) {
|
||||
msgMap["sign"] =
|
||||
HttpServerUtils.calcSign(timestamp.toString(), clientSignKey.toString())
|
||||
}
|
||||
msgMap["data"] = HttpServerUtils.exportSettings()
|
||||
|
||||
var requestMsg: String = Gson().toJson(msgMap)
|
||||
Log.i(TAG, "requestMsg:$requestMsg")
|
||||
|
||||
val postRequest = XHttp.post(requestUrl)
|
||||
.keepJson(true)
|
||||
.timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
|
||||
.cacheMode(CacheMode.NO_CACHE)
|
||||
.timeStamp(true)
|
||||
|
||||
when (HttpServerUtils.clientSafetyMeasures) {
|
||||
2 -> {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey.toString())
|
||||
try {
|
||||
requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
requestMsg = RSACrypt.encryptByPublicKey(requestMsg, publicKey)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
3 -> {
|
||||
try {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey.toString())
|
||||
//requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
val encryptCBC = SM4Crypt.encrypt(requestMsg.toByteArray(), sm4Key)
|
||||
requestMsg = ConvertTools.bytes2HexString(encryptCBC)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
else -> {
|
||||
postRequest.upJson(requestMsg)
|
||||
}
|
||||
}
|
||||
|
||||
postRequest.execute(object : SimpleCallBack<String>() {
|
||||
override fun onError(e: ApiException) {
|
||||
XToastUtils.error(e.displayMessage)
|
||||
pushCountDownHelper?.finish()
|
||||
}
|
||||
|
||||
override fun onSuccess(response: String) {
|
||||
Log.i(TAG, response)
|
||||
try {
|
||||
var json = response
|
||||
if (HttpServerUtils.clientSafetyMeasures == 2) {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey.toString())
|
||||
json = RSACrypt.decryptByPublicKey(json, publicKey)
|
||||
json = String(Base64.decode(json))
|
||||
} else if (HttpServerUtils.clientSafetyMeasures == 3) {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey.toString())
|
||||
val encryptCBC = ConvertTools.hexStringToByteArray(json)
|
||||
val decryptCBC = SM4Crypt.decrypt(encryptCBC, sm4Key)
|
||||
json = String(decryptCBC)
|
||||
}
|
||||
val resp: BaseResponse<String> = Gson().fromJson(json, object : TypeToken<BaseResponse<String>>() {}.type)
|
||||
if (resp.code == 200) {
|
||||
XToastUtils.success(ResUtils.getString(R.string.request_succeeded))
|
||||
} else {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + resp.msg)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + response)
|
||||
}
|
||||
pushCountDownHelper?.finish()
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
//拉取配置
|
||||
private fun pullData() {
|
||||
if (!CommonUtils.checkUrl(HttpServerUtils.serverAddress)) {
|
||||
XToastUtils.error(getString(R.string.invalid_service_address))
|
||||
return
|
||||
}
|
||||
|
||||
exportCountDownHelper?.start()
|
||||
|
||||
val requestUrl: String = HttpServerUtils.serverAddress + "/clone/pull"
|
||||
Log.i(TAG, "requestUrl:$requestUrl")
|
||||
|
||||
val msgMap: MutableMap<String, Any> = mutableMapOf()
|
||||
val timestamp = System.currentTimeMillis()
|
||||
msgMap["timestamp"] = timestamp
|
||||
val clientSignKey = HttpServerUtils.clientSignKey
|
||||
if (!TextUtils.isEmpty(clientSignKey)) {
|
||||
msgMap["sign"] =
|
||||
HttpServerUtils.calcSign(timestamp.toString(), clientSignKey.toString())
|
||||
}
|
||||
|
||||
val dataMap: MutableMap<String, Any> = mutableMapOf()
|
||||
dataMap["version_code"] = AppUtils.getAppVersionCode()
|
||||
msgMap["data"] = dataMap
|
||||
|
||||
var requestMsg: String = Gson().toJson(msgMap)
|
||||
Log.i(TAG, "requestMsg:$requestMsg")
|
||||
|
||||
val postRequest = XHttp.post(requestUrl)
|
||||
.keepJson(true)
|
||||
.timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
|
||||
.cacheMode(CacheMode.NO_CACHE)
|
||||
.timeStamp(true)
|
||||
|
||||
when (HttpServerUtils.clientSafetyMeasures) {
|
||||
2 -> {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey.toString())
|
||||
try {
|
||||
requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
requestMsg = RSACrypt.encryptByPublicKey(requestMsg, publicKey)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
3 -> {
|
||||
try {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey.toString())
|
||||
//requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
val encryptCBC = SM4Crypt.encrypt(requestMsg.toByteArray(), sm4Key)
|
||||
requestMsg = ConvertTools.bytes2HexString(encryptCBC)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
else -> {
|
||||
postRequest.upJson(requestMsg)
|
||||
}
|
||||
}
|
||||
|
||||
postRequest.execute(object : SimpleCallBack<String>() {
|
||||
override fun onError(e: ApiException) {
|
||||
XToastUtils.error(e.displayMessage)
|
||||
exportCountDownHelper?.finish()
|
||||
}
|
||||
|
||||
override fun onSuccess(response: String) {
|
||||
Log.i(TAG, response)
|
||||
try {
|
||||
var json = response
|
||||
if (HttpServerUtils.clientSafetyMeasures == 2) {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey.toString())
|
||||
json = RSACrypt.decryptByPublicKey(json, publicKey)
|
||||
json = String(Base64.decode(json))
|
||||
} else if (HttpServerUtils.clientSafetyMeasures == 3) {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey.toString())
|
||||
val encryptCBC = ConvertTools.hexStringToByteArray(json)
|
||||
val decryptCBC = SM4Crypt.decrypt(encryptCBC, sm4Key)
|
||||
json = String(decryptCBC)
|
||||
}
|
||||
|
||||
//替换Date字段为当前时间
|
||||
val builder = GsonBuilder()
|
||||
builder.registerTypeAdapter(
|
||||
Date::class.java,
|
||||
JsonDeserializer<Any?> { _, _, _ -> Date() })
|
||||
val gson = builder.create()
|
||||
val resp: BaseResponse<CloneInfo> = gson.fromJson(json, object : TypeToken<BaseResponse<CloneInfo>>() {}.type)
|
||||
if (resp.code == 200) {
|
||||
val cloneInfo = resp.data
|
||||
Log.d(TAG, "cloneInfo = $cloneInfo")
|
||||
|
||||
if (cloneInfo == null) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed))
|
||||
return
|
||||
}
|
||||
|
||||
//判断版本是否一致
|
||||
HttpServerUtils.compareVersion(cloneInfo)
|
||||
|
||||
if (HttpServerUtils.restoreSettings(cloneInfo)) {
|
||||
XToastUtils.success(getString(R.string.import_succeeded))
|
||||
}
|
||||
} else {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + resp.msg)
|
||||
}
|
||||
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + response)
|
||||
}
|
||||
exportCountDownHelper?.finish()
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
if (pushCountDownHelper != null) pushCountDownHelper!!.recycle()
|
||||
if (pullCountDownHelper != null) pullCountDownHelper!!.recycle()
|
||||
if (exportCountDownHelper != null) exportCountDownHelper!!.recycle()
|
||||
if (importCountDownHelper != null) importCountDownHelper!!.recycle()
|
||||
super.onDestroyView()
|
||||
}
|
||||
package com.idormy.sms.forwarder.fragment.client
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.os.Environment
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.JsonDeserializer
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.hjq.permissions.OnPermissionCallback
|
||||
import com.hjq.permissions.Permission
|
||||
import com.hjq.permissions.XXPermissions
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.idormy.sms.forwarder.core.BaseFragment
|
||||
import com.idormy.sms.forwarder.databinding.FragmentClientCloneBinding
|
||||
import com.idormy.sms.forwarder.entity.CloneInfo
|
||||
import com.idormy.sms.forwarder.server.model.BaseResponse
|
||||
import com.idormy.sms.forwarder.utils.*
|
||||
import com.idormy.sms.forwarder.utils.Base64
|
||||
import com.xuexiang.xaop.annotation.SingleClick
|
||||
import com.xuexiang.xhttp2.XHttp
|
||||
import com.xuexiang.xhttp2.cache.model.CacheMode
|
||||
import com.xuexiang.xhttp2.callback.SimpleCallBack
|
||||
import com.xuexiang.xhttp2.exception.ApiException
|
||||
import com.xuexiang.xpage.annotation.Page
|
||||
import com.xuexiang.xrouter.utils.TextUtils
|
||||
import com.xuexiang.xui.utils.CountDownButtonHelper
|
||||
import com.xuexiang.xui.utils.ResUtils
|
||||
import com.xuexiang.xui.widget.actionbar.TitleBar
|
||||
import com.xuexiang.xutil.app.AppUtils
|
||||
import com.xuexiang.xutil.data.ConvertTools
|
||||
import com.xuexiang.xutil.file.FileIOUtils
|
||||
import com.xuexiang.xutil.file.FileUtils
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
|
||||
|
||||
@Suppress("PropertyName")
|
||||
@Page(name = "一键换新机")
|
||||
class CloneFragment : BaseFragment<FragmentClientCloneBinding?>(), View.OnClickListener {
|
||||
|
||||
val TAG: String = SmsQueryFragment::class.java.simpleName
|
||||
private var backupPath: String? = null
|
||||
private val backupFile = "SmsForwarder.json"
|
||||
private var pushCountDownHelper: CountDownButtonHelper? = null
|
||||
private var pullCountDownHelper: CountDownButtonHelper? = null
|
||||
private var exportCountDownHelper: CountDownButtonHelper? = null
|
||||
private var importCountDownHelper: CountDownButtonHelper? = null
|
||||
|
||||
override fun viewBindingInflate(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup,
|
||||
): FragmentClientCloneBinding {
|
||||
return FragmentClientCloneBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
override fun initTitle(): TitleBar? {
|
||||
val titleBar = super.initTitle()!!.setImmersive(false)
|
||||
titleBar.setTitle(R.string.api_clone)
|
||||
return titleBar
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化控件
|
||||
*/
|
||||
override fun initViews() {
|
||||
// 申请储存权限
|
||||
XXPermissions.with(this)
|
||||
//.permission(*Permission.Group.STORAGE)
|
||||
.permission(Permission.MANAGE_EXTERNAL_STORAGE)
|
||||
.request(object : OnPermissionCallback {
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun onGranted(permissions: List<String>, all: Boolean) {
|
||||
backupPath =
|
||||
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).path
|
||||
binding!!.tvBackupPath.text = backupPath + File.separator + backupFile
|
||||
}
|
||||
|
||||
override fun onDenied(permissions: List<String>, never: Boolean) {
|
||||
if (never) {
|
||||
XToastUtils.error(R.string.toast_denied_never)
|
||||
// 如果是被永久拒绝就跳转到应用权限系统设置页面
|
||||
XXPermissions.startPermissionActivity(requireContext(), permissions)
|
||||
} else {
|
||||
XToastUtils.error(R.string.toast_denied)
|
||||
}
|
||||
binding!!.tvBackupPath.text = getString(R.string.storage_permission_tips)
|
||||
}
|
||||
})
|
||||
|
||||
binding!!.tabBar.setTabTitles(ResUtils.getStringArray(R.array.clone_type_option))
|
||||
binding!!.tabBar.setOnTabClickListener { _, position ->
|
||||
//XToastUtils.toast("点击了$title--$position")
|
||||
if (position == 1) {
|
||||
binding!!.layoutNetwork.visibility = View.GONE
|
||||
binding!!.layoutOffline.visibility = View.VISIBLE
|
||||
} else {
|
||||
binding!!.layoutNetwork.visibility = View.VISIBLE
|
||||
binding!!.layoutOffline.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
//按钮增加倒计时,避免重复点击
|
||||
pushCountDownHelper = CountDownButtonHelper(binding!!.btnPush, SettingUtils.requestTimeout)
|
||||
pushCountDownHelper!!.setOnCountDownListener(object :
|
||||
CountDownButtonHelper.OnCountDownListener {
|
||||
override fun onCountDown(time: Int) {
|
||||
binding!!.btnPush.text = String.format(getString(R.string.seconds_n), time)
|
||||
}
|
||||
|
||||
override fun onFinished() {
|
||||
binding!!.btnPush.text = getString(R.string.push)
|
||||
}
|
||||
})
|
||||
pullCountDownHelper = CountDownButtonHelper(binding!!.btnPull, SettingUtils.requestTimeout)
|
||||
pullCountDownHelper!!.setOnCountDownListener(object :
|
||||
CountDownButtonHelper.OnCountDownListener {
|
||||
override fun onCountDown(time: Int) {
|
||||
binding!!.btnPull.text = String.format(getString(R.string.seconds_n), time)
|
||||
}
|
||||
|
||||
override fun onFinished() {
|
||||
binding!!.btnPull.text = getString(R.string.pull)
|
||||
}
|
||||
})
|
||||
exportCountDownHelper = CountDownButtonHelper(binding!!.btnExport, 3)
|
||||
exportCountDownHelper!!.setOnCountDownListener(object :
|
||||
CountDownButtonHelper.OnCountDownListener {
|
||||
override fun onCountDown(time: Int) {
|
||||
binding!!.btnExport.text = String.format(getString(R.string.seconds_n), time)
|
||||
}
|
||||
|
||||
override fun onFinished() {
|
||||
binding!!.btnExport.text = getString(R.string.export)
|
||||
}
|
||||
})
|
||||
importCountDownHelper = CountDownButtonHelper(binding!!.btnImport, 3)
|
||||
importCountDownHelper!!.setOnCountDownListener(object :
|
||||
CountDownButtonHelper.OnCountDownListener {
|
||||
override fun onCountDown(time: Int) {
|
||||
binding!!.btnImport.text = String.format(getString(R.string.seconds_n), time)
|
||||
}
|
||||
|
||||
override fun onFinished() {
|
||||
binding!!.btnImport.text = getString(R.string.imports)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun initListeners() {
|
||||
binding!!.btnPush.setOnClickListener(this)
|
||||
binding!!.btnPull.setOnClickListener(this)
|
||||
binding!!.btnExport.setOnClickListener(this)
|
||||
binding!!.btnImport.setOnClickListener(this)
|
||||
}
|
||||
|
||||
@SingleClick
|
||||
override fun onClick(v: View) {
|
||||
when (v.id) {
|
||||
//推送配置
|
||||
R.id.btn_push -> pushData()
|
||||
//拉取配置
|
||||
R.id.btn_pull -> pullData()
|
||||
//导出配置
|
||||
R.id.btn_export -> {
|
||||
try {
|
||||
exportCountDownHelper?.start()
|
||||
val file = File(backupPath + File.separator + backupFile)
|
||||
//判断文件是否存在,存在则在创建之前删除
|
||||
FileUtils.createFileByDeleteOldFile(file)
|
||||
val cloneInfo = HttpServerUtils.exportSettings()
|
||||
val jsonStr = Gson().toJson(cloneInfo)
|
||||
if (FileIOUtils.writeFileFromString(file, jsonStr)) {
|
||||
XToastUtils.success(getString(R.string.export_succeeded))
|
||||
} else {
|
||||
binding!!.tvExport.text = getString(R.string.export_failed)
|
||||
XToastUtils.error(getString(R.string.export_failed))
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(String.format(getString(R.string.export_failed_tips), e.message))
|
||||
}
|
||||
}
|
||||
//导入配置
|
||||
R.id.btn_import -> {
|
||||
try {
|
||||
importCountDownHelper?.start()
|
||||
val file = File(backupPath + File.separator + backupFile)
|
||||
//判断文件是否存在
|
||||
if (!FileUtils.isFileExists(file)) {
|
||||
XToastUtils.error(getString(R.string.import_failed_file_not_exist))
|
||||
return
|
||||
}
|
||||
|
||||
val jsonStr = FileIOUtils.readFile2String(file)
|
||||
Log.d(TAG, "jsonStr = $jsonStr")
|
||||
if (TextUtils.isEmpty(jsonStr)) {
|
||||
XToastUtils.error(getString(R.string.import_failed))
|
||||
return
|
||||
}
|
||||
|
||||
//替换Date字段为当前时间
|
||||
val builder = GsonBuilder()
|
||||
builder.registerTypeAdapter(
|
||||
Date::class.java,
|
||||
JsonDeserializer<Any?> { _, _, _ -> Date() })
|
||||
val gson = builder.create()
|
||||
val cloneInfo = gson.fromJson(jsonStr, CloneInfo::class.java)
|
||||
Log.d(TAG, "cloneInfo = $cloneInfo")
|
||||
|
||||
//判断版本是否一致
|
||||
HttpServerUtils.compareVersion(cloneInfo)
|
||||
|
||||
if (HttpServerUtils.restoreSettings(cloneInfo)) {
|
||||
XToastUtils.success(getString(R.string.import_succeeded))
|
||||
} else {
|
||||
XToastUtils.error(getString(R.string.import_failed))
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(String.format(getString(R.string.import_failed_tips), e.message))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//推送配置
|
||||
private fun pushData() {
|
||||
if (!CommonUtils.checkUrl(HttpServerUtils.serverAddress)) {
|
||||
XToastUtils.error(getString(R.string.invalid_service_address))
|
||||
return
|
||||
}
|
||||
|
||||
pushCountDownHelper?.start()
|
||||
|
||||
val requestUrl: String = HttpServerUtils.serverAddress + "/clone/push"
|
||||
Log.i(TAG, "requestUrl:$requestUrl")
|
||||
|
||||
val msgMap: MutableMap<String, Any> = mutableMapOf()
|
||||
val timestamp = System.currentTimeMillis()
|
||||
msgMap["timestamp"] = timestamp
|
||||
val clientSignKey = HttpServerUtils.clientSignKey
|
||||
if (!TextUtils.isEmpty(clientSignKey)) {
|
||||
msgMap["sign"] =
|
||||
HttpServerUtils.calcSign(timestamp.toString(), clientSignKey)
|
||||
}
|
||||
msgMap["data"] = HttpServerUtils.exportSettings()
|
||||
|
||||
var requestMsg: String = Gson().toJson(msgMap)
|
||||
Log.i(TAG, "requestMsg:$requestMsg")
|
||||
|
||||
val postRequest = XHttp.post(requestUrl)
|
||||
.keepJson(true)
|
||||
.timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
|
||||
.cacheMode(CacheMode.NO_CACHE)
|
||||
.timeStamp(true)
|
||||
|
||||
when (HttpServerUtils.clientSafetyMeasures) {
|
||||
2 -> {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey)
|
||||
try {
|
||||
requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
requestMsg = RSACrypt.encryptByPublicKey(requestMsg, publicKey)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
3 -> {
|
||||
try {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey)
|
||||
//requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
val encryptCBC = SM4Crypt.encrypt(requestMsg.toByteArray(), sm4Key)
|
||||
requestMsg = ConvertTools.bytes2HexString(encryptCBC)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
else -> {
|
||||
postRequest.upJson(requestMsg)
|
||||
}
|
||||
}
|
||||
|
||||
postRequest.execute(object : SimpleCallBack<String>() {
|
||||
override fun onError(e: ApiException) {
|
||||
XToastUtils.error(e.displayMessage)
|
||||
pushCountDownHelper?.finish()
|
||||
}
|
||||
|
||||
override fun onSuccess(response: String) {
|
||||
Log.i(TAG, response)
|
||||
try {
|
||||
var json = response
|
||||
if (HttpServerUtils.clientSafetyMeasures == 2) {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey)
|
||||
json = RSACrypt.decryptByPublicKey(json, publicKey)
|
||||
json = String(Base64.decode(json))
|
||||
} else if (HttpServerUtils.clientSafetyMeasures == 3) {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey)
|
||||
val encryptCBC = ConvertTools.hexStringToByteArray(json)
|
||||
val decryptCBC = SM4Crypt.decrypt(encryptCBC, sm4Key)
|
||||
json = String(decryptCBC)
|
||||
}
|
||||
val resp: BaseResponse<String> = Gson().fromJson(json, object : TypeToken<BaseResponse<String>>() {}.type)
|
||||
if (resp.code == 200) {
|
||||
XToastUtils.success(ResUtils.getString(R.string.request_succeeded))
|
||||
} else {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + resp.msg)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + response)
|
||||
}
|
||||
pushCountDownHelper?.finish()
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
//拉取配置
|
||||
private fun pullData() {
|
||||
if (!CommonUtils.checkUrl(HttpServerUtils.serverAddress)) {
|
||||
XToastUtils.error(getString(R.string.invalid_service_address))
|
||||
return
|
||||
}
|
||||
|
||||
exportCountDownHelper?.start()
|
||||
|
||||
val requestUrl: String = HttpServerUtils.serverAddress + "/clone/pull"
|
||||
Log.i(TAG, "requestUrl:$requestUrl")
|
||||
|
||||
val msgMap: MutableMap<String, Any> = mutableMapOf()
|
||||
val timestamp = System.currentTimeMillis()
|
||||
msgMap["timestamp"] = timestamp
|
||||
val clientSignKey = HttpServerUtils.clientSignKey
|
||||
if (!TextUtils.isEmpty(clientSignKey)) {
|
||||
msgMap["sign"] =
|
||||
HttpServerUtils.calcSign(timestamp.toString(), clientSignKey)
|
||||
}
|
||||
|
||||
val dataMap: MutableMap<String, Any> = mutableMapOf()
|
||||
dataMap["version_code"] = AppUtils.getAppVersionCode()
|
||||
msgMap["data"] = dataMap
|
||||
|
||||
var requestMsg: String = Gson().toJson(msgMap)
|
||||
Log.i(TAG, "requestMsg:$requestMsg")
|
||||
|
||||
val postRequest = XHttp.post(requestUrl)
|
||||
.keepJson(true)
|
||||
.timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
|
||||
.cacheMode(CacheMode.NO_CACHE)
|
||||
.timeStamp(true)
|
||||
|
||||
when (HttpServerUtils.clientSafetyMeasures) {
|
||||
2 -> {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey)
|
||||
try {
|
||||
requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
requestMsg = RSACrypt.encryptByPublicKey(requestMsg, publicKey)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
3 -> {
|
||||
try {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey)
|
||||
//requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
val encryptCBC = SM4Crypt.encrypt(requestMsg.toByteArray(), sm4Key)
|
||||
requestMsg = ConvertTools.bytes2HexString(encryptCBC)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
else -> {
|
||||
postRequest.upJson(requestMsg)
|
||||
}
|
||||
}
|
||||
|
||||
postRequest.execute(object : SimpleCallBack<String>() {
|
||||
override fun onError(e: ApiException) {
|
||||
XToastUtils.error(e.displayMessage)
|
||||
exportCountDownHelper?.finish()
|
||||
}
|
||||
|
||||
override fun onSuccess(response: String) {
|
||||
Log.i(TAG, response)
|
||||
try {
|
||||
var json = response
|
||||
if (HttpServerUtils.clientSafetyMeasures == 2) {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey)
|
||||
json = RSACrypt.decryptByPublicKey(json, publicKey)
|
||||
json = String(Base64.decode(json))
|
||||
} else if (HttpServerUtils.clientSafetyMeasures == 3) {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey)
|
||||
val encryptCBC = ConvertTools.hexStringToByteArray(json)
|
||||
val decryptCBC = SM4Crypt.decrypt(encryptCBC, sm4Key)
|
||||
json = String(decryptCBC)
|
||||
}
|
||||
|
||||
//替换Date字段为当前时间
|
||||
val builder = GsonBuilder()
|
||||
builder.registerTypeAdapter(
|
||||
Date::class.java,
|
||||
JsonDeserializer<Any?> { _, _, _ -> Date() })
|
||||
val gson = builder.create()
|
||||
val resp: BaseResponse<CloneInfo> = gson.fromJson(json, object : TypeToken<BaseResponse<CloneInfo>>() {}.type)
|
||||
if (resp.code == 200) {
|
||||
val cloneInfo = resp.data
|
||||
Log.d(TAG, "cloneInfo = $cloneInfo")
|
||||
|
||||
if (cloneInfo == null) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed))
|
||||
return
|
||||
}
|
||||
|
||||
//判断版本是否一致
|
||||
HttpServerUtils.compareVersion(cloneInfo)
|
||||
|
||||
if (HttpServerUtils.restoreSettings(cloneInfo)) {
|
||||
XToastUtils.success(getString(R.string.import_succeeded))
|
||||
}
|
||||
} else {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + resp.msg)
|
||||
}
|
||||
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + response)
|
||||
}
|
||||
exportCountDownHelper?.finish()
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
if (pushCountDownHelper != null) pushCountDownHelper!!.recycle()
|
||||
if (pullCountDownHelper != null) pullCountDownHelper!!.recycle()
|
||||
if (exportCountDownHelper != null) exportCountDownHelper!!.recycle()
|
||||
if (importCountDownHelper != null) importCountDownHelper!!.recycle()
|
||||
super.onDestroyView()
|
||||
}
|
||||
}
|
@ -1,273 +1,273 @@
|
||||
package com.idormy.sms.forwarder.fragment.client
|
||||
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.text.isDigitsOnly
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.alibaba.android.vlayout.DelegateAdapter
|
||||
import com.alibaba.android.vlayout.VirtualLayoutManager
|
||||
import com.alibaba.android.vlayout.layout.LinearLayoutHelper
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.idormy.sms.forwarder.adapter.base.broccoli.BroccoliSimpleDelegateAdapter
|
||||
import com.idormy.sms.forwarder.adapter.base.delegate.SimpleDelegateAdapter
|
||||
import com.idormy.sms.forwarder.core.BaseFragment
|
||||
import com.idormy.sms.forwarder.databinding.FragmentClientContactQueryBinding
|
||||
import com.idormy.sms.forwarder.entity.ContactInfo
|
||||
import com.idormy.sms.forwarder.server.model.BaseResponse
|
||||
import com.idormy.sms.forwarder.server.model.ContactQueryData
|
||||
import com.idormy.sms.forwarder.utils.*
|
||||
import com.jeremyliao.liveeventbus.LiveEventBus
|
||||
import com.scwang.smartrefresh.layout.api.RefreshLayout
|
||||
import com.xuexiang.xaop.annotation.SingleClick
|
||||
import com.xuexiang.xhttp2.XHttp
|
||||
import com.xuexiang.xhttp2.cache.model.CacheMode
|
||||
import com.xuexiang.xhttp2.callback.SimpleCallBack
|
||||
import com.xuexiang.xhttp2.exception.ApiException
|
||||
import com.xuexiang.xpage.annotation.Page
|
||||
import com.xuexiang.xpage.base.XPageActivity
|
||||
import com.xuexiang.xpage.core.PageOption
|
||||
import com.xuexiang.xrouter.utils.TextUtils
|
||||
import com.xuexiang.xui.adapter.recyclerview.RecyclerViewHolder
|
||||
import com.xuexiang.xui.utils.ResUtils
|
||||
import com.xuexiang.xui.utils.SnackbarUtils
|
||||
import com.xuexiang.xui.widget.actionbar.TitleBar
|
||||
import com.xuexiang.xui.widget.searchview.MaterialSearchView
|
||||
import com.xuexiang.xutil.data.ConvertTools
|
||||
import com.xuexiang.xutil.system.ClipboardUtils
|
||||
import me.samlss.broccoli.Broccoli
|
||||
|
||||
|
||||
@Suppress("PropertyName")
|
||||
@Page(name = "远程查话簿")
|
||||
class ContactQueryFragment : BaseFragment<FragmentClientContactQueryBinding?>() {
|
||||
|
||||
val TAG: String = ContactQueryFragment::class.java.simpleName
|
||||
private var mAdapter: SimpleDelegateAdapter<ContactInfo>? = null
|
||||
private var keyword: String = ""
|
||||
|
||||
override fun viewBindingInflate(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup,
|
||||
): FragmentClientContactQueryBinding {
|
||||
return FragmentClientContactQueryBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
override fun initTitle(): TitleBar {
|
||||
val titleBar = super.initTitle()!!.setImmersive(false)
|
||||
titleBar.setTitle(R.string.api_contact_query)
|
||||
titleBar!!.addAction(object : TitleBar.ImageAction(R.drawable.ic_query) {
|
||||
@SingleClick
|
||||
override fun performAction(view: View) {
|
||||
binding!!.searchView.showSearch()
|
||||
}
|
||||
})
|
||||
return titleBar
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化控件
|
||||
*/
|
||||
override fun initViews() {
|
||||
val virtualLayoutManager = VirtualLayoutManager(requireContext())
|
||||
binding!!.recyclerView.layoutManager = virtualLayoutManager
|
||||
val viewPool = RecyclerView.RecycledViewPool()
|
||||
binding!!.recyclerView.setRecycledViewPool(viewPool)
|
||||
viewPool.setMaxRecycledViews(0, 10)
|
||||
|
||||
mAdapter = object : BroccoliSimpleDelegateAdapter<ContactInfo>(
|
||||
R.layout.adapter_contact_card_view_list_item,
|
||||
LinearLayoutHelper(),
|
||||
DataProvider.emptyContactInfo
|
||||
) {
|
||||
override fun onBindData(
|
||||
holder: RecyclerViewHolder,
|
||||
model: ContactInfo,
|
||||
position: Int,
|
||||
) {
|
||||
holder.text(R.id.sb_letter, model.firstLetter)
|
||||
holder.text(R.id.tv_name, model.name)
|
||||
holder.text(R.id.tv_phone_number, model.phoneNumber)
|
||||
holder.image(R.id.iv_copy, R.drawable.ic_copy)
|
||||
holder.image(R.id.iv_call, R.drawable.ic_phone_out)
|
||||
holder.image(R.id.iv_reply, R.drawable.ic_reply)
|
||||
holder.click(R.id.iv_copy) {
|
||||
val str = model.toString()
|
||||
XToastUtils.info(String.format(getString(R.string.copied_to_clipboard), str))
|
||||
ClipboardUtils.copyText(str)
|
||||
}
|
||||
holder.click(R.id.iv_call) {
|
||||
XToastUtils.info(getString(R.string.local_call) + model.phoneNumber)
|
||||
PhoneUtils.dial(model.phoneNumber)
|
||||
}
|
||||
holder.click(R.id.iv_reply) {
|
||||
XToastUtils.info(getString(R.string.remote_sms) + model.phoneNumber)
|
||||
/*val params = Bundle()
|
||||
params.putString(KEY_PHONE_NUMBERS, model.phoneNumber)
|
||||
openPage(SmsSendFragment::class.java, params)*/
|
||||
LiveEventBus.get<String>(EVENT_KEY_PHONE_NUMBERS).post(model.phoneNumber)
|
||||
PageOption.to(SmsSendFragment::class.java).setNewActivity(true).open((context as XPageActivity?)!!)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindBroccoli(holder: RecyclerViewHolder, broccoli: Broccoli) {
|
||||
broccoli.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.tv_name)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.tv_phone_number)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.sb_letter)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.iv_copy)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.iv_call)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.iv_reply)))
|
||||
}
|
||||
}
|
||||
|
||||
val delegateAdapter = DelegateAdapter(virtualLayoutManager)
|
||||
delegateAdapter.addAdapter(mAdapter)
|
||||
binding!!.recyclerView.adapter = delegateAdapter
|
||||
|
||||
//搜索框
|
||||
binding!!.searchView.findViewById<View>(com.xuexiang.xui.R.id.search_layout).visibility = View.GONE
|
||||
binding!!.searchView.setVoiceSearch(true)
|
||||
binding!!.searchView.setEllipsize(true)
|
||||
binding!!.searchView.setSuggestions(resources.getStringArray(R.array.query_suggestions))
|
||||
binding!!.searchView.setOnQueryTextListener(object : MaterialSearchView.OnQueryTextListener {
|
||||
override fun onQueryTextSubmit(query: String): Boolean {
|
||||
SnackbarUtils.Indefinite(view, String.format(getString(R.string.search_keyword), query)).info()
|
||||
.actionColor(ResUtils.getColor(R.color.xui_config_color_white))
|
||||
.setAction(getString(R.string.clear)) {
|
||||
keyword = ""
|
||||
loadRemoteData()
|
||||
}.show()
|
||||
if (keyword != query) {
|
||||
keyword = query
|
||||
loadRemoteData()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onQueryTextChange(newText: String): Boolean {
|
||||
//Do some magic
|
||||
return false
|
||||
}
|
||||
})
|
||||
binding!!.searchView.setOnSearchViewListener(object : MaterialSearchView.SearchViewListener {
|
||||
override fun onSearchViewShown() {
|
||||
//Do some magic
|
||||
}
|
||||
|
||||
override fun onSearchViewClosed() {
|
||||
//Do some magic
|
||||
}
|
||||
})
|
||||
binding!!.searchView.setSubmitOnClick(true)
|
||||
}
|
||||
|
||||
override fun initListeners() {
|
||||
//下拉刷新
|
||||
binding!!.refreshLayout.setOnRefreshListener { refreshLayout: RefreshLayout ->
|
||||
refreshLayout.layout.postDelayed({
|
||||
loadRemoteData()
|
||||
}, 1000)
|
||||
}
|
||||
binding!!.refreshLayout.autoRefresh() //第一次进入触发自动刷新,演示效果
|
||||
}
|
||||
|
||||
private fun loadRemoteData() {
|
||||
|
||||
val requestUrl: String = HttpServerUtils.serverAddress + "/contact/query"
|
||||
Log.i(TAG, "requestUrl:$requestUrl")
|
||||
|
||||
val msgMap: MutableMap<String, Any> = mutableMapOf()
|
||||
val timestamp = System.currentTimeMillis()
|
||||
msgMap["timestamp"] = timestamp
|
||||
val clientSignKey = HttpServerUtils.clientSignKey
|
||||
if (!TextUtils.isEmpty(clientSignKey)) {
|
||||
msgMap["sign"] = HttpServerUtils.calcSign(timestamp.toString(), clientSignKey.toString())
|
||||
}
|
||||
msgMap["data"] = if (keyword.isDigitsOnly())
|
||||
ContactQueryData(1, 20, keyword, null)
|
||||
else
|
||||
ContactQueryData(1, 20, null, keyword)
|
||||
|
||||
var requestMsg: String = Gson().toJson(msgMap)
|
||||
Log.i(TAG, "requestMsg:$requestMsg")
|
||||
|
||||
val postRequest = XHttp.post(requestUrl)
|
||||
.keepJson(true)
|
||||
.timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
|
||||
.cacheMode(CacheMode.NO_CACHE)
|
||||
.timeStamp(true)
|
||||
|
||||
when (HttpServerUtils.clientSafetyMeasures) {
|
||||
2 -> {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey.toString())
|
||||
try {
|
||||
requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
requestMsg = RSACrypt.encryptByPublicKey(requestMsg, publicKey)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
3 -> {
|
||||
try {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey.toString())
|
||||
//requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
val encryptCBC = SM4Crypt.encrypt(requestMsg.toByteArray(), sm4Key)
|
||||
requestMsg = ConvertTools.bytes2HexString(encryptCBC)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
else -> {
|
||||
postRequest.upJson(requestMsg)
|
||||
}
|
||||
}
|
||||
|
||||
postRequest.execute(object : SimpleCallBack<String>() {
|
||||
override fun onError(e: ApiException) {
|
||||
XToastUtils.error(e.displayMessage)
|
||||
}
|
||||
|
||||
override fun onSuccess(response: String) {
|
||||
Log.i(TAG, response)
|
||||
try {
|
||||
var json = response
|
||||
if (HttpServerUtils.clientSafetyMeasures == 2) {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey.toString())
|
||||
json = RSACrypt.decryptByPublicKey(json, publicKey)
|
||||
json = String(Base64.decode(json))
|
||||
} else if (HttpServerUtils.clientSafetyMeasures == 3) {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey.toString())
|
||||
val encryptCBC = ConvertTools.hexStringToByteArray(json)
|
||||
val decryptCBC = SM4Crypt.decrypt(encryptCBC, sm4Key)
|
||||
json = String(decryptCBC)
|
||||
}
|
||||
val resp: BaseResponse<List<ContactInfo>?> = Gson().fromJson(json, object : TypeToken<BaseResponse<List<ContactInfo>?>>() {}.type)
|
||||
if (resp.code == 200) {
|
||||
//XToastUtils.success(ResUtils.getString(R.string.request_succeeded))
|
||||
mAdapter!!.refresh(resp.data)
|
||||
binding!!.refreshLayout.finishRefresh()
|
||||
binding!!.recyclerView.scrollToPosition(0)
|
||||
} else {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + resp.msg)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + response)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
package com.idormy.sms.forwarder.fragment.client
|
||||
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.text.isDigitsOnly
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.alibaba.android.vlayout.DelegateAdapter
|
||||
import com.alibaba.android.vlayout.VirtualLayoutManager
|
||||
import com.alibaba.android.vlayout.layout.LinearLayoutHelper
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.idormy.sms.forwarder.adapter.base.broccoli.BroccoliSimpleDelegateAdapter
|
||||
import com.idormy.sms.forwarder.adapter.base.delegate.SimpleDelegateAdapter
|
||||
import com.idormy.sms.forwarder.core.BaseFragment
|
||||
import com.idormy.sms.forwarder.databinding.FragmentClientContactQueryBinding
|
||||
import com.idormy.sms.forwarder.entity.ContactInfo
|
||||
import com.idormy.sms.forwarder.server.model.BaseResponse
|
||||
import com.idormy.sms.forwarder.server.model.ContactQueryData
|
||||
import com.idormy.sms.forwarder.utils.*
|
||||
import com.jeremyliao.liveeventbus.LiveEventBus
|
||||
import com.scwang.smartrefresh.layout.api.RefreshLayout
|
||||
import com.xuexiang.xaop.annotation.SingleClick
|
||||
import com.xuexiang.xhttp2.XHttp
|
||||
import com.xuexiang.xhttp2.cache.model.CacheMode
|
||||
import com.xuexiang.xhttp2.callback.SimpleCallBack
|
||||
import com.xuexiang.xhttp2.exception.ApiException
|
||||
import com.xuexiang.xpage.annotation.Page
|
||||
import com.xuexiang.xpage.base.XPageActivity
|
||||
import com.xuexiang.xpage.core.PageOption
|
||||
import com.xuexiang.xrouter.utils.TextUtils
|
||||
import com.xuexiang.xui.adapter.recyclerview.RecyclerViewHolder
|
||||
import com.xuexiang.xui.utils.ResUtils
|
||||
import com.xuexiang.xui.utils.SnackbarUtils
|
||||
import com.xuexiang.xui.widget.actionbar.TitleBar
|
||||
import com.xuexiang.xui.widget.searchview.MaterialSearchView
|
||||
import com.xuexiang.xutil.data.ConvertTools
|
||||
import com.xuexiang.xutil.system.ClipboardUtils
|
||||
import me.samlss.broccoli.Broccoli
|
||||
|
||||
|
||||
@Suppress("PropertyName")
|
||||
@Page(name = "远程查话簿")
|
||||
class ContactQueryFragment : BaseFragment<FragmentClientContactQueryBinding?>() {
|
||||
|
||||
val TAG: String = ContactQueryFragment::class.java.simpleName
|
||||
private var mAdapter: SimpleDelegateAdapter<ContactInfo>? = null
|
||||
private var keyword: String = ""
|
||||
|
||||
override fun viewBindingInflate(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup,
|
||||
): FragmentClientContactQueryBinding {
|
||||
return FragmentClientContactQueryBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
override fun initTitle(): TitleBar {
|
||||
val titleBar = super.initTitle()!!.setImmersive(false)
|
||||
titleBar.setTitle(R.string.api_contact_query)
|
||||
titleBar!!.addAction(object : TitleBar.ImageAction(R.drawable.ic_query) {
|
||||
@SingleClick
|
||||
override fun performAction(view: View) {
|
||||
binding!!.searchView.showSearch()
|
||||
}
|
||||
})
|
||||
return titleBar
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化控件
|
||||
*/
|
||||
override fun initViews() {
|
||||
val virtualLayoutManager = VirtualLayoutManager(requireContext())
|
||||
binding!!.recyclerView.layoutManager = virtualLayoutManager
|
||||
val viewPool = RecyclerView.RecycledViewPool()
|
||||
binding!!.recyclerView.setRecycledViewPool(viewPool)
|
||||
viewPool.setMaxRecycledViews(0, 10)
|
||||
|
||||
mAdapter = object : BroccoliSimpleDelegateAdapter<ContactInfo>(
|
||||
R.layout.adapter_contact_card_view_list_item,
|
||||
LinearLayoutHelper(),
|
||||
DataProvider.emptyContactInfo
|
||||
) {
|
||||
override fun onBindData(
|
||||
holder: RecyclerViewHolder,
|
||||
model: ContactInfo,
|
||||
position: Int,
|
||||
) {
|
||||
holder.text(R.id.sb_letter, model.firstLetter)
|
||||
holder.text(R.id.tv_name, model.name)
|
||||
holder.text(R.id.tv_phone_number, model.phoneNumber)
|
||||
holder.image(R.id.iv_copy, R.drawable.ic_copy)
|
||||
holder.image(R.id.iv_call, R.drawable.ic_phone_out)
|
||||
holder.image(R.id.iv_reply, R.drawable.ic_reply)
|
||||
holder.click(R.id.iv_copy) {
|
||||
val str = model.toString()
|
||||
XToastUtils.info(String.format(getString(R.string.copied_to_clipboard), str))
|
||||
ClipboardUtils.copyText(str)
|
||||
}
|
||||
holder.click(R.id.iv_call) {
|
||||
XToastUtils.info(getString(R.string.local_call) + model.phoneNumber)
|
||||
PhoneUtils.dial(model.phoneNumber)
|
||||
}
|
||||
holder.click(R.id.iv_reply) {
|
||||
XToastUtils.info(getString(R.string.remote_sms) + model.phoneNumber)
|
||||
/*val params = Bundle()
|
||||
params.putString(KEY_PHONE_NUMBERS, model.phoneNumber)
|
||||
openPage(SmsSendFragment::class.java, params)*/
|
||||
LiveEventBus.get<String>(EVENT_KEY_PHONE_NUMBERS).post(model.phoneNumber)
|
||||
PageOption.to(SmsSendFragment::class.java).setNewActivity(true).open((context as XPageActivity?)!!)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindBroccoli(holder: RecyclerViewHolder, broccoli: Broccoli) {
|
||||
broccoli.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.tv_name)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.tv_phone_number)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.sb_letter)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.iv_copy)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.iv_call)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.iv_reply)))
|
||||
}
|
||||
}
|
||||
|
||||
val delegateAdapter = DelegateAdapter(virtualLayoutManager)
|
||||
delegateAdapter.addAdapter(mAdapter)
|
||||
binding!!.recyclerView.adapter = delegateAdapter
|
||||
|
||||
//搜索框
|
||||
binding!!.searchView.findViewById<View>(com.xuexiang.xui.R.id.search_layout).visibility = View.GONE
|
||||
binding!!.searchView.setVoiceSearch(true)
|
||||
binding!!.searchView.setEllipsize(true)
|
||||
binding!!.searchView.setSuggestions(resources.getStringArray(R.array.query_suggestions))
|
||||
binding!!.searchView.setOnQueryTextListener(object : MaterialSearchView.OnQueryTextListener {
|
||||
override fun onQueryTextSubmit(query: String): Boolean {
|
||||
SnackbarUtils.Indefinite(view, String.format(getString(R.string.search_keyword), query)).info()
|
||||
.actionColor(ResUtils.getColor(R.color.xui_config_color_white))
|
||||
.setAction(getString(R.string.clear)) {
|
||||
keyword = ""
|
||||
loadRemoteData()
|
||||
}.show()
|
||||
if (keyword != query) {
|
||||
keyword = query
|
||||
loadRemoteData()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onQueryTextChange(newText: String): Boolean {
|
||||
//Do some magic
|
||||
return false
|
||||
}
|
||||
})
|
||||
binding!!.searchView.setOnSearchViewListener(object : MaterialSearchView.SearchViewListener {
|
||||
override fun onSearchViewShown() {
|
||||
//Do some magic
|
||||
}
|
||||
|
||||
override fun onSearchViewClosed() {
|
||||
//Do some magic
|
||||
}
|
||||
})
|
||||
binding!!.searchView.setSubmitOnClick(true)
|
||||
}
|
||||
|
||||
override fun initListeners() {
|
||||
//下拉刷新
|
||||
binding!!.refreshLayout.setOnRefreshListener { refreshLayout: RefreshLayout ->
|
||||
refreshLayout.layout.postDelayed({
|
||||
loadRemoteData()
|
||||
}, 1000)
|
||||
}
|
||||
binding!!.refreshLayout.autoRefresh() //第一次进入触发自动刷新,演示效果
|
||||
}
|
||||
|
||||
private fun loadRemoteData() {
|
||||
|
||||
val requestUrl: String = HttpServerUtils.serverAddress + "/contact/query"
|
||||
Log.i(TAG, "requestUrl:$requestUrl")
|
||||
|
||||
val msgMap: MutableMap<String, Any> = mutableMapOf()
|
||||
val timestamp = System.currentTimeMillis()
|
||||
msgMap["timestamp"] = timestamp
|
||||
val clientSignKey = HttpServerUtils.clientSignKey
|
||||
if (!TextUtils.isEmpty(clientSignKey)) {
|
||||
msgMap["sign"] = HttpServerUtils.calcSign(timestamp.toString(), clientSignKey)
|
||||
}
|
||||
msgMap["data"] = if (keyword.isDigitsOnly())
|
||||
ContactQueryData(1, 20, keyword, null)
|
||||
else
|
||||
ContactQueryData(1, 20, null, keyword)
|
||||
|
||||
var requestMsg: String = Gson().toJson(msgMap)
|
||||
Log.i(TAG, "requestMsg:$requestMsg")
|
||||
|
||||
val postRequest = XHttp.post(requestUrl)
|
||||
.keepJson(true)
|
||||
.timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
|
||||
.cacheMode(CacheMode.NO_CACHE)
|
||||
.timeStamp(true)
|
||||
|
||||
when (HttpServerUtils.clientSafetyMeasures) {
|
||||
2 -> {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey)
|
||||
try {
|
||||
requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
requestMsg = RSACrypt.encryptByPublicKey(requestMsg, publicKey)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
3 -> {
|
||||
try {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey)
|
||||
//requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
val encryptCBC = SM4Crypt.encrypt(requestMsg.toByteArray(), sm4Key)
|
||||
requestMsg = ConvertTools.bytes2HexString(encryptCBC)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
else -> {
|
||||
postRequest.upJson(requestMsg)
|
||||
}
|
||||
}
|
||||
|
||||
postRequest.execute(object : SimpleCallBack<String>() {
|
||||
override fun onError(e: ApiException) {
|
||||
XToastUtils.error(e.displayMessage)
|
||||
}
|
||||
|
||||
override fun onSuccess(response: String) {
|
||||
Log.i(TAG, response)
|
||||
try {
|
||||
var json = response
|
||||
if (HttpServerUtils.clientSafetyMeasures == 2) {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey)
|
||||
json = RSACrypt.decryptByPublicKey(json, publicKey)
|
||||
json = String(Base64.decode(json))
|
||||
} else if (HttpServerUtils.clientSafetyMeasures == 3) {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey)
|
||||
val encryptCBC = ConvertTools.hexStringToByteArray(json)
|
||||
val decryptCBC = SM4Crypt.decrypt(encryptCBC, sm4Key)
|
||||
json = String(decryptCBC)
|
||||
}
|
||||
val resp: BaseResponse<List<ContactInfo>?> = Gson().fromJson(json, object : TypeToken<BaseResponse<List<ContactInfo>?>>() {}.type)
|
||||
if (resp.code == 200) {
|
||||
//XToastUtils.success(ResUtils.getString(R.string.request_succeeded))
|
||||
mAdapter!!.refresh(resp.data)
|
||||
binding!!.refreshLayout.finishRefresh()
|
||||
binding!!.recyclerView.scrollToPosition(0)
|
||||
} else {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + resp.msg)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + response)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,286 +1,286 @@
|
||||
package com.idormy.sms.forwarder.fragment.client
|
||||
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.alibaba.android.vlayout.DelegateAdapter
|
||||
import com.alibaba.android.vlayout.VirtualLayoutManager
|
||||
import com.alibaba.android.vlayout.layout.LinearLayoutHelper
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.idormy.sms.forwarder.adapter.base.broccoli.BroccoliSimpleDelegateAdapter
|
||||
import com.idormy.sms.forwarder.adapter.base.delegate.SimpleDelegateAdapter
|
||||
import com.idormy.sms.forwarder.core.BaseFragment
|
||||
import com.idormy.sms.forwarder.databinding.FragmentClientSmsQueryBinding
|
||||
import com.idormy.sms.forwarder.entity.SmsInfo
|
||||
import com.idormy.sms.forwarder.server.model.BaseResponse
|
||||
import com.idormy.sms.forwarder.server.model.SmsQueryData
|
||||
import com.idormy.sms.forwarder.utils.*
|
||||
import com.idormy.sms.forwarder.utils.DataProvider.emptySmsInfo
|
||||
import com.jeremyliao.liveeventbus.LiveEventBus
|
||||
import com.scwang.smartrefresh.layout.api.RefreshLayout
|
||||
import com.xuexiang.xaop.annotation.SingleClick
|
||||
import com.xuexiang.xhttp2.XHttp
|
||||
import com.xuexiang.xhttp2.cache.model.CacheMode
|
||||
import com.xuexiang.xhttp2.callback.SimpleCallBack
|
||||
import com.xuexiang.xhttp2.exception.ApiException
|
||||
import com.xuexiang.xpage.annotation.Page
|
||||
import com.xuexiang.xpage.base.XPageActivity
|
||||
import com.xuexiang.xpage.core.PageOption
|
||||
import com.xuexiang.xrouter.utils.TextUtils
|
||||
import com.xuexiang.xui.adapter.recyclerview.RecyclerViewHolder
|
||||
import com.xuexiang.xui.utils.ResUtils
|
||||
import com.xuexiang.xui.utils.SnackbarUtils
|
||||
import com.xuexiang.xui.widget.actionbar.TitleBar
|
||||
import com.xuexiang.xui.widget.searchview.MaterialSearchView
|
||||
import com.xuexiang.xui.widget.searchview.MaterialSearchView.SearchViewListener
|
||||
import com.xuexiang.xutil.data.ConvertTools
|
||||
import com.xuexiang.xutil.data.DateUtils
|
||||
import me.samlss.broccoli.Broccoli
|
||||
|
||||
|
||||
@Suppress("PropertyName")
|
||||
@Page(name = "远程查短信")
|
||||
class SmsQueryFragment : BaseFragment<FragmentClientSmsQueryBinding?>() {
|
||||
|
||||
val TAG: String = SmsQueryFragment::class.java.simpleName
|
||||
private var mAdapter: SimpleDelegateAdapter<SmsInfo>? = null
|
||||
private var smsType: Int = 1
|
||||
private var pageNum: Int = 1
|
||||
private val pageSize: Int = 20
|
||||
private var keyword: String = ""
|
||||
|
||||
override fun viewBindingInflate(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup,
|
||||
): FragmentClientSmsQueryBinding {
|
||||
return FragmentClientSmsQueryBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
override fun initTitle(): TitleBar {
|
||||
val titleBar = super.initTitle()!!.setImmersive(false)
|
||||
titleBar.setTitle(R.string.api_sms_query)
|
||||
titleBar!!.addAction(object : TitleBar.ImageAction(R.drawable.ic_query) {
|
||||
@SingleClick
|
||||
override fun performAction(view: View) {
|
||||
binding!!.searchView.showSearch()
|
||||
}
|
||||
})
|
||||
return titleBar
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化控件
|
||||
*/
|
||||
override fun initViews() {
|
||||
val virtualLayoutManager = VirtualLayoutManager(requireContext())
|
||||
binding!!.recyclerView.layoutManager = virtualLayoutManager
|
||||
val viewPool = RecyclerView.RecycledViewPool()
|
||||
binding!!.recyclerView.setRecycledViewPool(viewPool)
|
||||
viewPool.setMaxRecycledViews(0, 10)
|
||||
|
||||
mAdapter = object : BroccoliSimpleDelegateAdapter<SmsInfo>(
|
||||
R.layout.adapter_sms_card_view_list_item,
|
||||
LinearLayoutHelper(),
|
||||
emptySmsInfo
|
||||
) {
|
||||
override fun onBindData(
|
||||
holder: RecyclerViewHolder,
|
||||
model: SmsInfo,
|
||||
position: Int,
|
||||
) {
|
||||
holder.text(R.id.tv_from, model.number)
|
||||
holder.text(R.id.tv_time, DateUtils.getFriendlyTimeSpanByNow(model.date))
|
||||
holder.image(R.id.iv_image, model.typeImageId)
|
||||
holder.image(R.id.iv_sim_image, model.simImageId)
|
||||
holder.text(R.id.tv_content, model.content)
|
||||
holder.image(R.id.iv_reply, R.drawable.ic_reply)
|
||||
holder.click(R.id.iv_reply) {
|
||||
XToastUtils.info(getString(R.string.remote_sms) + model.number)
|
||||
LiveEventBus.get<Int>(EVENT_KEY_SIM_SLOT).post(model.simId)
|
||||
LiveEventBus.get<String>(EVENT_KEY_PHONE_NUMBERS).post(model.number)
|
||||
PageOption.to(SmsSendFragment::class.java).setNewActivity(true).open((context as XPageActivity?)!!)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindBroccoli(holder: RecyclerViewHolder, broccoli: Broccoli) {
|
||||
broccoli.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.tv_from)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.tv_time)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.iv_sim_image)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.tv_content)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.iv_image)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.iv_reply)))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
val delegateAdapter = DelegateAdapter(virtualLayoutManager)
|
||||
delegateAdapter.addAdapter(mAdapter)
|
||||
binding!!.recyclerView.adapter = delegateAdapter
|
||||
|
||||
binding!!.tabBar.setTabTitles(ResUtils.getStringArray(R.array.sms_type_option))
|
||||
binding!!.tabBar.setOnTabClickListener { _, position ->
|
||||
//XToastUtils.toast("点击了$title--$position")
|
||||
smsType = position + 1
|
||||
loadRemoteData(true)
|
||||
binding!!.recyclerView.scrollToPosition(0)
|
||||
}
|
||||
|
||||
//搜索框
|
||||
binding!!.searchView.findViewById<View>(com.xuexiang.xui.R.id.search_layout).visibility = View.GONE
|
||||
binding!!.searchView.setVoiceSearch(true)
|
||||
binding!!.searchView.setEllipsize(true)
|
||||
binding!!.searchView.setSuggestions(resources.getStringArray(R.array.query_suggestions))
|
||||
binding!!.searchView.setOnQueryTextListener(object : MaterialSearchView.OnQueryTextListener {
|
||||
override fun onQueryTextSubmit(query: String): Boolean {
|
||||
SnackbarUtils.Indefinite(view, String.format(getString(R.string.search_keyword), query)).info()
|
||||
.actionColor(ResUtils.getColor(R.color.xui_config_color_white))
|
||||
.setAction(getString(R.string.clear)) {
|
||||
keyword = ""
|
||||
loadRemoteData(true)
|
||||
}.show()
|
||||
if (keyword != query) {
|
||||
keyword = query
|
||||
loadRemoteData(true)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onQueryTextChange(newText: String): Boolean {
|
||||
//Do some magic
|
||||
return false
|
||||
}
|
||||
})
|
||||
binding!!.searchView.setOnSearchViewListener(object : SearchViewListener {
|
||||
override fun onSearchViewShown() {
|
||||
//Do some magic
|
||||
}
|
||||
|
||||
override fun onSearchViewClosed() {
|
||||
//Do some magic
|
||||
}
|
||||
})
|
||||
binding!!.searchView.setSubmitOnClick(true)
|
||||
}
|
||||
|
||||
override fun initListeners() {
|
||||
//下拉刷新
|
||||
binding!!.refreshLayout.setOnRefreshListener { refreshLayout: RefreshLayout ->
|
||||
refreshLayout.layout.postDelayed({
|
||||
loadRemoteData(true)
|
||||
}, 1000)
|
||||
}
|
||||
//上拉加载
|
||||
binding!!.refreshLayout.setOnLoadMoreListener { refreshLayout: RefreshLayout ->
|
||||
refreshLayout.layout.postDelayed({
|
||||
loadRemoteData(false)
|
||||
}, 1000)
|
||||
}
|
||||
binding!!.refreshLayout.autoRefresh() //第一次进入触发自动刷新,演示效果
|
||||
}
|
||||
|
||||
private fun loadRemoteData(refresh: Boolean) {
|
||||
|
||||
val requestUrl: String = HttpServerUtils.serverAddress + "/sms/query"
|
||||
Log.i(TAG, "requestUrl:$requestUrl")
|
||||
|
||||
val msgMap: MutableMap<String, Any> = mutableMapOf()
|
||||
val timestamp = System.currentTimeMillis()
|
||||
msgMap["timestamp"] = timestamp
|
||||
val clientSignKey = HttpServerUtils.clientSignKey
|
||||
if (!TextUtils.isEmpty(clientSignKey)) {
|
||||
msgMap["sign"] = HttpServerUtils.calcSign(timestamp.toString(), clientSignKey.toString())
|
||||
}
|
||||
|
||||
if (refresh) pageNum = 1
|
||||
msgMap["data"] = SmsQueryData(smsType, pageNum, pageSize, keyword)
|
||||
|
||||
var requestMsg: String = Gson().toJson(msgMap)
|
||||
Log.i(TAG, "requestMsg:$requestMsg")
|
||||
|
||||
val postRequest = XHttp.post(requestUrl)
|
||||
.keepJson(true)
|
||||
.timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
|
||||
.cacheMode(CacheMode.NO_CACHE)
|
||||
.timeStamp(true)
|
||||
|
||||
when (HttpServerUtils.clientSafetyMeasures) {
|
||||
2 -> {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey.toString())
|
||||
try {
|
||||
requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
requestMsg = RSACrypt.encryptByPublicKey(requestMsg, publicKey)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
3 -> {
|
||||
try {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey.toString())
|
||||
//requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
val encryptCBC = SM4Crypt.encrypt(requestMsg.toByteArray(), sm4Key)
|
||||
requestMsg = ConvertTools.bytes2HexString(encryptCBC)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
else -> {
|
||||
postRequest.upJson(requestMsg)
|
||||
}
|
||||
}
|
||||
|
||||
postRequest.execute(object : SimpleCallBack<String>() {
|
||||
override fun onError(e: ApiException) {
|
||||
XToastUtils.error(e.displayMessage)
|
||||
}
|
||||
|
||||
override fun onSuccess(response: String) {
|
||||
Log.i(TAG, response)
|
||||
try {
|
||||
var json = response
|
||||
if (HttpServerUtils.clientSafetyMeasures == 2) {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey.toString())
|
||||
json = RSACrypt.decryptByPublicKey(json, publicKey)
|
||||
json = String(Base64.decode(json))
|
||||
} else if (HttpServerUtils.clientSafetyMeasures == 3) {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey.toString())
|
||||
val encryptCBC = ConvertTools.hexStringToByteArray(json)
|
||||
val decryptCBC = SM4Crypt.decrypt(encryptCBC, sm4Key)
|
||||
json = String(decryptCBC)
|
||||
}
|
||||
val resp: BaseResponse<List<SmsInfo>?> = Gson().fromJson(json, object : TypeToken<BaseResponse<List<SmsInfo>?>>() {}.type)
|
||||
if (resp.code == 200) {
|
||||
//XToastUtils.success(ResUtils.getString(R.string.request_succeeded))
|
||||
pageNum++
|
||||
if (refresh) {
|
||||
mAdapter!!.refresh(resp.data)
|
||||
binding!!.refreshLayout.finishRefresh()
|
||||
binding!!.recyclerView.scrollToPosition(0)
|
||||
} else {
|
||||
mAdapter!!.loadMore(resp.data)
|
||||
binding!!.refreshLayout.finishLoadMore()
|
||||
}
|
||||
} else {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + resp.msg)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + response)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
package com.idormy.sms.forwarder.fragment.client
|
||||
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.alibaba.android.vlayout.DelegateAdapter
|
||||
import com.alibaba.android.vlayout.VirtualLayoutManager
|
||||
import com.alibaba.android.vlayout.layout.LinearLayoutHelper
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.idormy.sms.forwarder.adapter.base.broccoli.BroccoliSimpleDelegateAdapter
|
||||
import com.idormy.sms.forwarder.adapter.base.delegate.SimpleDelegateAdapter
|
||||
import com.idormy.sms.forwarder.core.BaseFragment
|
||||
import com.idormy.sms.forwarder.databinding.FragmentClientSmsQueryBinding
|
||||
import com.idormy.sms.forwarder.entity.SmsInfo
|
||||
import com.idormy.sms.forwarder.server.model.BaseResponse
|
||||
import com.idormy.sms.forwarder.server.model.SmsQueryData
|
||||
import com.idormy.sms.forwarder.utils.*
|
||||
import com.idormy.sms.forwarder.utils.DataProvider.emptySmsInfo
|
||||
import com.jeremyliao.liveeventbus.LiveEventBus
|
||||
import com.scwang.smartrefresh.layout.api.RefreshLayout
|
||||
import com.xuexiang.xaop.annotation.SingleClick
|
||||
import com.xuexiang.xhttp2.XHttp
|
||||
import com.xuexiang.xhttp2.cache.model.CacheMode
|
||||
import com.xuexiang.xhttp2.callback.SimpleCallBack
|
||||
import com.xuexiang.xhttp2.exception.ApiException
|
||||
import com.xuexiang.xpage.annotation.Page
|
||||
import com.xuexiang.xpage.base.XPageActivity
|
||||
import com.xuexiang.xpage.core.PageOption
|
||||
import com.xuexiang.xrouter.utils.TextUtils
|
||||
import com.xuexiang.xui.adapter.recyclerview.RecyclerViewHolder
|
||||
import com.xuexiang.xui.utils.ResUtils
|
||||
import com.xuexiang.xui.utils.SnackbarUtils
|
||||
import com.xuexiang.xui.widget.actionbar.TitleBar
|
||||
import com.xuexiang.xui.widget.searchview.MaterialSearchView
|
||||
import com.xuexiang.xui.widget.searchview.MaterialSearchView.SearchViewListener
|
||||
import com.xuexiang.xutil.data.ConvertTools
|
||||
import com.xuexiang.xutil.data.DateUtils
|
||||
import me.samlss.broccoli.Broccoli
|
||||
|
||||
|
||||
@Suppress("PropertyName")
|
||||
@Page(name = "远程查短信")
|
||||
class SmsQueryFragment : BaseFragment<FragmentClientSmsQueryBinding?>() {
|
||||
|
||||
val TAG: String = SmsQueryFragment::class.java.simpleName
|
||||
private var mAdapter: SimpleDelegateAdapter<SmsInfo>? = null
|
||||
private var smsType: Int = 1
|
||||
private var pageNum: Int = 1
|
||||
private val pageSize: Int = 20
|
||||
private var keyword: String = ""
|
||||
|
||||
override fun viewBindingInflate(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup,
|
||||
): FragmentClientSmsQueryBinding {
|
||||
return FragmentClientSmsQueryBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
override fun initTitle(): TitleBar {
|
||||
val titleBar = super.initTitle()!!.setImmersive(false)
|
||||
titleBar.setTitle(R.string.api_sms_query)
|
||||
titleBar!!.addAction(object : TitleBar.ImageAction(R.drawable.ic_query) {
|
||||
@SingleClick
|
||||
override fun performAction(view: View) {
|
||||
binding!!.searchView.showSearch()
|
||||
}
|
||||
})
|
||||
return titleBar
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化控件
|
||||
*/
|
||||
override fun initViews() {
|
||||
val virtualLayoutManager = VirtualLayoutManager(requireContext())
|
||||
binding!!.recyclerView.layoutManager = virtualLayoutManager
|
||||
val viewPool = RecyclerView.RecycledViewPool()
|
||||
binding!!.recyclerView.setRecycledViewPool(viewPool)
|
||||
viewPool.setMaxRecycledViews(0, 10)
|
||||
|
||||
mAdapter = object : BroccoliSimpleDelegateAdapter<SmsInfo>(
|
||||
R.layout.adapter_sms_card_view_list_item,
|
||||
LinearLayoutHelper(),
|
||||
emptySmsInfo
|
||||
) {
|
||||
override fun onBindData(
|
||||
holder: RecyclerViewHolder,
|
||||
model: SmsInfo,
|
||||
position: Int,
|
||||
) {
|
||||
holder.text(R.id.tv_from, model.number)
|
||||
holder.text(R.id.tv_time, DateUtils.getFriendlyTimeSpanByNow(model.date))
|
||||
holder.image(R.id.iv_image, model.typeImageId)
|
||||
holder.image(R.id.iv_sim_image, model.simImageId)
|
||||
holder.text(R.id.tv_content, model.content)
|
||||
holder.image(R.id.iv_reply, R.drawable.ic_reply)
|
||||
holder.click(R.id.iv_reply) {
|
||||
XToastUtils.info(getString(R.string.remote_sms) + model.number)
|
||||
LiveEventBus.get<Int>(EVENT_KEY_SIM_SLOT).post(model.simId)
|
||||
LiveEventBus.get<String>(EVENT_KEY_PHONE_NUMBERS).post(model.number)
|
||||
PageOption.to(SmsSendFragment::class.java).setNewActivity(true).open((context as XPageActivity?)!!)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindBroccoli(holder: RecyclerViewHolder, broccoli: Broccoli) {
|
||||
broccoli.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.tv_from)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.tv_time)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.iv_sim_image)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.tv_content)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.iv_image)))
|
||||
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.iv_reply)))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
val delegateAdapter = DelegateAdapter(virtualLayoutManager)
|
||||
delegateAdapter.addAdapter(mAdapter)
|
||||
binding!!.recyclerView.adapter = delegateAdapter
|
||||
|
||||
binding!!.tabBar.setTabTitles(ResUtils.getStringArray(R.array.sms_type_option))
|
||||
binding!!.tabBar.setOnTabClickListener { _, position ->
|
||||
//XToastUtils.toast("点击了$title--$position")
|
||||
smsType = position + 1
|
||||
loadRemoteData(true)
|
||||
binding!!.recyclerView.scrollToPosition(0)
|
||||
}
|
||||
|
||||
//搜索框
|
||||
binding!!.searchView.findViewById<View>(com.xuexiang.xui.R.id.search_layout).visibility = View.GONE
|
||||
binding!!.searchView.setVoiceSearch(true)
|
||||
binding!!.searchView.setEllipsize(true)
|
||||
binding!!.searchView.setSuggestions(resources.getStringArray(R.array.query_suggestions))
|
||||
binding!!.searchView.setOnQueryTextListener(object : MaterialSearchView.OnQueryTextListener {
|
||||
override fun onQueryTextSubmit(query: String): Boolean {
|
||||
SnackbarUtils.Indefinite(view, String.format(getString(R.string.search_keyword), query)).info()
|
||||
.actionColor(ResUtils.getColor(R.color.xui_config_color_white))
|
||||
.setAction(getString(R.string.clear)) {
|
||||
keyword = ""
|
||||
loadRemoteData(true)
|
||||
}.show()
|
||||
if (keyword != query) {
|
||||
keyword = query
|
||||
loadRemoteData(true)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onQueryTextChange(newText: String): Boolean {
|
||||
//Do some magic
|
||||
return false
|
||||
}
|
||||
})
|
||||
binding!!.searchView.setOnSearchViewListener(object : SearchViewListener {
|
||||
override fun onSearchViewShown() {
|
||||
//Do some magic
|
||||
}
|
||||
|
||||
override fun onSearchViewClosed() {
|
||||
//Do some magic
|
||||
}
|
||||
})
|
||||
binding!!.searchView.setSubmitOnClick(true)
|
||||
}
|
||||
|
||||
override fun initListeners() {
|
||||
//下拉刷新
|
||||
binding!!.refreshLayout.setOnRefreshListener { refreshLayout: RefreshLayout ->
|
||||
refreshLayout.layout.postDelayed({
|
||||
loadRemoteData(true)
|
||||
}, 1000)
|
||||
}
|
||||
//上拉加载
|
||||
binding!!.refreshLayout.setOnLoadMoreListener { refreshLayout: RefreshLayout ->
|
||||
refreshLayout.layout.postDelayed({
|
||||
loadRemoteData(false)
|
||||
}, 1000)
|
||||
}
|
||||
binding!!.refreshLayout.autoRefresh() //第一次进入触发自动刷新,演示效果
|
||||
}
|
||||
|
||||
private fun loadRemoteData(refresh: Boolean) {
|
||||
|
||||
val requestUrl: String = HttpServerUtils.serverAddress + "/sms/query"
|
||||
Log.i(TAG, "requestUrl:$requestUrl")
|
||||
|
||||
val msgMap: MutableMap<String, Any> = mutableMapOf()
|
||||
val timestamp = System.currentTimeMillis()
|
||||
msgMap["timestamp"] = timestamp
|
||||
val clientSignKey = HttpServerUtils.clientSignKey
|
||||
if (!TextUtils.isEmpty(clientSignKey)) {
|
||||
msgMap["sign"] = HttpServerUtils.calcSign(timestamp.toString(), clientSignKey)
|
||||
}
|
||||
|
||||
if (refresh) pageNum = 1
|
||||
msgMap["data"] = SmsQueryData(smsType, pageNum, pageSize, keyword)
|
||||
|
||||
var requestMsg: String = Gson().toJson(msgMap)
|
||||
Log.i(TAG, "requestMsg:$requestMsg")
|
||||
|
||||
val postRequest = XHttp.post(requestUrl)
|
||||
.keepJson(true)
|
||||
.timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
|
||||
.cacheMode(CacheMode.NO_CACHE)
|
||||
.timeStamp(true)
|
||||
|
||||
when (HttpServerUtils.clientSafetyMeasures) {
|
||||
2 -> {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey)
|
||||
try {
|
||||
requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
requestMsg = RSACrypt.encryptByPublicKey(requestMsg, publicKey)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
3 -> {
|
||||
try {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey)
|
||||
//requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
val encryptCBC = SM4Crypt.encrypt(requestMsg.toByteArray(), sm4Key)
|
||||
requestMsg = ConvertTools.bytes2HexString(encryptCBC)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
else -> {
|
||||
postRequest.upJson(requestMsg)
|
||||
}
|
||||
}
|
||||
|
||||
postRequest.execute(object : SimpleCallBack<String>() {
|
||||
override fun onError(e: ApiException) {
|
||||
XToastUtils.error(e.displayMessage)
|
||||
}
|
||||
|
||||
override fun onSuccess(response: String) {
|
||||
Log.i(TAG, response)
|
||||
try {
|
||||
var json = response
|
||||
if (HttpServerUtils.clientSafetyMeasures == 2) {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey)
|
||||
json = RSACrypt.decryptByPublicKey(json, publicKey)
|
||||
json = String(Base64.decode(json))
|
||||
} else if (HttpServerUtils.clientSafetyMeasures == 3) {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey)
|
||||
val encryptCBC = ConvertTools.hexStringToByteArray(json)
|
||||
val decryptCBC = SM4Crypt.decrypt(encryptCBC, sm4Key)
|
||||
json = String(decryptCBC)
|
||||
}
|
||||
val resp: BaseResponse<List<SmsInfo>?> = Gson().fromJson(json, object : TypeToken<BaseResponse<List<SmsInfo>?>>() {}.type)
|
||||
if (resp.code == 200) {
|
||||
//XToastUtils.success(ResUtils.getString(R.string.request_succeeded))
|
||||
pageNum++
|
||||
if (refresh) {
|
||||
mAdapter!!.refresh(resp.data)
|
||||
binding!!.refreshLayout.finishRefresh()
|
||||
binding!!.recyclerView.scrollToPosition(0)
|
||||
} else {
|
||||
mAdapter!!.loadMore(resp.data)
|
||||
binding!!.refreshLayout.finishLoadMore()
|
||||
}
|
||||
} else {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + resp.msg)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + response)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,235 +1,235 @@
|
||||
package com.idormy.sms.forwarder.fragment.client
|
||||
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.idormy.sms.forwarder.core.BaseFragment
|
||||
import com.idormy.sms.forwarder.databinding.FragmentClientWolSendBinding
|
||||
import com.idormy.sms.forwarder.server.model.BaseResponse
|
||||
import com.idormy.sms.forwarder.utils.*
|
||||
import com.xuexiang.xaop.annotation.SingleClick
|
||||
import com.xuexiang.xhttp2.XHttp
|
||||
import com.xuexiang.xhttp2.cache.model.CacheMode
|
||||
import com.xuexiang.xhttp2.callback.SimpleCallBack
|
||||
import com.xuexiang.xhttp2.exception.ApiException
|
||||
import com.xuexiang.xpage.annotation.Page
|
||||
import com.xuexiang.xrouter.utils.TextUtils
|
||||
import com.xuexiang.xui.utils.CountDownButtonHelper
|
||||
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 com.xuexiang.xutil.data.ConvertTools
|
||||
|
||||
@Suppress("PropertyName")
|
||||
@Page(name = "远程WOL")
|
||||
class WolSendFragment : BaseFragment<FragmentClientWolSendBinding?>(), View.OnClickListener {
|
||||
|
||||
val TAG: String = WolSendFragment::class.java.simpleName
|
||||
private var mCountDownHelper: CountDownButtonHelper? = null
|
||||
private var wolHistory: MutableMap<String, String> = mutableMapOf()
|
||||
|
||||
override fun viewBindingInflate(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup,
|
||||
): FragmentClientWolSendBinding {
|
||||
return FragmentClientWolSendBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
override fun initTitle(): TitleBar? {
|
||||
return super.initTitle()!!.setImmersive(false).setTitle(R.string.api_wol)
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化控件
|
||||
*/
|
||||
override fun initViews() {
|
||||
//发送按钮增加倒计时,避免重复点击
|
||||
mCountDownHelper = CountDownButtonHelper(binding!!.btnSubmit, SettingUtils.requestTimeout)
|
||||
mCountDownHelper!!.setOnCountDownListener(object :
|
||||
CountDownButtonHelper.OnCountDownListener {
|
||||
override fun onCountDown(time: Int) {
|
||||
binding!!.btnSubmit.text = String.format(getString(R.string.seconds_n), time)
|
||||
}
|
||||
|
||||
override fun onFinished() {
|
||||
binding!!.btnSubmit.text = getString(R.string.send)
|
||||
}
|
||||
})
|
||||
|
||||
//取出历史记录
|
||||
val history = HttpServerUtils.wolHistory
|
||||
if (!TextUtils.isEmpty(history)) {
|
||||
wolHistory =
|
||||
Gson().fromJson(history, object : TypeToken<MutableMap<String, String>>() {}.type)
|
||||
}
|
||||
}
|
||||
|
||||
override fun initListeners() {
|
||||
binding!!.btnServerHistory.setOnClickListener(this)
|
||||
binding!!.btnSubmit.setOnClickListener(this)
|
||||
}
|
||||
|
||||
@SingleClick
|
||||
override fun onClick(v: View) {
|
||||
when (v.id) {
|
||||
R.id.btn_server_history -> {
|
||||
if (wolHistory.isEmpty()) {
|
||||
XToastUtils.warning(getString(R.string.no_server_history))
|
||||
return
|
||||
}
|
||||
Log.d(TAG, "wolHistory = $wolHistory")
|
||||
|
||||
MaterialDialog.Builder(requireContext())
|
||||
.title(R.string.server_history)
|
||||
.items(wolHistory.keys)
|
||||
.itemsCallbackSingleChoice(0) { _: MaterialDialog?, _: View?, _: Int, text: CharSequence ->
|
||||
//XToastUtils.info("$which: $text")
|
||||
binding!!.etMac.setText(text)
|
||||
binding!!.etIp.setText(wolHistory[text])
|
||||
true // allow selection
|
||||
}
|
||||
.positiveText(R.string.select)
|
||||
.negativeText(R.string.cancel)
|
||||
.neutralText(R.string.clear_history)
|
||||
.neutralColor(ResUtils.getColors(R.color.red))
|
||||
.onNeutral { _: MaterialDialog?, _: DialogAction? ->
|
||||
wolHistory.clear()
|
||||
HttpServerUtils.wolHistory = ""
|
||||
}
|
||||
.show()
|
||||
}
|
||||
R.id.btn_submit -> {
|
||||
val requestUrl: String = HttpServerUtils.serverAddress + "/wol/send"
|
||||
Log.i(TAG, "requestUrl:$requestUrl")
|
||||
|
||||
val msgMap: MutableMap<String, Any> = mutableMapOf()
|
||||
val timestamp = System.currentTimeMillis()
|
||||
msgMap["timestamp"] = timestamp
|
||||
val clientSignKey = HttpServerUtils.clientSignKey
|
||||
if (!TextUtils.isEmpty(clientSignKey)) {
|
||||
msgMap["sign"] =
|
||||
HttpServerUtils.calcSign(timestamp.toString(), clientSignKey.toString())
|
||||
}
|
||||
|
||||
val mac = binding!!.etMac.text.toString()
|
||||
val macRegex = getString(R.string.mac_regex).toRegex()
|
||||
if (!macRegex.matches(mac)) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.mac_error))
|
||||
return
|
||||
}
|
||||
|
||||
val ip = binding!!.etIp.text.toString()
|
||||
val ipRegex = getString(R.string.ip_regex).toRegex()
|
||||
if (!TextUtils.isEmpty(ip) && !ipRegex.matches(ip)) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.ip_error))
|
||||
return
|
||||
}
|
||||
|
||||
val port = binding!!.etPort.text.toString()
|
||||
val portRegex = getString(R.string.wol_port_regex).toRegex()
|
||||
if (!TextUtils.isEmpty(port) && !portRegex.matches(port)) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.wol_port_error))
|
||||
return
|
||||
}
|
||||
|
||||
val dataMap: MutableMap<String, Any> = mutableMapOf()
|
||||
dataMap["ip"] = ip
|
||||
dataMap["mac"] = mac
|
||||
dataMap["port"] = port
|
||||
msgMap["data"] = dataMap
|
||||
|
||||
var requestMsg: String = Gson().toJson(msgMap)
|
||||
Log.i(TAG, "requestMsg:$requestMsg")
|
||||
|
||||
val postRequest = XHttp.post(requestUrl)
|
||||
.keepJson(true)
|
||||
.timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
|
||||
.cacheMode(CacheMode.NO_CACHE)
|
||||
.timeStamp(true)
|
||||
|
||||
when (HttpServerUtils.clientSafetyMeasures) {
|
||||
2 -> {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey.toString())
|
||||
try {
|
||||
requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
requestMsg = RSACrypt.encryptByPublicKey(requestMsg, publicKey)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
3 -> {
|
||||
try {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey.toString())
|
||||
//requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
val encryptCBC = SM4Crypt.encrypt(requestMsg.toByteArray(), sm4Key)
|
||||
requestMsg = ConvertTools.bytes2HexString(encryptCBC)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
else -> {
|
||||
postRequest.upJson(requestMsg)
|
||||
}
|
||||
}
|
||||
|
||||
mCountDownHelper?.start()
|
||||
postRequest.execute(object : SimpleCallBack<String>() {
|
||||
override fun onError(e: ApiException) {
|
||||
XToastUtils.error(e.displayMessage)
|
||||
mCountDownHelper?.finish()
|
||||
}
|
||||
|
||||
override fun onSuccess(response: String) {
|
||||
Log.i(TAG, response)
|
||||
try {
|
||||
var json = response
|
||||
if (HttpServerUtils.clientSafetyMeasures == 2) {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey.toString())
|
||||
json = RSACrypt.decryptByPublicKey(json, publicKey)
|
||||
json = String(Base64.decode(json))
|
||||
} else if (HttpServerUtils.clientSafetyMeasures == 3) {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey.toString())
|
||||
val encryptCBC = ConvertTools.hexStringToByteArray(json)
|
||||
val decryptCBC = SM4Crypt.decrypt(encryptCBC, sm4Key)
|
||||
json = String(decryptCBC)
|
||||
}
|
||||
val resp: BaseResponse<String> = Gson().fromJson(json, object : TypeToken<BaseResponse<String>>() {}.type)
|
||||
if (resp.code == 200) {
|
||||
XToastUtils.success(ResUtils.getString(R.string.request_succeeded))
|
||||
//添加到历史记录
|
||||
wolHistory[mac] = ip
|
||||
HttpServerUtils.wolHistory = Gson().toJson(wolHistory)
|
||||
} else {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + resp.msg)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + response)
|
||||
}
|
||||
mCountDownHelper?.finish()
|
||||
}
|
||||
})
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
if (mCountDownHelper != null) mCountDownHelper!!.recycle()
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
package com.idormy.sms.forwarder.fragment.client
|
||||
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.idormy.sms.forwarder.core.BaseFragment
|
||||
import com.idormy.sms.forwarder.databinding.FragmentClientWolSendBinding
|
||||
import com.idormy.sms.forwarder.server.model.BaseResponse
|
||||
import com.idormy.sms.forwarder.utils.*
|
||||
import com.xuexiang.xaop.annotation.SingleClick
|
||||
import com.xuexiang.xhttp2.XHttp
|
||||
import com.xuexiang.xhttp2.cache.model.CacheMode
|
||||
import com.xuexiang.xhttp2.callback.SimpleCallBack
|
||||
import com.xuexiang.xhttp2.exception.ApiException
|
||||
import com.xuexiang.xpage.annotation.Page
|
||||
import com.xuexiang.xrouter.utils.TextUtils
|
||||
import com.xuexiang.xui.utils.CountDownButtonHelper
|
||||
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 com.xuexiang.xutil.data.ConvertTools
|
||||
|
||||
@Suppress("PropertyName")
|
||||
@Page(name = "远程WOL")
|
||||
class WolSendFragment : BaseFragment<FragmentClientWolSendBinding?>(), View.OnClickListener {
|
||||
|
||||
val TAG: String = WolSendFragment::class.java.simpleName
|
||||
private var mCountDownHelper: CountDownButtonHelper? = null
|
||||
private var wolHistory: MutableMap<String, String> = mutableMapOf()
|
||||
|
||||
override fun viewBindingInflate(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup,
|
||||
): FragmentClientWolSendBinding {
|
||||
return FragmentClientWolSendBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
override fun initTitle(): TitleBar? {
|
||||
return super.initTitle()!!.setImmersive(false).setTitle(R.string.api_wol)
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化控件
|
||||
*/
|
||||
override fun initViews() {
|
||||
//发送按钮增加倒计时,避免重复点击
|
||||
mCountDownHelper = CountDownButtonHelper(binding!!.btnSubmit, SettingUtils.requestTimeout)
|
||||
mCountDownHelper!!.setOnCountDownListener(object :
|
||||
CountDownButtonHelper.OnCountDownListener {
|
||||
override fun onCountDown(time: Int) {
|
||||
binding!!.btnSubmit.text = String.format(getString(R.string.seconds_n), time)
|
||||
}
|
||||
|
||||
override fun onFinished() {
|
||||
binding!!.btnSubmit.text = getString(R.string.send)
|
||||
}
|
||||
})
|
||||
|
||||
//取出历史记录
|
||||
val history = HttpServerUtils.wolHistory
|
||||
if (!TextUtils.isEmpty(history)) {
|
||||
wolHistory =
|
||||
Gson().fromJson(history, object : TypeToken<MutableMap<String, String>>() {}.type)
|
||||
}
|
||||
}
|
||||
|
||||
override fun initListeners() {
|
||||
binding!!.btnServerHistory.setOnClickListener(this)
|
||||
binding!!.btnSubmit.setOnClickListener(this)
|
||||
}
|
||||
|
||||
@SingleClick
|
||||
override fun onClick(v: View) {
|
||||
when (v.id) {
|
||||
R.id.btn_server_history -> {
|
||||
if (wolHistory.isEmpty()) {
|
||||
XToastUtils.warning(getString(R.string.no_server_history))
|
||||
return
|
||||
}
|
||||
Log.d(TAG, "wolHistory = $wolHistory")
|
||||
|
||||
MaterialDialog.Builder(requireContext())
|
||||
.title(R.string.server_history)
|
||||
.items(wolHistory.keys)
|
||||
.itemsCallbackSingleChoice(0) { _: MaterialDialog?, _: View?, _: Int, text: CharSequence ->
|
||||
//XToastUtils.info("$which: $text")
|
||||
binding!!.etMac.setText(text)
|
||||
binding!!.etIp.setText(wolHistory[text])
|
||||
true // allow selection
|
||||
}
|
||||
.positiveText(R.string.select)
|
||||
.negativeText(R.string.cancel)
|
||||
.neutralText(R.string.clear_history)
|
||||
.neutralColor(ResUtils.getColors(R.color.red))
|
||||
.onNeutral { _: MaterialDialog?, _: DialogAction? ->
|
||||
wolHistory.clear()
|
||||
HttpServerUtils.wolHistory = ""
|
||||
}
|
||||
.show()
|
||||
}
|
||||
R.id.btn_submit -> {
|
||||
val requestUrl: String = HttpServerUtils.serverAddress + "/wol/send"
|
||||
Log.i(TAG, "requestUrl:$requestUrl")
|
||||
|
||||
val msgMap: MutableMap<String, Any> = mutableMapOf()
|
||||
val timestamp = System.currentTimeMillis()
|
||||
msgMap["timestamp"] = timestamp
|
||||
val clientSignKey = HttpServerUtils.clientSignKey
|
||||
if (!TextUtils.isEmpty(clientSignKey)) {
|
||||
msgMap["sign"] =
|
||||
HttpServerUtils.calcSign(timestamp.toString(), clientSignKey)
|
||||
}
|
||||
|
||||
val mac = binding!!.etMac.text.toString()
|
||||
val macRegex = getString(R.string.mac_regex).toRegex()
|
||||
if (!macRegex.matches(mac)) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.mac_error))
|
||||
return
|
||||
}
|
||||
|
||||
val ip = binding!!.etIp.text.toString()
|
||||
val ipRegex = getString(R.string.ip_regex).toRegex()
|
||||
if (!TextUtils.isEmpty(ip) && !ipRegex.matches(ip)) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.ip_error))
|
||||
return
|
||||
}
|
||||
|
||||
val port = binding!!.etPort.text.toString()
|
||||
val portRegex = getString(R.string.wol_port_regex).toRegex()
|
||||
if (!TextUtils.isEmpty(port) && !portRegex.matches(port)) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.wol_port_error))
|
||||
return
|
||||
}
|
||||
|
||||
val dataMap: MutableMap<String, Any> = mutableMapOf()
|
||||
dataMap["ip"] = ip
|
||||
dataMap["mac"] = mac
|
||||
dataMap["port"] = port
|
||||
msgMap["data"] = dataMap
|
||||
|
||||
var requestMsg: String = Gson().toJson(msgMap)
|
||||
Log.i(TAG, "requestMsg:$requestMsg")
|
||||
|
||||
val postRequest = XHttp.post(requestUrl)
|
||||
.keepJson(true)
|
||||
.timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
|
||||
.cacheMode(CacheMode.NO_CACHE)
|
||||
.timeStamp(true)
|
||||
|
||||
when (HttpServerUtils.clientSafetyMeasures) {
|
||||
2 -> {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey)
|
||||
try {
|
||||
requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
requestMsg = RSACrypt.encryptByPublicKey(requestMsg, publicKey)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
3 -> {
|
||||
try {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey)
|
||||
//requestMsg = Base64.encode(requestMsg.toByteArray())
|
||||
val encryptCBC = SM4Crypt.encrypt(requestMsg.toByteArray(), sm4Key)
|
||||
requestMsg = ConvertTools.bytes2HexString(encryptCBC)
|
||||
Log.i(TAG, "requestMsg: $requestMsg")
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
}
|
||||
else -> {
|
||||
postRequest.upJson(requestMsg)
|
||||
}
|
||||
}
|
||||
|
||||
mCountDownHelper?.start()
|
||||
postRequest.execute(object : SimpleCallBack<String>() {
|
||||
override fun onError(e: ApiException) {
|
||||
XToastUtils.error(e.displayMessage)
|
||||
mCountDownHelper?.finish()
|
||||
}
|
||||
|
||||
override fun onSuccess(response: String) {
|
||||
Log.i(TAG, response)
|
||||
try {
|
||||
var json = response
|
||||
if (HttpServerUtils.clientSafetyMeasures == 2) {
|
||||
val publicKey = RSACrypt.getPublicKey(HttpServerUtils.clientSignKey)
|
||||
json = RSACrypt.decryptByPublicKey(json, publicKey)
|
||||
json = String(Base64.decode(json))
|
||||
} else if (HttpServerUtils.clientSafetyMeasures == 3) {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.clientSignKey)
|
||||
val encryptCBC = ConvertTools.hexStringToByteArray(json)
|
||||
val decryptCBC = SM4Crypt.decrypt(encryptCBC, sm4Key)
|
||||
json = String(decryptCBC)
|
||||
}
|
||||
val resp: BaseResponse<String> = Gson().fromJson(json, object : TypeToken<BaseResponse<String>>() {}.type)
|
||||
if (resp.code == 200) {
|
||||
XToastUtils.success(ResUtils.getString(R.string.request_succeeded))
|
||||
//添加到历史记录
|
||||
wolHistory[mac] = ip
|
||||
HttpServerUtils.wolHistory = Gson().toJson(wolHistory)
|
||||
} else {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + resp.msg)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + response)
|
||||
}
|
||||
mCountDownHelper?.finish()
|
||||
}
|
||||
})
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
if (mCountDownHelper != null) mCountDownHelper!!.recycle()
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
}
|
@ -1,35 +1,35 @@
|
||||
package com.idormy.sms.forwarder.server.component
|
||||
|
||||
import android.content.Context
|
||||
import com.idormy.sms.forwarder.utils.HttpServerUtils
|
||||
import com.xuexiang.xrouter.utils.TextUtils
|
||||
import com.yanzhenjie.andserver.annotation.Config
|
||||
import com.yanzhenjie.andserver.framework.config.WebConfig
|
||||
import com.yanzhenjie.andserver.framework.website.AssetsWebsite
|
||||
import com.yanzhenjie.andserver.framework.website.StorageWebsite
|
||||
|
||||
@Config
|
||||
class AppConfig : WebConfig {
|
||||
|
||||
override fun onConfig(context: Context, delegate: WebConfig.Delegate) {
|
||||
|
||||
val serverWebPath = HttpServerUtils.serverWebPath
|
||||
if (!TextUtils.isEmpty(serverWebPath)) {
|
||||
// 增加一个位于/sdcard/Download/目录下的网站
|
||||
delegate.addWebsite(StorageWebsite(serverWebPath.toString()))
|
||||
} else {
|
||||
// 增加一个位于assets的web目录的网站
|
||||
delegate.addWebsite(AssetsWebsite(context, "/web/"))
|
||||
}
|
||||
|
||||
/*delegate.setMultipart(
|
||||
Multipart.newBuilder()
|
||||
.allFileMaxSize(1024 * 1024 * 20) // 单个请求所有文件总大小
|
||||
.fileMaxSize(1024 * 1024 * 5) // 单个请求每个文件大小
|
||||
.maxInMemorySize(1024 * 20) // 内存缓存大小
|
||||
.uploadTempDir(context.cacheDir) // 上传文件保存目录
|
||||
.build()
|
||||
)*/
|
||||
}
|
||||
|
||||
package com.idormy.sms.forwarder.server.component
|
||||
|
||||
import android.content.Context
|
||||
import com.idormy.sms.forwarder.utils.HttpServerUtils
|
||||
import com.xuexiang.xrouter.utils.TextUtils
|
||||
import com.yanzhenjie.andserver.annotation.Config
|
||||
import com.yanzhenjie.andserver.framework.config.WebConfig
|
||||
import com.yanzhenjie.andserver.framework.website.AssetsWebsite
|
||||
import com.yanzhenjie.andserver.framework.website.StorageWebsite
|
||||
|
||||
@Config
|
||||
class AppConfig : WebConfig {
|
||||
|
||||
override fun onConfig(context: Context, delegate: WebConfig.Delegate) {
|
||||
|
||||
val serverWebPath = HttpServerUtils.serverWebPath
|
||||
if (!TextUtils.isEmpty(serverWebPath)) {
|
||||
// 增加一个位于/sdcard/Download/目录下的网站
|
||||
delegate.addWebsite(StorageWebsite(serverWebPath))
|
||||
} else {
|
||||
// 增加一个位于assets的web目录的网站
|
||||
delegate.addWebsite(AssetsWebsite(context, "/web/"))
|
||||
}
|
||||
|
||||
/*delegate.setMultipart(
|
||||
Multipart.newBuilder()
|
||||
.allFileMaxSize(1024 * 1024 * 20) // 单个请求所有文件总大小
|
||||
.fileMaxSize(1024 * 1024 * 5) // 单个请求每个文件大小
|
||||
.maxInMemorySize(1024 * 20) // 内存缓存大小
|
||||
.uploadTempDir(context.cacheDir) // 上传文件保存目录
|
||||
.build()
|
||||
)*/
|
||||
}
|
||||
|
||||
}
|
@ -1,56 +1,56 @@
|
||||
package com.idormy.sms.forwarder.server.component
|
||||
|
||||
import android.util.Log
|
||||
import com.idormy.sms.forwarder.utils.Base64
|
||||
import com.idormy.sms.forwarder.utils.HttpServerUtils
|
||||
import com.idormy.sms.forwarder.utils.RSACrypt
|
||||
import com.idormy.sms.forwarder.utils.SM4Crypt
|
||||
import com.xuexiang.xutil.data.ConvertTools
|
||||
import com.yanzhenjie.andserver.annotation.Resolver
|
||||
import com.yanzhenjie.andserver.error.HttpException
|
||||
import com.yanzhenjie.andserver.framework.ExceptionResolver
|
||||
import com.yanzhenjie.andserver.framework.body.JsonBody
|
||||
import com.yanzhenjie.andserver.framework.body.StringBody
|
||||
import com.yanzhenjie.andserver.http.HttpRequest
|
||||
import com.yanzhenjie.andserver.http.HttpResponse
|
||||
import com.yanzhenjie.andserver.http.StatusCode
|
||||
|
||||
@Suppress("PrivatePropertyName")
|
||||
@Resolver
|
||||
class AppExceptionResolver : ExceptionResolver {
|
||||
|
||||
private val TAG: String = "AppExceptionResolver"
|
||||
|
||||
override fun onResolve(request: HttpRequest, response: HttpResponse, e: Throwable) {
|
||||
e.printStackTrace()
|
||||
if (e is HttpException) {
|
||||
//response.status = e.statusCode
|
||||
//异常捕获返回 http 200
|
||||
response.status = StatusCode.SC_OK
|
||||
} else {
|
||||
response.status = StatusCode.SC_INTERNAL_SERVER_ERROR
|
||||
}
|
||||
|
||||
//返回统一结构报文
|
||||
var resp = HttpServerUtils.response(e.message.toString())
|
||||
Log.d(TAG, "resp: $resp")
|
||||
when (HttpServerUtils.safetyMeasures) {
|
||||
2 -> {
|
||||
val privateKey = RSACrypt.getPrivateKey(HttpServerUtils.serverPrivateKey.toString())
|
||||
resp = Base64.encode(resp.toByteArray())
|
||||
resp = RSACrypt.encryptByPrivateKey(resp, privateKey)
|
||||
response.setBody(StringBody(resp))
|
||||
}
|
||||
3 -> {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.serverSm4Key.toString())
|
||||
//response = Base64.encode(response.toByteArray())
|
||||
val encryptCBC = SM4Crypt.encrypt(resp.toByteArray(), sm4Key)
|
||||
response.setBody(StringBody(ConvertTools.bytes2HexString(encryptCBC)))
|
||||
}
|
||||
else -> {
|
||||
response.setBody(JsonBody(resp))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
package com.idormy.sms.forwarder.server.component
|
||||
|
||||
import android.util.Log
|
||||
import com.idormy.sms.forwarder.utils.Base64
|
||||
import com.idormy.sms.forwarder.utils.HttpServerUtils
|
||||
import com.idormy.sms.forwarder.utils.RSACrypt
|
||||
import com.idormy.sms.forwarder.utils.SM4Crypt
|
||||
import com.xuexiang.xutil.data.ConvertTools
|
||||
import com.yanzhenjie.andserver.annotation.Resolver
|
||||
import com.yanzhenjie.andserver.error.HttpException
|
||||
import com.yanzhenjie.andserver.framework.ExceptionResolver
|
||||
import com.yanzhenjie.andserver.framework.body.JsonBody
|
||||
import com.yanzhenjie.andserver.framework.body.StringBody
|
||||
import com.yanzhenjie.andserver.http.HttpRequest
|
||||
import com.yanzhenjie.andserver.http.HttpResponse
|
||||
import com.yanzhenjie.andserver.http.StatusCode
|
||||
|
||||
@Suppress("PrivatePropertyName")
|
||||
@Resolver
|
||||
class AppExceptionResolver : ExceptionResolver {
|
||||
|
||||
private val TAG: String = "AppExceptionResolver"
|
||||
|
||||
override fun onResolve(request: HttpRequest, response: HttpResponse, e: Throwable) {
|
||||
e.printStackTrace()
|
||||
if (e is HttpException) {
|
||||
//response.status = e.statusCode
|
||||
//异常捕获返回 http 200
|
||||
response.status = StatusCode.SC_OK
|
||||
} else {
|
||||
response.status = StatusCode.SC_INTERNAL_SERVER_ERROR
|
||||
}
|
||||
|
||||
//返回统一结构报文
|
||||
var resp = HttpServerUtils.response(e.message.toString())
|
||||
Log.d(TAG, "resp: $resp")
|
||||
when (HttpServerUtils.safetyMeasures) {
|
||||
2 -> {
|
||||
val privateKey = RSACrypt.getPrivateKey(HttpServerUtils.serverPrivateKey)
|
||||
resp = Base64.encode(resp.toByteArray())
|
||||
resp = RSACrypt.encryptByPrivateKey(resp, privateKey)
|
||||
response.setBody(StringBody(resp))
|
||||
}
|
||||
3 -> {
|
||||
val sm4Key = ConvertTools.hexStringToByteArray(HttpServerUtils.serverSm4Key)
|
||||
//response = Base64.encode(response.toByteArray())
|
||||
val encryptCBC = SM4Crypt.encrypt(resp.toByteArray(), sm4Key)
|
||||
response.setBody(StringBody(ConvertTools.bytes2HexString(encryptCBC)))
|
||||
}
|
||||
else -> {
|
||||
response.setBody(JsonBody(resp))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,49 +1,49 @@
|
||||
package com.idormy.sms.forwarder.server.controller
|
||||
|
||||
import android.util.Log
|
||||
import com.idormy.sms.forwarder.App
|
||||
import com.idormy.sms.forwarder.server.model.BaseRequest
|
||||
import com.idormy.sms.forwarder.server.model.ConfigData
|
||||
import com.idormy.sms.forwarder.utils.HttpServerUtils
|
||||
import com.idormy.sms.forwarder.utils.PhoneUtils
|
||||
import com.idormy.sms.forwarder.utils.SettingUtils
|
||||
import com.xuexiang.xutil.app.AppUtils
|
||||
import com.yanzhenjie.andserver.annotation.*
|
||||
|
||||
@Suppress("PrivatePropertyName")
|
||||
@RestController
|
||||
@RequestMapping(path = ["/config"])
|
||||
class ConfigController {
|
||||
|
||||
private val TAG: String = CloneController::class.java.simpleName
|
||||
|
||||
//远程查配置
|
||||
@CrossOrigin(methods = [RequestMethod.POST])
|
||||
@PostMapping("/query")
|
||||
fun test(@RequestBody bean: BaseRequest<*>): ConfigData {
|
||||
Log.d(TAG, bean.data.toString())
|
||||
|
||||
//获取卡槽信息
|
||||
if (App.SimInfoList.isEmpty()) {
|
||||
App.SimInfoList = PhoneUtils.getSimMultiInfo()
|
||||
}
|
||||
Log.d(TAG, App.SimInfoList.toString())
|
||||
|
||||
return ConfigData(
|
||||
HttpServerUtils.enableApiClone,
|
||||
HttpServerUtils.enableApiSmsSend,
|
||||
HttpServerUtils.enableApiSmsQuery,
|
||||
HttpServerUtils.enableApiCallQuery,
|
||||
HttpServerUtils.enableApiContactQuery,
|
||||
HttpServerUtils.enableApiBatteryQuery,
|
||||
HttpServerUtils.enableApiWol,
|
||||
SettingUtils.extraDeviceMark.toString(),
|
||||
SettingUtils.extraSim1.toString(),
|
||||
SettingUtils.extraSim2.toString(),
|
||||
App.SimInfoList,
|
||||
AppUtils.getAppVersionCode(),
|
||||
AppUtils.getAppVersionName(),
|
||||
)
|
||||
}
|
||||
|
||||
package com.idormy.sms.forwarder.server.controller
|
||||
|
||||
import android.util.Log
|
||||
import com.idormy.sms.forwarder.App
|
||||
import com.idormy.sms.forwarder.server.model.BaseRequest
|
||||
import com.idormy.sms.forwarder.server.model.ConfigData
|
||||
import com.idormy.sms.forwarder.utils.HttpServerUtils
|
||||
import com.idormy.sms.forwarder.utils.PhoneUtils
|
||||
import com.idormy.sms.forwarder.utils.SettingUtils
|
||||
import com.xuexiang.xutil.app.AppUtils
|
||||
import com.yanzhenjie.andserver.annotation.*
|
||||
|
||||
@Suppress("PrivatePropertyName")
|
||||
@RestController
|
||||
@RequestMapping(path = ["/config"])
|
||||
class ConfigController {
|
||||
|
||||
private val TAG: String = CloneController::class.java.simpleName
|
||||
|
||||
//远程查配置
|
||||
@CrossOrigin(methods = [RequestMethod.POST])
|
||||
@PostMapping("/query")
|
||||
fun test(@RequestBody bean: BaseRequest<*>): ConfigData {
|
||||
Log.d(TAG, bean.data.toString())
|
||||
|
||||
//获取卡槽信息
|
||||
if (App.SimInfoList.isEmpty()) {
|
||||
App.SimInfoList = PhoneUtils.getSimMultiInfo()
|
||||
}
|
||||
Log.d(TAG, App.SimInfoList.toString())
|
||||
|
||||
return ConfigData(
|
||||
HttpServerUtils.enableApiClone,
|
||||
HttpServerUtils.enableApiSmsSend,
|
||||
HttpServerUtils.enableApiSmsQuery,
|
||||
HttpServerUtils.enableApiCallQuery,
|
||||
HttpServerUtils.enableApiContactQuery,
|
||||
HttpServerUtils.enableApiBatteryQuery,
|
||||
HttpServerUtils.enableApiWol,
|
||||
SettingUtils.extraDeviceMark,
|
||||
SettingUtils.extraSim1,
|
||||
SettingUtils.extraSim2,
|
||||
App.SimInfoList,
|
||||
AppUtils.getAppVersionCode(),
|
||||
AppUtils.getAppVersionName(),
|
||||
)
|
||||
}
|
||||
|
||||
}
|
@ -1,32 +1,15 @@
|
||||
package com.idormy.sms.forwarder.utils
|
||||
|
||||
object CactusSave {
|
||||
//Cactus存活时间
|
||||
var timer: Long
|
||||
get() = MMKVUtils.getLong(CACTUS_TIMER, 0L)
|
||||
set(timer) {
|
||||
MMKVUtils.put(CACTUS_TIMER, timer)
|
||||
}
|
||||
|
||||
//Cactus上次存活时间
|
||||
var lastTimer: Long
|
||||
get() = MMKVUtils.getLong(CACTUS_LAST_TIMER, 0L)
|
||||
set(timer) {
|
||||
MMKVUtils.put(CACTUS_LAST_TIMER, timer)
|
||||
}
|
||||
|
||||
//Cactus运行时间
|
||||
var date: String?
|
||||
get() = MMKVUtils.getString(SP_EXTRA_DEVICE_MARK, "0000-01-01 00:00:00")
|
||||
set(extraDeviceMark) {
|
||||
MMKVUtils.put(SP_EXTRA_DEVICE_MARK, extraDeviceMark)
|
||||
}
|
||||
|
||||
//Cactus结束时间
|
||||
var endDate: String?
|
||||
get() = MMKVUtils.getString(CACTUS_DATE, "0000-01-01 00:00:00")
|
||||
set(extraDeviceMark) {
|
||||
MMKVUtils.put(CACTUS_END_DATE, extraDeviceMark)
|
||||
}
|
||||
|
||||
package com.idormy.sms.forwarder.utils
|
||||
|
||||
object CactusSave {
|
||||
//Cactus存活时间
|
||||
var timer: Long by SharedPreference(CACTUS_TIMER, 0L)
|
||||
|
||||
//Cactus上次存活时间
|
||||
var lastTimer: Long by SharedPreference(CACTUS_LAST_TIMER, 0L)
|
||||
|
||||
//Cactus运行时间
|
||||
var date: String by SharedPreference(SP_EXTRA_DEVICE_MARK, "0000-01-01 00:00:00")
|
||||
|
||||
//Cactus结束时间
|
||||
var endDate: String by SharedPreference(CACTUS_DATE, "0000-01-01 00:00:00")
|
||||
}
|
@ -1,366 +1,261 @@
|
||||
package com.idormy.sms.forwarder.utils
|
||||
|
||||
|
||||
import android.text.TextUtils
|
||||
import android.util.Base64
|
||||
import android.util.Log
|
||||
import com.google.gson.Gson
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.idormy.sms.forwarder.core.Core
|
||||
import com.idormy.sms.forwarder.entity.CloneInfo
|
||||
import com.idormy.sms.forwarder.server.model.BaseRequest
|
||||
import com.xuexiang.xui.utils.ResUtils.getString
|
||||
import com.xuexiang.xutil.app.AppUtils
|
||||
import com.yanzhenjie.andserver.error.HttpException
|
||||
import java.net.URLEncoder
|
||||
import java.nio.charset.StandardCharsets
|
||||
import javax.crypto.Mac
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
|
||||
/**
|
||||
* HttpServer工具类
|
||||
*/
|
||||
class HttpServerUtils private constructor() {
|
||||
|
||||
companion object {
|
||||
|
||||
//是否启用HttpServer开机自启
|
||||
@JvmStatic
|
||||
var enableServerAutorun: Boolean
|
||||
get() = MMKVUtils.getBoolean(SP_ENABLE_SERVER_AUTORUN, false)
|
||||
set(enableServerAutorun) {
|
||||
MMKVUtils.put(SP_ENABLE_SERVER_AUTORUN, enableServerAutorun)
|
||||
}
|
||||
|
||||
//服务端安全设置
|
||||
@JvmStatic
|
||||
var safetyMeasures: Int
|
||||
get() = MMKVUtils.getInt(SP_SERVER_SAFETY_MEASURES, if (TextUtils.isEmpty(serverSignKey)) 0 else 1)
|
||||
set(safetyMeasures) {
|
||||
MMKVUtils.put(SP_SERVER_SAFETY_MEASURES, safetyMeasures)
|
||||
}
|
||||
|
||||
//服务端SM4密钥
|
||||
@JvmStatic
|
||||
var serverSm4Key: String?
|
||||
get() = MMKVUtils.getString(SP_SERVER_SM4_KEY, "")
|
||||
set(serverSm4Key) {
|
||||
MMKVUtils.put(SP_SERVER_SM4_KEY, serverSm4Key)
|
||||
}
|
||||
|
||||
//服务端RSA公钥
|
||||
@JvmStatic
|
||||
var serverPublicKey: String?
|
||||
get() = MMKVUtils.getString(SP_SERVER_PUBLIC_KEY, "")
|
||||
set(serverPublicKey) {
|
||||
MMKVUtils.put(SP_SERVER_PUBLIC_KEY, serverPublicKey)
|
||||
}
|
||||
|
||||
//服务端RSA私钥
|
||||
@JvmStatic
|
||||
var serverPrivateKey: String?
|
||||
get() = MMKVUtils.getString(SP_SERVER_PRIVATE_KEY, "")
|
||||
set(serverPrivateKey) {
|
||||
MMKVUtils.put(SP_SERVER_PRIVATE_KEY, serverPrivateKey)
|
||||
}
|
||||
|
||||
//服务端签名密钥
|
||||
@JvmStatic
|
||||
var serverSignKey: String?
|
||||
get() = MMKVUtils.getString(SP_SERVER_SIGN_KEY, "")
|
||||
set(serverSignKey) {
|
||||
MMKVUtils.put(SP_SERVER_SIGN_KEY, serverSignKey)
|
||||
}
|
||||
|
||||
//时间容差
|
||||
@JvmStatic
|
||||
var timeTolerance: Int
|
||||
get() = MMKVUtils.getInt(SP_SERVER_TIME_TOLERANCE, 600)
|
||||
set(timeTolerance) {
|
||||
MMKVUtils.put(SP_SERVER_TIME_TOLERANCE, timeTolerance)
|
||||
}
|
||||
|
||||
//自定义web客户端目录
|
||||
@JvmStatic
|
||||
var serverWebPath: String?
|
||||
get() = MMKVUtils.getString(SP_SERVER_WEB_PATH, "")
|
||||
set(serverWebPath) {
|
||||
MMKVUtils.put(SP_SERVER_WEB_PATH, serverWebPath)
|
||||
}
|
||||
|
||||
//服务地址
|
||||
@JvmStatic
|
||||
var serverAddress: String?
|
||||
get() = MMKVUtils.getString(SP_SERVER_ADDRESS, "")
|
||||
set(clientSignKey) {
|
||||
MMKVUtils.put(SP_SERVER_ADDRESS, clientSignKey)
|
||||
}
|
||||
|
||||
//服务地址历史记录
|
||||
@JvmStatic
|
||||
var serverHistory: String?
|
||||
get() = MMKVUtils.getString(SP_SERVER_HISTORY, "")
|
||||
set(serverHistory) {
|
||||
MMKVUtils.put(SP_SERVER_HISTORY, serverHistory)
|
||||
}
|
||||
|
||||
//服务端配置
|
||||
@JvmStatic
|
||||
var serverConfig: String?
|
||||
get() = MMKVUtils.getString(SP_SERVER_CONFIG, "")
|
||||
set(serverConfig) {
|
||||
MMKVUtils.put(SP_SERVER_CONFIG, serverConfig)
|
||||
}
|
||||
|
||||
//服务端安全设置
|
||||
@JvmStatic
|
||||
var clientSafetyMeasures: Int
|
||||
get() = MMKVUtils.getInt(SP_CLIENT_SAFETY_MEASURES, if (TextUtils.isEmpty(clientSignKey)) 0 else 1)
|
||||
set(clientSafetyMeasures) {
|
||||
MMKVUtils.put(SP_CLIENT_SAFETY_MEASURES, clientSafetyMeasures)
|
||||
}
|
||||
|
||||
//客户端签名密钥/RSA公钥
|
||||
@JvmStatic
|
||||
var clientSignKey: String?
|
||||
get() = MMKVUtils.getString(SP_CLIENT_SIGN_KEY, "")
|
||||
set(clientSignKey) {
|
||||
MMKVUtils.put(SP_CLIENT_SIGN_KEY, clientSignKey)
|
||||
}
|
||||
|
||||
//是否启用一键克隆
|
||||
@JvmStatic
|
||||
var enableApiClone: Boolean
|
||||
get() = MMKVUtils.getBoolean(SP_ENABLE_API_CLONE, false)
|
||||
set(enableApiClone) {
|
||||
MMKVUtils.put(SP_ENABLE_API_CLONE, enableApiClone)
|
||||
}
|
||||
|
||||
//是否启用远程发短信
|
||||
@JvmStatic
|
||||
var enableApiSmsSend: Boolean
|
||||
get() = MMKVUtils.getBoolean(SP_ENABLE_API_SMS_SEND, false)
|
||||
set(enableApiSendSms) {
|
||||
MMKVUtils.put(SP_ENABLE_API_SMS_SEND, enableApiSendSms)
|
||||
}
|
||||
|
||||
//是否启用远程查短信
|
||||
@JvmStatic
|
||||
var enableApiSmsQuery: Boolean
|
||||
get() = MMKVUtils.getBoolean(SP_ENABLE_API_SMS_QUERY, false)
|
||||
set(enableApiQuerySms) {
|
||||
MMKVUtils.put(SP_ENABLE_API_SMS_QUERY, enableApiQuerySms)
|
||||
}
|
||||
|
||||
//是否启用远程查通话
|
||||
@JvmStatic
|
||||
var enableApiCallQuery: Boolean
|
||||
get() = MMKVUtils.getBoolean(SP_ENABLE_API_CALL_QUERY, false)
|
||||
set(enableApiQueryCall) {
|
||||
MMKVUtils.put(SP_ENABLE_API_CALL_QUERY, enableApiQueryCall)
|
||||
}
|
||||
|
||||
//是否启用远程查话簿
|
||||
@JvmStatic
|
||||
var enableApiContactQuery: Boolean
|
||||
get() = MMKVUtils.getBoolean(SP_ENABLE_API_CONTACT_QUERY, false)
|
||||
set(enableApiQueryLinkman) {
|
||||
MMKVUtils.put(SP_ENABLE_API_CONTACT_QUERY, enableApiQueryLinkman)
|
||||
}
|
||||
|
||||
//是否启用远程查电量
|
||||
@JvmStatic
|
||||
var enableApiBatteryQuery: Boolean
|
||||
get() = MMKVUtils.getBoolean(SP_ENABLE_API_BATTERY_QUERY, false)
|
||||
set(enableApiQueryBattery) {
|
||||
MMKVUtils.put(SP_ENABLE_API_BATTERY_QUERY, enableApiQueryBattery)
|
||||
}
|
||||
|
||||
//是否启用远程WOL
|
||||
@JvmStatic
|
||||
var enableApiWol: Boolean
|
||||
get() = MMKVUtils.getBoolean(SP_ENABLE_API_WOL, false)
|
||||
set(enableApiWol) {
|
||||
MMKVUtils.put(SP_ENABLE_API_WOL, enableApiWol)
|
||||
}
|
||||
|
||||
//WOL历史记录
|
||||
@JvmStatic
|
||||
var wolHistory: String?
|
||||
get() = MMKVUtils.getString(SP_WOL_HISTORY, "")
|
||||
set(wolHistory) {
|
||||
MMKVUtils.put(SP_WOL_HISTORY, wolHistory)
|
||||
}
|
||||
|
||||
//计算签名
|
||||
fun calcSign(timestamp: String, signSecret: String): String {
|
||||
val stringToSign = "$timestamp\n" + signSecret
|
||||
val mac = Mac.getInstance("HmacSHA256")
|
||||
mac.init(SecretKeySpec(signSecret.toByteArray(StandardCharsets.UTF_8), "HmacSHA256"))
|
||||
val signData = mac.doFinal(stringToSign.toByteArray(StandardCharsets.UTF_8))
|
||||
return URLEncoder.encode(String(Base64.encode(signData, Base64.NO_WRAP)), "UTF-8")
|
||||
}
|
||||
|
||||
//校验签名
|
||||
@Throws(HttpException::class)
|
||||
fun checkSign(req: BaseRequest<*>) {
|
||||
val signSecret = serverSignKey
|
||||
if (TextUtils.isEmpty(signSecret)) return
|
||||
|
||||
if (TextUtils.isEmpty(req.sign)) throw HttpException(500, getString(R.string.sign_required))
|
||||
if (req.timestamp == 0L) throw HttpException(500, getString(R.string.timestamp_required))
|
||||
|
||||
val timestamp = System.currentTimeMillis()
|
||||
val diffTime = kotlin.math.abs(timestamp - req.timestamp)
|
||||
val tolerance = timeTolerance * 1000L
|
||||
if (diffTime > tolerance) {
|
||||
throw HttpException(500, String.format(getString(R.string.timestamp_verify_failed), timestamp, timeTolerance, diffTime))
|
||||
}
|
||||
|
||||
val sign = calcSign(req.timestamp.toString(), signSecret.toString())
|
||||
if (sign != req.sign) {
|
||||
Log.e("calcSign", sign)
|
||||
Log.e("reqSign", req.sign.toString())
|
||||
throw HttpException(500, getString(R.string.sign_verify_failed))
|
||||
}
|
||||
}
|
||||
|
||||
//判断版本是否一致
|
||||
@Throws(HttpException::class)
|
||||
fun compareVersion(cloneInfo: CloneInfo) {
|
||||
val versionCodeRequest = cloneInfo.versionCode
|
||||
if (versionCodeRequest == 0) throw HttpException(500, getString(R.string.version_code_required))
|
||||
val versionCodeLocal = AppUtils.getAppVersionCode().toString().substring(1)
|
||||
if (!versionCodeRequest.toString().endsWith(versionCodeLocal)) throw HttpException(500, getString(R.string.inconsistent_version))
|
||||
}
|
||||
|
||||
//导出设置
|
||||
fun exportSettings(): CloneInfo {
|
||||
val cloneInfo = CloneInfo()
|
||||
cloneInfo.versionCode = AppUtils.getAppVersionCode()
|
||||
cloneInfo.versionName = AppUtils.getAppVersionName()
|
||||
cloneInfo.enableSms = SettingUtils.enableSms
|
||||
cloneInfo.enablePhone = SettingUtils.enablePhone
|
||||
cloneInfo.callType1 = SettingUtils.enableCallType1
|
||||
cloneInfo.callType2 = SettingUtils.enableCallType2
|
||||
cloneInfo.callType3 = SettingUtils.enableCallType3
|
||||
cloneInfo.enableAppNotify = SettingUtils.enableAppNotify
|
||||
cloneInfo.cancelAppNotify = SettingUtils.enableCancelAppNotify
|
||||
cloneInfo.enableNotUserPresent = SettingUtils.enableNotUserPresent
|
||||
cloneInfo.enableLoadAppList = SettingUtils.enableLoadAppList
|
||||
cloneInfo.enableLoadUserAppList = SettingUtils.enableLoadUserAppList
|
||||
cloneInfo.enableLoadSystemAppList = SettingUtils.enableLoadSystemAppList
|
||||
cloneInfo.duplicateMessagesLimits = SettingUtils.duplicateMessagesLimits
|
||||
cloneInfo.enableBatteryReceiver = SettingUtils.enableBatteryReceiver
|
||||
cloneInfo.batteryLevelMin = SettingUtils.batteryLevelMin
|
||||
cloneInfo.batteryLevelMax = SettingUtils.batteryLevelMax
|
||||
cloneInfo.batteryLevelOnce = SettingUtils.batteryLevelOnce
|
||||
cloneInfo.enableBatteryCron = SettingUtils.enableBatteryCron
|
||||
cloneInfo.batteryCronStartTime = SettingUtils.batteryCronStartTime
|
||||
cloneInfo.batteryCronInterval = SettingUtils.batteryCronInterval
|
||||
cloneInfo.enableExcludeFromRecents = SettingUtils.enableExcludeFromRecents
|
||||
cloneInfo.enableCactus = SettingUtils.enableCactus
|
||||
cloneInfo.enablePlaySilenceMusic = SettingUtils.enablePlaySilenceMusic
|
||||
cloneInfo.enableOnePixelActivity = SettingUtils.enableOnePixelActivity
|
||||
cloneInfo.requestRetryTimes = SettingUtils.requestRetryTimes
|
||||
cloneInfo.requestDelayTime = SettingUtils.requestDelayTime
|
||||
cloneInfo.requestTimeout = SettingUtils.requestTimeout
|
||||
cloneInfo.notifyContent = SettingUtils.notifyContent
|
||||
cloneInfo.enableSmsTemplate = SettingUtils.enableSmsTemplate
|
||||
cloneInfo.smsTemplate = SettingUtils.smsTemplate
|
||||
cloneInfo.enableHelpTip = SettingUtils.enableHelpTip
|
||||
cloneInfo.enablePureClientMode = SettingUtils.enablePureClientMode
|
||||
cloneInfo.senderList = Core.sender.all
|
||||
cloneInfo.ruleList = Core.rule.all
|
||||
cloneInfo.frpcList = Core.frpc.all
|
||||
|
||||
return cloneInfo
|
||||
}
|
||||
|
||||
//还原设置
|
||||
fun restoreSettings(cloneInfo: CloneInfo): Boolean {
|
||||
return try {
|
||||
//应用配置
|
||||
SettingUtils.enableSms = cloneInfo.enableSms
|
||||
SettingUtils.enablePhone = cloneInfo.enablePhone
|
||||
SettingUtils.enableCallType1 = cloneInfo.callType1
|
||||
SettingUtils.enableCallType2 = cloneInfo.callType2
|
||||
SettingUtils.enableCallType3 = cloneInfo.callType3
|
||||
SettingUtils.enableAppNotify = cloneInfo.enableAppNotify
|
||||
SettingUtils.enableCancelAppNotify = cloneInfo.cancelAppNotify
|
||||
SettingUtils.enableNotUserPresent = cloneInfo.enableNotUserPresent
|
||||
SettingUtils.enableLoadAppList = cloneInfo.enableLoadAppList
|
||||
SettingUtils.enableLoadUserAppList = cloneInfo.enableLoadUserAppList
|
||||
SettingUtils.enableLoadSystemAppList = cloneInfo.enableLoadSystemAppList
|
||||
SettingUtils.duplicateMessagesLimits = cloneInfo.duplicateMessagesLimits
|
||||
SettingUtils.enableBatteryReceiver = cloneInfo.enableBatteryReceiver
|
||||
SettingUtils.batteryLevelMin = cloneInfo.batteryLevelMin
|
||||
SettingUtils.batteryLevelMax = cloneInfo.batteryLevelMax
|
||||
SettingUtils.batteryLevelOnce = cloneInfo.batteryLevelOnce
|
||||
SettingUtils.enableBatteryCron = cloneInfo.enableBatteryCron
|
||||
SettingUtils.batteryCronStartTime = cloneInfo.batteryCronStartTime
|
||||
SettingUtils.batteryCronInterval = cloneInfo.batteryCronInterval
|
||||
SettingUtils.enableExcludeFromRecents = cloneInfo.enableExcludeFromRecents
|
||||
SettingUtils.enableCactus = cloneInfo.enableCactus
|
||||
SettingUtils.enablePlaySilenceMusic = cloneInfo.enablePlaySilenceMusic
|
||||
SettingUtils.enableOnePixelActivity = cloneInfo.enableOnePixelActivity
|
||||
SettingUtils.requestRetryTimes = cloneInfo.requestRetryTimes
|
||||
SettingUtils.requestDelayTime = cloneInfo.requestDelayTime
|
||||
SettingUtils.requestTimeout = cloneInfo.requestTimeout
|
||||
SettingUtils.notifyContent = cloneInfo.notifyContent
|
||||
SettingUtils.enableSmsTemplate = cloneInfo.enableSmsTemplate
|
||||
SettingUtils.smsTemplate = cloneInfo.smsTemplate
|
||||
SettingUtils.enableHelpTip = cloneInfo.enableHelpTip
|
||||
SettingUtils.enablePureClientMode = cloneInfo.enablePureClientMode
|
||||
//删除发送通道、转发规则、转发日志
|
||||
Core.sender.deleteAll()
|
||||
//发送通道
|
||||
if (!cloneInfo.senderList.isNullOrEmpty()) {
|
||||
for (sender in cloneInfo.senderList!!) {
|
||||
Core.sender.insert(sender)
|
||||
}
|
||||
}
|
||||
//转发规则
|
||||
if (!cloneInfo.ruleList.isNullOrEmpty()) {
|
||||
for (rule in cloneInfo.ruleList!!) {
|
||||
Core.rule.insert(rule)
|
||||
}
|
||||
}
|
||||
//Frpc配置
|
||||
Core.frpc.deleteAll()
|
||||
if (!cloneInfo.frpcList.isNullOrEmpty()) {
|
||||
for (frpc in cloneInfo.frpcList!!) {
|
||||
Core.frpc.insert(frpc)
|
||||
}
|
||||
}
|
||||
true
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
throw HttpException(500, e.message)
|
||||
//false
|
||||
}
|
||||
}
|
||||
|
||||
//返回统一结构报文
|
||||
fun response(output: Any?): String {
|
||||
val resp: MutableMap<String, Any> = mutableMapOf()
|
||||
val timestamp = System.currentTimeMillis()
|
||||
resp["timestamp"] = timestamp
|
||||
if (output is String && output != "success") {
|
||||
resp["code"] = HTTP_FAILURE_CODE
|
||||
resp["msg"] = output
|
||||
} else {
|
||||
resp["code"] = HTTP_SUCCESS_CODE
|
||||
resp["msg"] = "success"
|
||||
if (output != null) {
|
||||
resp["data"] = output
|
||||
}
|
||||
if (safetyMeasures == 1) {
|
||||
resp["sign"] = calcSign(timestamp.toString(), serverSignKey.toString())
|
||||
}
|
||||
}
|
||||
|
||||
return Gson().toJson(resp)
|
||||
}
|
||||
}
|
||||
package com.idormy.sms.forwarder.utils
|
||||
|
||||
|
||||
import android.text.TextUtils
|
||||
import android.util.Base64
|
||||
import android.util.Log
|
||||
import com.google.gson.Gson
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.idormy.sms.forwarder.core.Core
|
||||
import com.idormy.sms.forwarder.entity.CloneInfo
|
||||
import com.idormy.sms.forwarder.server.model.BaseRequest
|
||||
import com.xuexiang.xui.utils.ResUtils.getString
|
||||
import com.xuexiang.xutil.app.AppUtils
|
||||
import com.yanzhenjie.andserver.error.HttpException
|
||||
import java.net.URLEncoder
|
||||
import java.nio.charset.StandardCharsets
|
||||
import javax.crypto.Mac
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
|
||||
/**
|
||||
* HttpServer工具类
|
||||
*/
|
||||
class HttpServerUtils private constructor() {
|
||||
|
||||
companion object {
|
||||
|
||||
//是否启用HttpServer开机自启
|
||||
var enableServerAutorun: Boolean by SharedPreference(SP_ENABLE_SERVER_AUTORUN, true)
|
||||
|
||||
//服务端签名密钥
|
||||
var serverSignKey: String by SharedPreference(SP_SERVER_SIGN_KEY, "")
|
||||
|
||||
//服务端安全设置
|
||||
var safetyMeasures: Int by SharedPreference(SP_SERVER_SAFETY_MEASURES, if (TextUtils.isEmpty(serverSignKey)) 0 else 1)
|
||||
|
||||
//服务端SM4密钥
|
||||
var serverSm4Key: String by SharedPreference(SP_SERVER_SM4_KEY, "")
|
||||
|
||||
//服务端RSA公钥
|
||||
var serverPublicKey: String by SharedPreference(SP_SERVER_PUBLIC_KEY, "")
|
||||
|
||||
//服务端RSA私钥
|
||||
var serverPrivateKey: String by SharedPreference(SP_SERVER_PRIVATE_KEY, "")
|
||||
|
||||
//时间容差
|
||||
var timeTolerance: Int by SharedPreference(SP_SERVER_TIME_TOLERANCE, 600)
|
||||
|
||||
//自定义web客户端目录
|
||||
var serverWebPath: String by SharedPreference(SP_SERVER_WEB_PATH, "")
|
||||
|
||||
//服务地址
|
||||
var serverAddress: String by SharedPreference(SP_SERVER_ADDRESS, "")
|
||||
|
||||
//服务地址历史记录
|
||||
var serverHistory: String by SharedPreference(SP_SERVER_HISTORY, "")
|
||||
|
||||
//服务端配置
|
||||
var serverConfig: String by SharedPreference(SP_SERVER_CONFIG, "")
|
||||
|
||||
//客户端签名密钥/RSA公钥
|
||||
var clientSignKey: String by SharedPreference(SP_CLIENT_SIGN_KEY, "")
|
||||
|
||||
//服务端安全设置
|
||||
var clientSafetyMeasures: Int by SharedPreference(SP_CLIENT_SAFETY_MEASURES, if (TextUtils.isEmpty(clientSignKey)) 0 else 1)
|
||||
|
||||
//是否启用一键克隆
|
||||
var enableApiClone: Boolean by SharedPreference(SP_ENABLE_API_CLONE, true)
|
||||
|
||||
//是否启用远程发短信
|
||||
var enableApiSmsSend: Boolean by SharedPreference(SP_ENABLE_API_SMS_SEND, true)
|
||||
|
||||
//是否启用远程查短信
|
||||
var enableApiSmsQuery: Boolean by SharedPreference(SP_ENABLE_API_SMS_QUERY, true)
|
||||
|
||||
//是否启用远程查通话
|
||||
var enableApiCallQuery: Boolean by SharedPreference(SP_ENABLE_API_CALL_QUERY, true)
|
||||
|
||||
//是否启用远程查话簿
|
||||
var enableApiContactQuery: Boolean by SharedPreference(SP_ENABLE_API_CONTACT_QUERY, true)
|
||||
|
||||
//是否启用远程查电量
|
||||
var enableApiBatteryQuery: Boolean by SharedPreference(SP_ENABLE_API_BATTERY_QUERY, true)
|
||||
|
||||
//是否启用远程WOL
|
||||
var enableApiWol: Boolean by SharedPreference(SP_ENABLE_API_WOL, true)
|
||||
|
||||
//WOL历史记录
|
||||
var wolHistory: String by SharedPreference(SP_WOL_HISTORY, "")
|
||||
|
||||
//计算签名
|
||||
fun calcSign(timestamp: String, signSecret: String): String {
|
||||
val stringToSign = "$timestamp\n" + signSecret
|
||||
val mac = Mac.getInstance("HmacSHA256")
|
||||
mac.init(SecretKeySpec(signSecret.toByteArray(StandardCharsets.UTF_8), "HmacSHA256"))
|
||||
val signData = mac.doFinal(stringToSign.toByteArray(StandardCharsets.UTF_8))
|
||||
return URLEncoder.encode(String(Base64.encode(signData, Base64.NO_WRAP)), "UTF-8")
|
||||
}
|
||||
|
||||
//校验签名
|
||||
@Throws(HttpException::class)
|
||||
fun checkSign(req: BaseRequest<*>) {
|
||||
val signSecret = serverSignKey
|
||||
if (TextUtils.isEmpty(signSecret)) return
|
||||
|
||||
if (TextUtils.isEmpty(req.sign)) throw HttpException(500, getString(R.string.sign_required))
|
||||
if (req.timestamp == 0L) throw HttpException(500, getString(R.string.timestamp_required))
|
||||
|
||||
val timestamp = System.currentTimeMillis()
|
||||
val diffTime = kotlin.math.abs(timestamp - req.timestamp)
|
||||
val tolerance = timeTolerance * 1000L
|
||||
if (diffTime > tolerance) {
|
||||
throw HttpException(500, String.format(getString(R.string.timestamp_verify_failed), timestamp, timeTolerance, diffTime))
|
||||
}
|
||||
|
||||
val sign = calcSign(req.timestamp.toString(), signSecret)
|
||||
if (sign != req.sign) {
|
||||
Log.e("calcSign", sign)
|
||||
Log.e("reqSign", req.sign.toString())
|
||||
throw HttpException(500, getString(R.string.sign_verify_failed))
|
||||
}
|
||||
}
|
||||
|
||||
//判断版本是否一致
|
||||
@Throws(HttpException::class)
|
||||
fun compareVersion(cloneInfo: CloneInfo) {
|
||||
val versionCodeRequest = cloneInfo.versionCode
|
||||
if (versionCodeRequest == 0) throw HttpException(500, getString(R.string.version_code_required))
|
||||
val versionCodeLocal = AppUtils.getAppVersionCode().toString().substring(1)
|
||||
if (!versionCodeRequest.toString().endsWith(versionCodeLocal)) throw HttpException(500, getString(R.string.inconsistent_version))
|
||||
}
|
||||
|
||||
//导出设置
|
||||
fun exportSettings(): CloneInfo {
|
||||
val cloneInfo = CloneInfo()
|
||||
cloneInfo.versionCode = AppUtils.getAppVersionCode()
|
||||
cloneInfo.versionName = AppUtils.getAppVersionName()
|
||||
cloneInfo.enableSms = SettingUtils.enableSms
|
||||
cloneInfo.enablePhone = SettingUtils.enablePhone
|
||||
cloneInfo.callType1 = SettingUtils.enableCallType1
|
||||
cloneInfo.callType2 = SettingUtils.enableCallType2
|
||||
cloneInfo.callType3 = SettingUtils.enableCallType3
|
||||
cloneInfo.enableAppNotify = SettingUtils.enableAppNotify
|
||||
cloneInfo.cancelAppNotify = SettingUtils.enableCancelAppNotify
|
||||
cloneInfo.enableNotUserPresent = SettingUtils.enableNotUserPresent
|
||||
cloneInfo.enableLoadAppList = SettingUtils.enableLoadAppList
|
||||
cloneInfo.enableLoadUserAppList = SettingUtils.enableLoadUserAppList
|
||||
cloneInfo.enableLoadSystemAppList = SettingUtils.enableLoadSystemAppList
|
||||
cloneInfo.duplicateMessagesLimits = SettingUtils.duplicateMessagesLimits
|
||||
cloneInfo.enableBatteryReceiver = SettingUtils.enableBatteryReceiver
|
||||
cloneInfo.batteryLevelMin = SettingUtils.batteryLevelMin
|
||||
cloneInfo.batteryLevelMax = SettingUtils.batteryLevelMax
|
||||
cloneInfo.batteryLevelOnce = SettingUtils.batteryLevelOnce
|
||||
cloneInfo.enableBatteryCron = SettingUtils.enableBatteryCron
|
||||
cloneInfo.batteryCronStartTime = SettingUtils.batteryCronStartTime
|
||||
cloneInfo.batteryCronInterval = SettingUtils.batteryCronInterval
|
||||
cloneInfo.enableExcludeFromRecents = SettingUtils.enableExcludeFromRecents
|
||||
cloneInfo.enableCactus = SettingUtils.enableCactus
|
||||
cloneInfo.enablePlaySilenceMusic = SettingUtils.enablePlaySilenceMusic
|
||||
cloneInfo.enableOnePixelActivity = SettingUtils.enableOnePixelActivity
|
||||
cloneInfo.requestRetryTimes = SettingUtils.requestRetryTimes
|
||||
cloneInfo.requestDelayTime = SettingUtils.requestDelayTime
|
||||
cloneInfo.requestTimeout = SettingUtils.requestTimeout
|
||||
cloneInfo.notifyContent = SettingUtils.notifyContent
|
||||
cloneInfo.enableSmsTemplate = SettingUtils.enableSmsTemplate
|
||||
cloneInfo.smsTemplate = SettingUtils.smsTemplate
|
||||
cloneInfo.enableHelpTip = SettingUtils.enableHelpTip
|
||||
cloneInfo.enablePureClientMode = SettingUtils.enablePureClientMode
|
||||
cloneInfo.senderList = Core.sender.all
|
||||
cloneInfo.ruleList = Core.rule.all
|
||||
cloneInfo.frpcList = Core.frpc.all
|
||||
|
||||
return cloneInfo
|
||||
}
|
||||
|
||||
//还原设置
|
||||
fun restoreSettings(cloneInfo: CloneInfo): Boolean {
|
||||
return try {
|
||||
//应用配置
|
||||
SettingUtils.enableSms = cloneInfo.enableSms
|
||||
SettingUtils.enablePhone = cloneInfo.enablePhone
|
||||
SettingUtils.enableCallType1 = cloneInfo.callType1
|
||||
SettingUtils.enableCallType2 = cloneInfo.callType2
|
||||
SettingUtils.enableCallType3 = cloneInfo.callType3
|
||||
SettingUtils.enableAppNotify = cloneInfo.enableAppNotify
|
||||
SettingUtils.enableCancelAppNotify = cloneInfo.cancelAppNotify
|
||||
SettingUtils.enableNotUserPresent = cloneInfo.enableNotUserPresent
|
||||
SettingUtils.enableLoadAppList = cloneInfo.enableLoadAppList
|
||||
SettingUtils.enableLoadUserAppList = cloneInfo.enableLoadUserAppList
|
||||
SettingUtils.enableLoadSystemAppList = cloneInfo.enableLoadSystemAppList
|
||||
SettingUtils.duplicateMessagesLimits = cloneInfo.duplicateMessagesLimits
|
||||
SettingUtils.enableBatteryReceiver = cloneInfo.enableBatteryReceiver
|
||||
SettingUtils.batteryLevelMin = cloneInfo.batteryLevelMin
|
||||
SettingUtils.batteryLevelMax = cloneInfo.batteryLevelMax
|
||||
SettingUtils.batteryLevelOnce = cloneInfo.batteryLevelOnce
|
||||
SettingUtils.enableBatteryCron = cloneInfo.enableBatteryCron
|
||||
SettingUtils.batteryCronStartTime = cloneInfo.batteryCronStartTime.toString()
|
||||
SettingUtils.batteryCronInterval = cloneInfo.batteryCronInterval
|
||||
SettingUtils.enableExcludeFromRecents = cloneInfo.enableExcludeFromRecents
|
||||
SettingUtils.enableCactus = cloneInfo.enableCactus
|
||||
SettingUtils.enablePlaySilenceMusic = cloneInfo.enablePlaySilenceMusic
|
||||
SettingUtils.enableOnePixelActivity = cloneInfo.enableOnePixelActivity
|
||||
SettingUtils.requestRetryTimes = cloneInfo.requestRetryTimes
|
||||
SettingUtils.requestDelayTime = cloneInfo.requestDelayTime
|
||||
SettingUtils.requestTimeout = cloneInfo.requestTimeout
|
||||
SettingUtils.notifyContent = cloneInfo.notifyContent.toString()
|
||||
SettingUtils.enableSmsTemplate = cloneInfo.enableSmsTemplate
|
||||
SettingUtils.smsTemplate = cloneInfo.smsTemplate.toString()
|
||||
SettingUtils.enableHelpTip = cloneInfo.enableHelpTip
|
||||
SettingUtils.enablePureClientMode = cloneInfo.enablePureClientMode
|
||||
//删除发送通道、转发规则、转发日志
|
||||
Core.sender.deleteAll()
|
||||
//发送通道
|
||||
if (!cloneInfo.senderList.isNullOrEmpty()) {
|
||||
for (sender in cloneInfo.senderList!!) {
|
||||
Core.sender.insert(sender)
|
||||
}
|
||||
}
|
||||
//转发规则
|
||||
if (!cloneInfo.ruleList.isNullOrEmpty()) {
|
||||
for (rule in cloneInfo.ruleList!!) {
|
||||
Core.rule.insert(rule)
|
||||
}
|
||||
}
|
||||
//Frpc配置
|
||||
Core.frpc.deleteAll()
|
||||
if (!cloneInfo.frpcList.isNullOrEmpty()) {
|
||||
for (frpc in cloneInfo.frpcList!!) {
|
||||
Core.frpc.insert(frpc)
|
||||
}
|
||||
}
|
||||
true
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
throw HttpException(500, e.message)
|
||||
//false
|
||||
}
|
||||
}
|
||||
|
||||
//返回统一结构报文
|
||||
fun response(output: Any?): String {
|
||||
val resp: MutableMap<String, Any> = mutableMapOf()
|
||||
val timestamp = System.currentTimeMillis()
|
||||
resp["timestamp"] = timestamp
|
||||
if (output is String && output != "success") {
|
||||
resp["code"] = HTTP_FAILURE_CODE
|
||||
resp["msg"] = output
|
||||
} else {
|
||||
resp["code"] = HTTP_SUCCESS_CODE
|
||||
resp["msg"] = "success"
|
||||
if (output != null) {
|
||||
resp["data"] = output
|
||||
}
|
||||
if (safetyMeasures == 1) {
|
||||
resp["sign"] = calcSign(timestamp.toString(), serverSignKey)
|
||||
}
|
||||
}
|
||||
|
||||
return Gson().toJson(resp)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,332 +0,0 @@
|
||||
package com.idormy.sms.forwarder.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Parcelable
|
||||
import android.util.Log
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.tencent.mmkv.MMKV
|
||||
|
||||
/**
|
||||
* MMKV工具类
|
||||
*
|
||||
* @author xuexiang
|
||||
* @since 2019-07-04 10:20
|
||||
*/
|
||||
@Suppress("PropertyName", "UNCHECKED_CAST", "MemberVisibilityCanBePrivate", "unused")
|
||||
class MMKVUtils private constructor() {
|
||||
|
||||
companion object {
|
||||
private var TAG: String = "MMKVUtils"
|
||||
private var sMMKV: MMKV? = null
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
*
|
||||
* @param context
|
||||
*/
|
||||
fun init(context: Context) {
|
||||
MMKV.initialize(context.applicationContext)
|
||||
sMMKV = MMKV.defaultMMKV()
|
||||
}
|
||||
|
||||
fun getsMMKV(): MMKV? {
|
||||
if (sMMKV == null) {
|
||||
sMMKV = MMKV.defaultMMKV()
|
||||
}
|
||||
return sMMKV
|
||||
}
|
||||
//=======================================键值保存==================================================//
|
||||
/**
|
||||
* 保存键值
|
||||
*
|
||||
* @param key
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
fun put(key: String?, value: Any?): Boolean {
|
||||
when (value) {
|
||||
is Int -> {
|
||||
return getsMMKV()!!.encode(key, (value as Int?)!!)
|
||||
}
|
||||
is Float -> {
|
||||
return getsMMKV()!!.encode(key, (value as Float?)!!)
|
||||
}
|
||||
is String -> {
|
||||
return getsMMKV()!!.encode(key, value as String?)
|
||||
}
|
||||
is Boolean -> {
|
||||
return getsMMKV()!!.encode(key, (value as Boolean?)!!)
|
||||
}
|
||||
is Long -> {
|
||||
return getsMMKV()!!.encode(key, (value as Long?)!!)
|
||||
}
|
||||
is Double -> {
|
||||
return getsMMKV()!!.encode(key, (value as Double?)!!)
|
||||
}
|
||||
is Parcelable -> {
|
||||
return getsMMKV()!!.encode(key, value as Parcelable?)
|
||||
}
|
||||
is ByteArray -> {
|
||||
return getsMMKV()!!.encode(key, value as ByteArray?)
|
||||
}
|
||||
is Set<*> -> {
|
||||
return getsMMKV()!!.encode(key, value as Set<String?>?)
|
||||
}
|
||||
else -> return false
|
||||
}
|
||||
}
|
||||
//=======================================键值获取==================================================//
|
||||
/**
|
||||
* 获取键值
|
||||
*
|
||||
* @param key
|
||||
* @param defaultValue
|
||||
* @return
|
||||
*/
|
||||
operator fun get(key: String?, defaultValue: Any?): Any? {
|
||||
when (defaultValue) {
|
||||
is Int -> {
|
||||
return getsMMKV()!!
|
||||
.decodeInt(key, (defaultValue as Int?)!!)
|
||||
}
|
||||
is Float -> {
|
||||
return getsMMKV()!!
|
||||
.decodeFloat(key, (defaultValue as Float?)!!)
|
||||
}
|
||||
is String -> {
|
||||
return getsMMKV()!!.decodeString(key, defaultValue as String?)
|
||||
}
|
||||
is Boolean -> {
|
||||
return getsMMKV()!!
|
||||
.decodeBool(key, (defaultValue as Boolean?)!!)
|
||||
}
|
||||
is Long -> {
|
||||
return getsMMKV()!!
|
||||
.decodeLong(key, (defaultValue as Long?)!!)
|
||||
}
|
||||
is Double -> {
|
||||
return getsMMKV()!!
|
||||
.decodeDouble(key, (defaultValue as Double?)!!)
|
||||
}
|
||||
is ByteArray -> {
|
||||
return getsMMKV()!!.decodeBytes(key)
|
||||
}
|
||||
is Set<*> -> {
|
||||
return getsMMKV()!!.decodeStringSet(key, defaultValue as Set<String?>?)
|
||||
}
|
||||
else -> return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据key获取boolean值
|
||||
*
|
||||
* @param key
|
||||
* @param defValue
|
||||
* @return
|
||||
*/
|
||||
fun getBoolean(key: String?, defValue: Boolean): Boolean {
|
||||
try {
|
||||
return getsMMKV()!!.getBoolean(key, defValue)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
return defValue
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据key获取long值
|
||||
*
|
||||
* @param key
|
||||
* @param defValue
|
||||
* @return
|
||||
*/
|
||||
fun getLong(key: String?, defValue: Long): Long {
|
||||
try {
|
||||
return getsMMKV()!!.getLong(key, defValue)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
return defValue
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据key获取float值
|
||||
*
|
||||
* @param key
|
||||
* @param defValue
|
||||
* @return
|
||||
*/
|
||||
fun getFloat(key: String?, defValue: Float): Float {
|
||||
try {
|
||||
return getsMMKV()!!.getFloat(key, defValue)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
return defValue
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据key获取String值
|
||||
*
|
||||
* @param key
|
||||
* @param defValue
|
||||
* @return
|
||||
*/
|
||||
fun getString(key: String?, defValue: String?): String? {
|
||||
try {
|
||||
return getsMMKV()!!.getString(key, defValue)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
return defValue
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据key获取int值
|
||||
*
|
||||
* @param key
|
||||
* @param defValue
|
||||
* @return
|
||||
*/
|
||||
fun getInt(key: String?, defValue: Int): Int {
|
||||
try {
|
||||
return getsMMKV()!!.getInt(key, defValue)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
return defValue
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据key获取double值
|
||||
*
|
||||
* @param key
|
||||
* @param defValue
|
||||
* @return
|
||||
*/
|
||||
fun getDouble(key: String?, defValue: Double): Double {
|
||||
try {
|
||||
return getsMMKV()!!.decodeDouble(key, defValue)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
return defValue
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对象
|
||||
*
|
||||
* @param key
|
||||
* @param tClass 类型
|
||||
* @param <T>
|
||||
* @return
|
||||
</T> */
|
||||
fun <T : Parcelable?> getObject(key: String?, tClass: Class<T>?): T? {
|
||||
return getsMMKV()!!.decodeParcelable(key, tClass)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对象
|
||||
*
|
||||
* @param key
|
||||
* @param tClass 类型
|
||||
* @param <T>
|
||||
* @return
|
||||
</T> */
|
||||
fun <T : Parcelable?> getObject(key: String?, tClass: Class<T>?, defValue: T): T? {
|
||||
try {
|
||||
return getsMMKV()!!.decodeParcelable(key, tClass, defValue)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
return defValue
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断键值对是否存在
|
||||
*
|
||||
* @param key 键
|
||||
* @return 键值对是否存在
|
||||
*/
|
||||
fun containsKey(key: String?): Boolean {
|
||||
return getsMMKV()!!.containsKey(key)
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除指定键值对
|
||||
*
|
||||
* @param key 键
|
||||
*/
|
||||
fun remove(key: String?) {
|
||||
getsMMKV()!!.remove(key).apply()
|
||||
}
|
||||
|
||||
/**
|
||||
* 从SP迁移数据
|
||||
*/
|
||||
fun importSharedPreferences(context: Context) {
|
||||
Log.d(TAG, "从SP迁移数据")
|
||||
val preferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
val editor = preferences.edit()
|
||||
getsMMKV()!!.importFromSharedPreferences(preferences)
|
||||
editor.clear().apply()
|
||||
|
||||
Log.d(TAG, "转换旧的SP配置")
|
||||
loop@ for (key: String in getsMMKV()!!.allKeys()!!) {
|
||||
when {
|
||||
key.startsWith("tsms_msg_key_switch_") || key.startsWith("tsms_msg_key_string_enable_") || key.endsWith("battery_level_once") -> {
|
||||
val newKey = key.replace("tsms_msg_key_switch_", "enable_")
|
||||
.replace("tsms_msg_key_string_", "enable_")
|
||||
.replace("enable_enable_", "enable_")
|
||||
val value = getBoolean(key, false)
|
||||
Log.d(TAG, String.format("oldKey=%s, newKey=%s, value=%s", key, newKey, value.toString()))
|
||||
put(newKey, value)
|
||||
remove(key)
|
||||
continue@loop
|
||||
}
|
||||
key.endsWith("battery_level_alarm") || key.endsWith("battery_level_max") || key.endsWith("battery_level_current") || key.endsWith("battery_status") || key.endsWith("battery_cron_interval") -> {
|
||||
val newKey = key.replace("tsms_msg_key_switch_", "")
|
||||
.replace("tsms_msg_key_string_", "")
|
||||
.replace("alarm", "min")
|
||||
.replace("tsms_msg_key_", "request_")
|
||||
val value = getInt(key, 0)
|
||||
Log.d(TAG, String.format("oldKey=%s, newKey=%s, value=%s", key, newKey, value.toString()))
|
||||
put(newKey, value)
|
||||
remove(key)
|
||||
continue@loop
|
||||
}
|
||||
key.startsWith("tsms_msg_key_") -> {
|
||||
val newKey = key.replace("tsms_msg_key_string_", "")
|
||||
.replace("add_", "")
|
||||
.replace("tsms_msg_key_", "request_")
|
||||
val value = getString(key, "")
|
||||
Log.d(TAG, String.format("oldKey=%s, newKey=%s, value=%s", key, newKey, value.toString()))
|
||||
put(newKey, value)
|
||||
remove(key)
|
||||
continue@loop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Log.d(TAG, "转换后的数据")
|
||||
for (key: String in getsMMKV()!!.allKeys()!!) {
|
||||
when {
|
||||
key.startsWith("enable_") -> {
|
||||
Log.d(TAG, String.format("key=%s, value=%s", key, getBoolean(key, false).toString()))
|
||||
}
|
||||
key.startsWith("battery_") || key.startsWith("request_") -> {
|
||||
Log.d(TAG, String.format("key=%s, value=%s", key, getInt(key, 0).toString()))
|
||||
}
|
||||
else -> {
|
||||
Log.d(TAG, String.format("key=%s, value=%s", key, getString(key, "").toString()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
throw UnsupportedOperationException("u can't instantiate me...")
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue