parent
1b93aeb857
commit
581c0c2ef2
|
@ -6,6 +6,7 @@ import android.util.Log
|
|||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.RadioGroup
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.idormy.sms.forwarder.App
|
||||
|
@ -38,9 +39,7 @@ import com.xuexiang.xutil.XUtil
|
|||
|
||||
@Suppress("PrivatePropertyName", "PropertyName")
|
||||
@Page(name = "主动控制·客户端")
|
||||
class ClientFragment : BaseFragment<FragmentClientBinding?>(),
|
||||
View.OnClickListener,
|
||||
RecyclerViewHolder.OnItemClickListener<PageInfo> {
|
||||
class ClientFragment : BaseFragment<FragmentClientBinding?>(), View.OnClickListener, RecyclerViewHolder.OnItemClickListener<PageInfo> {
|
||||
|
||||
val TAG: String = ClientFragment::class.java.simpleName
|
||||
private var appContext: App? = null
|
||||
|
@ -113,6 +112,41 @@ class ClientFragment : BaseFragment<FragmentClientBinding?>(),
|
|||
}
|
||||
})
|
||||
|
||||
//安全措施
|
||||
var safetyMeasuresId = R.id.rb_safety_measures_none
|
||||
when (HttpServerUtils.clientSafetyMeasures) {
|
||||
1 -> {
|
||||
safetyMeasuresId = R.id.rb_safety_measures_sign
|
||||
binding!!.tvSignKey.text = getString(R.string.sign_key)
|
||||
}
|
||||
2 -> {
|
||||
safetyMeasuresId = R.id.rb_safety_measures_rsa
|
||||
binding!!.tvSignKey.text = getString(R.string.public_key)
|
||||
}
|
||||
else -> {
|
||||
binding!!.layoutSignKey.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
binding!!.rgSafetyMeasures.check(safetyMeasuresId)
|
||||
binding!!.rgSafetyMeasures.setOnCheckedChangeListener { _: RadioGroup?, checkedId: Int ->
|
||||
var safetyMeasures = 0
|
||||
binding!!.layoutSignKey.visibility = View.GONE
|
||||
when (checkedId) {
|
||||
R.id.rb_safety_measures_sign -> {
|
||||
safetyMeasures = 1
|
||||
binding!!.tvSignKey.text = getString(R.string.sign_key)
|
||||
binding!!.layoutSignKey.visibility = View.VISIBLE
|
||||
}
|
||||
R.id.rb_safety_measures_rsa -> {
|
||||
safetyMeasures = 2
|
||||
binding!!.tvSignKey.text = getString(R.string.public_key)
|
||||
binding!!.layoutSignKey.visibility = View.VISIBLE
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
HttpServerUtils.clientSafetyMeasures = safetyMeasures
|
||||
}
|
||||
|
||||
binding!!.etSignKey.setText(HttpServerUtils.clientSignKey)
|
||||
binding!!.etSignKey.addTextChangedListener(object : TextWatcher {
|
||||
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
|
||||
|
@ -136,30 +170,46 @@ class ClientFragment : BaseFragment<FragmentClientBinding?>(),
|
|||
}
|
||||
Log.d(TAG, "serverHistory = $serverHistory")
|
||||
|
||||
MaterialDialog.Builder(requireContext())
|
||||
.title(R.string.server_history)
|
||||
.items(serverHistory.keys)
|
||||
.itemsCallbackSingleChoice(0) { _: MaterialDialog?, _: View?, _: Int, text: CharSequence ->
|
||||
//XToastUtils.info("$which: $text")
|
||||
val matches = Regex("【(.*)】(.*)", RegexOption.IGNORE_CASE).findAll(text).toList().flatMap(MatchResult::groupValues)
|
||||
Log.i(TAG, "matches = $matches")
|
||||
if (matches.isNotEmpty()) {
|
||||
binding!!.etServerAddress.setText(matches[2])
|
||||
MaterialDialog.Builder(requireContext()).title(R.string.server_history).items(serverHistory.keys).itemsCallbackSingleChoice(0) { _: MaterialDialog?, _: View?, _: Int, text: CharSequence ->
|
||||
//XToastUtils.info("$which: $text")
|
||||
val matches = Regex("【(.*)】(.*)", RegexOption.IGNORE_CASE).findAll(text).toList().flatMap(MatchResult::groupValues)
|
||||
Log.i(TAG, "matches = $matches")
|
||||
if (matches.isNotEmpty()) {
|
||||
binding!!.etServerAddress.setText(matches[2])
|
||||
} else {
|
||||
binding!!.etServerAddress.setText(text)
|
||||
}
|
||||
val signKey = serverHistory[text].toString()
|
||||
if (!TextUtils.isEmpty(signKey)) {
|
||||
val keyMatches = Regex("(.*)##(.*)", RegexOption.IGNORE_CASE).findAll(signKey).toList().flatMap(MatchResult::groupValues)
|
||||
Log.i(TAG, "keyMatches = $keyMatches")
|
||||
if (keyMatches.isNotEmpty()) {
|
||||
binding!!.etSignKey.setText(keyMatches[1])
|
||||
var safetyMeasuresId = R.id.rb_safety_measures_none
|
||||
when (keyMatches[2]) {
|
||||
"1" -> {
|
||||
safetyMeasuresId = R.id.rb_safety_measures_sign
|
||||
binding!!.tvSignKey.text = getString(R.string.sign_key)
|
||||
}
|
||||
"2" -> {
|
||||
safetyMeasuresId = R.id.rb_safety_measures_rsa
|
||||
binding!!.tvSignKey.text = getString(R.string.public_key)
|
||||
}
|
||||
else -> {
|
||||
binding!!.tvSignKey.visibility = View.GONE
|
||||
binding!!.etSignKey.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
binding!!.rgSafetyMeasures.check(safetyMeasuresId)
|
||||
} else {
|
||||
binding!!.etServerAddress.setText(text)
|
||||
binding!!.etSignKey.setText(serverHistory[text])
|
||||
}
|
||||
binding!!.etSignKey.setText(serverHistory[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? ->
|
||||
serverHistory.clear()
|
||||
HttpServerUtils.serverHistory = ""
|
||||
}
|
||||
.show()
|
||||
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? ->
|
||||
serverHistory.clear()
|
||||
HttpServerUtils.serverHistory = ""
|
||||
}.show()
|
||||
}
|
||||
R.id.btn_server_test -> {
|
||||
if (!CommonUtils.checkUrl(HttpServerUtils.serverAddress)) {
|
||||
|
@ -183,22 +233,12 @@ class ClientFragment : BaseFragment<FragmentClientBinding?>(),
|
|||
XToastUtils.error(getString(R.string.click_test_button_first))
|
||||
return
|
||||
}
|
||||
if (serverConfig != null && (
|
||||
(item.name == ResUtils.getString(R.string.api_sms_send) && !serverConfig!!.enableApiSmsSend)
|
||||
|| (item.name == ResUtils.getString(R.string.api_sms_query) && !serverConfig!!.enableApiSmsQuery)
|
||||
|| (item.name == ResUtils.getString(R.string.api_call_query) && !serverConfig!!.enableApiCallQuery)
|
||||
|| (item.name == ResUtils.getString(R.string.api_contact_query) && !serverConfig!!.enableApiContactQuery)
|
||||
|| (item.name == ResUtils.getString(R.string.api_battery_query) && !serverConfig!!.enableApiBatteryQuery)
|
||||
|| (item.name == ResUtils.getString(R.string.api_wol) && !serverConfig!!.enableApiWol)
|
||||
)
|
||||
) {
|
||||
if (serverConfig != null && ((item.name == ResUtils.getString(R.string.api_sms_send) && !serverConfig!!.enableApiSmsSend) || (item.name == ResUtils.getString(R.string.api_sms_query) && !serverConfig!!.enableApiSmsQuery) || (item.name == ResUtils.getString(R.string.api_call_query) && !serverConfig!!.enableApiCallQuery) || (item.name == ResUtils.getString(R.string.api_contact_query) && !serverConfig!!.enableApiContactQuery) || (item.name == ResUtils.getString(R.string.api_battery_query) && !serverConfig!!.enableApiBatteryQuery) || (item.name == ResUtils.getString(R.string.api_wol) && !serverConfig!!.enableApiWol))) {
|
||||
XToastUtils.error(getString(R.string.disabled_on_the_server))
|
||||
return
|
||||
}
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
PageOption.to(Class.forName(item.classPath) as Class<XPageFragment>) //跳转的fragment
|
||||
.setNewActivity(true)
|
||||
.open(this)
|
||||
@Suppress("UNCHECKED_CAST") PageOption.to(Class.forName(item.classPath) as Class<XPageFragment>) //跳转的fragment
|
||||
.setNewActivity(true).open(this)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
XToastUtils.error(e.message.toString())
|
||||
|
@ -212,58 +252,87 @@ class ClientFragment : BaseFragment<FragmentClientBinding?>(),
|
|||
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 clientSignKey = HttpServerUtils.clientSignKey.toString()
|
||||
if ((HttpServerUtils.clientSafetyMeasures == 1 || HttpServerUtils.clientSafetyMeasures == 2) && TextUtils.isEmpty(clientSignKey)) {
|
||||
if (needToast) XToastUtils.error("请输入签名密钥或RSA公钥")
|
||||
return
|
||||
}
|
||||
|
||||
if (HttpServerUtils.clientSafetyMeasures == 1 && !TextUtils.isEmpty(clientSignKey)) {
|
||||
msgMap["sign"] = HttpServerUtils.calcSign(timestamp.toString(), clientSignKey)
|
||||
}
|
||||
val dataMap: MutableMap<String, Any> = mutableMapOf()
|
||||
msgMap["data"] = dataMap
|
||||
|
||||
val requestMsg: String = Gson().toJson(msgMap)
|
||||
var requestMsg: String = Gson().toJson(msgMap)
|
||||
Log.i(TAG, "requestMsg:$requestMsg")
|
||||
|
||||
if (needToast) mCountDownHelper?.start()
|
||||
XHttp.post(requestUrl)
|
||||
.upJson(requestMsg)
|
||||
val postRequest = XHttp.post(requestUrl)
|
||||
.keepJson(true)
|
||||
.timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
|
||||
.cacheMode(CacheMode.NO_CACHE)
|
||||
//.retryCount(SettingUtils.requestRetryTimes) //超时重试的次数
|
||||
//.retryDelay(SettingUtils.requestDelayTime) //超时重试的延迟时间
|
||||
//.retryIncreaseDelay(SettingUtils.requestDelayTime) //超时重试叠加延时
|
||||
.timeStamp(true)
|
||||
.execute(object : SimpleCallBack<String>() {
|
||||
.cacheMode(CacheMode.NO_CACHE).timeStamp(true)
|
||||
|
||||
override fun onError(e: ApiException) {
|
||||
XToastUtils.error(e.displayMessage)
|
||||
if (needToast) mCountDownHelper?.finish()
|
||||
}
|
||||
if (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) {
|
||||
if (needToast) XToastUtils.error(ResUtils.getString(R.string.request_failed) + e.message)
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
postRequest.upString(requestMsg)
|
||||
} else {
|
||||
postRequest.upJson(requestMsg)
|
||||
}
|
||||
|
||||
override fun onSuccess(response: String) {
|
||||
Log.i(TAG, response)
|
||||
try {
|
||||
val resp: BaseResponse<ConfigData> = Gson().fromJson(response, object : TypeToken<BaseResponse<ConfigData>>() {}.type)
|
||||
if (resp.code == 200) {
|
||||
serverConfig = resp.data!!
|
||||
if (needToast) XToastUtils.success(ResUtils.getString(R.string.request_succeeded))
|
||||
//删除3.0.8之前保存的记录
|
||||
serverHistory.remove(HttpServerUtils.serverAddress.toString())
|
||||
//添加到历史记录
|
||||
val key = "【${serverConfig?.extraDeviceMark}】${HttpServerUtils.serverAddress.toString()}"
|
||||
serverHistory[key] = HttpServerUtils.clientSignKey ?: ""
|
||||
HttpServerUtils.serverHistory = Gson().toJson(serverHistory)
|
||||
HttpServerUtils.serverConfig = Gson().toJson(serverConfig)
|
||||
if (needToast) mCountDownHelper?.start()
|
||||
postRequest.execute(object : SimpleCallBack<String>() {
|
||||
override fun onError(e: ApiException) {
|
||||
XToastUtils.error(e.displayMessage)
|
||||
if (needToast) 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))
|
||||
}
|
||||
val resp: BaseResponse<ConfigData> = Gson().fromJson(json, object : TypeToken<BaseResponse<ConfigData>>() {}.type)
|
||||
if (resp.code == 200) {
|
||||
serverConfig = resp.data!!
|
||||
if (needToast) XToastUtils.success(ResUtils.getString(R.string.request_succeeded))
|
||||
//删除3.0.8之前保存的记录
|
||||
serverHistory.remove(HttpServerUtils.serverAddress.toString())
|
||||
//添加到历史记录
|
||||
val key = "【${serverConfig?.extraDeviceMark}】${HttpServerUtils.serverAddress.toString()}"
|
||||
if (TextUtils.isEmpty(HttpServerUtils.clientSignKey)) {
|
||||
serverHistory[key] = "SMSFORWARDER##" + HttpServerUtils.clientSafetyMeasures.toString()
|
||||
} else {
|
||||
if (needToast) XToastUtils.error(ResUtils.getString(R.string.request_failed) + resp.msg)
|
||||
serverHistory[key] = HttpServerUtils.clientSignKey + "##" + HttpServerUtils.clientSafetyMeasures.toString()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
if (needToast) XToastUtils.error(ResUtils.getString(R.string.request_failed) + response)
|
||||
HttpServerUtils.serverHistory = Gson().toJson(serverHistory)
|
||||
HttpServerUtils.serverConfig = Gson().toJson(serverConfig)
|
||||
} else {
|
||||
if (needToast) XToastUtils.error(ResUtils.getString(R.string.request_failed) + resp.msg)
|
||||
}
|
||||
if (needToast) mCountDownHelper?.finish()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
if (needToast) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.request_failed) + response)
|
||||
mCountDownHelper?.finish()
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
|
|
|
@ -10,6 +10,7 @@ import android.view.LayoutInflater
|
|||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.CompoundButton
|
||||
import android.widget.RadioGroup
|
||||
import com.hjq.permissions.OnPermissionCallback
|
||||
import com.hjq.permissions.Permission
|
||||
import com.hjq.permissions.XXPermissions
|
||||
|
@ -18,20 +19,19 @@ import com.idormy.sms.forwarder.R
|
|||
import com.idormy.sms.forwarder.core.BaseFragment
|
||||
import com.idormy.sms.forwarder.databinding.FragmentServerBinding
|
||||
import com.idormy.sms.forwarder.service.HttpService
|
||||
import com.idormy.sms.forwarder.utils.HTTP_SERVER_PORT
|
||||
import com.idormy.sms.forwarder.utils.HttpServerUtils
|
||||
import com.idormy.sms.forwarder.utils.RandomUtils
|
||||
import com.idormy.sms.forwarder.utils.XToastUtils
|
||||
import com.idormy.sms.forwarder.utils.*
|
||||
import com.xuexiang.xaop.annotation.SingleClick
|
||||
import com.xuexiang.xpage.annotation.Page
|
||||
import com.xuexiang.xui.widget.actionbar.TitleBar
|
||||
import com.xuexiang.xui.widget.button.SmoothCheckBox
|
||||
import com.xuexiang.xui.widget.dialog.materialdialog.MaterialDialog
|
||||
import com.xuexiang.xui.widget.picker.XSeekBar
|
||||
import com.xuexiang.xutil.app.ServiceUtils
|
||||
import com.xuexiang.xutil.net.NetworkUtils
|
||||
import com.xuexiang.xutil.system.ClipboardUtils
|
||||
import java.io.File
|
||||
import java.net.InetAddress
|
||||
import java.security.KeyPairGenerator
|
||||
|
||||
|
||||
@Suppress("PrivatePropertyName")
|
||||
|
@ -79,6 +79,65 @@ class ServerFragment : BaseFragment<FragmentServerBinding?>(), View.OnClickListe
|
|||
//启动更新UI定时器
|
||||
handler.post(runnable)
|
||||
|
||||
//安全措施
|
||||
var safetyMeasuresId = R.id.rb_safety_measures_none
|
||||
when (HttpServerUtils.safetyMeasures) {
|
||||
1 -> {
|
||||
safetyMeasuresId = R.id.rb_safety_measures_sign
|
||||
binding!!.layoutSignKey.visibility = View.VISIBLE
|
||||
binding!!.layoutTimeTolerance.visibility = View.VISIBLE
|
||||
}
|
||||
2 -> {
|
||||
safetyMeasuresId = R.id.rb_safety_measures_rsa
|
||||
binding!!.layoutPrivateKey.visibility = View.VISIBLE
|
||||
binding!!.layoutPublicKey.visibility = View.VISIBLE
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
binding!!.rgSafetyMeasures.check(safetyMeasuresId)
|
||||
binding!!.rgSafetyMeasures.setOnCheckedChangeListener { _: RadioGroup?, checkedId: Int ->
|
||||
var safetyMeasures = 0
|
||||
binding!!.layoutSignKey.visibility = View.GONE
|
||||
binding!!.layoutTimeTolerance.visibility = View.GONE
|
||||
binding!!.layoutPrivateKey.visibility = View.GONE
|
||||
binding!!.layoutPublicKey.visibility = View.GONE
|
||||
when (checkedId) {
|
||||
R.id.rb_safety_measures_sign -> {
|
||||
safetyMeasures = 1
|
||||
binding!!.layoutSignKey.visibility = View.VISIBLE
|
||||
binding!!.layoutTimeTolerance.visibility = View.VISIBLE
|
||||
}
|
||||
R.id.rb_safety_measures_rsa -> {
|
||||
safetyMeasures = 2
|
||||
binding!!.layoutPrivateKey.visibility = View.VISIBLE
|
||||
binding!!.layoutPublicKey.visibility = View.VISIBLE
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
HttpServerUtils.safetyMeasures = safetyMeasures
|
||||
}
|
||||
|
||||
//RSA公私钥
|
||||
binding!!.btnCopyPublicKey.setOnClickListener(this)
|
||||
binding!!.btnGenerateKey.setOnClickListener(this)
|
||||
binding!!.etPublicKey.setText(HttpServerUtils.serverPublicKey)
|
||||
binding!!.etPublicKey.addTextChangedListener(object : TextWatcher {
|
||||
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
|
||||
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
|
||||
override fun afterTextChanged(s: Editable) {
|
||||
HttpServerUtils.serverPublicKey = binding!!.etPublicKey.text.toString().trim()
|
||||
}
|
||||
})
|
||||
binding!!.etPrivateKey.setText(HttpServerUtils.serverPrivateKey)
|
||||
binding!!.etPrivateKey.addTextChangedListener(object : TextWatcher {
|
||||
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
|
||||
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
|
||||
override fun afterTextChanged(s: Editable) {
|
||||
HttpServerUtils.serverPrivateKey = binding!!.etPrivateKey.text.toString().trim()
|
||||
}
|
||||
})
|
||||
|
||||
//签名密钥
|
||||
binding!!.btnSignKey.setOnClickListener(this)
|
||||
binding!!.btnPathPicker.setOnClickListener(this)
|
||||
binding!!.etSignKey.setText(HttpServerUtils.serverSignKey)
|
||||
|
@ -89,6 +148,13 @@ class ServerFragment : BaseFragment<FragmentServerBinding?>(), View.OnClickListe
|
|||
HttpServerUtils.serverSignKey = binding!!.etSignKey.text.toString().trim()
|
||||
}
|
||||
})
|
||||
//时间容差
|
||||
binding!!.xsbTimeTolerance.setDefaultValue(HttpServerUtils.timeTolerance)
|
||||
binding!!.xsbTimeTolerance.setOnSeekBarListener { _: XSeekBar?, newValue: Int ->
|
||||
HttpServerUtils.timeTolerance = newValue
|
||||
}
|
||||
|
||||
//web客户端
|
||||
binding!!.etWebPath.setText(HttpServerUtils.serverWebPath)
|
||||
binding!!.etWebPath.addTextChangedListener(object : TextWatcher {
|
||||
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
|
||||
|
@ -155,6 +221,29 @@ class ServerFragment : BaseFragment<FragmentServerBinding?>(), View.OnClickListe
|
|||
}
|
||||
refreshButtonText()
|
||||
}
|
||||
R.id.btn_generate_key -> {
|
||||
val generator = KeyPairGenerator.getInstance("RSA") //密钥生成器
|
||||
generator.initialize(2048)
|
||||
val keyPair = generator.genKeyPair()
|
||||
val publicKey = keyPair.public
|
||||
val privateKey = keyPair.private
|
||||
|
||||
val publicKeyEncoded = Base64.encode(publicKey.encoded)
|
||||
val privateKeyEncoded = Base64.encode(privateKey.encoded)
|
||||
|
||||
println("publicKey=$publicKeyEncoded")
|
||||
println("privateKey=$privateKeyEncoded")
|
||||
|
||||
binding!!.etPublicKey.setText(publicKeyEncoded)
|
||||
binding!!.etPrivateKey.setText(privateKeyEncoded)
|
||||
|
||||
ClipboardUtils.copyText(publicKeyEncoded)
|
||||
XToastUtils.info(getString(R.string.rsa_key_tips))
|
||||
}
|
||||
R.id.btn_copy_public_key -> {
|
||||
ClipboardUtils.copyText(binding!!.etPublicKey.text)
|
||||
XToastUtils.info(getString(R.string.rsa_key_tips2))
|
||||
}
|
||||
R.id.btn_sign_key -> {
|
||||
val sign = RandomUtils.getRandomNumbersAndLetters(16)
|
||||
ClipboardUtils.copyText(sign)
|
||||
|
|
|
@ -10,9 +10,7 @@ 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.HttpServerUtils
|
||||
import com.idormy.sms.forwarder.utils.SettingUtils
|
||||
import com.idormy.sms.forwarder.utils.XToastUtils
|
||||
import com.idormy.sms.forwarder.utils.*
|
||||
import com.xuexiang.xhttp2.XHttp
|
||||
import com.xuexiang.xhttp2.cache.model.CacheMode
|
||||
import com.xuexiang.xhttp2.callback.SimpleCallBack
|
||||
|
@ -60,53 +58,71 @@ class BatteryQueryFragment : BaseFragment<FragmentClientBatteryQueryBinding?>()
|
|||
val dataMap: MutableMap<String, Any> = mutableMapOf()
|
||||
msgMap["data"] = dataMap
|
||||
|
||||
val requestMsg: String = Gson().toJson(msgMap)
|
||||
var requestMsg: String = Gson().toJson(msgMap)
|
||||
Log.i(TAG, "requestMsg:$requestMsg")
|
||||
|
||||
XHttp.post(requestUrl)
|
||||
.upJson(requestMsg)
|
||||
val postRequest = XHttp.post(requestUrl)
|
||||
.keepJson(true)
|
||||
.timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
|
||||
.cacheMode(CacheMode.NO_CACHE)
|
||||
//.retryCount(SettingUtils.requestRetryTimes) //超时重试的次数
|
||||
//.retryDelay(SettingUtils.requestDelayTime) //超时重试的延迟时间
|
||||
//.retryIncreaseDelay(SettingUtils.requestDelayTime) //超时重试叠加延时
|
||||
.timeStamp(true)
|
||||
.execute(object : SimpleCallBack<String>() {
|
||||
|
||||
override fun onError(e: ApiException) {
|
||||
XToastUtils.error(e.displayMessage)
|
||||
}
|
||||
if (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)
|
||||
} else {
|
||||
postRequest.upJson(requestMsg)
|
||||
}
|
||||
|
||||
override fun onSuccess(response: String) {
|
||||
Log.i(TAG, response)
|
||||
try {
|
||||
val resp: BaseResponse<BatteryInfo> = Gson().fromJson(response, object : TypeToken<BaseResponse<BatteryInfo>>() {}.type)
|
||||
if (resp.code == 200) {
|
||||
XToastUtils.success(ResUtils.getString(R.string.request_succeeded))
|
||||
val batteryInfo = resp.data ?: return
|
||||
postRequest.execute(object : SimpleCallBack<String>() {
|
||||
override fun onError(e: ApiException) {
|
||||
XToastUtils.error(e.displayMessage)
|
||||
}
|
||||
|
||||
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)
|
||||
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))
|
||||
}
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
}
|
|
@ -207,46 +207,65 @@ class CallQueryFragment : BaseFragment<FragmentClientCallQueryBinding?>() {
|
|||
if (refresh) pageNum = 1
|
||||
msgMap["data"] = CallQueryData(callType, pageNum, pageSize, keyword)
|
||||
|
||||
val requestMsg: String = Gson().toJson(msgMap)
|
||||
var requestMsg: String = Gson().toJson(msgMap)
|
||||
Log.i(TAG, "requestMsg:$requestMsg")
|
||||
|
||||
XHttp.post(requestUrl)
|
||||
.upJson(requestMsg)
|
||||
val postRequest = XHttp.post(requestUrl)
|
||||
.keepJson(true)
|
||||
.timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
|
||||
.cacheMode(CacheMode.NO_CACHE)
|
||||
.timeStamp(true)
|
||||
.execute(object : SimpleCallBack<String>() {
|
||||
|
||||
override fun onError(e: ApiException) {
|
||||
XToastUtils.error(e.displayMessage)
|
||||
}
|
||||
if (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)
|
||||
} else {
|
||||
postRequest.upJson(requestMsg)
|
||||
}
|
||||
|
||||
override fun onSuccess(response: String) {
|
||||
Log.i(TAG, response)
|
||||
try {
|
||||
val resp: BaseResponse<List<CallInfo>?> = Gson().fromJson(response, object : TypeToken<BaseResponse<List<CallInfo>?>>() {}.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)
|
||||
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))
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -18,10 +18,8 @@ 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.CommonUtils
|
||||
import com.idormy.sms.forwarder.utils.HttpServerUtils
|
||||
import com.idormy.sms.forwarder.utils.SettingUtils
|
||||
import com.idormy.sms.forwarder.utils.XToastUtils
|
||||
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
|
||||
|
@ -181,12 +179,7 @@ class CloneFragment : BaseFragment<FragmentClientCloneBinding?>(), View.OnClickL
|
|||
XToastUtils.error(getString(R.string.export_failed))
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(
|
||||
String.format(
|
||||
getString(R.string.export_failed_tips),
|
||||
e.message
|
||||
)
|
||||
)
|
||||
XToastUtils.error(String.format(getString(R.string.export_failed_tips), e.message))
|
||||
}
|
||||
}
|
||||
//导入配置
|
||||
|
@ -225,12 +218,7 @@ class CloneFragment : BaseFragment<FragmentClientCloneBinding?>(), View.OnClickL
|
|||
XToastUtils.error(getString(R.string.import_failed))
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(
|
||||
String.format(
|
||||
getString(R.string.import_failed_tips),
|
||||
e.message
|
||||
)
|
||||
)
|
||||
XToastUtils.error(String.format(getString(R.string.import_failed_tips), e.message))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -258,43 +246,59 @@ class CloneFragment : BaseFragment<FragmentClientCloneBinding?>(), View.OnClickL
|
|||
}
|
||||
msgMap["data"] = HttpServerUtils.exportSettings()
|
||||
|
||||
val requestMsg: String = Gson().toJson(msgMap)
|
||||
var requestMsg: String = Gson().toJson(msgMap)
|
||||
Log.i(TAG, "requestMsg:$requestMsg")
|
||||
|
||||
XHttp.post(requestUrl)
|
||||
.upJson(requestMsg)
|
||||
val postRequest = XHttp.post(requestUrl)
|
||||
.keepJson(true)
|
||||
.timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
|
||||
.cacheMode(CacheMode.NO_CACHE)
|
||||
//.retryCount(SettingUtils.requestRetryTimes) //超时重试的次数
|
||||
//.retryDelay(SettingUtils.requestDelayTime) //超时重试的延迟时间
|
||||
//.retryIncreaseDelay(SettingUtils.requestDelayTime) //超时重试叠加延时
|
||||
.timeStamp(true)
|
||||
.execute(object : SimpleCallBack<String>() {
|
||||
|
||||
override fun onError(e: ApiException) {
|
||||
XToastUtils.error(e.displayMessage)
|
||||
}
|
||||
if (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)
|
||||
} else {
|
||||
postRequest.upJson(requestMsg)
|
||||
}
|
||||
|
||||
override fun onSuccess(response: String) {
|
||||
Log.i(TAG, response)
|
||||
try {
|
||||
val resp: BaseResponse<String> = Gson().fromJson(
|
||||
response,
|
||||
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)
|
||||
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))
|
||||
}
|
||||
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()
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
|
@ -323,62 +327,80 @@ class CloneFragment : BaseFragment<FragmentClientCloneBinding?>(), View.OnClickL
|
|||
dataMap["version_code"] = AppUtils.getAppVersionCode()
|
||||
msgMap["data"] = dataMap
|
||||
|
||||
val requestMsg: String = Gson().toJson(msgMap)
|
||||
var requestMsg: String = Gson().toJson(msgMap)
|
||||
Log.i(TAG, "requestMsg:$requestMsg")
|
||||
|
||||
XHttp.post(requestUrl)
|
||||
.upJson(requestMsg)
|
||||
val postRequest = XHttp.post(requestUrl)
|
||||
.keepJson(true)
|
||||
.timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
|
||||
.cacheMode(CacheMode.NO_CACHE)
|
||||
//.retryCount(SettingUtils.requestRetryTimes) //超时重试的次数
|
||||
//.retryDelay(SettingUtils.requestDelayTime) //超时重试的延迟时间
|
||||
//.retryIncreaseDelay(SettingUtils.requestDelayTime) //超时重试叠加延时
|
||||
.timeStamp(true)
|
||||
.execute(object : SimpleCallBack<String>() {
|
||||
|
||||
override fun onError(e: ApiException) {
|
||||
XToastUtils.error(e.displayMessage)
|
||||
}
|
||||
if (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)
|
||||
} else {
|
||||
postRequest.upJson(requestMsg)
|
||||
}
|
||||
|
||||
override fun onSuccess(response: String) {
|
||||
Log.i(TAG, response)
|
||||
try {
|
||||
//替换Date字段为当前时间
|
||||
val builder = GsonBuilder()
|
||||
builder.registerTypeAdapter(
|
||||
Date::class.java,
|
||||
JsonDeserializer<Any?> { _, _, _ -> Date() })
|
||||
val gson = builder.create()
|
||||
val resp: BaseResponse<CloneInfo> = gson.fromJson(
|
||||
response,
|
||||
object : TypeToken<BaseResponse<CloneInfo>>() {}.type
|
||||
)
|
||||
if (resp.code == 200) {
|
||||
val cloneInfo = resp.data
|
||||
Log.d(TAG, "cloneInfo = $cloneInfo")
|
||||
postRequest.execute(object : SimpleCallBack<String>() {
|
||||
override fun onError(e: ApiException) {
|
||||
XToastUtils.error(e.displayMessage)
|
||||
exportCountDownHelper?.finish()
|
||||
}
|
||||
|
||||
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)
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
//替换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()
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -190,43 +190,60 @@ class ContactQueryFragment : BaseFragment<FragmentClientContactQueryBinding?>()
|
|||
else
|
||||
ContactQueryData(1, 20, null, keyword)
|
||||
|
||||
val requestMsg: String = Gson().toJson(msgMap)
|
||||
var requestMsg: String = Gson().toJson(msgMap)
|
||||
Log.i(TAG, "requestMsg:$requestMsg")
|
||||
|
||||
XHttp.post(requestUrl)
|
||||
.upJson(requestMsg)
|
||||
val postRequest = XHttp.post(requestUrl)
|
||||
.keepJson(true)
|
||||
.timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
|
||||
.cacheMode(CacheMode.NO_CACHE)
|
||||
//.retryCount(SettingUtils.requestRetryTimes) //超时重试的次数
|
||||
//.retryDelay(SettingUtils.requestDelayTime) //超时重试的延迟时间
|
||||
//.retryIncreaseDelay(SettingUtils.requestDelayTime) //超时重试叠加延时
|
||||
.timeStamp(true)
|
||||
.execute(object : SimpleCallBack<String>() {
|
||||
|
||||
override fun onError(e: ApiException) {
|
||||
XToastUtils.error(e.displayMessage)
|
||||
}
|
||||
if (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)
|
||||
} else {
|
||||
postRequest.upJson(requestMsg)
|
||||
}
|
||||
|
||||
override fun onSuccess(response: String) {
|
||||
Log.i(TAG, response)
|
||||
try {
|
||||
val resp: BaseResponse<List<ContactInfo>?> = Gson().fromJson(response, 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)
|
||||
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))
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -197,46 +197,66 @@ class SmsQueryFragment : BaseFragment<FragmentClientSmsQueryBinding?>() {
|
|||
if (refresh) pageNum = 1
|
||||
msgMap["data"] = SmsQueryData(smsType, pageNum, pageSize, keyword)
|
||||
|
||||
val requestMsg: String = Gson().toJson(msgMap)
|
||||
var requestMsg: String = Gson().toJson(msgMap)
|
||||
Log.i(TAG, "requestMsg:$requestMsg")
|
||||
|
||||
XHttp.post(requestUrl)
|
||||
.upJson(requestMsg)
|
||||
val postRequest = XHttp.post(requestUrl)
|
||||
.keepJson(true)
|
||||
.timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
|
||||
.cacheMode(CacheMode.NO_CACHE)
|
||||
.timeStamp(true)
|
||||
.execute(object : SimpleCallBack<String>() {
|
||||
|
||||
override fun onError(e: ApiException) {
|
||||
XToastUtils.error(e.displayMessage)
|
||||
}
|
||||
if (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)
|
||||
} else {
|
||||
postRequest.upJson(requestMsg)
|
||||
}
|
||||
|
||||
override fun onSuccess(response: String) {
|
||||
Log.i(TAG, response)
|
||||
try {
|
||||
val resp: BaseResponse<List<SmsInfo>?> = Gson().fromJson(response, 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)
|
||||
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))
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -112,43 +112,60 @@ class SmsSendFragment : BaseFragment<FragmentClientSmsSendBinding?>(), View.OnCl
|
|||
dataMap["msg_content"] = msgContent
|
||||
msgMap["data"] = dataMap
|
||||
|
||||
val requestMsg: String = Gson().toJson(msgMap)
|
||||
var requestMsg: String = Gson().toJson(msgMap)
|
||||
Log.i(TAG, "requestMsg:$requestMsg")
|
||||
|
||||
mCountDownHelper?.start()
|
||||
XHttp.post(requestUrl)
|
||||
.upJson(requestMsg)
|
||||
val postRequest = XHttp.post(requestUrl)
|
||||
.keepJson(true)
|
||||
.timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
|
||||
.cacheMode(CacheMode.NO_CACHE)
|
||||
//.retryCount(SettingUtils.requestRetryTimes) //超时重试的次数
|
||||
//.retryDelay(SettingUtils.requestDelayTime) //超时重试的延迟时间
|
||||
//.retryIncreaseDelay(SettingUtils.requestDelayTime) //超时重试叠加延时
|
||||
.timeStamp(true)
|
||||
.execute(object : SimpleCallBack<String>() {
|
||||
|
||||
override fun onError(e: ApiException) {
|
||||
XToastUtils.error(e.displayMessage)
|
||||
mCountDownHelper?.finish()
|
||||
}
|
||||
if (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)
|
||||
} else {
|
||||
postRequest.upJson(requestMsg)
|
||||
}
|
||||
|
||||
override fun onSuccess(response: String) {
|
||||
Log.i(TAG, response)
|
||||
try {
|
||||
val resp: BaseResponse<String> = Gson().fromJson(response, 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)
|
||||
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))
|
||||
}
|
||||
mCountDownHelper?.finish()
|
||||
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)
|
||||
}
|
||||
|
||||
})
|
||||
mCountDownHelper?.finish()
|
||||
}
|
||||
})
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
|
|
|
@ -10,9 +10,7 @@ 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.HttpServerUtils
|
||||
import com.idormy.sms.forwarder.utils.SettingUtils
|
||||
import com.idormy.sms.forwarder.utils.XToastUtils
|
||||
import com.idormy.sms.forwarder.utils.*
|
||||
import com.xuexiang.xaop.annotation.SingleClick
|
||||
import com.xuexiang.xhttp2.XHttp
|
||||
import com.xuexiang.xhttp2.cache.model.CacheMode
|
||||
|
@ -144,46 +142,63 @@ class WolSendFragment : BaseFragment<FragmentClientWolSendBinding?>(), View.OnCl
|
|||
dataMap["port"] = port
|
||||
msgMap["data"] = dataMap
|
||||
|
||||
val requestMsg: String = Gson().toJson(msgMap)
|
||||
var requestMsg: String = Gson().toJson(msgMap)
|
||||
Log.i(TAG, "requestMsg:$requestMsg")
|
||||
|
||||
mCountDownHelper?.start()
|
||||
XHttp.post(requestUrl)
|
||||
.upJson(requestMsg)
|
||||
val postRequest = XHttp.post(requestUrl)
|
||||
.keepJson(true)
|
||||
.timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
|
||||
.cacheMode(CacheMode.NO_CACHE)
|
||||
.timeStamp(true)
|
||||
.execute(object : SimpleCallBack<String>() {
|
||||
|
||||
override fun onError(e: ApiException) {
|
||||
XToastUtils.error(e.displayMessage)
|
||||
mCountDownHelper?.finish()
|
||||
}
|
||||
if (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)
|
||||
} else {
|
||||
postRequest.upJson(requestMsg)
|
||||
}
|
||||
|
||||
override fun onSuccess(response: String) {
|
||||
Log.i(TAG, response)
|
||||
try {
|
||||
val resp: BaseResponse<String> = Gson().fromJson(
|
||||
response,
|
||||
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?.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))
|
||||
}
|
||||
mCountDownHelper?.finish()
|
||||
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 -> {}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,17 @@
|
|||
package com.idormy.sms.forwarder.server.component
|
||||
|
||||
import android.text.TextUtils
|
||||
import android.util.Log
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.idormy.sms.forwarder.server.model.BaseRequest
|
||||
import com.idormy.sms.forwarder.utils.Base64
|
||||
import com.idormy.sms.forwarder.utils.HttpServerUtils
|
||||
import com.idormy.sms.forwarder.utils.RSACrypt
|
||||
import com.xuexiang.xrouter.utils.TextUtils
|
||||
import com.yanzhenjie.andserver.annotation.Converter
|
||||
import com.yanzhenjie.andserver.error.HttpException
|
||||
import com.yanzhenjie.andserver.framework.MessageConverter
|
||||
import com.yanzhenjie.andserver.framework.body.JsonBody
|
||||
import com.yanzhenjie.andserver.framework.body.StringBody
|
||||
import com.yanzhenjie.andserver.http.ResponseBody
|
||||
import com.yanzhenjie.andserver.util.IOUtils
|
||||
import com.yanzhenjie.andserver.util.MediaType
|
||||
|
@ -25,7 +29,17 @@ class AppMessageConverter : MessageConverter {
|
|||
|
||||
override fun convert(output: Any?, mediaType: MediaType?): ResponseBody {
|
||||
//返回统一结构报文
|
||||
return JsonBody(HttpServerUtils.response(output))
|
||||
var response = HttpServerUtils.response(output)
|
||||
Log.d(TAG, "response: $response")
|
||||
|
||||
if (HttpServerUtils.safetyMeasures != 2) {
|
||||
return JsonBody(response)
|
||||
}
|
||||
|
||||
val privateKey = RSACrypt.getPrivateKey(HttpServerUtils.serverPrivateKey.toString())
|
||||
response = Base64.encode(response.toByteArray())
|
||||
response = RSACrypt.encryptByPrivateKey(response, privateKey)
|
||||
return StringBody(response)
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
|
@ -33,9 +47,21 @@ class AppMessageConverter : MessageConverter {
|
|||
val charset: Charset? = mediaType?.charset
|
||||
Log.d(TAG, "Charset: $charset")
|
||||
|
||||
val json = if (charset == null) IOUtils.toString(stream) else IOUtils.toString(stream, charset)
|
||||
var json = if (charset == null) IOUtils.toString(stream) else IOUtils.toString(stream, charset)
|
||||
Log.d(TAG, "Json: $json")
|
||||
|
||||
if (HttpServerUtils.safetyMeasures == 2) {
|
||||
if (TextUtils.isEmpty(HttpServerUtils.serverPrivateKey)) {
|
||||
Log.e(TAG, "RSA解密失败: 私钥为空")
|
||||
throw HttpException(500, "服务端未配置私钥")
|
||||
}
|
||||
|
||||
val privateKey = RSACrypt.getPrivateKey(HttpServerUtils.serverPrivateKey.toString())
|
||||
json = RSACrypt.decryptByPrivateKey(json, privateKey)
|
||||
json = String(Base64.decode(json))
|
||||
Log.d(TAG, "Json: $json")
|
||||
}
|
||||
|
||||
//修改接口数据中的null、“”为默认值
|
||||
val builder = GsonBuilder()
|
||||
builder.registerTypeAdapter(Int::class.java, IntegerDefaultAdapter())
|
||||
|
@ -45,7 +71,7 @@ class AppMessageConverter : MessageConverter {
|
|||
Log.d(TAG, "Bean: $t")
|
||||
|
||||
//校验时间戳(时间误差不能超过1小时)&& 签名
|
||||
if (!TextUtils.isEmpty(HttpServerUtils.serverSignKey)) {
|
||||
if (HttpServerUtils.safetyMeasures == 1) {
|
||||
HttpServerUtils.checkSign(t as BaseRequest<*>)
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
package com.idormy.sms.forwarder.utils
|
||||
|
||||
import java.io.UnsupportedEncodingException
|
||||
|
||||
/**
|
||||
* Base64编码解码
|
||||
*/
|
||||
object Base64 {
|
||||
|
||||
private val base64EncodeChars = charArrayOf('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/')
|
||||
|
||||
private val base64DecodeChars = byteArrayOf(-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1)
|
||||
|
||||
fun encode(data: ByteArray): String {
|
||||
val sb = StringBuffer()
|
||||
val len = data.size
|
||||
var i = 0
|
||||
var b1: Int
|
||||
var b2: Int
|
||||
var b3: Int
|
||||
while (i < len) {
|
||||
b1 = ((data[i++]).toInt() and 0xff)
|
||||
if (i == len) {
|
||||
sb.append(base64EncodeChars[b1.ushr(2)])
|
||||
sb.append(base64EncodeChars[b1 and 0x3 shl 4])
|
||||
sb.append("==")
|
||||
break
|
||||
}
|
||||
b2 = (data[i++]).toInt() and 0xff
|
||||
if (i == len) {
|
||||
sb.append(base64EncodeChars[b1.ushr(2)])
|
||||
sb.append(base64EncodeChars[b1 and 0x03 shl 4 or (b2 and 0xf0).ushr(4)])
|
||||
sb.append(base64EncodeChars[b2 and 0x0f shl 2])
|
||||
sb.append("=")
|
||||
break
|
||||
}
|
||||
b3 = (data[i++]).toInt() and 0xff
|
||||
sb.append(base64EncodeChars[b1.ushr(2)])
|
||||
sb.append(base64EncodeChars[b1 and 0x03 shl 4 or (b2 and 0xf0).ushr(4)])
|
||||
sb.append(base64EncodeChars[b2 and 0x0f shl 2 or (b3 and 0xc0).ushr(6)])
|
||||
sb.append(base64EncodeChars[b3 and 0x3f])
|
||||
}
|
||||
return sb.toString()
|
||||
}
|
||||
|
||||
@Throws(UnsupportedEncodingException::class)
|
||||
fun decode(str: String): ByteArray {
|
||||
val sb = StringBuffer()
|
||||
val data = str.toByteArray(charset("US-ASCII"))
|
||||
val len = data.size
|
||||
var i = 0
|
||||
var b1: Int
|
||||
var b2: Int
|
||||
var b3: Int
|
||||
var b4: Int
|
||||
while (i < len) {
|
||||
/* b1 */
|
||||
do {
|
||||
b1 = base64DecodeChars[(data[i++]).toInt()].toInt()
|
||||
} while (i < len && b1 == -1)
|
||||
if (b1 == -1) break
|
||||
/* b2 */
|
||||
do {
|
||||
b2 = base64DecodeChars[(data[i++]).toInt()].toInt()
|
||||
} while (i < len && b2 == -1)
|
||||
if (b2 == -1) break
|
||||
sb.append((b1 shl 2 or (b2 and 0x30).ushr(4)).toChar())
|
||||
/* b3 */
|
||||
do {
|
||||
b3 = data[i++].toInt()
|
||||
if (b3 == 61) return sb.toString().toByteArray(charset("ISO-8859-1"))
|
||||
b3 = base64DecodeChars[b3].toInt()
|
||||
} while (i < len && b3 == -1)
|
||||
if (b3 == -1) break
|
||||
sb.append((b2 and 0x0f shl 4 or (b3 and 0x3c).ushr(2)).toChar())
|
||||
/* b4 */
|
||||
do {
|
||||
b4 = data[i++].toInt()
|
||||
if (b4 == 61) return sb.toString().toByteArray(charset("ISO-8859-1"))
|
||||
b4 = base64DecodeChars[b4].toInt()
|
||||
} while (i < len && b4 == -1)
|
||||
if (b4 == -1) break
|
||||
sb.append((b3 and 0x03 shl 6 or b4).toChar())
|
||||
}
|
||||
return sb.toString().toByteArray(charset("ISO-8859-1"))
|
||||
}
|
||||
|
||||
}
|
|
@ -322,7 +322,11 @@ const val HTTP_SUCCESS_CODE: Int = 200
|
|||
const val HTTP_FAILURE_CODE: Int = 500
|
||||
const val SP_ENABLE_SERVER = "enable_server"
|
||||
const val SP_ENABLE_SERVER_AUTORUN = "enable_server_autorun"
|
||||
const val SP_SERVER_SAFETY_MEASURES = "server_safety_measures"
|
||||
const val SP_SERVER_SIGN_KEY = "server_sign_key"
|
||||
const val SP_SERVER_TIME_TOLERANCE = "server_time_tolerance"
|
||||
const val SP_SERVER_PUBLIC_KEY = "server_public_key"
|
||||
const val SP_SERVER_PRIVATE_KEY = "server_private_key"
|
||||
const val SP_SERVER_WEB_PATH = "server_web_path"
|
||||
const val SP_ENABLE_API_CLONE = "enable_api_clone"
|
||||
const val SP_ENABLE_API_SMS_SEND = "enable_api_sms_send"
|
||||
|
@ -335,6 +339,7 @@ const val SP_WOL_HISTORY = "wol_history"
|
|||
const val SP_SERVER_ADDRESS = "server_address"
|
||||
const val SP_SERVER_HISTORY = "server_history"
|
||||
const val SP_SERVER_CONFIG = "server_config"
|
||||
const val SP_CLIENT_SAFETY_MEASURES = "client_safety_measures"
|
||||
const val SP_CLIENT_SIGN_KEY = "client_sign_key"
|
||||
var CLIENT_FRAGMENT_LIST = listOf(
|
||||
PageInfo(
|
||||
|
|
|
@ -32,6 +32,30 @@ class HttpServerUtils private constructor() {
|
|||
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)
|
||||
}
|
||||
|
||||
//服务端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?
|
||||
|
@ -40,6 +64,14 @@ class HttpServerUtils private constructor() {
|
|||
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?
|
||||
|
@ -72,7 +104,15 @@ class HttpServerUtils private constructor() {
|
|||
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, "")
|
||||
|
@ -164,8 +204,9 @@ class HttpServerUtils private constructor() {
|
|||
|
||||
val timestamp = System.currentTimeMillis()
|
||||
val diffTime = kotlin.math.abs(timestamp - req.timestamp)
|
||||
if (diffTime > 3600000L) {
|
||||
throw HttpException(500, String.format(getString(R.string.timestamp_verify_failed), timestamp, diffTime))
|
||||
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())
|
||||
|
@ -306,7 +347,7 @@ class HttpServerUtils private constructor() {
|
|||
if (output != null) {
|
||||
resp["data"] = output
|
||||
}
|
||||
if (!TextUtils.isEmpty(serverSignKey)) {
|
||||
if (safetyMeasures == 1) {
|
||||
resp["sign"] = calcSign(timestamp.toString(), serverSignKey.toString())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,210 @@
|
|||
package com.idormy.sms.forwarder.utils
|
||||
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.security.KeyFactory
|
||||
import java.security.PrivateKey
|
||||
import java.security.PublicKey
|
||||
import java.security.spec.PKCS8EncodedKeySpec
|
||||
import java.security.spec.X509EncodedKeySpec
|
||||
import javax.crypto.Cipher
|
||||
|
||||
/**
|
||||
* 非对称加密RSA加密和解密
|
||||
*/
|
||||
@Suppress("unused")
|
||||
object RSACrypt {
|
||||
|
||||
private const val transformation = "RSA"
|
||||
private const val ENCRYPT_MAX_SIZE = 245
|
||||
private const val DECRYPT_MAX_SIZE = 256
|
||||
|
||||
/**
|
||||
* 私钥加密
|
||||
* @param input 原文
|
||||
* @param privateKey 私钥
|
||||
*/
|
||||
fun encryptByPrivateKey(input: String, privateKey: PrivateKey): String {
|
||||
|
||||
//创建cipher对象
|
||||
val cipher = Cipher.getInstance(transformation)
|
||||
//初始化cipher
|
||||
cipher.init(Cipher.ENCRYPT_MODE, privateKey)
|
||||
|
||||
//****非对称加密****
|
||||
val byteArray = input.toByteArray()
|
||||
|
||||
//分段加密
|
||||
var temp: ByteArray?
|
||||
var offset = 0 //当前偏移的位置
|
||||
|
||||
val outputStream = ByteArrayOutputStream()
|
||||
|
||||
//拆分input
|
||||
while (byteArray.size - offset > 0) {
|
||||
//每次最大加密245个字节
|
||||
if (byteArray.size - offset >= ENCRYPT_MAX_SIZE) {
|
||||
//剩余部分大于245
|
||||
//加密完整245
|
||||
temp = cipher.doFinal(byteArray, offset, ENCRYPT_MAX_SIZE)
|
||||
//重新计算偏移位置
|
||||
offset += ENCRYPT_MAX_SIZE
|
||||
} else {
|
||||
//加密最后一块
|
||||
temp = cipher.doFinal(byteArray, offset, byteArray.size - offset)
|
||||
//重新计算偏移位置
|
||||
offset = byteArray.size
|
||||
}
|
||||
//存储到临时的缓冲区
|
||||
outputStream.write(temp)
|
||||
}
|
||||
outputStream.close()
|
||||
|
||||
return Base64.encode(outputStream.toByteArray())
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 公钥加密
|
||||
* @param input 原文
|
||||
* @param publicKey 公钥
|
||||
*/
|
||||
fun encryptByPublicKey(input: String, publicKey: PublicKey): String {
|
||||
|
||||
//创建cipher对象
|
||||
val cipher = Cipher.getInstance(transformation)
|
||||
//初始化cipher
|
||||
cipher.init(Cipher.ENCRYPT_MODE, publicKey)
|
||||
|
||||
//****非对称加密****
|
||||
val byteArray = input.toByteArray()
|
||||
|
||||
var temp: ByteArray?
|
||||
var offset = 0 //当前偏移的位置
|
||||
|
||||
val outputStream = ByteArrayOutputStream()
|
||||
|
||||
//拆分input
|
||||
while (byteArray.size - offset > 0) {
|
||||
//每次最大加密117个字节
|
||||
if (byteArray.size - offset >= ENCRYPT_MAX_SIZE) {
|
||||
//剩余部分大于117
|
||||
//加密完整117
|
||||
temp = cipher.doFinal(byteArray, offset, ENCRYPT_MAX_SIZE)
|
||||
//重新计算偏移位置
|
||||
offset += ENCRYPT_MAX_SIZE
|
||||
} else {
|
||||
//加密最后一块
|
||||
temp = cipher.doFinal(byteArray, offset, byteArray.size - offset)
|
||||
//重新计算偏移位置
|
||||
offset = byteArray.size
|
||||
}
|
||||
//存储到临时的缓冲区
|
||||
outputStream.write(temp)
|
||||
}
|
||||
outputStream.close()
|
||||
|
||||
return Base64.encode(outputStream.toByteArray())
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 私钥解密
|
||||
* @param input 秘文
|
||||
* @param privateKey 私钥
|
||||
*/
|
||||
fun decryptByPrivateKey(input: String, privateKey: PrivateKey): String {
|
||||
|
||||
//创建cipher对象
|
||||
val cipher = Cipher.getInstance(transformation)
|
||||
//初始化cipher
|
||||
cipher.init(Cipher.DECRYPT_MODE, privateKey)
|
||||
|
||||
//****非对称加密****
|
||||
val byteArray = Base64.decode(input)
|
||||
|
||||
//分段解密
|
||||
var temp: ByteArray?
|
||||
var offset = 0 //当前偏移的位置
|
||||
|
||||
val outputStream = ByteArrayOutputStream()
|
||||
|
||||
//拆分input
|
||||
while (byteArray.size - offset > 0) {
|
||||
//每次最大解密256个字节
|
||||
if (byteArray.size - offset >= DECRYPT_MAX_SIZE) {
|
||||
|
||||
temp = cipher.doFinal(byteArray, offset, DECRYPT_MAX_SIZE)
|
||||
//重新计算偏移位置
|
||||
offset += DECRYPT_MAX_SIZE
|
||||
} else {
|
||||
//加密最后一块
|
||||
temp = cipher.doFinal(byteArray, offset, byteArray.size - offset)
|
||||
//重新计算偏移位置
|
||||
offset = byteArray.size
|
||||
}
|
||||
//存储到临时的缓冲区
|
||||
outputStream.write(temp)
|
||||
}
|
||||
outputStream.close()
|
||||
|
||||
return String(outputStream.toByteArray())
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 公钥解密
|
||||
* @param input 秘文
|
||||
* @param publicKey 公钥
|
||||
*/
|
||||
fun decryptByPublicKey(input: String, publicKey: PublicKey): String {
|
||||
|
||||
//创建cipher对象
|
||||
val cipher = Cipher.getInstance(transformation)
|
||||
//初始化cipher
|
||||
cipher.init(Cipher.DECRYPT_MODE, publicKey)
|
||||
|
||||
//****非对称加密****
|
||||
val byteArray = Base64.decode(input)
|
||||
|
||||
//分段解密
|
||||
var temp: ByteArray?
|
||||
var offset = 0 //当前偏移的位置
|
||||
|
||||
val outputStream = ByteArrayOutputStream()
|
||||
|
||||
//拆分input
|
||||
while (byteArray.size - offset > 0) {
|
||||
//每次最大解密256个字节
|
||||
if (byteArray.size - offset >= DECRYPT_MAX_SIZE) {
|
||||
|
||||
temp = cipher.doFinal(byteArray, offset, DECRYPT_MAX_SIZE)
|
||||
//重新计算偏移位置
|
||||
offset += DECRYPT_MAX_SIZE
|
||||
} else {
|
||||
//加密最后一块
|
||||
temp = cipher.doFinal(byteArray, offset, byteArray.size - offset)
|
||||
//重新计算偏移位置
|
||||
offset = byteArray.size
|
||||
}
|
||||
//存储到临时的缓冲区
|
||||
outputStream.write(temp)
|
||||
}
|
||||
outputStream.close()
|
||||
|
||||
return String(outputStream.toByteArray())
|
||||
|
||||
}
|
||||
|
||||
fun getPrivateKey(privateKeyStr: String): PrivateKey {
|
||||
//字符串转成秘钥对对象
|
||||
val generator = KeyFactory.getInstance("RSA")
|
||||
return generator.generatePrivate(PKCS8EncodedKeySpec(Base64.decode(privateKeyStr)))
|
||||
}
|
||||
|
||||
fun getPublicKey(publicKeyStr: String): PublicKey {
|
||||
//字符串转成秘钥对对象
|
||||
val kf = KeyFactory.getInstance("RSA")
|
||||
return kf.generatePublic(X509EncodedKeySpec(Base64.decode(publicKeyStr)))
|
||||
}
|
||||
|
||||
}
|
|
@ -61,20 +61,55 @@
|
|||
android:layout_weight="1"
|
||||
android:hint="@string/service_address_hint" />
|
||||
|
||||
<com.xuexiang.xui.widget.button.ButtonView
|
||||
android:id="@+id/btn_server_history"
|
||||
style="@style/ButtonView.Gray"
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
style="@style/settingBarStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="5dp"
|
||||
android:gravity="center"
|
||||
android:minWidth="70dp"
|
||||
android:padding="5dp"
|
||||
android:text="@string/server_history" />
|
||||
android:text="@string/safety_measures"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<RadioGroup
|
||||
android:id="@+id/rg_safety_measures"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="3dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/rb_safety_measures_none"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="true"
|
||||
android:text="@string/safety_measures_none"
|
||||
android:textSize="11sp" />
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/rb_safety_measures_sign"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/safety_measures_sign"
|
||||
android:textSize="11sp" />
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/rb_safety_measures_rsa"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/safety_measures_encrypt"
|
||||
android:textSize="11sp" />
|
||||
|
||||
</RadioGroup>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_sign_key"
|
||||
style="@style/settingBarStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
@ -82,24 +117,52 @@
|
|||
tools:ignore="RtlSymmetry">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_sign_key"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/sign_key"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<com.xuexiang.xui.widget.edittext.ClearEditText
|
||||
<EditText
|
||||
android:id="@+id/et_sign_key"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="5dp"
|
||||
android:layout_weight="1" />
|
||||
android:layout_weight="1"
|
||||
android:gravity="top"
|
||||
android:importantForAutofill="no"
|
||||
android:inputType="textMultiLine"
|
||||
android:maxLines="5"
|
||||
android:minLines="1"
|
||||
android:scrollbars="vertical"
|
||||
android:textSize="10sp"
|
||||
tools:ignore="LabelFor,SmallSp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_margin="10dp"
|
||||
android:gravity="center_horizontal">
|
||||
|
||||
<com.xuexiang.xui.widget.button.ButtonView
|
||||
android:id="@+id/btn_server_history"
|
||||
style="@style/ButtonView.Gray"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:minWidth="70dp"
|
||||
android:padding="5dp"
|
||||
android:text="@string/server_history" />
|
||||
|
||||
<com.xuexiang.xui.widget.button.ButtonView
|
||||
android:id="@+id/btn_server_test"
|
||||
style="@style/ButtonView.Green"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="5dp"
|
||||
android:layout_marginStart="15dp"
|
||||
android:gravity="center"
|
||||
android:minWidth="70dp"
|
||||
android:padding="5dp"
|
||||
|
|
|
@ -87,19 +87,11 @@
|
|||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_server_tips"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/http_server_stopped"
|
||||
android:textSize="10sp"
|
||||
tools:ignore="SmallSp" />
|
||||
|
||||
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
|
||||
android:id="@+id/iv_copy"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="5dp"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:gravity="center"
|
||||
android:padding="3dp"
|
||||
android:text="@string/copy"
|
||||
|
@ -111,6 +103,14 @@
|
|||
app:sb_shape_type="rectangle"
|
||||
tools:ignore="PrivateResource,SmallSp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_server_tips"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/http_server_stopped"
|
||||
android:textSize="10sp"
|
||||
tools:ignore="SmallSp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
@ -134,10 +134,57 @@
|
|||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
style="@style/settingBarStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/safety_measures"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<RadioGroup
|
||||
android:id="@+id/rg_safety_measures"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="3dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/rb_safety_measures_none"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="true"
|
||||
android:text="@string/safety_measures_none"
|
||||
android:textSize="11sp" />
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/rb_safety_measures_sign"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/safety_measures_sign"
|
||||
android:textSize="11sp" />
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/rb_safety_measures_rsa"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/safety_measures_encrypt"
|
||||
android:textSize="11sp" />
|
||||
|
||||
</RadioGroup>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_sign_key"
|
||||
style="@style/settingBarStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingEnd="15dp"
|
||||
android:visibility="gone"
|
||||
tools:ignore="RtlSymmetry">
|
||||
|
||||
<TextView
|
||||
|
@ -172,33 +219,142 @@
|
|||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_time_tolerance"
|
||||
style="@style/settingBarStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingEnd="15dp"
|
||||
android:visibility="gone"
|
||||
tools:ignore="RtlSymmetry">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/time_tolerance"
|
||||
android:textStyle="bold"
|
||||
tools:ignore="RelativeOverlap" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/time_tolerance_tips"
|
||||
android:textSize="9sp"
|
||||
tools:ignore="SmallSp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<com.xuexiang.xui.widget.picker.XSeekBar
|
||||
android:id="@+id/xsb_time_tolerance"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="5dp"
|
||||
android:layout_weight="1"
|
||||
app:xsb_max="600"
|
||||
app:xsb_min="1" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="5dp"
|
||||
android:text="@string/seconds"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_private_key"
|
||||
style="@style/settingBarStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingEnd="15dp"
|
||||
android:visibility="gone"
|
||||
tools:ignore="RtlSymmetry">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/web_client"
|
||||
android:text="@string/private_key"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<com.xuexiang.xui.widget.edittext.ClearEditText
|
||||
android:id="@+id/et_web_path"
|
||||
<EditText
|
||||
android:id="@+id/et_private_key"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="5dp"
|
||||
android:layout_weight="1" />
|
||||
android:layout_weight="1"
|
||||
android:gravity="top"
|
||||
android:hint="@string/private_key_tips"
|
||||
android:importantForAutofill="no"
|
||||
android:inputType="textMultiLine"
|
||||
android:maxLines="5"
|
||||
android:minLines="2"
|
||||
android:scrollbars="vertical"
|
||||
android:textSize="10sp"
|
||||
tools:ignore="SmallSp" />
|
||||
|
||||
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
|
||||
android:id="@+id/btn_path_picker"
|
||||
android:id="@+id/btn_generate_key"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="5dp"
|
||||
android:gravity="center"
|
||||
android:padding="5dp"
|
||||
android:text="@string/select_directory"
|
||||
android:text="@string/generate_key"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="10sp"
|
||||
app:sb_color_unpressed="@color/colorPrimary"
|
||||
app:sb_ripple_color="@color/white"
|
||||
app:sb_ripple_duration="500"
|
||||
app:sb_shape_type="rectangle"
|
||||
tools:ignore="SmallSp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_public_key"
|
||||
style="@style/settingBarStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingEnd="15dp"
|
||||
android:visibility="gone"
|
||||
tools:ignore="RtlSymmetry">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/public_key"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et_public_key"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="5dp"
|
||||
android:layout_weight="1"
|
||||
android:gravity="top"
|
||||
android:hint="@string/public_key_tips"
|
||||
android:importantForAutofill="no"
|
||||
android:inputType="textMultiLine"
|
||||
android:maxLines="5"
|
||||
android:minLines="2"
|
||||
android:scrollbars="vertical"
|
||||
android:textSize="10sp"
|
||||
tools:ignore="SmallSp" />
|
||||
|
||||
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
|
||||
android:id="@+id/btn_copy_public_key"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="5dp"
|
||||
android:gravity="center"
|
||||
android:padding="5dp"
|
||||
android:text="@string/copy_public_key"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="10sp"
|
||||
app:sb_color_unpressed="@color/colorPrimary"
|
||||
|
@ -233,6 +389,47 @@
|
|||
tools:ignore="SmallSp" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
style="@style/settingBarStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingEnd="15dp"
|
||||
tools:ignore="RtlSymmetry">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/web_client"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<com.xuexiang.xui.widget.edittext.ClearEditText
|
||||
android:id="@+id/et_web_path"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="5dp"
|
||||
android:layout_weight="1"
|
||||
android:hint="@string/web_path_tips"
|
||||
android:textSize="10sp"
|
||||
tools:ignore="SmallSp" />
|
||||
|
||||
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
|
||||
android:id="@+id/btn_path_picker"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="5dp"
|
||||
android:gravity="center"
|
||||
android:padding="5dp"
|
||||
android:text="@string/select_directory"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="10sp"
|
||||
app:sb_color_unpressed="@color/colorPrimary"
|
||||
app:sb_ripple_color="@color/white"
|
||||
app:sb_ripple_duration="500"
|
||||
app:sb_shape_type="rectangle"
|
||||
tools:ignore="SmallSp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
style="@style/settingBarStyle"
|
||||
android:layout_width="match_parent"
|
||||
|
@ -459,7 +656,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:text="@string/api_wol"
|
||||
android:textStyle="bold"
|
||||
tools:ignore="RelativeOverlap" />
|
||||
tools:ignore="RelativeOverlap,TooManyViews" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
|
|
|
@ -778,6 +778,8 @@
|
|||
<string name="server_settings">Server Settings</string>
|
||||
<string name="server_settings_tips">It is recommended to enable signature settings, click "Random" to generate and copy to clipboard</string>
|
||||
<string name="sign_key">Sign Key</string>
|
||||
<string name="rsa_key_tips">Key pair generated and copied to clipboard</string>
|
||||
<string name="rsa_key_tips2">Copied to clipboard</string>
|
||||
<string name="sign_key_tips">Key generated and copied to clipboard</string>
|
||||
<string name="copy" tools:ignore="PrivateResource">Copy</string>
|
||||
<string name="random">Random</string>
|
||||
|
@ -867,7 +869,7 @@
|
|||
<string name="sign_verify_failed">Sign verify failed</string>
|
||||
<string name="version_code_required">version_code required</string>
|
||||
<string name="inconsistent_version">The app versions of the client and server are inconsistent</string>
|
||||
<string name="timestamp_verify_failed" formatted="false">The timestamp verification failed, and the difference with the server time (%s) cannot exceed 1 hour (diffTime=%s)</string>
|
||||
<string name="timestamp_verify_failed" formatted="false">The timestamp verification failed, and the difference with the server time (%s) cannot exceed %s sec. (diffTime=%s)</string>
|
||||
|
||||
<string name="main_title">Main title</string>
|
||||
<string name="subtitle">Subtitle</string>
|
||||
|
@ -921,5 +923,18 @@
|
|||
<string name="user_id">User ID</string>
|
||||
<string name="auto_clean_logs">Auto delete logs N days ago</string>
|
||||
<string name="auto_clean_logs_tips">0=disabled, scan when battery change</string>
|
||||
<string name="day">天</string>
|
||||
<string name="day">Day</string>
|
||||
<string name="safety_measures">Safety Measures</string>
|
||||
<string name="safety_measures_none">None</string>
|
||||
<string name="safety_measures_sign">Sign</string>
|
||||
<string name="safety_measures_encrypt">Encrypt</string>
|
||||
<string name="web_path_tips">See Github Wiki, download to Download directory</string>
|
||||
<string name="time_tolerance">Time Tolerance</string>
|
||||
<string name="time_tolerance_tips">Minimize time tolerance to avoid request replay attacks</string>
|
||||
<string name="private_key">Private Key</string>
|
||||
<string name="private_key_tips">Private key is used on the server: the private key of the server response message is encrypted, and the client public key is decrypted</string>
|
||||
<string name="generate_key">Generate</string>
|
||||
<string name="public_key">Public Key</string>
|
||||
<string name="public_key_tips">Public key is used on the client: client request message public key encryption, server private key decryption</string>
|
||||
<string name="copy_public_key">Copy</string>
|
||||
</resources>
|
||||
|
|
|
@ -781,6 +781,8 @@
|
|||
<string name="copy" tools:ignore="PrivateResource">复制</string>
|
||||
<string name="random">随机生成</string>
|
||||
<string name="sign_key">签名密钥</string>
|
||||
<string name="rsa_key_tips">已生成公私钥对,并复制公钥到剪贴板</string>
|
||||
<string name="rsa_key_tips2">已复制公钥到剪贴板</string>
|
||||
<string name="sign_key_tips">已生成密钥,并复制到剪贴板</string>
|
||||
<string name="enable_function">启用功能</string>
|
||||
<string name="enable_function_tips">按需选择您要启用远程控制的功能</string>
|
||||
|
@ -832,9 +834,9 @@
|
|||
<string name="battery_plugged">充电器:%s</string>
|
||||
|
||||
<string name="server_history">历史记录</string>
|
||||
<string name="server_test">测试接口</string>
|
||||
<string name="server_test">登录服务</string>
|
||||
<string name="invalid_service_address">无效的服务地址!\n格式:http://127.0.0.1:5000 或 https://smsf.demo.com</string>
|
||||
<string name="click_test_button_first">请先点击【测试接口】按钮,获取服务端已启用的功能列表</string>
|
||||
<string name="click_test_button_first">请先点击【登录服务】按钮,获取服务端已启用的功能列表</string>
|
||||
<string name="disabled_on_the_server">服务端禁用此功能</string>
|
||||
<string name="frpc_failed_to_run">Frpc运行失败</string>
|
||||
<string name="successfully_deleted">删除成功</string>
|
||||
|
@ -868,7 +870,7 @@
|
|||
<string name="sign_verify_failed">签名校验失败</string>
|
||||
<string name="version_code_required">version_code节点必传</string>
|
||||
<string name="inconsistent_version">客户端与服务端的App版本不一致</string>
|
||||
<string name="timestamp_verify_failed" formatted="false">timestamp校验失败,与服务器时间(%s)误差不能超过1小时(diffTime=%s)</string>
|
||||
<string name="timestamp_verify_failed" formatted="false">timestamp校验失败,与服务器时间(%s)误差不能超过%s秒(diffTime=%s)</string>
|
||||
|
||||
<string name="main_title">主标题</string>
|
||||
<string name="subtitle">副标题</string>
|
||||
|
@ -923,4 +925,17 @@
|
|||
<string name="auto_clean_logs">自动删除N天前的转发记录</string>
|
||||
<string name="auto_clean_logs_tips">0=禁用,触发机制:每次电量变化时扫描</string>
|
||||
<string name="day">天</string>
|
||||
<string name="safety_measures">安全措施</string>
|
||||
<string name="safety_measures_none">不需要</string>
|
||||
<string name="safety_measures_sign">校验签名</string>
|
||||
<string name="safety_measures_encrypt">加密传输</string>
|
||||
<string name="web_path_tips">参见 Github Wiki,下载到 Download 目录</string>
|
||||
<string name="time_tolerance">客户端与服务端时间容差</string>
|
||||
<string name="time_tolerance_tips">尽量缩短时间容差,避免请求重放攻击</string>
|
||||
<string name="private_key">RSA私钥</string>
|
||||
<string name="private_key_tips">RSA私钥用在服务端:服务端应答报文私钥加密,客户端公钥解密</string>
|
||||
<string name="generate_key">生成密钥</string>
|
||||
<string name="public_key">RSA公钥</string>
|
||||
<string name="public_key_tips">RSA公钥用在客户端:客户端请求报文公钥加密,服务端私钥解密</string>
|
||||
<string name="copy_public_key">复制公钥</string>
|
||||
</resources>
|
||||
|
|
Loading…
Reference in New Issue