整理:中英文语言包

This commit is contained in:
pppscn 2022-06-07 20:51:16 +08:00
parent 628b704d02
commit 5712c32ff3
35 changed files with 193 additions and 77 deletions

View File

@ -158,7 +158,7 @@ class MainActivity : BaseActivity<ActivityMainBinding?>(),
R.id.nav_logcat -> openNewPage(LogcatFragment::class.java) R.id.nav_logcat -> openNewPage(LogcatFragment::class.java)
R.id.nav_help -> AgentWebActivity.goWeb(this, getString(R.string.url_help)) R.id.nav_help -> AgentWebActivity.goWeb(this, getString(R.string.url_help))
R.id.nav_about -> openNewPage(AboutFragment::class.java) R.id.nav_about -> openNewPage(AboutFragment::class.java)
else -> XToastUtils.toast("点击了:" + menuItem.title) else -> XToastUtils.toast("Click:" + menuItem.title)
} }
} }
true true

View File

@ -27,7 +27,7 @@ abstract class NoTipCallBack<T> : SimpleCallBack<T> {
override fun onError(e: ApiException) { override fun onError(e: ApiException) {
if (!StringUtils.isEmpty(mUrl)) { if (!StringUtils.isEmpty(mUrl)) {
Logger.e("网络请求的url:$mUrl", e) Logger.e("Request Url: $mUrl", e)
} else { } else {
Logger.e(e) Logger.e(e)
} }

View File

@ -29,7 +29,7 @@ abstract class TipCallBack<T> : SimpleCallBack<T> {
override fun onError(e: ApiException) { override fun onError(e: ApiException) {
XToastUtils.error(e) XToastUtils.error(e)
if (!StringUtils.isEmpty(mUrl)) { if (!StringUtils.isEmpty(mUrl)) {
Logger.e("网络请求的url:$mUrl", e) Logger.e("Request Url: $mUrl", e)
} else { } else {
Logger.e(e) Logger.e(e)
} }

View File

@ -37,7 +37,7 @@ abstract class TipProgressLoadingCallBack<T> : ProgressLoadingCallBack<T> {
super.onError(e) super.onError(e)
XToastUtils.error(e) XToastUtils.error(e)
if (!StringUtils.isEmpty(mUrl)) { if (!StringUtils.isEmpty(mUrl)) {
Logger.e("网络请求的url:$mUrl", e) Logger.e("Request Url: $mUrl", e)
} else { } else {
Logger.e(e) Logger.e(e)
} }

View File

@ -13,7 +13,7 @@ import com.xuexiang.xui.widget.dialog.MiniLoadingDialog
*/ */
class MiniLoadingDialogLoader @JvmOverloads constructor( class MiniLoadingDialogLoader @JvmOverloads constructor(
context: Context?, context: Context?,
msg: String? = "请求中...", msg: String? = "Loading...",
) : IProgressLoader { ) : IProgressLoader {
/** /**
* 进度loading弹窗 * 进度loading弹窗

View File

@ -27,7 +27,7 @@ abstract class NoTipRequestSubscriber<T> : BaseSubscriber<T> {
public override fun onError(e: ApiException) { public override fun onError(e: ApiException) {
if (!StringUtils.isEmpty(mUrl)) { if (!StringUtils.isEmpty(mUrl)) {
Logger.e("网络请求的url:$mUrl", e) Logger.e("Request Url: $mUrl", e)
} else { } else {
Logger.e(e) Logger.e(e)
} }

View File

@ -34,7 +34,7 @@ abstract class TipProgressLoadingSubscriber<T> : ProgressLoadingSubscriber<T> {
super.onError(e) super.onError(e)
XToastUtils.error(e) XToastUtils.error(e)
if (!StringUtils.isEmpty(mUrl)) { if (!StringUtils.isEmpty(mUrl)) {
Logger.e("网络请求的url:$mUrl", e) Logger.e("Request Url: $mUrl", e)
} else { } else {
Logger.e(e) Logger.e(e)
} }

View File

@ -29,7 +29,7 @@ abstract class TipRequestSubscriber<T> : BaseSubscriber<T> {
public override fun onError(e: ApiException) { public override fun onError(e: ApiException) {
XToastUtils.error(e) XToastUtils.error(e)
if (!StringUtils.isEmpty(mUrl)) { if (!StringUtils.isEmpty(mUrl)) {
Logger.e("网络请求的url:$mUrl", e) Logger.e("Request Url: $mUrl", e)
} else { } else {
Logger.e(e) Logger.e(e)
} }

View File

@ -43,7 +43,7 @@ class WebViewInterceptDialog : AppCompatActivity(), DialogInterface.OnDismissLis
private fun getOpenTitle(url: String): String { private fun getOpenTitle(url: String): String {
val scheme = getScheme(url) val scheme = getScheme(url)
return if ("mqqopensdkapi" == scheme) { return if ("mqqopensdkapi" == scheme) {
"是否允许页面打开\"QQ\"?" ResUtils.getString(R.string.lab_open_qq_app)
} else { } else {
ResUtils.getString(R.string.lab_open_third_app) ResUtils.getString(R.string.lab_open_third_app)
} }

View File

@ -2,6 +2,8 @@ package com.idormy.sms.forwarder.entity
import android.util.Patterns import android.util.Patterns
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
import com.idormy.sms.forwarder.R
import com.xuexiang.xui.utils.ResUtils.getString
import java.io.Serializable import java.io.Serializable
import java.util.* import java.util.*
@ -17,6 +19,6 @@ data class ContactInfo(
} }
override fun toString(): String { override fun toString(): String {
return String.format("姓名:%s\n号码:%s", name, phoneNumber) return String.format(getString(R.string.contact_info), name, phoneNumber)
} }
} }

View File

@ -17,6 +17,7 @@ import com.scwang.smartrefresh.layout.listener.OnRefreshLoadMoreListener
import com.xuexiang.xaop.annotation.SingleClick import com.xuexiang.xaop.annotation.SingleClick
import com.xuexiang.xpage.annotation.Page import com.xuexiang.xpage.annotation.Page
import com.xuexiang.xui.utils.DensityUtils import com.xuexiang.xui.utils.DensityUtils
import com.xuexiang.xui.utils.ResUtils
import com.xuexiang.xui.utils.ThemeUtils import com.xuexiang.xui.utils.ThemeUtils
import com.xuexiang.xui.utils.WidgetUtils import com.xuexiang.xui.utils.WidgetUtils
import com.xuexiang.xui.widget.actionbar.TitleBar import com.xuexiang.xui.widget.actionbar.TitleBar
@ -73,7 +74,7 @@ class AppListFragment : BaseFragment<FragmentAppListBinding?>() {
val cm = requireContext().getSystemService(AppCompatActivity.CLIPBOARD_SERVICE) as ClipboardManager val cm = requireContext().getSystemService(AppCompatActivity.CLIPBOARD_SERVICE) as ClipboardManager
val mClipData = ClipData.newPlainText("pkgName", item?.packageName) val mClipData = ClipData.newPlainText("pkgName", item?.packageName)
cm.setPrimaryClip(mClipData) cm.setPrimaryClip(mClipData)
XToastUtils.toast("已复制包名:" + item?.packageName, 2000) XToastUtils.toast(ResUtils.getString(R.string.package_name_copied) + item?.packageName, 2000)
} }
//设置刷新加载时禁止所有列表操作 //设置刷新加载时禁止所有列表操作

View File

@ -107,7 +107,7 @@ class ClientFragment : BaseFragment<FragmentClientBinding?>(),
when (v.id) { when (v.id) {
R.id.btn_server_test -> { R.id.btn_server_test -> {
if (!CommonUtils.checkUrl(HttpServerUtils.serverAddress)) { if (!CommonUtils.checkUrl(HttpServerUtils.serverAddress)) {
XToastUtils.error("请输入有效的服务地址") XToastUtils.error(getString(R.string.invalid_service_address))
return return
} }
queryConfig(true) queryConfig(true)
@ -119,12 +119,12 @@ class ClientFragment : BaseFragment<FragmentClientBinding?>(),
override fun onItemClick(itemView: View, item: PageInfo, position: Int) { override fun onItemClick(itemView: View, item: PageInfo, position: Int) {
try { try {
if (!CommonUtils.checkUrl(HttpServerUtils.serverAddress)) { if (!CommonUtils.checkUrl(HttpServerUtils.serverAddress)) {
XToastUtils.error("请输入有效的服务地址") XToastUtils.error(getString(R.string.invalid_service_address))
serverConfig = null serverConfig = null
return return
} }
if (serverConfig == null && item.name != ResUtils.getString(R.string.api_clone)) { if (serverConfig == null && item.name != ResUtils.getString(R.string.api_clone)) {
XToastUtils.error("请先点击【测试接口】按钮") XToastUtils.error(getString(R.string.click_test_button_first))
return return
} }
if (serverConfig != null && ( if (serverConfig != null && (
@ -135,7 +135,7 @@ class ClientFragment : BaseFragment<FragmentClientBinding?>(),
|| (item.name == ResUtils.getString(R.string.api_battery_query) && !serverConfig!!.enableApiBatteryQuery) || (item.name == ResUtils.getString(R.string.api_battery_query) && !serverConfig!!.enableApiBatteryQuery)
) )
) { ) {
XToastUtils.error("服务端未开启此功能") XToastUtils.error(getString(R.string.disabled_on_the_server))
return return
} }
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")

View File

@ -117,7 +117,7 @@ class FrpcFragment : BaseFragment<FragmentFrpcsBinding?>(), FrpcPagingAdapter.On
//运行出错时间 //运行出错时间
LiveEventBus.get(EVENT_FRPC_RUNNING_ERROR, String::class.java).observe(this) { LiveEventBus.get(EVENT_FRPC_RUNNING_ERROR, String::class.java).observe(this) {
XToastUtils.error("Frpc运行失败") XToastUtils.error(getString(R.string.frpc_failed_to_run))
//FrpcUtils.checkAndStopService(requireContext()) //FrpcUtils.checkAndStopService(requireContext())
adapter.refresh() adapter.refresh()
} }
@ -190,7 +190,7 @@ class FrpcFragment : BaseFragment<FragmentFrpcsBinding?>(), FrpcPagingAdapter.On
try { try {
viewModel.delete(item) viewModel.delete(item)
LiveEventBus.get<Frpc>(EVENT_FRPC_DELETE_CONFIG).post(item) LiveEventBus.get<Frpc>(EVENT_FRPC_DELETE_CONFIG).post(item)
XToastUtils.success("删除成功") XToastUtils.success(getString(R.string.successfully_deleted))
} catch (e: Exception) { } catch (e: Exception) {
e.message?.let { XToastUtils.error(it) } e.message?.let { XToastUtils.error(it) }
} }

View File

@ -339,7 +339,7 @@ class RulesEditFragment : BaseFragment<FragmentRulesEditBinding?>(), View.OnClic
) )
binding!!.tvSenderName.text = sender.title binding!!.tvSenderName.text = sender.title
if (STATUS_OFF == sender.status) { if (STATUS_OFF == sender.status) {
XToastUtils.warning("【注意】该发送通道已经禁用,其关联的规则即便匹配上也不会发送!") XToastUtils.warning(getString(R.string.sender_disabled_tips))
} }
} catch (e: Exception) { } catch (e: Exception) {
XToastUtils.error(e.message.toString()) XToastUtils.error(e.message.toString())
@ -557,7 +557,7 @@ class RulesEditFragment : BaseFragment<FragmentRulesEditBinding?>(), View.OnClic
val testSim = "SIM" + (simSlot + 1) val testSim = "SIM" + (simSlot + 1)
val ruleSim: String = rule.simSlot val ruleSim: String = rule.simSlot
if (ruleSim != "ALL" && ruleSim != testSim) { if (ruleSim != "ALL" && ruleSim != testSim) {
throw java.lang.Exception("卡槽未匹配中规则") throw java.lang.Exception(getString(R.string.card_slot_does_not_match))
} }
//获取卡槽信息 //获取卡槽信息
@ -569,7 +569,7 @@ class RulesEditFragment : BaseFragment<FragmentRulesEditBinding?>(), View.OnClic
val msgInfo = MsgInfo(ruleType, etFrom.text.toString(), etContent.text.toString(), Date(), simInfo, simSlot) val msgInfo = MsgInfo(ruleType, etFrom.text.toString(), etContent.text.toString(), Date(), simInfo, simSlot)
if (!rule.checkMsg(msgInfo)) { if (!rule.checkMsg(msgInfo)) {
throw java.lang.Exception("未匹配中规则") throw java.lang.Exception(getString(R.string.unmatched_rule))
} }
AppDatabase.getInstance(requireContext()) AppDatabase.getInstance(requireContext())

View File

@ -144,7 +144,7 @@ class ServerFragment : BaseFragment<FragmentServerBinding?>(), View.OnClickListe
if (inetAddress != null) { if (inetAddress != null) {
val url = "http://${inetAddress!!.hostAddress}:5000" val url = "http://${inetAddress!!.hostAddress}:5000"
ClipboardUtils.copyText(url) ClipboardUtils.copyText(url)
XToastUtils.info("已复制到剪贴板:$url") XToastUtils.info(String.format(getString(R.string.copied_to_clipboard), url))
} }
} }
else -> {} else -> {}

View File

@ -99,15 +99,15 @@ class CallQueryFragment : BaseFragment<FragmentClientCallQueryBinding?>() {
holder.image(R.id.iv_call, R.drawable.ic_phone_out) holder.image(R.id.iv_call, R.drawable.ic_phone_out)
holder.image(R.id.iv_reply, R.drawable.ic_reply) holder.image(R.id.iv_reply, R.drawable.ic_reply)
holder.click(R.id.iv_copy) { holder.click(R.id.iv_copy) {
XToastUtils.info("已经复制到剪贴板!\n$from") XToastUtils.info(String.format(getString(R.string.copied_to_clipboard), from))
ClipboardUtils.copyText(from) ClipboardUtils.copyText(from)
} }
holder.click(R.id.iv_call) { holder.click(R.id.iv_call) {
XToastUtils.info("本地呼叫:" + model.number) XToastUtils.info(getString(R.string.local_call) + model.number)
PhoneUtils.dial(model.number) PhoneUtils.dial(model.number)
} }
holder.click(R.id.iv_reply) { holder.click(R.id.iv_reply) {
XToastUtils.info("远程发短信:" + model.number) XToastUtils.info(getString(R.string.remote_sms) + model.number)
LiveEventBus.get<Int>(EVENT_KEY_SIM_SLOT).post(model.simId) LiveEventBus.get<Int>(EVENT_KEY_SIM_SLOT).post(model.simId)
LiveEventBus.get<String>(EVENT_KEY_PHONE_NUMBERS).post(model.number) LiveEventBus.get<String>(EVENT_KEY_PHONE_NUMBERS).post(model.number)
PageOption.to(SmsSendFragment::class.java).setNewActivity(true).open((context as XPageActivity?)!!) PageOption.to(SmsSendFragment::class.java).setNewActivity(true).open((context as XPageActivity?)!!)
@ -146,9 +146,9 @@ class CallQueryFragment : BaseFragment<FragmentClientCallQueryBinding?>() {
binding!!.searchView.setSuggestions(resources.getStringArray(R.array.query_suggestions)) binding!!.searchView.setSuggestions(resources.getStringArray(R.array.query_suggestions))
binding!!.searchView.setOnQueryTextListener(object : MaterialSearchView.OnQueryTextListener { binding!!.searchView.setOnQueryTextListener(object : MaterialSearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String): Boolean { override fun onQueryTextSubmit(query: String): Boolean {
SnackbarUtils.Indefinite(view, "搜索关键字: $query").info() SnackbarUtils.Indefinite(view, String.format(getString(R.string.search_keyword), query)).info()
.actionColor(ResUtils.getColor(R.color.xui_config_color_white)) .actionColor(ResUtils.getColor(R.color.xui_config_color_white))
.setAction("清除") { .setAction(getString(R.string.clear)) {
keyword = "" keyword = ""
loadRemoteData(true) loadRemoteData(true)
}.show() }.show()

View File

@ -86,7 +86,7 @@ class CloneFragment : BaseFragment<FragmentClientCloneBinding?>(), View.OnClickL
} else { } else {
XToastUtils.error(R.string.toast_denied) XToastUtils.error(R.string.toast_denied)
} }
binding!!.tvBackupPath.text = "未授权储存权限,该功能无法使用!" binding!!.tvBackupPath.text = getString(R.string.storage_permission_tips)
} }
}) })
@ -169,13 +169,13 @@ class CloneFragment : BaseFragment<FragmentClientCloneBinding?>(), View.OnClickL
val cloneInfo = HttpServerUtils.exportSettings() val cloneInfo = HttpServerUtils.exportSettings()
val jsonStr = Gson().toJson(cloneInfo) val jsonStr = Gson().toJson(cloneInfo)
if (FileIOUtils.writeFileFromString(file, jsonStr)) { if (FileIOUtils.writeFileFromString(file, jsonStr)) {
XToastUtils.success("导出配置成功!") XToastUtils.success(getString(R.string.export_succeeded))
} else { } else {
binding!!.tvExport.text = "导出失败,请检查写入权限!" binding!!.tvExport.text = getString(R.string.export_failed)
XToastUtils.error("导出失败,请检查写入权限!") XToastUtils.error(getString(R.string.export_failed))
} }
} catch (e: Exception) { } catch (e: Exception) {
XToastUtils.error("导出失败:" + e.message) XToastUtils.error(String.format(getString(R.string.export_failed_tips), e.message))
} }
} }
//导入配置 //导入配置
@ -185,14 +185,14 @@ class CloneFragment : BaseFragment<FragmentClientCloneBinding?>(), View.OnClickL
val file = File(backupPath + File.separator + backupFile) val file = File(backupPath + File.separator + backupFile)
//判断文件是否存在 //判断文件是否存在
if (!FileUtils.isFileExists(file)) { if (!FileUtils.isFileExists(file)) {
XToastUtils.error("导入失败:本地备份文件不存在!") XToastUtils.error(getString(R.string.import_failed_file_not_exist))
return return
} }
val jsonStr = FileIOUtils.readFile2String(file) val jsonStr = FileIOUtils.readFile2String(file)
Log.d(TAG, "jsonStr = $jsonStr") Log.d(TAG, "jsonStr = $jsonStr")
if (TextUtils.isEmpty(jsonStr)) { if (TextUtils.isEmpty(jsonStr)) {
XToastUtils.error("导入失败:请检查是否有外部存储访问权限!") XToastUtils.error(getString(R.string.import_failed))
return return
} }
@ -207,12 +207,12 @@ class CloneFragment : BaseFragment<FragmentClientCloneBinding?>(), View.OnClickL
HttpServerUtils.compareVersion(cloneInfo) HttpServerUtils.compareVersion(cloneInfo)
if (HttpServerUtils.restoreSettings(cloneInfo)) { if (HttpServerUtils.restoreSettings(cloneInfo)) {
XToastUtils.success("导入配置成功!") XToastUtils.success(getString(R.string.import_succeeded))
} else { } else {
XToastUtils.error("导入失败") XToastUtils.error(getString(R.string.import_failed))
} }
} catch (e: Exception) { } catch (e: Exception) {
XToastUtils.error("导入失败:" + e.message) XToastUtils.error(String.format(getString(R.string.import_failed_tips), e.message))
} }
} }
} }
@ -329,7 +329,7 @@ class CloneFragment : BaseFragment<FragmentClientCloneBinding?>(), View.OnClickL
HttpServerUtils.compareVersion(cloneInfo) HttpServerUtils.compareVersion(cloneInfo)
if (HttpServerUtils.restoreSettings(cloneInfo)) { if (HttpServerUtils.restoreSettings(cloneInfo)) {
XToastUtils.success("导入配置成功!") XToastUtils.success(getString(R.string.import_succeeded))
} }
} else { } else {
XToastUtils.error(ResUtils.getString(R.string.request_failed) + resp.msg) XToastUtils.error(ResUtils.getString(R.string.request_failed) + resp.msg)

View File

@ -95,15 +95,15 @@ class ContactQueryFragment : BaseFragment<FragmentClientContactQueryBinding?>()
holder.image(R.id.iv_reply, R.drawable.ic_reply) holder.image(R.id.iv_reply, R.drawable.ic_reply)
holder.click(R.id.iv_copy) { holder.click(R.id.iv_copy) {
val str = model.toString() val str = model.toString()
XToastUtils.info("已经复制到剪贴板!\n$str") XToastUtils.info(String.format(getString(R.string.copied_to_clipboard), str))
ClipboardUtils.copyText(str) ClipboardUtils.copyText(str)
} }
holder.click(R.id.iv_call) { holder.click(R.id.iv_call) {
XToastUtils.info("本地呼叫:" + model.phoneNumber) XToastUtils.info(getString(R.string.local_call) + model.phoneNumber)
PhoneUtils.dial(model.phoneNumber) PhoneUtils.dial(model.phoneNumber)
} }
holder.click(R.id.iv_reply) { holder.click(R.id.iv_reply) {
XToastUtils.info("远程发短信:" + model.phoneNumber) XToastUtils.info(getString(R.string.remote_sms) + model.phoneNumber)
/*val params = Bundle() /*val params = Bundle()
params.putString(KEY_PHONE_NUMBERS, model.phoneNumber) params.putString(KEY_PHONE_NUMBERS, model.phoneNumber)
openPage(SmsSendFragment::class.java, params)*/ openPage(SmsSendFragment::class.java, params)*/
@ -133,9 +133,9 @@ class ContactQueryFragment : BaseFragment<FragmentClientContactQueryBinding?>()
binding!!.searchView.setSuggestions(resources.getStringArray(R.array.query_suggestions)) binding!!.searchView.setSuggestions(resources.getStringArray(R.array.query_suggestions))
binding!!.searchView.setOnQueryTextListener(object : MaterialSearchView.OnQueryTextListener { binding!!.searchView.setOnQueryTextListener(object : MaterialSearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String): Boolean { override fun onQueryTextSubmit(query: String): Boolean {
SnackbarUtils.Indefinite(view, "搜索关键字: $query").info() SnackbarUtils.Indefinite(view, String.format(getString(R.string.search_keyword), query)).info()
.actionColor(ResUtils.getColor(R.color.xui_config_color_white)) .actionColor(ResUtils.getColor(R.color.xui_config_color_white))
.setAction("清除") { .setAction(getString(R.string.clear)) {
keyword = "" keyword = ""
loadRemoteData() loadRemoteData()
}.show() }.show()

View File

@ -98,7 +98,7 @@ class SmsQueryFragment : BaseFragment<FragmentClientSmsQueryBinding?>() {
holder.text(R.id.tv_content, model.content) holder.text(R.id.tv_content, model.content)
holder.image(R.id.iv_reply, R.drawable.ic_reply) holder.image(R.id.iv_reply, R.drawable.ic_reply)
holder.click(R.id.iv_reply) { holder.click(R.id.iv_reply) {
XToastUtils.info("远程回短信:" + model.number) XToastUtils.info(getString(R.string.remote_sms) + model.number)
LiveEventBus.get<Int>(EVENT_KEY_SIM_SLOT).post(model.simId) LiveEventBus.get<Int>(EVENT_KEY_SIM_SLOT).post(model.simId)
LiveEventBus.get<String>(EVENT_KEY_PHONE_NUMBERS).post(model.number) LiveEventBus.get<String>(EVENT_KEY_PHONE_NUMBERS).post(model.number)
PageOption.to(SmsSendFragment::class.java).setNewActivity(true).open((context as XPageActivity?)!!) PageOption.to(SmsSendFragment::class.java).setNewActivity(true).open((context as XPageActivity?)!!)
@ -136,9 +136,9 @@ class SmsQueryFragment : BaseFragment<FragmentClientSmsQueryBinding?>() {
binding!!.searchView.setSuggestions(resources.getStringArray(R.array.query_suggestions)) binding!!.searchView.setSuggestions(resources.getStringArray(R.array.query_suggestions))
binding!!.searchView.setOnQueryTextListener(object : MaterialSearchView.OnQueryTextListener { binding!!.searchView.setOnQueryTextListener(object : MaterialSearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String): Boolean { override fun onQueryTextSubmit(query: String): Boolean {
SnackbarUtils.Indefinite(view, "搜索关键字: $query").info() SnackbarUtils.Indefinite(view, String.format(getString(R.string.search_keyword), query)).info()
.actionColor(ResUtils.getColor(R.color.xui_config_color_white)) .actionColor(ResUtils.getColor(R.color.xui_config_color_white))
.setAction("清除") { .setAction(getString(R.string.clear)) {
keyword = "" keyword = ""
loadRemoteData(true) loadRemoteData(true)
}.show() }.show()

View File

@ -1,7 +1,9 @@
package com.idormy.sms.forwarder.server.component package com.idormy.sms.forwarder.server.component
import android.util.Log import android.util.Log
import com.idormy.sms.forwarder.R
import com.idormy.sms.forwarder.utils.HttpServerUtils import com.idormy.sms.forwarder.utils.HttpServerUtils
import com.xuexiang.xui.utils.ResUtils.getString
import com.yanzhenjie.andserver.annotation.Interceptor import com.yanzhenjie.andserver.annotation.Interceptor
import com.yanzhenjie.andserver.error.HttpException import com.yanzhenjie.andserver.error.HttpException
import com.yanzhenjie.andserver.framework.HandlerInterceptor import com.yanzhenjie.andserver.framework.HandlerInterceptor
@ -39,7 +41,7 @@ class LoggerInterceptor : HandlerInterceptor {
|| (httpPath.startsWith("/contact/query") && !HttpServerUtils.enableApiContactQuery) || (httpPath.startsWith("/contact/query") && !HttpServerUtils.enableApiContactQuery)
|| (httpPath.startsWith("/battery/query") && !HttpServerUtils.enableApiBatteryQuery) || (httpPath.startsWith("/battery/query") && !HttpServerUtils.enableApiBatteryQuery)
) { ) {
throw HttpException(500, "服务端未开启此功能") throw HttpException(500, getString(R.string.disabled_on_the_server))
} }
/* /*

View File

@ -1,9 +1,11 @@
package com.idormy.sms.forwarder.server.controller package com.idormy.sms.forwarder.server.controller
import android.util.Log import android.util.Log
import com.idormy.sms.forwarder.R
import com.idormy.sms.forwarder.entity.CloneInfo import com.idormy.sms.forwarder.entity.CloneInfo
import com.idormy.sms.forwarder.server.model.BaseRequest import com.idormy.sms.forwarder.server.model.BaseRequest
import com.idormy.sms.forwarder.utils.HttpServerUtils import com.idormy.sms.forwarder.utils.HttpServerUtils
import com.xuexiang.xui.utils.ResUtils.getString
import com.yanzhenjie.andserver.annotation.* import com.yanzhenjie.andserver.annotation.*
@Suppress("PrivatePropertyName") @Suppress("PrivatePropertyName")
@ -36,7 +38,7 @@ class CloneController {
HttpServerUtils.compareVersion(cloneInfo) HttpServerUtils.compareVersion(cloneInfo)
return if (HttpServerUtils.restoreSettings(cloneInfo)) "success" else "还原失败" return if (HttpServerUtils.restoreSettings(cloneInfo)) "success" else getString(R.string.restore_failed)
} }
} }

View File

@ -12,6 +12,7 @@ import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.WorkManager import androidx.work.WorkManager
import androidx.work.workDataOf import androidx.work.workDataOf
import com.google.gson.Gson import com.google.gson.Gson
import com.idormy.sms.forwarder.R
import com.idormy.sms.forwarder.entity.MsgInfo import com.idormy.sms.forwarder.entity.MsgInfo
import com.idormy.sms.forwarder.utils.BatteryUtils import com.idormy.sms.forwarder.utils.BatteryUtils
import com.idormy.sms.forwarder.utils.SettingUtils import com.idormy.sms.forwarder.utils.SettingUtils
@ -66,19 +67,19 @@ class BatteryService : Service() {
val levelMin: Int = SettingUtils.batteryLevelMin val levelMin: Int = SettingUtils.batteryLevelMin
val levelMax: Int = SettingUtils.batteryLevelMax val levelMax: Int = SettingUtils.batteryLevelMax
if (SettingUtils.batteryLevelOnce && levelMin > 0 && levelPre > levelCur && levelCur <= levelMin) { //电量下降到下限 if (SettingUtils.batteryLevelOnce && levelMin > 0 && levelPre > levelCur && levelCur <= levelMin) { //电量下降到下限
msg = "【电量预警】已低于电量预警下限,请及时充电!$msg" msg = String.format(getString(R.string.below_level_min), msg)
sendMessage(context, msg) sendMessage(context, msg)
return return
} else if (SettingUtils.batteryLevelOnce && levelMax > 0 && levelPre < levelCur && levelCur >= levelMax) { //电量上升到上限 } else if (SettingUtils.batteryLevelOnce && levelMax > 0 && levelPre < levelCur && levelCur >= levelMax) { //电量上升到上限
msg = "【电量预警】已高于电量预警上限,请拔掉充电器!$msg" msg = String.format(getString(R.string.over_level_max), msg)
sendMessage(context, msg) sendMessage(context, msg)
return return
} else if (!SettingUtils.batteryLevelOnce && levelMin > 0 && levelPre > levelCur && levelCur == levelMin) { //电量下降到下限 } else if (!SettingUtils.batteryLevelOnce && levelMin > 0 && levelPre > levelCur && levelCur == levelMin) { //电量下降到下限
msg = "【电量预警】已到达电量预警下限,请及时充电!$msg" msg = String.format(getString(R.string.reach_level_min), msg)
sendMessage(context, msg) sendMessage(context, msg)
return return
} else if (!SettingUtils.batteryLevelOnce && levelMax > 0 && levelPre < levelCur && levelCur == levelMax) { //电量上升到上限 } else if (!SettingUtils.batteryLevelOnce && levelMax > 0 && levelPre < levelCur && levelCur == levelMax) { //电量上升到上限
msg = "【电量预警】已到达电量预警上限,请拔掉充电器!$msg" msg = String.format(getString(R.string.reach_level_max), msg)
sendMessage(context, msg) sendMessage(context, msg)
return return
} }
@ -91,7 +92,7 @@ class BatteryService : Service() {
if (status != oldStatus) { if (status != oldStatus) {
var msg: String = BatteryUtils.getBatteryInfo(intent).toString() var msg: String = BatteryUtils.getBatteryInfo(intent).toString()
SettingUtils.batteryStatus = status SettingUtils.batteryStatus = status
msg = "【充电状态】发生变化:" + BatteryUtils.getStatus(oldStatus) + "" + BatteryUtils.getStatus(status) + msg msg = getString(R.string.battery_status_changed) + BatteryUtils.getStatus(oldStatus) + "" + BatteryUtils.getStatus(status) + msg
sendMessage(context, msg) sendMessage(context, msg)
} }
} }
@ -102,7 +103,7 @@ class BatteryService : Service() {
private fun sendMessage(context: Context, msg: String) { private fun sendMessage(context: Context, msg: String) {
Log.i(TAG, msg) Log.i(TAG, msg)
try { try {
val msgInfo = MsgInfo("app", "88888888", msg, Date(), "电池状态监听", -1) val msgInfo = MsgInfo("app", "88888888", msg, Date(), getString(R.string.battery_status_monitor), -1)
val request = OneTimeWorkRequestBuilder<SendWorker>() val request = OneTimeWorkRequestBuilder<SendWorker>()
.setInputData( .setInputData(
workDataOf( workDataOf(

View File

@ -236,11 +236,16 @@ class CommonUtils private constructor() {
} }
//是否合法的url //是否合法的url
fun checkUrl(urls: String, emptyResult: Boolean): Boolean { fun checkUrl(urls: String?): Boolean {
return checkUrl(urls, false)
}
//是否合法的url
fun checkUrl(urls: String?, emptyResult: Boolean): Boolean {
if (TextUtils.isEmpty(urls)) return emptyResult if (TextUtils.isEmpty(urls)) return emptyResult
val regex = "(ht|f)tp(s?)\\:\\/\\/[\\da-zA-Z]([-.\\w]*[\\da-zA-Z])*(:(0-9)*)*(\\/?)([a-zA-Z\\d\\-\\.\\?\\,\\'\\/\\\\&%\\+\\$#_=]*)?" val regex = "(ht|f)tp(s?)\\:\\/\\/[\\da-zA-Z]([-.\\w]*[\\da-zA-Z])*(:(0-9)*)*(\\/?)([a-zA-Z\\d\\-\\.\\?\\,\\'\\/\\\\&%\\+\\$#_=]*)?"
val pat = Pattern.compile(regex) val pat = Pattern.compile(regex)
val mat = pat.matcher(urls.trim()) val mat = pat.matcher(urls?.trim() ?: "")
return mat.matches() return mat.matches()
} }

View File

@ -4,9 +4,11 @@ package com.idormy.sms.forwarder.utils
import android.text.TextUtils import android.text.TextUtils
import android.util.Base64 import android.util.Base64
import com.google.gson.Gson import com.google.gson.Gson
import com.idormy.sms.forwarder.R
import com.idormy.sms.forwarder.core.Core import com.idormy.sms.forwarder.core.Core
import com.idormy.sms.forwarder.entity.CloneInfo import com.idormy.sms.forwarder.entity.CloneInfo
import com.idormy.sms.forwarder.server.model.BaseRequest import com.idormy.sms.forwarder.server.model.BaseRequest
import com.xuexiang.xui.utils.ResUtils.getString
import com.xuexiang.xutil.app.AppUtils import com.xuexiang.xutil.app.AppUtils
import com.yanzhenjie.andserver.error.HttpException import com.yanzhenjie.andserver.error.HttpException
import java.net.URLEncoder import java.net.URLEncoder
@ -116,26 +118,26 @@ class HttpServerUtils private constructor() {
val signSecret = serverSignKey val signSecret = serverSignKey
if (TextUtils.isEmpty(signSecret)) return if (TextUtils.isEmpty(signSecret)) return
if (TextUtils.isEmpty(req.sign)) throw HttpException(500, "服务端启用签名密钥sign节点必传") if (TextUtils.isEmpty(req.sign)) throw HttpException(500, getString(R.string.sign_required))
if (req.timestamp == 0L) throw HttpException(500, "服务端启用签名密钥timestamp节点必传") if (req.timestamp == 0L) throw HttpException(500, getString(R.string.timestamp_required))
val timestamp = System.currentTimeMillis() val timestamp = System.currentTimeMillis()
val diffTime = kotlin.math.abs(timestamp - req.timestamp) val diffTime = kotlin.math.abs(timestamp - req.timestamp)
if (diffTime > 3600000L) { if (diffTime > 3600000L) {
throw HttpException(500, "timestamp校验失败与服务器时间($timestamp)误差不能超过1小时(diffTime=$diffTime)") throw HttpException(500, String.format(getString(R.string.timestamp_verify_failed), timestamp, diffTime))
} }
val sign = calcSign(req.timestamp.toString(), signSecret.toString()) val sign = calcSign(req.timestamp.toString(), signSecret.toString())
if (sign != req.sign) throw HttpException(500, "签名校验失败") if (sign != req.sign) throw HttpException(500, getString(R.string.sign_verify_failed))
} }
//判断版本是否一致 //判断版本是否一致
@Throws(HttpException::class) @Throws(HttpException::class)
fun compareVersion(cloneInfo: CloneInfo) { fun compareVersion(cloneInfo: CloneInfo) {
val versionCodeRequest = cloneInfo.versionCode val versionCodeRequest = cloneInfo.versionCode
if (versionCodeRequest == 0) throw HttpException(500, "version_code节点必传") if (versionCodeRequest == 0) throw HttpException(500, getString(R.string.version_code_required))
val versionCodeLocal = AppUtils.getAppVersionCode().toString().substring(1) val versionCodeLocal = AppUtils.getAppVersionCode().toString().substring(1)
if (!versionCodeRequest.toString().endsWith(versionCodeLocal)) throw HttpException(500, "客户端与服务端的App版本不一致") if (!versionCodeRequest.toString().endsWith(versionCodeLocal)) throw HttpException(500, getString(R.string.inconsistent_version))
} }
//导出设置 //导出设置

View File

@ -1,7 +1,9 @@
package com.idormy.sms.forwarder.utils package com.idormy.sms.forwarder.utils
import android.util.Log import android.util.Log
import com.idormy.sms.forwarder.R
import com.idormy.sms.forwarder.entity.MsgInfo import com.idormy.sms.forwarder.entity.MsgInfo
import com.xuexiang.xui.utils.ResUtils.getString
import java.util.* import java.util.*
@Suppress("unused") @Suppress("unused")
@ -45,7 +47,7 @@ object RuleLineUtils {
if (lineNum == 0) { if (lineNum == 0) {
//第一行不允许缩进 //第一行不允许缩进
if (line.startsWith(" ")) { if (line.startsWith(" ")) {
throw Exception("第一行不允许缩进") throw Exception(getString(R.string.no_indentation_allowed_on_the_first_line))
} }
} }

View File

@ -18,6 +18,7 @@ import com.xuexiang.xhttp2.cache.model.CacheMode
import com.xuexiang.xhttp2.callback.SimpleCallBack import com.xuexiang.xhttp2.callback.SimpleCallBack
import com.xuexiang.xhttp2.exception.ApiException import com.xuexiang.xhttp2.exception.ApiException
import com.xuexiang.xui.utils.ResUtils import com.xuexiang.xui.utils.ResUtils
import com.xuexiang.xui.utils.ResUtils.getString
@Suppress("PrivatePropertyName", "UNUSED_PARAMETER") @Suppress("PrivatePropertyName", "UNUSED_PARAMETER")
class WeworkAgentUtils private constructor() { class WeworkAgentUtils private constructor() {
@ -65,7 +66,7 @@ class WeworkAgentUtils private constructor() {
MMKVUtils.put("expires_in_" + setting.agentID, System.currentTimeMillis() + ((resp.expires_in ?: 7200) - 120) * 1000L) //提前2分钟过期 MMKVUtils.put("expires_in_" + setting.agentID, System.currentTimeMillis() + ((resp.expires_in ?: 7200) - 120) * 1000L) //提前2分钟过期
sendTextMsg(setting, msgInfo, rule, logId) sendTextMsg(setting, msgInfo, rule, logId)
} else { } else {
XToastUtils.error("请求失败:$response") XToastUtils.error(String.format(getString(R.string.request_failed_tips), response))
} }
} }

View File

@ -12,12 +12,12 @@
<TextView <TextView
android:id="@+id/tv_title" android:id="@+id/tv_title"
style="@style/TextStyle.Title" style="@style/TextStyle.Title"
tools:text="主标题" /> tools:text="@string/main_title" />
<TextView <TextView
android:id="@+id/tv_sub_title" android:id="@+id/tv_sub_title"
style="@style/TextStyle.Explain" style="@style/TextStyle.Explain"
android:layout_marginTop="4dp" android:layout_marginTop="4dp"
tools:text="副标题" /> tools:text="@string/subtitle" />
</LinearLayout> </LinearLayout>

View File

@ -67,7 +67,7 @@
android:paddingBottom="@dimen/config_margin_10dp" android:paddingBottom="@dimen/config_margin_10dp"
android:singleLine="false" android:singleLine="false"
android:textColor="@color/xui_config_color_primary_text" android:textColor="@color/xui_config_color_primary_text"
tools:text="获取更多资讯欢迎关注我的微信公众号【我的Android开源之旅】" /> tools:text="" />
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@ -26,14 +26,14 @@
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="服务端设置" android:text="@string/server_settings"
android:textStyle="bold" /> android:textStyle="bold" />
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="10dp" android:layout_marginStart="10dp"
android:text="按照主动控制·服务端的配置填写以下两项,签名密钥可选" android:text="@string/server_settings_tips2"
android:textSize="10sp" android:textSize="10sp"
tools:ignore="SmallSp" /> tools:ignore="SmallSp" />
@ -49,7 +49,7 @@
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="服务地址" android:text="@string/service_address"
android:textStyle="bold" /> android:textStyle="bold" />
<com.xuexiang.xui.widget.edittext.ClearEditText <com.xuexiang.xui.widget.edittext.ClearEditText
@ -58,7 +58,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="5dp" android:layout_marginStart="5dp"
android:layout_weight="1" android:layout_weight="1"
android:hint="例如http://127.0.0.1:5000" /> android:hint="@string/service_address_hint" />
</LinearLayout> </LinearLayout>
@ -72,7 +72,7 @@
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="签名密钥" android:text="@string/sign_key"
android:textStyle="bold" /> android:textStyle="bold" />
<com.xuexiang.xui.widget.edittext.ClearEditText <com.xuexiang.xui.widget.edittext.ClearEditText
@ -108,14 +108,14 @@
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="功能列表" android:text="@string/features_list"
android:textStyle="bold" /> android:textStyle="bold" />
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="10dp" android:layout_marginStart="10dp"
android:text="请先点击【测试接口】按钮,获取服务端已启用的功能列表" android:text="@string/click_test_button_first"
android:textSize="10sp" android:textSize="10sp"
tools:ignore="SmallSp" /> tools:ignore="SmallSp" />

View File

@ -12,7 +12,7 @@
android:id="@+id/search_view" android:id="@+id/search_view"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:hint="输入关键字模糊匹配手机号码" /> android:hint="@string/call_keyword_hint" />
<com.xuexiang.xui.widget.tabbar.EasyIndicator <com.xuexiang.xui.widget.tabbar.EasyIndicator
android:id="@+id/tabBar" android:id="@+id/tabBar"

View File

@ -12,7 +12,7 @@
android:id="@+id/search_view" android:id="@+id/search_view"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:hint="纯数字匹配号码/非数字匹配姓名" /> android:hint="@string/contact_keyword_hint" />
<com.scwang.smartrefresh.layout.SmartRefreshLayout <com.scwang.smartrefresh.layout.SmartRefreshLayout
android:id="@+id/refreshLayout" android:id="@+id/refreshLayout"

View File

@ -12,7 +12,7 @@
android:id="@+id/search_view" android:id="@+id/search_view"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:hint="输入关键字模糊匹配短信内容" /> android:hint="@string/sms_keyword_hint" />
<com.xuexiang.xui.widget.tabbar.EasyIndicator <com.xuexiang.xui.widget.tabbar.EasyIndicator
android:id="@+id/tabBar" android:id="@+id/tabBar"

View File

@ -105,7 +105,7 @@
app:met_maxCharacters="390" app:met_maxCharacters="390"
app:met_regexp="@string/msg_content_regex" app:met_regexp="@string/msg_content_regex"
app:met_validateOnFocusLost="true" app:met_validateOnFocusLost="true"
app:mlet_hintText="请输入短信内容70个字以内算一条超过每增加64字加一条" /> app:mlet_hintText="@string/msg_content_hint" />
</LinearLayout> </LinearLayout>

View File

@ -55,6 +55,7 @@
<string name="lab_yes">Yes</string> <string name="lab_yes">Yes</string>
<string name="lab_no">No</string> <string name="lab_no">No</string>
<string name="lab_open_qq_app">Allow pages to open QQ apps?</string>
<string name="lab_open_third_app">Allow pages to open third-party apps?</string> <string name="lab_open_third_app">Allow pages to open third-party apps?</string>
<!-- 隐私政策弹窗 --> <!-- 隐私政策弹窗 -->
@ -703,6 +704,7 @@
<string name="other_login_methods">Other login methods</string> <string name="other_login_methods">Other login methods</string>
<string name="request_succeeded">Request succeeded</string> <string name="request_succeeded">Request succeeded</string>
<string name="request_failed">Request failed: </string> <string name="request_failed">Request failed: </string>
<string name="request_failed_tips">Request failed: %s</string>
<string name="no_sms_sending_permission">No SMS sending permission</string> <string name="no_sms_sending_permission">No SMS sending permission</string>
<string name="frpclib_download_title">Missing FrpcLib v%s</string> <string name="frpclib_download_title">Missing FrpcLib v%s</string>
<string name="frpclib_download_content">Downloading, please wait…</string> <string name="frpclib_download_content">Downloading, please wait…</string>
@ -802,5 +804,52 @@
<string name="battery_status">Status: %s</string> <string name="battery_status">Status: %s</string>
<string name="battery_health">Health: %s</string> <string name="battery_health">Health: %s</string>
<string name="battery_plugged">Plugged: %s</string> <string name="battery_plugged">Plugged: %s</string>
<string name="server_test">Server Test</string> <string name="server_test">Server Test</string>
<string name="invalid_service_address">Invalid service address</string>
<string name="click_test_button_first">Click the test button first, to get the list of features enabled by the server</string>
<string name="disabled_on_the_server">Disable this feature on the server</string>
<string name="frpc_failed_to_run">Frpc failed to run</string>
<string name="successfully_deleted">Successfully deleted</string>
<string name="sender_disabled_tips">[Note] The sending channel has been disabled, and its associated rules will not be sent even if they match!</string>
<string name="local_call">Local Call:</string>
<string name="remote_sms">Remote SMS</string>
<string name="clear">Clear</string>
<string name="storage_permission_tips">Unauthorized storage permission, this function cannot be used!</string>
<string name="contact_info">Name%s\nPhone%s</string>
<string name="card_slot_does_not_match">Card slot does not match the rule</string>
<string name="unmatched_rule">Unmatched rule</string>
<string name="copied_to_clipboard">Copied to clipboard:\n%s</string>
<string name="search_keyword">Search Keyword: %s</string>
<string name="export_succeeded">Export configuration succeeded!</string>
<string name="export_failed">Export failed, please check write permission!</string>
<string name="export_failed_tips">Export failed: %s</string>
<string name="import_failed">Import failed: Please check for external storage access!</string>
<string name="import_failed_file_not_exist">Import failed: local backup file does not exist</string>
<string name="import_succeeded">Import configuration successful!</string>
<string name="import_failed_tips">Import failed: %s</string>
<string name="restore_failed">Restore failed</string>
<string name="battery_status_monitor">Battery Status Monitor</string>
<string name="below_level_min">[Battery Warning] The battery has been lower than the lower limit of the battery warning, please charge it in time!%s</string>
<string name="over_level_max">[Battery Warning] The battery warning limit has been exceeded, please unplug the charger!%s</string>
<string name="reach_level_min">[Battery Warning] The lower limit of the battery warning has been reached, please charge it in time!%s</string>
<string name="reach_level_max">[Battery Warning] The upper limit of battery warning has been reached, please unplug the charger!%s</string>
<string name="battery_status_changed">【充电状态】发生变化:</string>
<string name="no_indentation_allowed_on_the_first_line">第一行不允许缩进</string>
<string name="sign_required">The server enables the signing key, and the sign node required</string>
<string name="timestamp_required">The server enables the signing key, and the timestamp node required</string>
<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">The timestamp verification failed, and the difference with the server time (%s) cannot exceed 1 hour (diffTime=%s)</string>
<string name="main_title">Main title</string>
<string name="subtitle">Subtitle</string>
<string name="sms_keyword_hint">Input keywords to fuzzy match SMS content</string>
<string name="contact_keyword_hint">Pure numbers match numbers / non-numbers match names</string>
<string name="call_keyword_hint">Input keyword to fuzzy match mobile phone number</string>
<string name="server_settings_tips2">Fill two items according to the config of server, the signature key is optional</string>
<string name="service_address">Service Address</string>
<string name="service_address_hint">E.g: http://127.0.0.1:5000</string>
<string name="features_list">Features List</string>
</resources> </resources>

View File

@ -55,6 +55,7 @@
<string name="lab_yes"></string> <string name="lab_yes"></string>
<string name="lab_no"></string> <string name="lab_no"></string>
<string name="lab_open_qq_app">是否允许页面打开"QQ"?</string>
<string name="lab_open_third_app">是否允许页面打开第三方应用?</string> <string name="lab_open_third_app">是否允许页面打开第三方应用?</string>
<!-- 隐私政策弹窗 --> <!-- 隐私政策弹窗 -->
@ -704,6 +705,7 @@
<string name="other_login_methods">其他登录方式</string> <string name="other_login_methods">其他登录方式</string>
<string name="request_succeeded">请求成功</string> <string name="request_succeeded">请求成功</string>
<string name="request_failed">请求失败:</string> <string name="request_failed">请求失败:</string>
<string name="request_failed_tips">请求失败:%s</string>
<string name="no_sms_sending_permission">没有短信发送权限</string> <string name="no_sms_sending_permission">没有短信发送权限</string>
<string name="frpclib_download_title">缺少 FrpcLib v%s 动态库</string> <string name="frpclib_download_title">缺少 FrpcLib v%s 动态库</string>
<string name="frpclib_download_content">正在下载中,请稍后……</string> <string name="frpclib_download_content">正在下载中,请稍后……</string>
@ -803,5 +805,52 @@
<string name="battery_status">电池状态:%s</string> <string name="battery_status">电池状态:%s</string>
<string name="battery_health">健康度:%s</string> <string name="battery_health">健康度:%s</string>
<string name="battery_plugged">充电器:%s</string> <string name="battery_plugged">充电器:%s</string>
<string name="server_test">测试接口</string> <string name="server_test">测试接口</string>
<string name="invalid_service_address">无效的服务地址</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>
<string name="sender_disabled_tips">【注意】该发送通道已经禁用,其关联的规则即便匹配上也不会发送!</string>
<string name="local_call">本地呼叫:</string>
<string name="remote_sms">远程发短信:</string>
<string name="clear">清除</string>
<string name="storage_permission_tips">未授权储存权限,该功能无法使用!</string>
<string name="contact_info">姓名:%s\n号码%s</string>
<string name="card_slot_does_not_match">卡槽未匹配中规则</string>
<string name="unmatched_rule">未匹配中规则</string>
<string name="copied_to_clipboard">已复制到剪贴板:\n%s</string>
<string name="search_keyword">搜索关键字: %s</string>
<string name="export_succeeded">导出配置成功!</string>
<string name="export_failed">导出失败,请检查写入权限!</string>
<string name="export_failed_tips">导出失败: %s</string>
<string name="import_failed">导入失败:请检查是否有外部存储访问权限!</string>
<string name="import_failed_file_not_exist">导入失败:本地备份文件不存在!</string>
<string name="import_succeeded">导入配置成功!</string>
<string name="import_failed_tips">导入失败: %s</string>
<string name="restore_failed">还原失败</string>
<string name="battery_status_monitor">电池状态监听</string>
<string name="below_level_min">【电量预警】已低于电量预警下限,请及时充电!%s</string>
<string name="over_level_max">【电量预警】已高于电量预警上限,请拔掉充电器!%s</string>
<string name="reach_level_min">【电量预警】已低于电量预警下限,请及时充电!%s</string>
<string name="reach_level_max">【电量预警】已高于电量预警上限,请拔掉充电器!%s</string>
<string name="battery_status_changed">【充电状态】发生变化:</string>
<string name="no_indentation_allowed_on_the_first_line">第一行不允许缩进</string>
<string name="sign_required">服务端启用签名密钥sign节点必传</string>
<string name="timestamp_required">服务端启用签名密钥timestamp节点必传</string>
<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">timestamp校验失败与服务器时间(%s)误差不能超过1小时(diffTime=%s)</string>
<string name="main_title">主标题</string>
<string name="subtitle">副标题</string>
<string name="sms_keyword_hint">输入关键字模糊匹配短信内容</string>
<string name="contact_keyword_hint">纯数字匹配号码/非数字匹配姓名</string>
<string name="call_keyword_hint">输入关键字模糊匹配手机号码</string>
<string name="server_settings_tips2">按照主动控制·服务端的配置填写以下两项,签名密钥可选</string>
<string name="service_address">服务地址</string>
<string name="service_address_hint">例如http://127.0.0.1:5000</string>
<string name="features_list">功能列表</string>
</resources> </resources>