mirror of
https://github.com/pppscn/SmsForwarder
synced 2024-11-04 06:00:11 +00:00
新增:主动控制
增加远程WOL
功能(用于远程唤醒同一个局域网其他设备) #190
This commit is contained in:
parent
2ca88ae495
commit
c53c3de118
1
.gitignore
vendored
1
.gitignore
vendored
@ -34,3 +34,4 @@
|
||||
/app/mapping.txt
|
||||
/app/seeds.txt
|
||||
/app/unused.txt
|
||||
/pic/*.bkp
|
||||
|
@ -119,6 +119,11 @@ class ServerFragment : BaseFragment<FragmentServerBinding?>(), View.OnClickListe
|
||||
HttpServerUtils.enableApiBatteryQuery = isChecked
|
||||
}
|
||||
|
||||
binding!!.sbApiWol.isChecked = HttpServerUtils.enableApiWol
|
||||
binding!!.sbApiWol.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
|
||||
HttpServerUtils.enableApiWol = isChecked
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SingleClick
|
||||
|
@ -0,0 +1,181 @@
|
||||
package com.idormy.sms.forwarder.fragment.client
|
||||
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.idormy.sms.forwarder.core.BaseFragment
|
||||
import com.idormy.sms.forwarder.databinding.FragmentClientWolSendBinding
|
||||
import com.idormy.sms.forwarder.server.model.BaseResponse
|
||||
import com.idormy.sms.forwarder.utils.HttpServerUtils
|
||||
import com.idormy.sms.forwarder.utils.SettingUtils
|
||||
import com.idormy.sms.forwarder.utils.XToastUtils
|
||||
import com.xuexiang.xaop.annotation.SingleClick
|
||||
import com.xuexiang.xhttp2.XHttp
|
||||
import com.xuexiang.xhttp2.cache.model.CacheMode
|
||||
import com.xuexiang.xhttp2.callback.SimpleCallBack
|
||||
import com.xuexiang.xhttp2.exception.ApiException
|
||||
import com.xuexiang.xpage.annotation.Page
|
||||
import com.xuexiang.xrouter.utils.TextUtils
|
||||
import com.xuexiang.xui.utils.CountDownButtonHelper
|
||||
import com.xuexiang.xui.utils.ResUtils
|
||||
import com.xuexiang.xui.widget.actionbar.TitleBar
|
||||
import com.xuexiang.xui.widget.dialog.materialdialog.DialogAction
|
||||
import com.xuexiang.xui.widget.dialog.materialdialog.MaterialDialog
|
||||
|
||||
@Suppress("PropertyName")
|
||||
@Page(name = "远程WOL")
|
||||
class WolSendFragment : BaseFragment<FragmentClientWolSendBinding?>(), View.OnClickListener {
|
||||
|
||||
val TAG: String = WolSendFragment::class.java.simpleName
|
||||
private var mCountDownHelper: CountDownButtonHelper? = null
|
||||
private var wolHistory: MutableMap<String, String> = mutableMapOf()
|
||||
|
||||
override fun viewBindingInflate(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup,
|
||||
): FragmentClientWolSendBinding {
|
||||
return FragmentClientWolSendBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
override fun initTitle(): TitleBar? {
|
||||
return super.initTitle()!!.setImmersive(false).setTitle(R.string.api_wol)
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化控件
|
||||
*/
|
||||
override fun initViews() {
|
||||
//发送按钮增加倒计时,避免重复点击
|
||||
mCountDownHelper = CountDownButtonHelper(binding!!.btnSubmit, SettingUtils.requestTimeout)
|
||||
mCountDownHelper!!.setOnCountDownListener(object : CountDownButtonHelper.OnCountDownListener {
|
||||
override fun onCountDown(time: Int) {
|
||||
binding!!.btnSubmit.text = String.format(getString(R.string.seconds_n), time)
|
||||
}
|
||||
|
||||
override fun onFinished() {
|
||||
binding!!.btnSubmit.text = getString(R.string.send)
|
||||
}
|
||||
})
|
||||
|
||||
//取出历史记录
|
||||
val history = HttpServerUtils.wolHistory
|
||||
if (!TextUtils.isEmpty(history)) {
|
||||
wolHistory = Gson().fromJson(history, object : TypeToken<MutableMap<String, String>>() {}.type)
|
||||
}
|
||||
}
|
||||
|
||||
override fun initListeners() {
|
||||
binding!!.btnServerHistory.setOnClickListener(this)
|
||||
binding!!.btnSubmit.setOnClickListener(this)
|
||||
}
|
||||
|
||||
@SingleClick
|
||||
override fun onClick(v: View) {
|
||||
when (v.id) {
|
||||
R.id.btn_server_history -> {
|
||||
if (wolHistory.isEmpty()) {
|
||||
XToastUtils.warning(getString(R.string.no_server_history))
|
||||
return
|
||||
}
|
||||
Log.d(TAG, "wolHistory = $wolHistory")
|
||||
|
||||
MaterialDialog.Builder(context!!)
|
||||
.title(R.string.server_history)
|
||||
.items(wolHistory.keys)
|
||||
.itemsCallbackSingleChoice(0) { _: MaterialDialog?, _: View?, _: Int, text: CharSequence ->
|
||||
//XToastUtils.info("$which: $text")
|
||||
binding!!.etIp.setText(text)
|
||||
binding!!.etMac.setText(wolHistory[text])
|
||||
true // allow selection
|
||||
}
|
||||
.positiveText(R.string.select)
|
||||
.negativeText(R.string.cancel)
|
||||
.neutralText(R.string.clear_history)
|
||||
.neutralColor(ResUtils.getColors(R.color.red))
|
||||
.onNeutral { _: MaterialDialog?, _: DialogAction? ->
|
||||
wolHistory.clear()
|
||||
HttpServerUtils.wolHistory = ""
|
||||
}
|
||||
.show()
|
||||
}
|
||||
R.id.btn_submit -> {
|
||||
val requestUrl: String = HttpServerUtils.serverAddress + "/wol/send"
|
||||
Log.i(TAG, "requestUrl:$requestUrl")
|
||||
|
||||
val msgMap: MutableMap<String, Any> = mutableMapOf()
|
||||
val timestamp = System.currentTimeMillis()
|
||||
msgMap["timestamp"] = timestamp
|
||||
val clientSignKey = HttpServerUtils.clientSignKey
|
||||
if (!TextUtils.isEmpty(clientSignKey)) {
|
||||
msgMap["sign"] = HttpServerUtils.calcSign(timestamp.toString(), clientSignKey.toString())
|
||||
}
|
||||
|
||||
val ip = binding!!.etIp.text.toString()
|
||||
val ipRegex = getString(R.string.ip_regex).toRegex()
|
||||
if (!ipRegex.matches(ip)) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.ip_error))
|
||||
return
|
||||
}
|
||||
|
||||
val mac = binding!!.etMac.text.toString()
|
||||
val macRegex = getString(R.string.mac_regex).toRegex()
|
||||
if (!macRegex.matches(mac)) {
|
||||
XToastUtils.error(ResUtils.getString(R.string.mac_error))
|
||||
return
|
||||
}
|
||||
|
||||
val dataMap: MutableMap<String, Any> = mutableMapOf()
|
||||
dataMap["ip"] = ip
|
||||
dataMap["mac"] = mac
|
||||
msgMap["data"] = dataMap
|
||||
|
||||
val requestMsg: String = Gson().toJson(msgMap)
|
||||
Log.i(TAG, "requestMsg:$requestMsg")
|
||||
|
||||
mCountDownHelper?.start()
|
||||
XHttp.post(requestUrl)
|
||||
.upJson(requestMsg)
|
||||
.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)
|
||||
}
|
||||
|
||||
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[ip] = mac
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
if (mCountDownHelper != null) mCountDownHelper!!.recycle()
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
package com.idormy.sms.forwarder.server.controller
|
||||
|
||||
import android.os.StrictMode
|
||||
import android.os.StrictMode.ThreadPolicy
|
||||
import android.util.Log
|
||||
import com.idormy.sms.forwarder.server.model.BaseRequest
|
||||
import com.idormy.sms.forwarder.server.model.WolData
|
||||
import com.yanzhenjie.andserver.annotation.*
|
||||
import java.net.DatagramPacket
|
||||
import java.net.DatagramSocket
|
||||
import java.net.InetAddress
|
||||
|
||||
@Suppress("PrivatePropertyName")
|
||||
@RestController
|
||||
@RequestMapping(path = ["/wol"])
|
||||
class WolController {
|
||||
|
||||
private val TAG: String = WolController::class.java.simpleName
|
||||
|
||||
//远程WOL
|
||||
@CrossOrigin(methods = [RequestMethod.POST])
|
||||
@PostMapping("/send")
|
||||
fun send(@RequestBody bean: BaseRequest<WolData>): String {
|
||||
val wolData = bean.data
|
||||
Log.d(TAG, wolData.toString())
|
||||
|
||||
val policy = ThreadPolicy.Builder().permitAll().build()
|
||||
StrictMode.setThreadPolicy(policy)
|
||||
DatagramSocket().use { socket ->
|
||||
try {
|
||||
val macBytes = getMacBytes(wolData.mac)
|
||||
val bytes = ByteArray(6 + 16 * macBytes.size)
|
||||
for (i in 0..5) {
|
||||
bytes[i] = 0xff.toByte()
|
||||
}
|
||||
var i = 6
|
||||
while (i < bytes.size) {
|
||||
System.arraycopy(macBytes, 0, bytes, i, macBytes.size)
|
||||
i += macBytes.size
|
||||
}
|
||||
val address: InetAddress = InetAddress.getByName(wolData.ip)
|
||||
val packet = DatagramPacket(bytes, bytes.size, address, 9)
|
||||
socket.send(packet)
|
||||
Log.d(TAG, "Wake-on-LAN packet sent.")
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Failed to send Wake-on-LAN packet: $e")
|
||||
}
|
||||
}
|
||||
|
||||
return "success"
|
||||
}
|
||||
|
||||
@Throws(IllegalArgumentException::class)
|
||||
private fun getMacBytes(macStr: String): ByteArray {
|
||||
val bytes = ByteArray(6)
|
||||
val hex = macStr.replace("-", ":").split(":").toTypedArray()
|
||||
require(hex.size == 6) { "Invalid MAC address." }
|
||||
try {
|
||||
for (i in 0..5) {
|
||||
bytes[i] = hex[i].toInt(16).toByte()
|
||||
}
|
||||
} catch (e: NumberFormatException) {
|
||||
throw IllegalArgumentException("Invalid hex digit in MAC address. $e")
|
||||
}
|
||||
return bytes
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package com.idormy.sms.forwarder.server.model
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import java.io.Serializable
|
||||
|
||||
data class WolData(
|
||||
@SerializedName("ip")
|
||||
var ip: String,
|
||||
@SerializedName("mac")
|
||||
var mac: String,
|
||||
) : Serializable
|
@ -240,6 +240,8 @@ const val SP_ENABLE_API_SMS_QUERY = "enable_api_sms_query"
|
||||
const val SP_ENABLE_API_CALL_QUERY = "enable_api_call_query"
|
||||
const val SP_ENABLE_API_CONTACT_QUERY = "enable_api_contact_query"
|
||||
const val SP_ENABLE_API_BATTERY_QUERY = "enable_api_battery_query"
|
||||
const val SP_ENABLE_API_WOL = "enable_api_wol"
|
||||
const val SP_WOL_HISTORY = "wol_history"
|
||||
const val SP_SERVER_ADDRESS = "server_address"
|
||||
const val SP_SERVER_HISTORY = "server_history"
|
||||
const val SP_CLIENT_SIGN_KEY = "client_sign_key"
|
||||
@ -250,4 +252,5 @@ var CLIENT_FRAGMENT_LIST = listOf(
|
||||
PageInfo(getString(R.string.api_call_query), "com.idormy.sms.forwarder.fragment.client.CallQueryFragment", "{\"\":\"\"}", CoreAnim.slide, R.drawable.icon_api_call_query),
|
||||
PageInfo(getString(R.string.api_contact_query), "com.idormy.sms.forwarder.fragment.client.ContactQueryFragment", "{\"\":\"\"}", CoreAnim.slide, R.drawable.icon_api_contact_query),
|
||||
PageInfo(getString(R.string.api_battery_query), "com.idormy.sms.forwarder.fragment.client.BatteryQueryFragment", "{\"\":\"\"}", CoreAnim.slide, R.drawable.icon_api_battery_query),
|
||||
PageInfo(getString(R.string.api_wol), "com.idormy.sms.forwarder.fragment.client.WolSendFragment", "{\"\":\"\"}", CoreAnim.slide, R.drawable.icon_api_wol),
|
||||
)
|
@ -112,6 +112,22 @@ class HttpServerUtils private constructor() {
|
||||
MMKVUtils.put(SP_ENABLE_API_BATTERY_QUERY, enableApiQueryBattery)
|
||||
}
|
||||
|
||||
//是否启用远程WOL
|
||||
@JvmStatic
|
||||
var enableApiWol: Boolean
|
||||
get() = MMKVUtils.getBoolean(SP_ENABLE_API_WOL, false)
|
||||
set(enableApiWol) {
|
||||
MMKVUtils.put(SP_ENABLE_API_WOL, enableApiWol)
|
||||
}
|
||||
|
||||
//WOL历史记录
|
||||
@JvmStatic
|
||||
var wolHistory: String?
|
||||
get() = MMKVUtils.getString(SP_WOL_HISTORY, "")
|
||||
set(wolHistory) {
|
||||
MMKVUtils.put(SP_WOL_HISTORY, wolHistory)
|
||||
}
|
||||
|
||||
//计算签名
|
||||
fun calcSign(timestamp: String, signSecret: String): String {
|
||||
val stringToSign = "$timestamp\n" + signSecret
|
||||
|
BIN
app/src/main/res/drawable/icon_api_wol.webp
Normal file
BIN
app/src/main/res/drawable/icon_api_wol.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.9 KiB |
BIN
app/src/main/res/drawable/icon_pushdeer.webp
Normal file
BIN
app/src/main/res/drawable/icon_pushdeer.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.0 KiB |
108
app/src/main/res/layout/fragment_client_wol_send.xml
Normal file
108
app/src/main/res/layout/fragment_client_wol_send.xml
Normal file
@ -0,0 +1,108 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?attr/xui_config_color_background"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:overScrollMode="never">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="5dp"
|
||||
android:gravity="center_horizontal"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:layout_width="250dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="10dp"
|
||||
android:contentDescription="@string/api_wol"
|
||||
app:srcCompat="@drawable/icon_api_wol" />
|
||||
|
||||
<LinearLayout
|
||||
style="@style/senderBarStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/ip" />
|
||||
|
||||
<com.xuexiang.xui.widget.edittext.materialedittext.MaterialEditText
|
||||
android:id="@+id/et_ip"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/ip_hint"
|
||||
android:singleLine="true"
|
||||
app:met_clearButton="true"
|
||||
app:met_errorMessage="@string/ip_error"
|
||||
app:met_regexp="@string/ip_regex"
|
||||
app:met_validateOnFocusLost="true" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
style="@style/senderBarStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/mac" />
|
||||
|
||||
<com.xuexiang.xui.widget.edittext.materialedittext.MaterialEditText
|
||||
android:id="@+id/et_mac"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/mac_hint"
|
||||
android:singleLine="true"
|
||||
app:met_clearButton="true"
|
||||
app:met_errorMessage="@string/mac_error"
|
||||
app:met_regexp="@string/mac_regex"
|
||||
app:met_validateOnFocusLost="true" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal"
|
||||
android:padding="10dp">
|
||||
|
||||
<com.xuexiang.xui.widget.textview.supertextview.SuperButton
|
||||
android:id="@+id/btn_server_history"
|
||||
style="@style/SuperButton.Gray.Icon"
|
||||
android:drawableStart="@drawable/ic_restore"
|
||||
android:paddingStart="7dp"
|
||||
android:text="@string/server_history"
|
||||
tools:ignore="RtlSymmetry" />
|
||||
|
||||
<com.xuexiang.xui.widget.textview.supertextview.SuperButton
|
||||
android:id="@+id/btn_submit"
|
||||
style="@style/SuperButton.Blue.Icon"
|
||||
android:layout_marginStart="20dp"
|
||||
android:drawableStart="@drawable/ic_send_white"
|
||||
android:paddingStart="20dp"
|
||||
android:text="@string/send"
|
||||
tools:ignore="RtlSymmetry" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
@ -415,6 +415,41 @@
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
style="@style/settingBarStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/api_wol"
|
||||
android:textStyle="bold"
|
||||
tools:ignore="RelativeOverlap" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/api_wol_tips"
|
||||
android:textSize="9sp"
|
||||
tools:ignore="SmallSp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<com.xuexiang.xui.widget.button.switchbutton.SwitchButton
|
||||
android:id="@+id/sb_api_wol"
|
||||
style="@style/SwitchButtonStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
@ -781,6 +781,8 @@
|
||||
<string name="api_contact_query_tips">Remotely check contact list</string>
|
||||
<string name="api_battery_query">Query Battery</string>
|
||||
<string name="api_battery_query_tips">Remotely query mobile phone power and battery status</string>
|
||||
<string name="api_wol">Remotely WOL</string>
|
||||
<string name="api_wol_tips">Turn on your Wake-On-LAN enabled devices remotely</string>
|
||||
|
||||
<string name="sim_slot">Sim Slot</string>
|
||||
<string name="phone_numbers">Phone Numbers</string>
|
||||
@ -844,8 +846,8 @@
|
||||
<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="battery_status_changed">[Charging status] changes:</string>
|
||||
<string name="no_indentation_allowed_on_the_first_line">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>
|
||||
@ -881,4 +883,13 @@
|
||||
<string name="appsecret">AppSecret</string>
|
||||
<string name="sampleText">Sample Text</string>
|
||||
<string name="sampleMarkdown">Sample Markdown</string>
|
||||
<string name="ip_hint">Please enter an IP address, eg. 192.168.168.168</string>
|
||||
<string name="ip_error">Malformed IP address, eg. 192.168.168.168</string>
|
||||
<string name="ip_regex">^((\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\.){3}(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])$</string>
|
||||
<string name="mac_hint">Please enter the network card mac, eg. AA:BB:CC:DD:EE:FF</string>
|
||||
<string name="mac_error">The network card mac format is incorrect, eg. AA:BB:CC:DD:EE:FF</string>
|
||||
<string name="mac_regex">^((([a-fA-F0-9]{2}:){5})|(([a-fA-F0-9]{2}-){5}))[a-fA-F0-9]{2}$</string>
|
||||
<string name="ip">IP</string>
|
||||
<string name="mac">MAC</string>
|
||||
<string name="no_wol_history">There is no history record, WOL will be added automatically after successful sending</string>
|
||||
</resources>
|
||||
|
@ -782,6 +782,8 @@
|
||||
<string name="api_contact_query_tips">远程查联系人列表</string>
|
||||
<string name="api_battery_query">远程查电量</string>
|
||||
<string name="api_battery_query_tips">远程查询手机电量与电池状态</string>
|
||||
<string name="api_wol">远程WOL</string>
|
||||
<string name="api_wol_tips">远程打开启用LAN唤醒功能(Wake-On-LAN)的设备</string>
|
||||
|
||||
<string name="sim_slot">发送卡槽</string>
|
||||
<string name="phone_numbers">手机号码</string>
|
||||
@ -882,4 +884,13 @@
|
||||
<string name="appsecret">AppSecret</string>
|
||||
<string name="sampleText">文本类型</string>
|
||||
<string name="sampleMarkdown">Markdown类型</string>
|
||||
<string name="ip_hint">请输入IP地址,例如:192.168.168.168</string>
|
||||
<string name="ip_error">IP地址格式错误,例如:192.168.168.168</string>
|
||||
<string name="ip_regex">^((\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\.){3}(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])$</string>
|
||||
<string name="mac_hint">请输入网卡mac,例如:AA:BB:CC:DD:EE:FF</string>
|
||||
<string name="mac_error">网卡mac格式错误,例如:AA:BB:CC:DD:EE:FF</string>
|
||||
<string name="mac_regex">^((([a-fA-F0-9]{2}:){5})|(([a-fA-F0-9]{2}-){5}))[a-fA-F0-9]{2}$</string>
|
||||
<string name="ip">IP地址</string>
|
||||
<string name="mac">网卡MAC</string>
|
||||
<string name="no_wol_history">暂无历史记录,WOL发送成功后自动加入</string>
|
||||
</resources>
|
||||
|
Loading…
Reference in New Issue
Block a user