From c53c3de1187369b7795e5a38b56147c3cef339ba Mon Sep 17 00:00:00 2001 From: pppscn <35696959@qq.com> Date: Thu, 28 Jul 2022 16:04:22 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=EF=BC=9A`=E4=B8=BB=E5=8A=A8?= =?UTF-8?q?=E6=8E=A7=E5=88=B6`=E5=A2=9E=E5=8A=A0`=E8=BF=9C=E7=A8=8BWOL`?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=EF=BC=88=E7=94=A8=E4=BA=8E=E8=BF=9C=E7=A8=8B?= =?UTF-8?q?=E5=94=A4=E9=86=92=E5=90=8C=E4=B8=80=E4=B8=AA=E5=B1=80=E5=9F=9F?= =?UTF-8?q?=E7=BD=91=E5=85=B6=E4=BB=96=E8=AE=BE=E5=A4=87=EF=BC=89=20#190?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + .../sms/forwarder/fragment/ServerFragment.kt | 5 + .../fragment/client/WolSendFragment.kt | 181 ++++++++++++++++++ .../server/controller/WolController.kt | 67 +++++++ .../sms/forwarder/server/model/WolData.kt | 11 ++ .../idormy/sms/forwarder/utils/Constants.kt | 3 + .../sms/forwarder/utils/HttpServerUtils.kt | 16 ++ app/src/main/res/drawable/icon_api_wol.webp | Bin 0 -> 1968 bytes app/src/main/res/drawable/icon_pushdeer.webp | Bin 0 -> 3062 bytes .../res/layout/fragment_client_wol_send.xml | 108 +++++++++++ app/src/main/res/layout/fragment_server.xml | 35 ++++ app/src/main/res/values-en/strings.xml | 15 +- app/src/main/res/values/strings.xml | 11 ++ 13 files changed, 451 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/com/idormy/sms/forwarder/fragment/client/WolSendFragment.kt create mode 100644 app/src/main/java/com/idormy/sms/forwarder/server/controller/WolController.kt create mode 100644 app/src/main/java/com/idormy/sms/forwarder/server/model/WolData.kt create mode 100644 app/src/main/res/drawable/icon_api_wol.webp create mode 100644 app/src/main/res/drawable/icon_pushdeer.webp create mode 100644 app/src/main/res/layout/fragment_client_wol_send.xml diff --git a/.gitignore b/.gitignore index bccd1136..57c9bd65 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,4 @@ /app/mapping.txt /app/seeds.txt /app/unused.txt +/pic/*.bkp diff --git a/app/src/main/java/com/idormy/sms/forwarder/fragment/ServerFragment.kt b/app/src/main/java/com/idormy/sms/forwarder/fragment/ServerFragment.kt index 184713a0..72aa4b26 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/fragment/ServerFragment.kt +++ b/app/src/main/java/com/idormy/sms/forwarder/fragment/ServerFragment.kt @@ -119,6 +119,11 @@ class ServerFragment : BaseFragment(), View.OnClickListe HttpServerUtils.enableApiBatteryQuery = isChecked } + binding!!.sbApiWol.isChecked = HttpServerUtils.enableApiWol + binding!!.sbApiWol.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean -> + HttpServerUtils.enableApiWol = isChecked + } + } @SingleClick diff --git a/app/src/main/java/com/idormy/sms/forwarder/fragment/client/WolSendFragment.kt b/app/src/main/java/com/idormy/sms/forwarder/fragment/client/WolSendFragment.kt new file mode 100644 index 00000000..e0c58f9e --- /dev/null +++ b/app/src/main/java/com/idormy/sms/forwarder/fragment/client/WolSendFragment.kt @@ -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(), View.OnClickListener { + + val TAG: String = WolSendFragment::class.java.simpleName + private var mCountDownHelper: CountDownButtonHelper? = null + private var wolHistory: MutableMap = 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>() {}.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 = 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 = 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() { + + override fun onError(e: ApiException) { + XToastUtils.error(e.displayMessage) + } + + override fun onSuccess(response: String) { + Log.i(TAG, response) + try { + val resp: BaseResponse = Gson().fromJson(response, object : TypeToken>() {}.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() + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/idormy/sms/forwarder/server/controller/WolController.kt b/app/src/main/java/com/idormy/sms/forwarder/server/controller/WolController.kt new file mode 100644 index 00000000..418bf711 --- /dev/null +++ b/app/src/main/java/com/idormy/sms/forwarder/server/controller/WolController.kt @@ -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): 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 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/idormy/sms/forwarder/server/model/WolData.kt b/app/src/main/java/com/idormy/sms/forwarder/server/model/WolData.kt new file mode 100644 index 00000000..c1aac3db --- /dev/null +++ b/app/src/main/java/com/idormy/sms/forwarder/server/model/WolData.kt @@ -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 \ No newline at end of file diff --git a/app/src/main/java/com/idormy/sms/forwarder/utils/Constants.kt b/app/src/main/java/com/idormy/sms/forwarder/utils/Constants.kt index 33a44105..58bee788 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/utils/Constants.kt +++ b/app/src/main/java/com/idormy/sms/forwarder/utils/Constants.kt @@ -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), ) \ No newline at end of file diff --git a/app/src/main/java/com/idormy/sms/forwarder/utils/HttpServerUtils.kt b/app/src/main/java/com/idormy/sms/forwarder/utils/HttpServerUtils.kt index a07349f3..98d41195 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/utils/HttpServerUtils.kt +++ b/app/src/main/java/com/idormy/sms/forwarder/utils/HttpServerUtils.kt @@ -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 diff --git a/app/src/main/res/drawable/icon_api_wol.webp b/app/src/main/res/drawable/icon_api_wol.webp new file mode 100644 index 0000000000000000000000000000000000000000..096c7fe9bb2e231a0c480e5665110489392ec27b GIT binary patch literal 1968 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 E0N<(uXaE2J literal 0 HcmV?d00001 diff --git a/app/src/main/res/layout/fragment_client_wol_send.xml b/app/src/main/res/layout/fragment_client_wol_send.xml new file mode 100644 index 00000000..d2dcb13f --- /dev/null +++ b/app/src/main/res/layout/fragment_client_wol_send.xml @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_server.xml b/app/src/main/res/layout/fragment_server.xml index b452baa9..8e73d32b 100644 --- a/app/src/main/res/layout/fragment_server.xml +++ b/app/src/main/res/layout/fragment_server.xml @@ -415,6 +415,41 @@ + + + + + + + + + + + + + + diff --git a/app/src/main/res/values-en/strings.xml b/app/src/main/res/values-en/strings.xml index 7b3d4930..d13efa19 100644 --- a/app/src/main/res/values-en/strings.xml +++ b/app/src/main/res/values-en/strings.xml @@ -781,6 +781,8 @@ Remotely check contact list Query Battery Remotely query mobile phone power and battery status + Remotely WOL + Turn on your Wake-On-LAN enabled devices remotely Sim Slot Phone Numbers @@ -844,8 +846,8 @@ [Battery Warning] The battery warning limit has been exceeded, please unplug the charger!%s [Battery Warning] The lower limit of the battery warning has been reached, please charge it in time!%s [Battery Warning] The upper limit of battery warning has been reached, please unplug the charger!%s - 【充电状态】发生变化: - 第一行不允许缩进 + [Charging status] changes: + No indentation allowed on the first line The server enables the signing key, and the sign node required The server enables the signing key, and the timestamp node required Sign verify failed @@ -881,4 +883,13 @@ AppSecret Sample Text Sample Markdown + Please enter an IP address, eg. 192.168.168.168 + Malformed IP address, eg. 192.168.168.168 + ^((\\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])$ + Please enter the network card mac, eg. AA:BB:CC:DD:EE:FF + The network card mac format is incorrect, eg. AA:BB:CC:DD:EE:FF + ^((([a-fA-F0-9]{2}:){5})|(([a-fA-F0-9]{2}-){5}))[a-fA-F0-9]{2}$ + IP + MAC + There is no history record, WOL will be added automatically after successful sending diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 20d70521..9b2e9cfb 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -782,6 +782,8 @@ 远程查联系人列表 远程查电量 远程查询手机电量与电池状态 + 远程WOL + 远程打开启用LAN唤醒功能(Wake-On-LAN)的设备 发送卡槽 手机号码 @@ -882,4 +884,13 @@ AppSecret 文本类型 Markdown类型 + 请输入IP地址,例如:192.168.168.168 + IP地址格式错误,例如:192.168.168.168 + ^((\\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])$ + 请输入网卡mac,例如:AA:BB:CC:DD:EE:FF + 网卡mac格式错误,例如:AA:BB:CC:DD:EE:FF + ^((([a-fA-F0-9]{2}:){5})|(([a-fA-F0-9]{2}-){5}))[a-fA-F0-9]{2}$ + IP地址 + 网卡MAC + 暂无历史记录,WOL发送成功后自动加入