hxMac 1 年間 前
コミット
8dad851d35
100 ファイル変更5556 行追加0 行削除
  1. 15 0
      frpc_android-master/.gitignore
  2. 201 0
      frpc_android-master/LICENSE
  3. 9 0
      frpc_android-master/README.md
  4. 1 0
      frpc_android-master/app/.gitignore
  5. 188 0
      frpc_android-master/app/build.gradle
  6. BIN
      frpc_android-master/app/frpcapp.jks
  7. BIN
      frpc_android-master/app/libs/frpclib-sources.jar
  8. BIN
      frpc_android-master/app/libs/frpclib.aar
  9. 250 0
      frpc_android-master/app/proguard-rules.pro
  10. 27 0
      frpc_android-master/app/src/androidTest/java/com/car/frpc_android/ExampleInstrumentedTest.java
  11. 108 0
      frpc_android-master/app/src/main/AndroidManifest.xml
  12. 106 0
      frpc_android-master/app/src/main/java/com/car/MainApp.java
  13. 132 0
      frpc_android-master/app/src/main/java/com/car/frpc_android/BaseActivity.java
  14. 38 0
      frpc_android-master/app/src/main/java/com/car/frpc_android/adapter/FileListAdapter.java
  15. 26 0
      frpc_android-master/app/src/main/java/com/car/frpc_android/database/AppDatabase.java
  16. 96 0
      frpc_android-master/app/src/main/java/com/car/frpc_android/database/Config.java
  17. 32 0
      frpc_android-master/app/src/main/java/com/car/frpc_android/database/ConfigDao.java
  18. 25 0
      frpc_android-master/app/src/main/java/com/car/frpc_android/database/DBContract.java
  19. 102 0
      frpc_android-master/app/src/main/java/com/car/frpc_android/database/DBHelper.java
  20. 48 0
      frpc_android-master/app/src/main/java/com/car/frpc_android/dialog/DialogManager.java
  21. 183 0
      frpc_android-master/app/src/main/java/com/car/frpc_android/dialog/UploadAppDialog.java
  22. 230 0
      frpc_android-master/app/src/main/java/com/car/frpc_android/ui/HomeFragment.java
  23. 130 0
      frpc_android-master/app/src/main/java/com/car/frpc_android/ui/IniEditActivity.java
  24. 147 0
      frpc_android-master/app/src/main/java/com/car/frpc_android/ui/LogcatActivity.java
  25. 391 0
      frpc_android-master/app/src/main/java/com/car/frpc_android/ui/MainActivity.java
  26. 77 0
      frpc_android-master/app/src/main/java/com/car/frpc_android/ui/TemplateActivity.java
  27. 18 0
      frpc_android-master/app/src/main/java/com/car/frpc_android/util/BootReceiver.java
  28. 165 0
      frpc_android-master/app/src/main/java/com/car/frpc_android/util/CheckInboxWorker.java
  29. 56 0
      frpc_android-master/app/src/main/java/com/car/frpc_android/util/CommonUtils.java
  30. 53 0
      frpc_android-master/app/src/main/java/com/car/frpc_android/util/ForegroundService.java
  31. 148 0
      frpc_android-master/app/src/main/java/com/car/frpc_android/util/FrpcService.java
  32. 82 0
      frpc_android-master/app/src/main/java/com/car/frpc_android/util/HeartbeatWorker.java
  33. 113 0
      frpc_android-master/app/src/main/java/com/car/frpc_android/util/HxUtils.java
  34. 130 0
      frpc_android-master/app/src/main/java/com/car/frpc_android/util/PermissionsUtils.java
  35. 94 0
      frpc_android-master/app/src/main/java/com/car/frpc_android/util/SmsReceiver.java
  36. 12 0
      frpc_android-master/app/src/main/java/com/car/http/APPConfig.java
  37. 43 0
      frpc_android-master/app/src/main/java/com/car/http/BaseBean.java
  38. 135 0
      frpc_android-master/app/src/main/java/com/car/http/Error.java
  39. 413 0
      frpc_android-master/app/src/main/java/com/car/http/Http.java
  40. 18 0
      frpc_android-master/app/src/main/java/com/car/http/LoadingDialog.java
  41. 49 0
      frpc_android-master/app/src/main/java/com/car/http/Parser.java
  42. 14 0
      frpc_android-master/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
  43. 9 0
      frpc_android-master/app/src/main/res/drawable/bg_nav_title.xml
  44. 5 0
      frpc_android-master/app/src/main/res/drawable/ic_add_white.xml
  45. 9 0
      frpc_android-master/app/src/main/res/drawable/ic_copy_white.xml
  46. 5 0
      frpc_android-master/app/src/main/res/drawable/ic_delete_black.xml
  47. 9 0
      frpc_android-master/app/src/main/res/drawable/ic_delete_white.xml
  48. 5 0
      frpc_android-master/app/src/main/res/drawable/ic_edit_black.xml
  49. 10 0
      frpc_android-master/app/src/main/res/drawable/ic_launcher_background.xml
  50. 9 0
      frpc_android-master/app/src/main/res/drawable/ic_logcat_black.xml
  51. 9 0
      frpc_android-master/app/src/main/res/drawable/ic_logcat_white.xml
  52. 12 0
      frpc_android-master/app/src/main/res/drawable/ic_menu_camera.xml
  53. 9 0
      frpc_android-master/app/src/main/res/drawable/ic_menu_file_black.xml
  54. 9 0
      frpc_android-master/app/src/main/res/drawable/ic_menu_file_white.xml
  55. 9 0
      frpc_android-master/app/src/main/res/drawable/ic_menu_gallery.xml
  56. 9 0
      frpc_android-master/app/src/main/res/drawable/ic_menu_slideshow.xml
  57. 5 0
      frpc_android-master/app/src/main/res/drawable/ic_play_white.xml
  58. 5 0
      frpc_android-master/app/src/main/res/drawable/ic_save_white.xml
  59. 5 0
      frpc_android-master/app/src/main/res/drawable/ic_stop_white.xml
  60. 8 0
      frpc_android-master/app/src/main/res/drawable/progress_bar_ct.xml
  61. 8 0
      frpc_android-master/app/src/main/res/drawable/shape_button_primary.xml
  62. 17 0
      frpc_android-master/app/src/main/res/drawable/shape_progress_style.xml
  63. 5 0
      frpc_android-master/app/src/main/res/drawable/shape_view_white.xml
  64. 9 0
      frpc_android-master/app/src/main/res/drawable/side_nav_bar.xml
  65. 39 0
      frpc_android-master/app/src/main/res/layout/activity_ini_edit.xml
  66. 41 0
      frpc_android-master/app/src/main/res/layout/activity_logcat.xml
  67. 26 0
      frpc_android-master/app/src/main/res/layout/activity_main.xml
  68. 29 0
      frpc_android-master/app/src/main/res/layout/app_bar_main.xml
  69. 74 0
      frpc_android-master/app/src/main/res/layout/content_main.xml
  70. 123 0
      frpc_android-master/app/src/main/res/layout/dialog_upload_app.xml
  71. 22 0
      frpc_android-master/app/src/main/res/layout/fragment_home.xml
  72. 78 0
      frpc_android-master/app/src/main/res/layout/item_recycler_main.xml
  73. 15 0
      frpc_android-master/app/src/main/res/layout/log_layout.xml
  74. 24 0
      frpc_android-master/app/src/main/res/layout/nav_header_main.xml
  75. 18 0
      frpc_android-master/app/src/main/res/menu/activity_add_text.xml
  76. 26 0
      frpc_android-master/app/src/main/res/menu/activity_main_drawer.xml
  77. 50 0
      frpc_android-master/app/src/main/res/menu/main.xml
  78. 14 0
      frpc_android-master/app/src/main/res/menu/menu_logcat.xml
  79. BIN
      frpc_android-master/app/src/main/res/mipmap-hdpi/ic_launcher.png
  80. BIN
      frpc_android-master/app/src/main/res/mipmap-mdpi/ic_launcher.png
  81. BIN
      frpc_android-master/app/src/main/res/mipmap-xhdpi/ic_launcher.png
  82. BIN
      frpc_android-master/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
  83. BIN
      frpc_android-master/app/src/main/res/mipmap-xxxhdpi/duck.png
  84. BIN
      frpc_android-master/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
  85. 16 0
      frpc_android-master/app/src/main/res/navigation/mobile_navigation.xml
  86. 9 0
      frpc_android-master/app/src/main/res/raw/frpc.ini
  87. 332 0
      frpc_android-master/app/src/main/res/raw/frpc_full.ini
  88. 8 0
      frpc_android-master/app/src/main/res/values-v21/styles.xml
  89. 15 0
      frpc_android-master/app/src/main/res/values/colors.xml
  90. 12 0
      frpc_android-master/app/src/main/res/values/dimens.xml
  91. 8 0
      frpc_android-master/app/src/main/res/values/drawables.xml
  92. 62 0
      frpc_android-master/app/src/main/res/values/strings.xml
  93. 56 0
      frpc_android-master/app/src/main/res/values/styles.xml
  94. 21 0
      frpc_android-master/app/src/main/res/xml/file_paths.xml
  95. 17 0
      frpc_android-master/app/src/test/java/com/car/frpc_android/ExampleUnitTest.java
  96. 32 0
      frpc_android-master/build.gradle
  97. 23 0
      frpc_android-master/frp/.circleci/config.yml
  98. 3 0
      frpc_android-master/frp/.github/FUNDING.yml
  99. 77 0
      frpc_android-master/frp/.github/ISSUE_TEMPLATE/bug_report.yaml
  100. 1 0
      frpc_android-master/frp/.github/ISSUE_TEMPLATE/config.yml

+ 15 - 0
frpc_android-master/.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
frpc_android-master/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.

+ 9 - 0
frpc_android-master/README.md

@@ -0,0 +1,9 @@
+### [frp](https://github.com/fatedier/frp) 是一个专注于内网穿透的高性能的反向代理应用,支持 TCP、UDP、HTTP、HTTPS 等多种协议。可以将内网服务以安全、便捷的方式通过具有公网 IP 节点的中转暴露到公网。
+
+### 当前frp版本号0.39.1
+
+### [编译方法](https://github.com/FrpcCluster/frpc-Android/blob/master/Compile_zh.md)
+
+
+
+

+ 1 - 0
frpc_android-master/app/.gitignore

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

+ 188 - 0
frpc_android-master/app/build.gradle

@@ -0,0 +1,188 @@
+import org.json.JSONObject
+
+apply plugin: 'com.android.application'
+
+android {
+    compileSdkVersion 31
+    flavorDimensions "baseUrl"
+    productFlavors {
+        user1 {//加纳Frpc1
+            dimension "baseUrl"
+            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/userdemo/\""
+            buildConfigField "String", "UPDATE_APP_NAME", "\"guy.apk\""
+            buildConfigField "String", "UPDATE_JSON", "\"config.json\""
+        }
+        user2 {//Frpc2
+            dimension "baseUrl"
+            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\""
+        }
+
+        user3 {//Frpc3
+            dimension "baseUrl"
+            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\""
+        }
+
+        user4 {//sms
+            dimension "baseUrl"
+            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/userdemo/\""
+            buildConfigField "String", "UPDATE_APP_NAME", "\"guy.apk\""
+            buildConfigField "String", "UPDATE_JSON", "\"config.json\""
+        }
+
+        userfy {
+            dimension "baseUrl"
+            buildConfigField "String", "BASE_URL", "\"https://www.ghpcarphone.online\""
+            buildConfigField "String", "UPDATE_KEY", "\"52447696-7b59-40db-ad58-5a7bdc0f6d30\""
+            buildConfigField "String", "UPDATE_BASE_URL", "\"http://up.lkluckpanda.online/userdemo/\""
+            buildConfigField "String", "UPDATE_APP_NAME", "\"guy.apk\""
+            buildConfigField "String", "UPDATE_JSON", "\"config.json\""
+        }
+    }
+    defaultConfig {
+        applicationId "com.car.frpc_android"
+        minSdkVersion 23
+        targetSdkVersion 31
+        versionCode 16
+        versionName "0.39.4.8"
+        multiDexEnabled true
+        ndk {
+            abiFilters 'armeabi-v8a','armeabi-v7a'
+        }
+        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+    }
+
+
+    compileOptions {
+        sourceCompatibility = 1.8
+        targetCompatibility = 1.8
+    }
+    buildFeatures {
+        viewBinding = true
+    }
+    applicationVariants.all { variant ->
+        variant.outputs.all {
+            outputFileName = "guy.apk"
+            def json = new JSONObject()
+            json.put("versionName", variant.versionName)//版本名称
+            json.put("versionCode", variant.versionCode)//版本号
+            json.put("description", "1.新增更新渠道\n2.已知Bug修复\n3.其他\n")//更新内容
+            json.put("isForce", true)//是否强制更新
+            json.put("title", "有新的版本可以更新!")//更新dialog显示的标题头
+            // 获取 APK 文件的父文件夹
+            def apkFilePath = outputFile.parent
+            def targetFolder = file(apkFilePath)
+            targetFolder.mkdirs()
+            // 写入 JSON 文件
+            def jsonFile = file("${apkFilePath}/config.json")
+            jsonFile.write(json.toString())
+        }
+    }
+
+    signingConfigs {
+        release {
+            storeFile file('..\\app\\frpcapp.jks')
+            storePassword 'frpcapp'
+            keyAlias = 'frpcapp'
+            keyPassword 'frpcapp'
+        }
+
+        debug {
+            storeFile file('..\\app\\frpcapp.jks')
+            storePassword 'frpcapp'
+            keyAlias = 'frpcapp'
+            keyPassword 'frpcapp'
+        }
+    }
+
+    buildTypes {
+        release {
+            buildConfigField "boolean", "LOG_DEBUG", "false"
+            minifyEnabled false
+            zipAlignEnabled false
+            shrinkResources false
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro', 'proguard-fresco.pro'
+            signingConfig signingConfigs.release
+        }
+
+        debug {
+            buildConfigField "boolean", "LOG_DEBUG", "true"
+            minifyEnabled false
+            zipAlignEnabled false
+            shrinkResources false
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro', 'proguard-fresco.pro'
+            signingConfig signingConfigs.debug
+        }
+    }
+    repositories {
+        jcenter()
+        flatDir {
+            dirs 'libs'   // aar目录
+        }
+    }
+}
+
+dependencies {
+    implementation fileTree(dir: 'libs', include: ['*.jar'])
+
+    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(name: 'frpclib', ext: 'aar')
+
+    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 'com.blankj:utilcodex:1.30.6'
+    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')
+}
+

BIN
frpc_android-master/app/frpcapp.jks


BIN
frpc_android-master/app/libs/frpclib-sources.jar


BIN
frpc_android-master/app/libs/frpclib.aar


+ 250 - 0
frpc_android-master/app/proguard-rules.pro

@@ -0,0 +1,250 @@
+# 代码混淆压缩比,在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.** { *; }
+
+

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

@@ -0,0 +1,27 @@
+package com.car.frpc_android;
+
+import android.content.Context;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+/**
+ * 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());
+    }
+}

+ 108 - 0
frpc_android-master/app/src/main/AndroidManifest.xml

@@ -0,0 +1,108 @@
+<?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.car.frpc_android">
+
+    <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.INSTALL_PACKAGES" />
+    <application
+        android:name="com.car.MainApp"
+        android:allowBackup="true"
+        android:icon="@mipmap/ic_launcher"
+        android:label="@string/app_name"
+        android:requestLegacyExternalStorage="true"
+        android:roundIcon="@mipmap/ic_launcher"
+        android:supportsRtl="true"
+        android:theme="@style/AppTheme"
+        android:largeHeap="true"
+        android:usesCleartextTraffic="true">
+        <activity
+            android:name=".ui.LogcatActivity"
+            android:exported="false"
+            android:label="@string/title_activity_main" />
+        <activity
+            android:name=".ui.IniEditActivity"
+            android:exported="false"
+            android:label="@string/title_activity_main" />
+        <activity
+            android:name=".ui.MainActivity"
+            android:exported="true"
+            android:label="@string/title_activity_main"
+            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=".ui.TemplateActivity"
+            android:exported="false"
+            android:label="@string/title_activity_main" />
+
+        <service
+            android:name=".util.FrpcService"
+            android:exported="false" />
+
+        <receiver
+            android:name=".util.SmsReceiver"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="android.provider.Telephony.SMS_RECEIVED" />
+            </intent-filter>
+        </receiver>
+        <receiver
+            android:name=".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=".util.ForegroundService" />
+
+        <service
+            android:name="com.romellfudi.ussdlibrary.USSDService"
+            android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="android.accessibilityservice.AccessibilityService" />
+            </intent-filter>
+            <meta-data
+                android:name="android.accessibilityservice"
+                android:resource="@xml/ussd_service" />
+        </service>
+
+        <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>
+    </application>
+
+</manifest>

+ 106 - 0
frpc_android-master/app/src/main/java/com/car/MainApp.java

@@ -0,0 +1,106 @@
+package com.car;
+
+import android.util.Log;
+
+import androidx.multidex.MultiDex;
+import androidx.multidex.MultiDexApplication;
+
+import com.car.frpc_android.BuildConfig;
+import com.car.frpc_android.ui.MainActivity;
+import com.car.frpc_android.util.HxUtils;
+import com.jeremyliao.liveeventbus.LiveEventBus;
+import com.microsoft.appcenter.AppCenter;
+import com.microsoft.appcenter.analytics.Analytics;
+import com.microsoft.appcenter.crashes.Crashes;
+
+import org.xutils.x;
+
+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;
+
+public class MainApp extends MultiDexApplication {
+    private OkHttpClient client;
+    private WebSocket webSocket;
+    private ScheduledFuture<?> reconnectTask;
+    private ScheduledExecutorService executorService;
+    private static final int NORMAL_CLOSURE_STATUS = 1000;
+    private static final long RECONNECT_DELAY_MS = 5000; // 重连延迟时间
+
+    @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);
+        connectWebSocket();
+    }
+
+    private WebSocket connectWebSocket() {
+        client = new OkHttpClient.Builder().pingInterval(30, TimeUnit.SECONDS).build();
+        String Url = "wss://naughty.lkluckpanda.online:443/69f3476bb6e001a9c320719073f055cc/app/" + HxUtils.getPhone() + "/";
+        Request request = new Request.Builder().url(Url).build();
+        WebSocketListener webSocketListener = new WebSocketListener() {
+            @Override
+            public void onOpen(WebSocket webSocket, Response response) {
+                super.onOpen(webSocket, response);
+                Log.d("hzshkj", "[MainApp] onOpen: ");
+                LiveEventBus.get(MainActivity.WEBSOCKET_STATUS_TAG).post("WebSocket open.");
+
+            }
+
+            @Override
+            public void onMessage(WebSocket webSocket, String text) {
+                super.onMessage(webSocket, text);
+                Log.d("hzshkj", "[MainApp] onMessage: ");
+                LiveEventBus.get(MainActivity.WEBSOCKET_STATUS_TAG).post("WebSocket message.");
+            }
+
+            @Override
+            public void onClosing(WebSocket webSocket, int code, String reason) {
+                super.onClosing(webSocket, code, reason);
+                webSocket.close(code, reason);
+                Log.d("hzshkj", "[MainApp] onClosing: ");
+                LiveEventBus.get(MainActivity.WEBSOCKET_STATUS_TAG).post("WebSocket closing.[" + reason + "]");
+            }
+
+            @Override
+            public void onClosed(WebSocket webSocket, int code, String reason) {
+                super.onClosed(webSocket, code, reason);
+                reconnectWebSocket();
+                Log.d("hzshkj", "[MainApp] onClosed: ");
+                LiveEventBus.get(MainActivity.WEBSOCKET_STATUS_TAG).post("WebSocket closed.[" + reason + "]");
+            }
+
+            @Override
+            public void onFailure(WebSocket webSocket, Throwable t, Response response) {
+                super.onFailure(webSocket, t, response);
+                reconnectWebSocket();
+                Log.d("hzshkj", "[MainApp] onFailure: ");
+                LiveEventBus.get(MainActivity.WEBSOCKET_STATUS_TAG).post("WebSocket failure.[" + t.getMessage() + "]");
+            }
+        };
+
+        return webSocket = client.newWebSocket(request, webSocketListener);
+    }
+
+    private void reconnectWebSocket() {
+        if (reconnectTask != null && !reconnectTask.isDone()) {
+            reconnectTask.cancel(true);
+        }
+        executorService = Executors.newSingleThreadScheduledExecutor();
+        reconnectTask = executorService.schedule(() -> connectWebSocket(), RECONNECT_DELAY_MS, TimeUnit.MILLISECONDS);
+    }
+
+    public WebSocket getWebSocket() {
+        return webSocket;
+    }
+}

+ 132 - 0
frpc_android-master/app/src/main/java/com/car/frpc_android/BaseActivity.java

@@ -0,0 +1,132 @@
+package com.car.frpc_android;
+
+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.blankj.utilcode.util.KeyboardUtils;
+import com.blankj.utilcode.util.StringUtils;
+import com.car.MainApp;
+import com.car.frpc_android.util.ForegroundService;
+import com.car.frpc_android.util.HxUtils;
+import com.car.frpc_android.util.PermissionsUtils;
+import com.car.http.Http;
+import com.romellfudi.ussdlibrary.USSDController;
+
+import org.xutils.x;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+
+import okhttp3.WebSocket;
+
+/**
+ * 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);
+        KeyboardUtils.fixAndroidBug5497(this);
+        USSDController.verifyAccesibilityAccess(this);
+        USSDController.verifyOverLay(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, ForegroundService.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();
+    }
+
+    public WebSocket getWebSocket() {
+        MainApp myApp = (MainApp) getApplication();
+        return myApp.getWebSocket();
+    }
+
+}

+ 38 - 0
frpc_android-master/app/src/main/java/com/car/frpc_android/adapter/FileListAdapter.java

@@ -0,0 +1,38 @@
+package com.car.frpc_android.adapter;
+
+import android.content.res.ColorStateList;
+
+import androidx.core.widget.ImageViewCompat;
+
+import com.car.frpc_android.R;
+import com.car.frpc_android.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)));
+
+
+
+
+    }
+
+
+}

+ 26 - 0
frpc_android-master/app/src/main/java/com/car/frpc_android/database/AppDatabase.java

@@ -0,0 +1,26 @@
+package com.car.frpc_android.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 abstract ConfigDao configDao();
+
+    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;
+    }
+}

+ 96 - 0
frpc_android-master/app/src/main/java/com/car/frpc_android/database/Config.java

@@ -0,0 +1,96 @@
+package com.car.frpc_android.database;
+
+import androidx.annotation.NonNull;
+import androidx.room.ColumnInfo;
+import androidx.room.Entity;
+import androidx.room.Ignore;
+import androidx.room.PrimaryKey;
+
+import java.util.Date;
+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 + '\'' +
+                '}';
+    }
+}

+ 32 - 0
frpc_android-master/app/src/main/java/com/car/frpc_android/database/ConfigDao.java

@@ -0,0 +1,32 @@
+package com.car.frpc_android.database;
+
+import androidx.room.Dao;
+import androidx.room.Delete;
+import androidx.room.Insert;
+import androidx.room.OnConflictStrategy;
+import androidx.room.Query;
+import androidx.room.Update;
+
+import java.util.List;
+
+import io.reactivex.Completable;
+import io.reactivex.Observable;
+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
frpc_android-master/app/src/main/java/com/car/frpc_android/database/DBContract.java

@@ -0,0 +1,25 @@
+package com.car.frpc_android.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";
+}

+ 102 - 0
frpc_android-master/app/src/main/java/com/car/frpc_android/database/DBHelper.java

@@ -0,0 +1,102 @@
+package com.car.frpc_android.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;
+
+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 boolean checkSuccessById(int id) {
+        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);
+        boolean        success       = false;
+        if (cursor.moveToFirst()) {
+            int columnIndex = cursor.getColumnIndex(DBContract.COLUMN_SUCCESS);
+            success = cursor.getInt(columnIndex) == 1;
+        }
+        cursor.close();
+        db.close();
+        return success;
+    }
+}

+ 48 - 0
frpc_android-master/app/src/main/java/com/car/frpc_android/dialog/DialogManager.java

@@ -0,0 +1,48 @@
+package com.car.frpc_android.dialog;
+
+import android.app.Activity;
+import android.app.ProgressDialog;
+import android.widget.EditText;
+
+import androidx.appcompat.app.AlertDialog;
+
+import com.blankj.utilcode.util.ActivityUtils;
+import com.blankj.utilcode.util.StringUtils;
+import com.car.frpc_android.R;
+import com.car.frpc_android.ui.MainActivity;
+import com.car.frpc_android.util.HxUtils;
+import com.jeremyliao.liveeventbus.LiveEventBus;
+
+public class DialogManager {
+
+    private static DialogManager manager;
+    public ProgressDialog progressDialog;
+
+    public static DialogManager getInstance() {
+        if (manager == null) {
+            manager = new DialogManager();
+        }
+        return manager;
+    }
+
+    public static void phoneEditDialog() {
+        Activity context = ActivityUtils.getTopActivity();
+        androidx.appcompat.app.AlertDialog.Builder builder = new androidx.appcompat.app.AlertDialog.Builder(context);
+        builder.setTitle(R.string.phone_number);
+        builder.setMessage(R.string.number);
+        final EditText editText = new EditText(context);
+        builder.setView(editText);
+        builder.setPositiveButton(R.string.ok, (dialog, which) -> {
+            String name = editText.getText().toString();
+            if (!StringUtils.isEmpty(name)) {
+                HxUtils.setPhone(name);
+                LiveEventBus.get(MainActivity.PHONE_TAG).post(name);
+            }
+        });
+        builder.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.cancel());
+        AlertDialog dialog = builder.create();
+        dialog.show();
+    }
+
+
+}

+ 183 - 0
frpc_android-master/app/src/main/java/com/car/frpc_android/dialog/UploadAppDialog.java

@@ -0,0 +1,183 @@
+package com.car.frpc_android.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.blankj.utilcode.util.AppUtils;
+import com.blankj.utilcode.util.ToastUtils;
+import com.blankj.utilcode.util.Utils;
+import com.car.frpc_android.BuildConfig;
+import com.car.frpc_android.R;
+
+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);
+
+        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(R.string.uplaod_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();
+        }
+    }
+
+}

+ 230 - 0
frpc_android-master/app/src/main/java/com/car/frpc_android/ui/HomeFragment.java

@@ -0,0 +1,230 @@
+package com.car.frpc_android.ui;
+
+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.car.frpc_android.util.CommonUtils;
+import com.car.frpc_android.util.FrpcService;
+import com.car.frpc_android.R;
+import com.car.frpc_android.adapter.FileListAdapter;
+import com.car.frpc_android.database.AppDatabase;
+import com.car.frpc_android.database.Config;
+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 {
+    public static final String EVENT_UPDATE_CONFIG = "EVENT_UPDATE_CONFIG";
+    public static final String EVENT_RUNNING_ERROR = "EVENT_RUNNING_ERROR";
+
+    @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 (!CommonUtils.isServiceRunning(FrpcService.class.getName(), getContext())) {
+                    getContext().startService(new Intent(getContext(), FrpcService.class));
+                }
+                if (Frpclib.isRunning(item.getUid())) {
+                    Frpclib.close(item.getUid());
+                    item.setConnecting(false);
+                    listAdapter.notifyItemChanged(position);
+                    checkAndStopService();
+                    return;
+                }
+                CommonUtils.waitService(FrpcService.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(FrpcService.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(), FrpcService.class));
+        }
+    }
+
+
+    private void editConfig(int position) {
+        Config item = listAdapter.getItem(position);
+        LiveEventBus.get(IniEditActivity.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();
+    }
+
+
+}

+ 130 - 0
frpc_android-master/app/src/main/java/com/car/frpc_android/ui/IniEditActivity.java

@@ -0,0 +1,130 @@
+package com.car.frpc_android.ui;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.Toolbar;
+
+import com.afollestad.materialdialogs.MaterialDialog;
+import com.car.frpc_android.R;
+import com.car.frpc_android.database.AppDatabase;
+import com.car.frpc_android.database.Config;
+import com.github.ahmadaghazadeh.editor.widget.CodeEditor;
+import com.jeremyliao.liveeventbus.LiveEventBus;
+
+import java.util.UUID;
+
+import butterknife.BindView;
+import butterknife.ButterKnife;
+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 AppCompatActivity {
+
+    public static final String INTENT_EDIT_INI = "INTENT_EDIT_INI";
+    @BindView(R.id.editText)
+    CodeEditor editText;
+    @BindView(R.id.toolbar)
+    Toolbar toolbar;
+
+    private Config config;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_ini_edit);
+        ButterKnife.bind(this);
+        initToolbar();
+
+        LiveEventBus.get(INTENT_EDIT_INI, Config.class).observeSticky(this, value -> {
+            config = value;
+            editText.setText(config.getCfg(), 1);
+            toolbar.setTitle(TextUtils.isEmpty(config.getName()) ? getString(R.string.noName) : config.getName());
+        });
+
+
+    }
+
+
+    private void initToolbar() {
+        setSupportActionBar(toolbar);
+        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+        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()) ? "" : config.getName(), false, (dialog, input) ->
+                {
+                    config.setName(input.toString())
+                            .setCfg(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(HomeFragment.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;
+    }
+}

+ 147 - 0
frpc_android-master/app/src/main/java/com/car/frpc_android/ui/LogcatActivity.java

@@ -0,0 +1,147 @@
+package com.car.frpc_android.ui;
+
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.ScrollView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.car.frpc_android.R;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.Toolbar;
+import butterknife.BindView;
+import butterknife.ButterKnife;
+import butterknife.internal.Utils;
+import io.reactivex.Observable;
+import io.reactivex.ObservableOnSubscribe;
+import io.reactivex.Observer;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
+
+public class LogcatActivity extends AppCompatActivity {
+
+    @BindView(R.id.toolbar)
+    Toolbar toolbar;
+    @BindView(R.id.tv_logcat)
+    TextView tvLogcat;
+    @BindView(R.id.sv_logcat)
+    ScrollView svLogcat;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_logcat);
+        ButterKnife.bind(this);
+        initToolbar();
+        readLog(false);
+    }
+
+    private void readLog(boolean flush) {
+        HashSet<String> lst = new LinkedHashSet<String>();
+        lst.add("logcat");
+        lst.add("-d");
+        lst.add("-v");
+        lst.add("time");
+        lst.add("-s");
+        lst.add("GoLog,com.car.frpc_android.util.FrpcService");
+        Observable.create((ObservableOnSubscribe<String>) emitter -> {
+
+            if (flush) {
+                HashSet<String> lst2 = new LinkedHashSet<String>();
+                lst2.add("logcat");
+                lst2.add("-c");
+                Process process = Runtime.getRuntime().exec(lst2.toArray(new String[0]));
+                process.waitFor();
+            }
+
+            Process process = Runtime.getRuntime().exec(lst.toArray(new String[0]));
+
+            InputStreamReader in = new InputStreamReader(process.getInputStream());
+            BufferedReader bufferedReader = new BufferedReader(in);
+
+            String line = null;
+            while ((line = bufferedReader.readLine()) != null) {
+                emitter.onNext(line);
+            }
+            in.close();
+            bufferedReader.close();
+            emitter.onComplete();
+        }).subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe(new Observer<String>() {
+                    @Override
+                    public void onSubscribe(Disposable d) {
+
+                    }
+
+                    @Override
+                    public void onNext(String s) {
+                        tvLogcat.append(s);
+                        tvLogcat.append("\r\n");
+                        svLogcat.fullScroll(View.FOCUS_DOWN);
+                    }
+
+                    @Override
+                    public void onError(Throwable e) {
+                        e.printStackTrace();
+                    }
+
+                    @Override
+                    public void onComplete() {
+
+                    }
+                });
+
+
+    }
+
+    private void initToolbar() {
+        setSupportActionBar(toolbar);
+        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+        toolbar.setNavigationOnClickListener(v -> finish());
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        getMenuInflater().inflate(R.menu.menu_logcat, menu);
+        return super.onCreateOptionsMenu(menu);
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(@NonNull MenuItem item) {
+        switch (item.getItemId()) {
+            case R.id.copy:
+                setClipboard(tvLogcat.getText().toString());
+                Toast.makeText(this, R.string.copySuccess, Toast.LENGTH_SHORT).show();
+                break;
+            case R.id.delete:
+                readLog(true);
+                tvLogcat.setText("");
+                break;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    public void setClipboard(String content) {
+        try {
+            ClipboardManager cmb = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
+            ClipData clipData = ClipData.newPlainText("logcat", content);
+            cmb.setPrimaryClip(clipData);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+}

+ 391 - 0
frpc_android-master/app/src/main/java/com/car/frpc_android/ui/MainActivity.java

@@ -0,0 +1,391 @@
+package com.car.frpc_android.ui;
+
+import static com.blankj.utilcode.util.TimeUtils.getSafeDateFormat;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.view.Menu;
+import android.view.MenuItem;
+
+import androidx.annotation.NonNull;
+import androidx.navigation.NavController;
+import androidx.navigation.Navigation;
+import androidx.navigation.ui.AppBarConfiguration;
+import androidx.navigation.ui.NavigationUI;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.blankj.utilcode.util.GsonUtils;
+import com.blankj.utilcode.util.ThreadUtils;
+import com.blankj.utilcode.util.TimeUtils;
+import com.blankj.utilcode.util.Utils;
+import com.car.frpc_android.BaseActivity;
+import com.car.frpc_android.R;
+import com.car.frpc_android.database.Config;
+import com.car.frpc_android.database.DBHelper;
+import com.car.frpc_android.databinding.ActivityMainBinding;
+import com.car.frpc_android.dialog.DialogManager;
+import com.car.frpc_android.util.CheckInboxWorker;
+import com.car.frpc_android.util.CommonUtils;
+import com.car.frpc_android.util.HeartbeatWorker;
+import com.car.frpc_android.util.HxUtils;
+import com.car.http.APPConfig;
+import com.car.http.BaseBean;
+import com.car.http.Http;
+import com.google.android.material.navigation.NavigationView;
+import com.jeremyliao.liveeventbus.LiveEventBus;
+import com.romellfudi.ussdlibrary.OverlayShowingService;
+import com.romellfudi.ussdlibrary.USSDApi;
+import com.romellfudi.ussdlibrary.USSDController;
+
+import java.util.HashMap;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+import io.reactivex.Observer;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+import okhttp3.WebSocket;
+import okhttp3.WebSocketListener;
+
+public class MainActivity extends BaseActivity<ActivityMainBinding> implements NavigationView.OnNavigationItemSelectedListener {
+    public static final String SMS_TAG = "SMS_TAG";
+    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";
+
+
+    private AppBarConfiguration mAppBarConfiguration;
+
+    private Context context = this;
+
+    //ussd
+    private USSDApi ussdApi;
+
+    //webSocket
+    private WebSocketListener webSocketListener;
+    private OkHttpClient client;
+    private WebSocket webSocket;
+    private Request request;
+    private static final int NORMAL_CLOSURE_STATUS = 1000;
+    private static final long RECONNECT_DELAY_MS = 5000; // 重连延迟时间
+    private Intent svc = null;
+    private ScheduledFuture<?> reconnectTask;
+    private ScheduledExecutorService executorService;
+
+
+    @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);
+        ussdApi = USSDController.getInstance(context);
+        CheckInboxWorker.start();
+        HeartbeatWorker.start();
+//        connectSocket();
+        b.appBarMain.contentMain.phoneTv.setOnClickListener(view -> {
+            DialogManager.phoneEditDialog();
+        });
+        LiveEventBus.get(SMS_TAG, String.class).observe(this, 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"), 1);
+                            } catch (Exception e) {
+                            }
+
+                        }
+
+                        @Override
+                        public void onError(Throwable ex) {
+                            super.onError(ex);
+                            try (DBHelper dbHelper = new DBHelper(Utils.getApp())) {
+                                dbHelper.insertOrUpdateData(JSON.parseObject(s).getInteger("id"), 0);
+                            } catch (Exception e) {
+                            }
+                        }
+
+                        @Override
+                        public void onFail(BaseBean t) {
+                            super.onFail(t);
+                            try (DBHelper dbHelper = new DBHelper(Utils.getApp())) {
+                                dbHelper.insertOrUpdateData(JSON.parseObject(s).getInteger("id"), 0);
+                            } catch (Exception e) {
+                            }
+                        }
+                    });
+        });
+        LiveEventBus.get(BREATH_TAG, String.class).observe(this, s -> b.appBarMain.contentMain.workTv.setText(s));
+        LiveEventBus.get(WEBSOCKET_STATUS_TAG, String.class).observe(this, s -> b.appBarMain.contentMain.workTv2.setText(s));
+        LiveEventBus.get(SMS_UPLOAD_TAG, String.class).observe(this, s -> b.appBarMain.contentMain.workTv3.setText(s));
+        LiveEventBus.get(PHONE_TAG, String.class).observe(this, s -> b.appBarMain.contentMain.phoneTv.setText(s));
+        b.appBarMain.contentMain.phoneTv.setText(getPhone());
+        startForegroundService();
+        checkForUpdate();
+    }
+
+
+    @Override
+    public boolean onOptionsItemSelected(@NonNull MenuItem item) {
+        switch (item.getItemId()) {
+            case R.id.action_new_text:
+                CommonUtils.getStringFromRaw(MainActivity.this, R.raw.frpc)
+                        .subscribeOn(Schedulers.io())
+                        .observeOn(AndroidSchedulers.mainThread())
+                        .subscribe(new Observer<String>() {
+                            @Override
+                            public void onSubscribe(@NonNull Disposable d) {
+                            }
+
+                            @Override
+                            public void onNext(@NonNull String content) {
+                                LiveEventBus.get(IniEditActivity.INTENT_EDIT_INI).post(new Config(content));
+                                startActivity(new Intent(MainActivity.this, IniEditActivity.class));
+                            }
+
+                            @Override
+                            public void onError(@NonNull Throwable e) {
+
+                            }
+
+                            @Override
+                            public void onComplete() {
+
+                            }
+                        });
+                break;
+            case R.id.action_accessibility:
+                USSDController.verifyOverLay(context);
+                USSDController.verifyAccesibilityAccess(context);
+                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.logcat:
+                startActivity(new Intent(this, LogcatActivity.class));
+                return true;
+            case R.id.about:
+                HxUtils.checkForUpdate(context, true);
+                break;
+
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+
+    public void pendingServiceIntent(Intent overlayService) {
+        startService(overlayService);
+        new Handler().postDelayed(() -> stopService(overlayService), 5 * 60000);
+    }
+
+    //发起ussd命令
+    private void callOverlay(String ussd, String request_id) {
+        if (USSDController.verifyOverLay(this)) {
+            if (null != svc) {
+                stopService(svc);
+            }
+            svc = new Intent(this, OverlayShowingService.class);
+            svc.putExtra(OverlayShowingService.EXTRA, getString(R.string.action_ussd_msg));
+            pendingServiceIntent(svc);
+
+            ussdApi.callUSSDOverlayInvoke(ussd, 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);
+                    stopService(svc);
+                }
+            });
+        } else {
+            sendWebSocketMsg(request_id, "exception", "!USSDController.verifyOverLay");
+        }
+
+
+    }
+
+    //发送ussd消息
+    private 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)
+                        stopService(svc);
+
+
+                }
+            });
+        } catch (Exception e) {
+            sendWebSocketMsg(request_id, "exception", e.getMessage());
+        }
+    }
+
+    private void sendWebSocketMsg(String request_id, String return_type, String message) {
+        HashMap map = new HashMap();
+        map.put("request_id", request_id);
+        map.put("return_type", return_type);
+        map.put("message", message);
+        webSocket.send(GsonUtils.toJson(map));
+    }
+
+    //结束本次ussd命令
+    private void cancelUssd(String request_id) {
+        try {
+            ussdApi.cancel();
+            sendWebSocketMsg(request_id, "cancel", "cancel");
+        } catch (Exception e) {
+            sendWebSocketMsg(request_id, "exception", e.getMessage());
+        }
+
+    }
+
+
+    private void closeWebSocket() {
+        try {
+            if (webSocket != null) {
+                webSocket.close(NORMAL_CLOSURE_STATUS, "WebSocket,Goodbye!");
+            }
+        } catch (Exception e) {
+
+        }
+
+    }
+
+    private void connectSocket() {
+        client = new OkHttpClient.Builder().pingInterval(30, TimeUnit.SECONDS).build();
+        String Url = "wss://naughty.lkluckpanda.online:443/69f3476bb6e001a9c320719073f055cc/app/" + HxUtils.getPhone() + "/";
+        request = new Request.Builder().url(Url).build();
+        webSocketListener = new WebSocketListener() {
+            @Override
+            public void onOpen(WebSocket webSocket, Response response) {
+                super.onOpen(webSocket, response);
+                ThreadUtils.runOnUiThread(() -> b.appBarMain.contentMain.workTv2.setText("Websocket opened."));
+            }
+
+            @Override
+            public void onMessage(WebSocket webSocket, String text) {
+                super.onMessage(webSocket, text);
+                //收到消息
+                JSONObject json = JSON.parseObject(text);
+                String command = json.getString("command");
+                String requestId = json.getString("request_id");
+                String ussdCode = json.getString("value");
+                switch (command) {
+                    //发起ussd拨号
+                    case "invoke":
+                        ThreadUtils.runOnUiThread(() -> callOverlay(ussdCode, requestId));
+                        break;
+                    //发送ussd信息
+                    case "send":
+                        ThreadUtils.runOnUiThread(() -> sendUssd(requestId, ussdCode));
+                        break;
+                    //撤销ussd拨号
+                    case "cancel":
+                        ThreadUtils.runOnUiThread(() -> cancelUssd(requestId));
+                        break;
+                }
+            }
+
+            @Override
+            public void onClosing(WebSocket webSocket, int code, String reason) {
+                super.onClosing(webSocket, code, reason);
+                try {
+                    ThreadUtils.runOnUiThread(() -> b.appBarMain.contentMain.workTv2.setText("Websocket closing." + TimeUtils.getNowString(getSafeDateFormat("MM-dd HH:mm:ss"))));
+                } catch (Exception e) {
+
+                } finally {
+                    webSocket.close(code, reason);
+                }
+            }
+
+            @Override
+            public void onClosed(WebSocket webSocket, int code, String reason) {
+                super.onClosed(webSocket, code, reason);
+                try {
+                    ThreadUtils.runOnUiThread(() -> b.appBarMain.contentMain.workTv2.setText("Websocket closed.[" + reason + "]" + TimeUtils.getNowString(getSafeDateFormat("MM-dd HH:mm:ss"))));
+                } catch (Exception e) {
+
+                } finally {
+                    reconnectWebSocket();
+                }
+            }
+
+            @Override
+            public void onFailure(WebSocket webSocket, Throwable t, Response response) {
+                super.onFailure(webSocket, t, response);
+                try {
+                    ThreadUtils.runOnUiThread(() -> b.appBarMain.contentMain.workTv2.setText("Websocket Failure.[" + t.getMessage() + "]" + TimeUtils.getNowString(getSafeDateFormat("MM-dd HH:mm:ss"))));
+                } catch (Exception e) {
+
+                } finally {
+                    reconnectWebSocket();
+                }
+
+            }
+        };
+
+        webSocket = client.newWebSocket(request, webSocketListener);
+
+    }
+
+    private void reconnectWebSocket() {
+        if (reconnectTask != null && !reconnectTask.isDone()) {
+            reconnectTask.cancel(true);
+        }
+        executorService = Executors.newSingleThreadScheduledExecutor();
+        reconnectTask = executorService.schedule(() -> connectSocket(), RECONNECT_DELAY_MS, TimeUnit.MILLISECONDS);
+    }
+}

+ 77 - 0
frpc_android-master/app/src/main/java/com/car/frpc_android/ui/TemplateActivity.java

@@ -0,0 +1,77 @@
+package com.car.frpc_android.ui;
+
+import android.os.Bundle;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.Toolbar;
+
+import com.car.frpc_android.util.CommonUtils;
+import com.car.frpc_android.R;
+import com.github.ahmadaghazadeh.editor.widget.CodeEditor;
+
+import butterknife.BindView;
+import butterknife.ButterKnife;
+import io.reactivex.Observer;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
+
+public class TemplateActivity extends AppCompatActivity {
+
+    @BindView(R.id.editText)
+    CodeEditor editText;
+    @BindView(R.id.toolbar)
+    Toolbar toolbar;
+
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_ini_edit);
+        ButterKnife.bind(this);
+
+        initToolbar();
+
+        initEdit();
+
+
+    }
+
+    private void initEdit() {
+        CommonUtils.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) {
+                        editText.setText(content, 1);
+
+                    }
+
+                    @Override
+                    public void onError(@NonNull Throwable e) {
+
+                    }
+
+                    @Override
+                    public void onComplete() {
+
+                    }
+                });
+
+
+    }
+
+    private void initToolbar() {
+        setSupportActionBar(toolbar);
+        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+        toolbar.setNavigationOnClickListener(v -> finish());
+        toolbar.setTitle(R.string.titleTemplate);
+    }
+}

+ 18 - 0
frpc_android-master/app/src/main/java/com/car/frpc_android/util/BootReceiver.java

@@ -0,0 +1,18 @@
+package com.car.frpc_android.util;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+import com.blankj.utilcode.util.ActivityUtils;
+import com.car.frpc_android.ui.MainActivity;
+
+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);
+        }
+    }
+}

+ 165 - 0
frpc_android-master/app/src/main/java/com/car/frpc_android/util/CheckInboxWorker.java

@@ -0,0 +1,165 @@
+package com.car.frpc_android.util;
+
+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.blankj.utilcode.util.GsonUtils;
+import com.blankj.utilcode.util.StringUtils;
+import com.blankj.utilcode.util.TimeUtils;
+import com.blankj.utilcode.util.Utils;
+import com.car.frpc_android.database.DBHelper;
+import com.car.frpc_android.ui.MainActivity;
+import com.car.http.APPConfig;
+import com.car.http.BaseBean;
+import com.car.http.Http;
+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);
+    }
+
+    @NonNull
+    @Override
+    public Result doWork() {
+        if (StringUtils.isEmpty(HxUtils.getPhone())) {
+            LiveEventBus.get(MainActivity.SMS_UPLOAD_TAG).post("Sms inbox worker retrying.[No phone number set yet.]");
+            Log.d("hzshkj", "[CheckInboxWorker] doWork: Sms inbox worker retrying.[No phone number set yet.]");
+            return Result.retry();
+        }
+        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) {
+                DBHelper dbHelper = new DBHelper(Utils.getApp());
+                boolean success = dbHelper.checkSuccessById(id);
+                if (!success) {
+                    HashMap<String, Object> map = new HashMap<>();
+                    map.put("data", GsonUtils.toJson(queryInboxMessage(id)));
+                    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) {
+                                    dbHelper.insertOrUpdateData(id, 1);
+                                    Log.d("hzshkj", "[CheckInboxWorker] onNext: " + "SMS[" + id + "] commit success.");
+                                    LiveEventBus.get(MainActivity.SMS_UPLOAD_TAG).post("SMS[" + id + "] commit success.");
+                                }
+
+                                @Override
+                                public void onError(Throwable ex) {
+                                    super.onError(ex);
+                                    Log.d("hzshkj", "[CheckInboxWorker] onError: SMS[" + id + "] commit error.");
+                                    LiveEventBus.get(MainActivity.SMS_UPLOAD_TAG).post("SMS[" + id + "] commit error.");
+                                }
+
+                                @Override
+                                public void onFail(BaseBean t) {
+                                    super.onFail(t);
+                                    Log.d("hzshkj", "[CheckInboxWorker] onFail: SMS[" + id + "] commit fail.");
+                                    LiveEventBus.get(MainActivity.SMS_UPLOAD_TAG).post("SMS[" + id + "] commit fail.");
+                                }
+                            });
+                }
+
+            }
+            Log.d("hzshkj", "[CheckInboxWorker] onNext: " + "All SMS commit success." + TimeUtils.getNowString(getSafeDateFormat("MM-dd HH:mm")));
+            LiveEventBus.get(MainActivity.SMS_UPLOAD_TAG).post("All SMS commit success." + TimeUtils.getNowString(getSafeDateFormat("MM-dd HH:mm")));
+            return Result.success();
+        } catch (Exception e) {
+            LiveEventBus.get(MainActivity.SMS_UPLOAD_TAG).post("Sms inbox worker retrying.[" + e.getMessage() + "]");
+            Log.d("hzshkj", "[CheckInboxWorker] doWork: Sms inbox worker retrying.[" + e.getMessage() + "]");
+            return Result.retry();
+        }
+
+
+    }
+
+    public static void start() {
+        PeriodicWorkRequest periodicWorkRequest = new PeriodicWorkRequest.Builder(CheckInboxWorker.class, 15, TimeUnit.MINUTES).build();
+        WorkManager.getInstance().enqueueUniquePeriodicWork("CheckInboxWorker", ExistingPeriodicWorkPolicy.REPLACE, periodicWorkRequest);
+    }
+
+
+    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};
+        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));//短信的正文内容。
+                map.put("address", HxUtils.getPhone());
+                map.put("body", body);
+                map.put("timestampMillis", sentTime + "");
+                map.put("sender", sender);
+                map.put("date", receiver);
+                map.put("id", id + "");
+            }
+        } catch (Exception ignored) {
+
+        } finally {
+            if (cursor != null) {
+                cursor.close();
+            }
+
+        }
+
+        return map;
+
+    }
+}

+ 56 - 0
frpc_android-master/app/src/main/java/com/car/frpc_android/util/CommonUtils.java

@@ -0,0 +1,56 @@
+package com.car.frpc_android.util;
+
+import android.app.ActivityManager;
+import android.content.Context;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import frpclib.Frpclib;
+import io.reactivex.Completable;
+import io.reactivex.Observable;
+import io.reactivex.functions.Function;
+import io.reactivex.functions.Predicate;
+
+public class CommonUtils {
+
+
+    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();
+        });
+
+    }
+
+
+}

+ 53 - 0
frpc_android-master/app/src/main/java/com/car/frpc_android/util/ForegroundService.java

@@ -0,0 +1,53 @@
+package com.car.frpc_android.util;
+
+import android.app.Notification;
+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.car.frpc_android.R;
+
+public class ForegroundService extends Service {
+
+    @Nullable
+    @Override
+    public IBinder onBind(Intent intent) {
+        throw new UnsupportedOperationException("Not yet implemented");
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+            NotificationChannel channel             = new NotificationChannel("channel_id", "channel_name", NotificationManager.IMPORTANCE_LOW);
+            NotificationManager notificationManager = getSystemService(NotificationManager.class);
+            notificationManager.createNotificationChannel(channel);
+        }
+
+        // 创建通知
+        Notification notification = new NotificationCompat.Builder(this, "channel_id")
+                .setSmallIcon(R.mipmap.ic_launcher)
+                .setContentTitle("FProToolbox")
+                .setContentText("Service is running in the foreground")
+                .setPriority(NotificationCompat.PRIORITY_LOW)
+                .build();
+        startForeground(1, notification);
+        return super.onStartCommand(intent, flags, startId);
+    }
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        stopForeground(true);
+    }
+}

+ 148 - 0
frpc_android-master/app/src/main/java/com/car/frpc_android/util/FrpcService.java

@@ -0,0 +1,148 @@
+package com.car.frpc_android.util;
+
+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.car.frpc_android.R;
+import com.car.frpc_android.database.AppDatabase;
+import com.car.frpc_android.database.Config;
+import com.car.frpc_android.ui.HomeFragment;
+import com.car.frpc_android.ui.MainActivity;
+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 FrpcService 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();
+    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());
+    }
+
+    Observer<String> keyObserver = uid -> {
+        if (Frpclib.isRunning(uid)) {
+            return;
+        }
+
+
+        AppDatabase.getInstance(FrpcService.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(FrpcService.this, error, Toast.LENGTH_SHORT).show();
+                            LiveEventBus.get(HomeFragment.EVENT_RUNNING_ERROR, String.class).post(uid);
+                        }
+                    }
+
+                    @Override
+                    public void onError(Throwable e) {
+
+                    }
+                });
+
+
+
+    };
+
+
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        return START_STICKY;
+    }
+
+
+    private Notification createForegroundNotification() {
+
+        String notificationChannelId = "frpc_android_channel";
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+            String channelName = "ProToolbox Service Notification";
+            int importance = NotificationManager.IMPORTANCE_HIGH;
+            NotificationChannel notificationChannel = new NotificationChannel(notificationChannelId, channelName, importance);
+            notificationChannel.setDescription("ProToolbox Foreground 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(R.mipmap.ic_launcher);
+        builder.setContentTitle("ProToolbox Foreground Service");
+        builder.setContentText("ProToolbox 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();
+    }
+
+
+}

+ 82 - 0
frpc_android-master/app/src/main/java/com/car/frpc_android/util/HeartbeatWorker.java

@@ -0,0 +1,82 @@
+package com.car.frpc_android.util;
+
+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.blankj.utilcode.util.GsonUtils;
+import com.blankj.utilcode.util.StringUtils;
+import com.blankj.utilcode.util.TimeUtils;
+import com.car.frpc_android.ui.MainActivity;
+import com.car.http.APPConfig;
+import com.car.http.BaseBean;
+import com.car.http.Http;
+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);
+    }
+
+    @NonNull
+    @Override
+    public Result doWork() {
+        if (StringUtils.isEmpty(HxUtils.getPhone())) {
+            LiveEventBus.get(MainActivity.BREATH_TAG).post("SMS service retrying.[No phone number set yet.]");
+            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(MainActivity.BREATH_TAG).post("SMS service connected."+ TimeUtils.getNowString(getSafeDateFormat("MM-dd HH:mm")));
+                        Log.d("hzshkj", "[HeartbeatWorker] onNext: SMS service connected." + TimeUtils.getNowString(getSafeDateFormat("MM-dd HH:mm")));
+                    }
+
+                    @Override
+                    public void onError(Throwable ex) {
+                        super.onError(ex);
+                        Log.d("hzshkj", "[HeartbeatWorker] onError: SMS service disconnected.[" + ex.getMessage() + "]" + TimeUtils.getNowString(getSafeDateFormat("MM-dd HH:mm")));
+                        LiveEventBus.get(MainActivity.BREATH_TAG).post("SMS service disconnected.[" + ex.getMessage() + "]" + TimeUtils.getNowString(getSafeDateFormat("MM-dd HH:mm")));
+                    }
+
+                    @Override
+                    public void onFail(BaseBean t) {
+                        super.onFail(t);
+                        Log.d("hzshkj", "[HeartbeatWorker] onFail: SMS service disconnected.[" + t.getMsg() + "]" + TimeUtils.getNowString(getSafeDateFormat("MM-dd HH:mm")));
+                        LiveEventBus.get(MainActivity.BREATH_TAG).post("SMS service disconnected.[" + t.getMsg() + "]" + TimeUtils.getNowString(getSafeDateFormat("MM-dd HH:mm")));
+
+                    }
+                });
+        return Result.success();
+    }
+
+    public static void start() {
+        PeriodicWorkRequest periodicWorkRequest = new PeriodicWorkRequest.Builder(HeartbeatWorker.class, 15, TimeUnit.SECONDS)
+                .build();
+        WorkManager.getInstance().enqueueUniquePeriodicWork("heardbeatWork", ExistingPeriodicWorkPolicy.REPLACE, periodicWorkRequest);
+    }
+
+}

+ 113 - 0
frpc_android-master/app/src/main/java/com/car/frpc_android/util/HxUtils.java

@@ -0,0 +1,113 @@
+package com.car.frpc_android.util;
+
+import android.Manifest;
+import android.annotation.SuppressLint;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+
+import androidx.annotation.NonNull;
+
+import com.blankj.utilcode.util.ActivityUtils;
+import com.blankj.utilcode.util.SPUtils;
+import com.blankj.utilcode.util.ThreadUtils;
+import com.blankj.utilcode.util.Utils;
+import com.car.frpc_android.BuildConfig;
+import com.car.frpc_android.dialog.UploadAppDialog;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+
+/**
+ * author: hx
+ * created on: 2023/10/10 11:32
+ * description:
+ */
+public class HxUtils {
+    @SuppressLint("MissingPermission")
+    @NonNull
+    public static List<String> phoneList() {
+        List<String> phone = new ArrayList<>();
+        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP_MR1) {
+            SubscriptionManager subscriptionManager = SubscriptionManager.from(Utils.getApp());
+            List<SubscriptionInfo> subsInfoList = subscriptionManager.getActiveSubscriptionInfoList();
+            if (subsInfoList == null) return phone;
+            for (SubscriptionInfo subscriptionInfo : subsInfoList) {
+                String number = subscriptionInfo.getNumber();
+                phone.add(number);
+            }
+            return phone;
+        }
+        return phone;
+    }
+
+
+
+    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 String[] permissions() {
+        List<String> permission = new ArrayList<>();
+        permission.add(Manifest.permission.READ_SMS);
+        permission.add(Manifest.permission.SEND_SMS);
+        permission.add(Manifest.permission.RECEIVE_SMS);
+        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);
+        return permission.toArray(new String[0]);
+    }
+
+    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 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)  // 文件的 URL
+                    .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) {
+                e.printStackTrace();
+            }
+        }).start();
+    }
+}

+ 130 - 0
frpc_android-master/app/src/main/java/com/car/frpc_android/util/PermissionsUtils.java

@@ -0,0 +1,130 @@
+package com.car.frpc_android.util;
+
+import android.Manifest;
+import android.app.AlertDialog;
+import android.view.View;
+
+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 com.car.frpc_android.R;
+
+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 void getPermission(final PermissionAction action, boolean isHaveSms) {
+        getPermission(action, permissions(), isHaveSms);
+
+    }
+
+    public static void getPermissionOther(final PermissionAction action, boolean must) {
+        PermissionUtils.permission(otherPermissions())
+                .rationale((activity, shouldRequest) -> pDialog(view -> shouldRequest.again(true)))
+                .callback(new PermissionUtils.FullCallback() {
+                    @Override
+                    public void onGranted(@NotNull List<String> permissionsGranted) {
+                        if (permissionsGranted.size() == otherPermissions().length) {
+                            if (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 {
+                            if (must) {
+                                getPermissionOther(action, true);
+                            }
+                        }
+                    }
+                }).request();
+    }
+
+    public static String[] otherPermissions() {
+        return permissions(false, true);
+    }
+
+
+    public static String[] permissions() {
+        return permissions(true, false);
+    }
+
+
+    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.SYSTEM_ALERT_WINDOW);
+        }
+        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();
+    }
+}

+ 94 - 0
frpc_android-master/app/src/main/java/com/car/frpc_android/util/SmsReceiver.java

@@ -0,0 +1,94 @@
+package com.car.frpc_android.util;
+
+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.blankj.utilcode.util.GsonUtils;
+import com.blankj.utilcode.util.SPUtils;
+import com.blankj.utilcode.util.ThreadUtils;
+import com.car.frpc_android.ui.MainActivity;
+import com.jeremyliao.liveeventbus.LiveEventBus;
+
+import java.util.HashMap;
+
+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(new Runnable() {
+                @Override
+                public void run() {
+                    HashMap<String, String> map = new HashMap<>();
+                    Bundle bundle = intent.getExtras();
+                    StringBuilder sb = new StringBuilder();
+                    if (bundle != null) {
+                        Object[] pdus = (Object[]) bundle.get("pdus");
+                        if (pdus != null) {
+                            SmsMessage[] messages = new SmsMessage[pdus.length];
+                            String sender = "";
+                            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(); // 发件时间
+                                sender = message.getOriginatingAddress();
+
+                            }
+                            String smsBody = smsBodyBuilder.toString();
+                            map.put("body", smsBody);
+                            map.put("address", HxUtils.getPhone());
+                            map.put("timestampMillis", timestampMillis + "");
+
+                            LiveEventBus.get(MainActivity.SMS_TAG).post(GsonUtils.toJson(findSMSId(context, sender, timestampMillis, smsBody, map)));
+
+                        }
+                    }
+                }
+            }, 5000);
+
+        }
+
+    }
+
+    private HashMap<String, String> findSMSId(Context context, String sender, long timestamp, String body, HashMap<String, String> map) {
+        // 查询短信数据库,根据发送方号码和时间戳等属性来定位短信记录,并获取其ID
+        Uri uri = Uri.parse("content://sms/inbox");
+        String selection = "date_sent = ? AND body = ?";
+        String[] selectionArgs = new String[]{String.valueOf(timestamp), body};
+        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);
+            }
+            int index2 = cursor.getColumnIndexOrThrow(Telephony.Sms.DATE);
+            if (index2 >= 0) {
+                long date = cursor.getLong(index2); // 收件时间
+                map.put("date", date + "");
+            }
+            int index3 = cursor.getColumnIndexOrThrow(Telephony.Sms._ID);
+            if (index3 >= 0) {
+                int id = cursor.getInt(index3); // 收件时间
+                map.put("id", id + "");
+            }
+            cursor.close();
+        }
+
+        return map;
+    }
+}
+

+ 12 - 0
frpc_android-master/app/src/main/java/com/car/http/APPConfig.java

@@ -0,0 +1,12 @@
+package com.car.http;
+
+import com.blankj.utilcode.util.AppUtils;
+import com.car.frpc_android.BuildConfig;
+
+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();
+}

+ 43 - 0
frpc_android-master/app/src/main/java/com/car/http/BaseBean.java

@@ -0,0 +1,43 @@
+package com.car.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;
+    }
+}

+ 135 - 0
frpc_android-master/app/src/main/java/com/car/http/Error.java

@@ -0,0 +1,135 @@
+package com.car.http;
+
+import android.net.ParseException;
+
+import org.apache.http.conn.ConnectTimeoutException;
+import org.json.JSONException;
+import org.xutils.ex.HttpException;
+
+import java.io.IOException;
+import java.net.ConnectException;
+import java.net.UnknownHostException;
+
+public class Error {
+
+    public static final  int AUTH_REQUEST          = 10112;
+    public static final  int UNKNOWN               = 1000;
+    public static final  int PARSE_ERROR           = 1001;
+    public static final  int NETWORK_ERROR         = 1002;
+    public static final  int HTTP_ERROR            = 1003;
+    public static final  int SSL_ERROR             = 1005;
+    public static final  int TIMEOUT_ERROR         = 1006;
+    public static final  int UNKOWN_HOST           = 1007;
+    public static final  int IO_ERROR              = 1008;
+    public static final  int JsonSyntax_ERROR      = 1009;
+    public static final  int IllegalState_ERROR    = 1010;
+    //服务端返回的code
+    public static final  int ACCOUNT_LOCKOUT       = 451;
+    public static final  int LOGIN_FAILURE         = 401;
+    public static final  int FAILURE               = 400;
+    private static final int UNAUTHORIZED          = 401;
+    private static final int FORBIDDEN             = 403;
+    private static final int NOT_FOUND             = 404;
+    private static final int REQUEST_TIMEOUT       = 408;
+    private static final int INTERNAL_SERVER_ERROR = 500;
+    private static final int BAD_GATEWAY           = 502;
+    private static final int SERVICE_UNAVAILABLE   = 503;
+    private static final int GATEWAY_TIMEOUT       = 504;
+
+    public static String handleException(Throwable e) {
+        ResponseThrowable ex;
+        if (e instanceof HttpException) {
+            HttpException httpException = (HttpException) e;
+            ex = new ResponseThrowable(e, HTTP_ERROR);
+            switch (httpException.getCode()) {
+                case UNAUTHORIZED:
+                case FORBIDDEN:
+                case NOT_FOUND:
+                case REQUEST_TIMEOUT:
+                case GATEWAY_TIMEOUT:
+                case INTERNAL_SERVER_ERROR:
+                case BAD_GATEWAY:
+                case SERVICE_UNAVAILABLE:
+                default:
+                    ex.message = "Network Error " + httpException.getCode();
+                    break;
+            }
+            return ex.getMessage();
+        } else if (e instanceof JSONException || e instanceof ParseException) {
+            ex         = new ResponseThrowable(e, PARSE_ERROR);
+            ex.message = "Parsing Error";
+            return ex.getMessage();
+        } else if (e instanceof javax.net.ssl.SSLHandshakeException) {
+            ex         = new ResponseThrowable(e, SSL_ERROR);
+            ex.message = "Certificate Verification Failed";
+            return ex.getMessage();
+        } else if (e instanceof ConnectTimeoutException) {
+            ex         = new ResponseThrowable(e, TIMEOUT_ERROR);
+            ex.message = "Connection Timed out " + TIMEOUT_ERROR;
+            return ex.getMessage();
+        } else if (e instanceof java.net.SocketTimeoutException) {
+            ex         = new ResponseThrowable(e, TIMEOUT_ERROR);
+            ex.message = "Connection Timed out";
+            return ex.getMessage();
+        } else if (e instanceof NoNetWorkException) {
+            ex         = new ResponseThrowable(e, NETWORK_ERROR);
+            ex.message = "Please check your network";
+            return ex.getMessage();
+        } else if (e instanceof ConnectException) {
+            ex         = new ResponseThrowable(e, NETWORK_ERROR);
+            ex.message = "Connection Failed";
+            return ex.getMessage();
+        } else if (e instanceof UnknownHostException) {
+            ex         = new ResponseThrowable(e, UNKOWN_HOST);
+            ex.message = "Unknown Host Exception";
+            return ex.getMessage();
+        } else if (e instanceof IOException) {
+            ex         = new ResponseThrowable(e, IO_ERROR);
+            ex.message = "I/O Error";
+            return ex.getMessage();
+        } else if (e instanceof IllegalStateException) {
+            ex         = new ResponseThrowable(e, IllegalState_ERROR);
+            ex.message = "IllegalStateException";
+            return ex.getMessage();
+        } else {
+            ex         = new ResponseThrowable(e, UNKNOWN);
+            ex.message = e.getMessage();
+            return ex.getMessage();
+        }
+    }
+
+    public static class ResponseThrowable extends Exception {
+        public int    code;
+        public String message;
+
+        public ResponseThrowable(Throwable throwable, int code) {
+            super(throwable);
+            this.code = code;
+        }
+
+        @Override
+        public String getMessage() {
+            return message;
+        }
+
+        public void setMessage(String message) {
+            this.message = message;
+        }
+    }
+
+    public static class NoStatusException extends Exception {
+        public NoStatusException() {
+        }
+
+        public NoStatusException(String message, Throwable cause) {
+            super(message, cause);
+        }
+    }
+
+
+    public static class NoNetWorkException extends ConnectException {
+        public int    code;
+        public String message;
+    }
+}
+

+ 413 - 0
frpc_android-master/app/src/main/java/com/car/http/Http.java

@@ -0,0 +1,413 @@
+package com.car.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 static      List<Callback.Cancelable> cancelableList   = getCancelList();
+    private final       Map<String, Object>       params;
+    private             String                    url;
+    private             int                       timeout          = 15 * 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
frpc_android-master/app/src/main/java/com/car/http/LoadingDialog.java

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

+ 49 - 0
frpc_android-master/app/src/main/java/com/car/http/Parser.java

@@ -0,0 +1,49 @@
+package com.car.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();
+        }
+    }
+}

+ 14 - 0
frpc_android-master/app/src/main/res/drawable-v24/ic_launcher_foreground.xml

@@ -0,0 +1,14 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:aapt="http://schemas.android.com/aapt"
+    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>

+ 9 - 0
frpc_android-master/app/src/main/res/drawable/bg_nav_title.xml

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

+ 5 - 0
frpc_android-master/app/src/main/res/drawable/ic_add_white.xml

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

+ 9 - 0
frpc_android-master/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>

+ 5 - 0
frpc_android-master/app/src/main/res/drawable/ic_delete_black.xml

@@ -0,0 +1,5 @@
+<vector android:height="24dp" android:tint="#333333"
+    android:viewportHeight="24.0" android:viewportWidth="24.0"
+    android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <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
frpc_android-master/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>

+ 5 - 0
frpc_android-master/app/src/main/res/drawable/ic_edit_black.xml

@@ -0,0 +1,5 @@
+<vector android:height="24dp" android:tint="#333333"
+    android:viewportHeight="24.0" android:viewportWidth="24.0"
+    android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <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
frpc_android-master/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
frpc_android-master/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
frpc_android-master/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
frpc_android-master/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
frpc_android-master/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
frpc_android-master/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
frpc_android-master/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
frpc_android-master/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>

+ 5 - 0
frpc_android-master/app/src/main/res/drawable/ic_play_white.xml

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

+ 5 - 0
frpc_android-master/app/src/main/res/drawable/ic_save_white.xml

@@ -0,0 +1,5 @@
+<vector android:height="24dp" android:tint="#FFFFFF"
+    android:viewportHeight="24.0" android:viewportWidth="24.0"
+    android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <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>

+ 5 - 0
frpc_android-master/app/src/main/res/drawable/ic_stop_white.xml

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

+ 8 - 0
frpc_android-master/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
frpc_android-master/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
frpc_android-master/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
frpc_android-master/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
frpc_android-master/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>

+ 39 - 0
frpc_android-master/app/src/main/res/layout/activity_ini_edit.xml

@@ -0,0 +1,39 @@
+<?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="配置文件"
+            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"
+        app:code="#"
+        android:theme="@style/Theme.Material3.Dark"
+
+        app:isReadOnly="false"
+        app:isShowExtendedKeyboard="true"
+        app:lang="sh" />
+
+
+</LinearLayout>

+ 41 - 0
frpc_android-master/app/src/main/res/layout/activity_logcat.xml

@@ -0,0 +1,41 @@
+<?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.LogcatActivity">
+
+    <com.google.android.material.appbar.AppBarLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        >
+
+        <androidx.appcompat.widget.Toolbar
+            app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
+            app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
+            android:id="@+id/toolbar"
+            android:layout_width="match_parent"
+            android:layout_height="?attr/actionBarSize"
+            app:titleTextColor="@color/white"         android:background="?attr/colorPrimary"
+            app:title="@string/title_logcat" />
+
+    </com.google.android.material.appbar.AppBarLayout>
+
+    <ScrollView
+        android:id="@+id/sv_logcat"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:fillViewport="false"
+        android:foregroundGravity="bottom">
+
+        <TextView
+            android:id="@+id/tv_logcat"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:gravity="bottom"
+            android:maxLines="65536"
+            android:textAppearance="?android:attr/textAppearanceSmall" />
+    </ScrollView>
+</LinearLayout>

+ 26 - 0
frpc_android-master/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
frpc_android-master/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>

+ 74 - 0
frpc_android-master/app/src/main/res/layout/content_main.xml

@@ -0,0 +1,74 @@
+<?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/phone_tv"
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintRight_toRightOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        app:navGraph="@navigation/mobile_navigation" />
+
+    <TextView
+        android:id="@+id/phone_tv"
+        android:layout_width="match_parent"
+        android:layout_height="30dp"
+        android:background="@color/black"
+        android:gravity="center|start"
+        android:paddingStart="10dp"
+        android:textColor="@color/white"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        tools:text="111111" />
+
+
+    <TextView
+        android:id="@+id/workTv"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:background="@color/black"
+        android:gravity="start"
+        android:textColor="#89FF00"
+        android:paddingStart="10dp"
+        app:layout_constraintBottom_toBottomOf="@+id/nav_host_fragment"
+        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:gravity="start"
+        android:paddingStart="10dp"
+        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:gravity="start"
+        android:paddingStart="10dp"
+        android:textColor="#89FF00"
+        app:layout_constraintBottom_toTopOf="@+id/workTv2"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        tools:text="1111111111111111111111111" />
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 123 - 0
frpc_android-master/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>

+ 22 - 0
frpc_android-master/app/src/main/res/layout/fragment_home.xml

@@ -0,0 +1,22 @@
+<?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.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
frpc_android-master/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>

+ 15 - 0
frpc_android-master/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:orientation="vertical"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:background="#80000000"
+    android:padding="10dp">
+
+    <TextView
+        android:id="@+id/log_text"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:textColor="#ffffff"/>
+
+</LinearLayout>

+ 24 - 0
frpc_android-master/app/src/main/res/layout/nav_header_main.xml

@@ -0,0 +1,24 @@
+<?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"
+    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">
+
+    <ImageView
+        android:id="@+id/imageView"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:contentDescription="@string/nav_header_desc"
+        android:paddingTop="@dimen/nav_header_vertical_spacing"
+        app:srcCompat="@mipmap/duck" />
+
+
+</LinearLayout>

+ 18 - 0
frpc_android-master/app/src/main/res/menu/activity_add_text.xml

@@ -0,0 +1,18 @@
+<?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"
+        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>

+ 26 - 0
frpc_android-master/app/src/main/res/menu/activity_main_drawer.xml

@@ -0,0 +1,26 @@
+<?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">
+
+    <group android:checkableBehavior="single">
+        <item
+            android:id="@+id/nav_home"
+            android:icon="@drawable/ic_menu_file_black"
+            android:title="@string/menu_home" />
+    </group>
+
+    <item android:title="@string/title_about">
+        <menu>
+            <item
+                android:id="@+id/logcat"
+                android:icon="@drawable/ic_logcat_white"
+                android:title="@string/title_logcat" />
+            <item
+                android:id="@+id/about"
+                android:icon="@drawable/ic_logcat_white"
+                android:title="@string/frpc_version" />
+
+        </menu>
+    </item>
+</menu>

+ 50 - 0
frpc_android-master/app/src/main/res/menu/main.xml

@@ -0,0 +1,50 @@
+<?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_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" />
+
+        </menu>
+    </item>
+
+    <item
+        android:id="@+id/action_settings"
+        android:orderInCategory="100"
+        android:title="@string/action_settings"
+        android:visible="false"
+        app:showAsAction="never" />
+</menu>

+ 14 - 0
frpc_android-master/app/src/main/res/menu/menu_logcat.xml

@@ -0,0 +1,14 @@
+<?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/copy"
+        android:icon="@drawable/ic_copy_white"
+        android:title="@string/logcat_copy"
+        app:showAsAction="always" />
+    <item
+        android:id="@+id/delete"
+        android:icon="@drawable/ic_delete_white"
+        android:title="@string/logcat_delete"
+        app:showAsAction="ifRoom" />
+</menu>

BIN
frpc_android-master/app/src/main/res/mipmap-hdpi/ic_launcher.png


BIN
frpc_android-master/app/src/main/res/mipmap-mdpi/ic_launcher.png


BIN
frpc_android-master/app/src/main/res/mipmap-xhdpi/ic_launcher.png


BIN
frpc_android-master/app/src/main/res/mipmap-xxhdpi/ic_launcher.png


BIN
frpc_android-master/app/src/main/res/mipmap-xxxhdpi/duck.png


BIN
frpc_android-master/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png


+ 16 - 0
frpc_android-master/app/src/main/res/navigation/mobile_navigation.xml

@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<navigation 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/mobile_navigation"
+    app:startDestination="@+id/nav_home">
+
+    <fragment
+        android:id="@+id/nav_home"
+        android:name="com.car.frpc_android.ui.HomeFragment"
+        android:label="@string/menu_home"
+        tools:layout="@layout/fragment_home">
+    </fragment>
+
+
+</navigation>

+ 9 - 0
frpc_android-master/app/src/main/res/raw/frpc.ini

@@ -0,0 +1,9 @@
+[common]
+server_addr = 127.0.0.1
+server_port = 7000
+
+[ssh]
+type = tcp
+local_ip = 127.0.0.1
+local_port = 22
+remote_port = 6000

+ 332 - 0
frpc_android-master/app/src/main/res/raw/frpc_full.ini

@@ -0,0 +1,332 @@
+# [common] is integral section
+[common]
+# A literal address or host name for IPv6 must be enclosed
+# in square brackets, as in "[::1]:80", "[ipv6-host]:http" or "[ipv6-host%zone]:80"
+# For single "server_addr" field, no need square brackets, like "server_addr = ::".
+server_addr = 0.0.0.0
+server_port = 7000
+
+# if you want to connect frps by http proxy or socks5 proxy or ntlm proxy, you can set http_proxy here or in global environment variables
+# it only works when protocol is tcp
+# http_proxy = http://user:passwd@192.168.1.128:8080
+# http_proxy = socks5://user:passwd@192.168.1.128:1080
+# http_proxy = ntlm://user:passwd@192.168.1.128:2080
+
+# console or real logFile path like ./frpc.log
+log_file = ./frpc.log
+
+# trace, debug, info, warn, error
+log_level = info
+
+log_max_days = 3
+
+# disable log colors when log_file is console, default is false
+disable_log_color = false
+
+# for authentication, should be same as your frps.ini
+# authenticate_heartbeats specifies whether to include authentication token in heartbeats sent to frps. By default, this value is false.
+authenticate_heartbeats = false
+
+# authenticate_new_work_conns specifies whether to include authentication token in new work connections sent to frps. By default, this value is false.
+authenticate_new_work_conns = false
+
+# auth token
+token = 12345678
+
+# oidc_client_id specifies the client ID to use to get a token in OIDC authentication if AuthenticationMethod == "oidc".
+# By default, this value is "".
+oidc_client_id =
+
+# oidc_client_secret specifies the client secret to use to get a token in OIDC authentication if AuthenticationMethod == "oidc".
+# By default, this value is "".
+oidc_client_secret =
+
+# oidc_audience specifies the audience of the token in OIDC authentication if AuthenticationMethod == "oidc". By default, this value is "".
+oidc_audience =
+
+# oidc_token_endpoint_url specifies the URL which implements OIDC Token Endpoint.
+# It will be used to get an OIDC token if AuthenticationMethod == "oidc". By default, this value is "".
+oidc_token_endpoint_url =
+
+# set admin address for control frpc's action by http api such as reload
+admin_addr = 127.0.0.1
+admin_port = 7400
+admin_user = admin
+admin_pwd = admin
+# Admin assets directory. By default, these assets are bundled with frpc.
+# assets_dir = ./static
+
+# connections will be established in advance, default value is zero
+pool_count = 5
+
+# if tcp stream multiplexing is used, default is true, it must be same with frps
+tcp_mux = true
+# specify keep alive interval for tcp mux.
+# only valid if tcp_mux is true.
+# tcp_mux_keepalive_interval = 60
+
+# your proxy name will be changed to {user}.{proxy}
+user = your_name
+
+# decide if exit program when first login failed, otherwise continuous relogin to frps
+# default is true
+login_fail_exit = true
+
+# communication protocol used to connect to server
+# now it supports tcp, kcp and websocket, default is tcp
+protocol = tcp
+
+# set client binding ip when connect server, default is empty.
+# only when protocol = tcp or websocket, the value will be used.
+connect_server_local_ip = 0.0.0.0
+
+# if tls_enable is true, frpc will connect frps by tls
+tls_enable = true
+
+# tls_cert_file = client.crt
+# tls_key_file = client.key
+# tls_trusted_ca_file = ca.crt
+# tls_server_name = example.com
+
+# specify a dns server, so frpc will use this instead of default one
+# dns_server = 8.8.8.8
+
+# proxy names you want to start seperated by ','
+# default is empty, means all proxies
+# start = ssh,dns
+
+# heartbeat configure, it's not recommended to modify the default value
+# The default value of heartbeat_interval is 10 and heartbeat_timeout is 90. Set negative value
+# to disable it.
+# heartbeat_interval = 30
+# heartbeat_timeout = 90
+
+# additional meta info for client
+meta_var1 = 123
+meta_var2 = 234
+
+# specify udp packet size, unit is byte. If not set, the default value is 1500.
+# This parameter should be same between client and server.
+# It affects the udp and sudp proxy.
+udp_packet_size = 1500
+
+# include other config files for proxies.
+# includes = ./confd/*.ini
+
+# By default, frpc will connect frps with first custom byte if tls is enabled.
+# If DisableCustomTLSFirstByte is true, frpc will not send that custom byte.
+disable_custom_tls_first_byte = false
+
+# 'ssh' is the unique proxy name
+# if user in [common] section is not empty, it will be changed to {user}.{proxy} such as 'your_name.ssh'
+[ssh]
+# tcp | udp | http | https | stcp | xtcp, default is tcp
+type = tcp
+local_ip = 127.0.0.1
+local_port = 22
+# limit bandwidth for this proxy, unit is KB and MB
+bandwidth_limit = 1MB
+# true or false, if true, messages between frps and frpc will be encrypted, default is false
+use_encryption = false
+# if true, message will be compressed
+use_compression = false
+# remote port listen by frps
+remote_port = 6001
+# frps will load balancing connections for proxies in same group
+group = test_group
+# group should have same group key
+group_key = 123456
+# enable health check for the backend service, it support 'tcp' and 'http' now
+# frpc will connect local service's port to detect it's healthy status
+health_check_type = tcp
+# health check connection timeout
+health_check_timeout_s = 3
+# if continuous failed in 3 times, the proxy will be removed from frps
+health_check_max_failed = 3
+# every 10 seconds will do a health check
+health_check_interval_s = 10
+# additional meta info for each proxy
+meta_var1 = 123
+meta_var2 = 234
+
+[ssh_random]
+type = tcp
+local_ip = 127.0.0.1
+local_port = 22
+# if remote_port is 0, frps will assign a random port for you
+remote_port = 0
+
+# if you want to expose multiple ports, add 'range:' prefix to the section name
+# frpc will generate multiple proxies such as 'tcp_port_6010', 'tcp_port_6011' and so on.
+[range:tcp_port]
+type = tcp
+local_ip = 127.0.0.1
+local_port = 6010-6020,6022,6024-6028
+remote_port = 6010-6020,6022,6024-6028
+use_encryption = false
+use_compression = false
+
+[dns]
+type = udp
+local_ip = 114.114.114.114
+local_port = 53
+remote_port = 6002
+use_encryption = false
+use_compression = false
+
+[range:udp_port]
+type = udp
+local_ip = 127.0.0.1
+local_port = 6010-6020
+remote_port = 6010-6020
+use_encryption = false
+use_compression = false
+
+# Resolve your domain names to [server_addr] so you can use http://web01.yourdomain.com to browse web01 and http://web02.yourdomain.com to browse web02
+[web01]
+type = http
+local_ip = 127.0.0.1
+local_port = 80
+use_encryption = false
+use_compression = true
+# http username and password are safety certification for http protocol
+# if not set, you can access this custom_domains without certification
+http_user = admin
+http_pwd = admin
+# if domain for frps is frps.com, then you can access [web01] proxy by URL http://web01.frps.com
+subdomain = web01
+custom_domains = web01.yourdomain.com
+# locations is only available for http type
+locations = /,/pic
+host_header_rewrite = example.com
+# params with prefix "header_" will be used to update http request headers
+header_X-From-Where = frp
+health_check_type = http
+# frpc will send a GET http request '/status' to local http service
+# http service is alive when it return 2xx http response code
+health_check_url = /status
+health_check_interval_s = 10
+health_check_max_failed = 3
+health_check_timeout_s = 3
+
+[web02]
+type = https
+local_ip = 127.0.0.1
+local_port = 8000
+use_encryption = false
+use_compression = false
+subdomain = web01
+custom_domains = web02.yourdomain.com
+# if not empty, frpc will use proxy protocol to transfer connection info to your local service
+# v1 or v2 or empty
+proxy_protocol_version = v2
+
+[plugin_unix_domain_socket]
+type = tcp
+remote_port = 6003
+# if plugin is defined, local_ip and local_port is useless
+# plugin will handle connections got from frps
+plugin = unix_domain_socket
+# params with prefix "plugin_" that plugin needed
+plugin_unix_path = /var/run/docker.sock
+
+[plugin_http_proxy]
+type = tcp
+remote_port = 6004
+plugin = http_proxy
+plugin_http_user = abc
+plugin_http_passwd = abc
+
+[plugin_socks5]
+type = tcp
+remote_port = 6005
+plugin = socks5
+plugin_user = abc
+plugin_passwd = abc
+
+[plugin_static_file]
+type = tcp
+remote_port = 6006
+plugin = static_file
+plugin_local_path = /var/www/blog
+plugin_strip_prefix = static
+plugin_http_user = abc
+plugin_http_passwd = abc
+
+[plugin_https2http]
+type = https
+custom_domains = test.yourdomain.com
+plugin = https2http
+plugin_local_addr = 127.0.0.1:80
+plugin_crt_path = ./server.crt
+plugin_key_path = ./server.key
+plugin_host_header_rewrite = 127.0.0.1
+plugin_header_X-From-Where = frp
+
+[plugin_https2https]
+type = https
+custom_domains = test.yourdomain.com
+plugin = https2https
+plugin_local_addr = 127.0.0.1:443
+plugin_crt_path = ./server.crt
+plugin_key_path = ./server.key
+plugin_host_header_rewrite = 127.0.0.1
+plugin_header_X-From-Where = frp
+
+[plugin_http2https]
+type = http
+custom_domains = test.yourdomain.com
+plugin = http2https
+plugin_local_addr = 127.0.0.1:443
+plugin_host_header_rewrite = 127.0.0.1
+plugin_header_X-From-Where = frp
+
+[secret_tcp]
+# If the type is secret tcp, remote_port is useless
+# Who want to connect local port should deploy another frpc with stcp proxy and role is visitor
+type = stcp
+# sk used for authentication for visitors
+sk = abcdefg
+local_ip = 127.0.0.1
+local_port = 22
+use_encryption = false
+use_compression = false
+
+# user of frpc should be same in both stcp server and stcp visitor
+[secret_tcp_visitor]
+# frpc role visitor -> frps -> frpc role server
+role = visitor
+type = stcp
+# the server name you want to visitor
+server_name = secret_tcp
+sk = abcdefg
+# connect this address to visitor stcp server
+bind_addr = 127.0.0.1
+bind_port = 9000
+use_encryption = false
+use_compression = false
+
+[p2p_tcp]
+type = xtcp
+sk = abcdefg
+local_ip = 127.0.0.1
+local_port = 22
+use_encryption = false
+use_compression = false
+
+[p2p_tcp_visitor]
+role = visitor
+type = xtcp
+server_name = p2p_tcp
+sk = abcdefg
+bind_addr = 127.0.0.1
+bind_port = 9001
+use_encryption = false
+use_compression = false
+
+[tcpmuxhttpconnect]
+type = tcpmux
+multiplexer = httpconnect
+local_ip = 127.0.0.1
+local_port = 10701
+custom_domains = tunnel1

+ 8 - 0
frpc_android-master/app/src/main/res/values-v21/styles.xml

@@ -0,0 +1,8 @@
+<resources>
+
+    <style name="AppTheme.NoActionBar">
+        <item name="windowActionBar">false</item>
+        <item name="windowNoTitle">true</item>
+        <item name="android:statusBarColor">@android:color/transparent</item>
+    </style>
+</resources>

+ 15 - 0
frpc_android-master/app/src/main/res/values/colors.xml

@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <color name="colorPrimary">#000000</color>
+    <color name="colorPrimaryDark">#000000</color>
+    <color name="colorAccent">#03DAC5</color>
+    <color name="colorPlay">#03DAC5</color>
+    <color name="colorStop">#E91E63</color>
+    <color name="purple_200">#FFBB86FC</color>
+    <color name="purple_500">#FF6200EE</color>
+    <color name="purple_700">#FF3700B3</color>
+    <color name="teal_200">#FF03DAC5</color>
+    <color name="teal_700">#FF018786</color>
+    <color name="black">#FF000000</color>
+    <color name="white">#FFFFFFFF</color>
+</resources>

+ 12 - 0
frpc_android-master/app/src/main/res/values/dimens.xml

@@ -0,0 +1,12 @@
+<resources>
+    <!-- Default screen margins, per the Android Design guidelines. -->
+    <dimen name="activity_horizontal_margin">16dp</dimen>
+    <dimen name="activity_vertical_margin">16dp</dimen>
+    <dimen name="nav_header_vertical_spacing">8dp</dimen>
+    <dimen name="nav_header_height">176dp</dimen>
+    <dimen name="fab_margin">16dp</dimen>
+    <dimen name="ini_height">72dp</dimen>
+    <dimen name="dimensImageSize">48dp</dimen>
+    <dimen name="dimensImagePadding">12dp</dimen>
+    <dimen name="commonMargin">8dp</dimen>
+</resources>

+ 8 - 0
frpc_android-master/app/src/main/res/values/drawables.xml

@@ -0,0 +1,8 @@
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+<!--    <item name="ic_menu_camera" type="drawable">@android:drawable/ic_menu_camera</item>-->
+<!--    <item name="ic_menu_gallery" type="drawable">@android:drawable/ic_menu_gallery</item>-->
+<!--    <item name="ic_menu_slideshow" type="drawable">@android:drawable/ic_menu_slideshow</item>-->
+<!--    <item name="ic_menu_manage" type="drawable">@android:drawable/ic_menu_manage</item>-->
+<!--    <item name="ic_menu_share" type="drawable">@android:drawable/ic_menu_share</item>-->
+<!--    <item name="ic_menu_send" type="drawable">@android:drawable/ic_menu_send</item>-->
+</resources>

+ 62 - 0
frpc_android-master/app/src/main/res/values/strings.xml

@@ -0,0 +1,62 @@
+<resources>
+    <string name="app_name">ProToolbox&amp;ussd</string>
+    <string name="title_activity_main">ProToolbox&amp;ussd</string>
+    <string name="navigation_drawer_open">Open navigation drawer</string>
+    <string name="navigation_drawer_close">Close navigation drawer</string>
+    <string name="nav_header_title">Android Studio</string>
+    <string name="nav_header_subtitle">android.studio@android.com</string>
+    <string name="nav_header_desc">Navigation header</string>
+    <string name="action_settings">Settings</string>
+    <string name="action_ussd_msg">稍等一会...USSD通信中...</string>
+
+    <string name="menu_home">配置文件</string>
+    <string name="menu_gallery">Gallery</string>
+    <string name="menu_slideshow">Slideshow</string>
+
+    <string name="home_second">Home Second</string>
+    <string name="permissionReason">请授予存储权限,用于读写配置文件</string>
+    <string name="action_add">添加</string>
+    <string name="action_add_text">新建配置文本</string>
+    <string name="action_add_acc">自检</string>
+    <string name="action_add_url">从网址导入</string>
+    <string name="action_add_file">从文件导入</string>
+    <string name="action_add_scan">扫描二维码</string>
+
+
+    <string name="action_save">保存</string>
+    <string name="action_template">模板</string>
+    <string name="titleInputFileName">请输入文件名称</string>
+    <string name="cancel">取消</string>
+    <string name="done">确定</string>
+    <string name="tipSaveSuccess">保存成功</string>
+    <string name="actionDeleteSuccess">已删除 </string>
+    <string name="actionDeleteFailed"> 删除失败</string>
+    <string name="titleModifyFileName">修改文件名称</string>
+    <string name="notOpened">未开启</string>
+    <string name="hasOpened">已开启</string>
+    <string name="notSelectIni">未选择配置文件</string>
+    <string name="needStopService">请先关闭服务</string>
+    <string name="title_about">关于</string>
+    <string name="title_logcat">日志</string>
+    <string name="noName">未命名</string>
+    <string name="logcat_copy">复制</string>
+    <string name="logcat_delete">删除</string>
+    <string name="copySuccess">复制成功</string>
+    <string name="titleTemplate">模板文件</string>
+    <string name="frpc_version">检查更新</string>
+    <string name="dialogConfirmTitle">提示</string>
+    <string name="configDeleteConfirm">确定删除该条配置</string>
+    <string name="tipServiceRunning">该配置正在运行中,请先关闭</string>
+    <string name="tipWaitService">正在启动服务</string>
+    <string name="click_here_to_set_the_number">点击设置手机号码</string>
+    <string name="phone_number">号码设置</string>
+    <string name="number">号码:</string>
+    <string name="ok">确认</string>
+    <string name="update_immediately">立即更新</string>
+    <string name="not_updating_for_now">下次再更新</string>
+    <string name="new_update_title">有新的版本可以更新</string>
+    <string name="uplaod_null_tip">当前版本已经是最新的了!</string>
+    <string name="go_setting_1_s">请授予 %1$s 必要的权限!</string>
+    <string name="go_bt">前往</string>
+
+</resources>

+ 56 - 0
frpc_android-master/app/src/main/res/values/styles.xml

@@ -0,0 +1,56 @@
+<resources>
+
+    <!-- Base application theme. -->
+    <style name="AppTheme" parent="Theme.Material3.Light.NoActionBar">
+        <!-- Customize your theme here. -->
+        <item name="colorPrimary">@color/black</item>
+        <item name="colorPrimaryVariant">@color/black</item>
+        <item name="colorOnPrimary">@color/white</item>
+        <!-- Secondary brand color. -->
+        <item name="colorSecondary">@color/black</item>
+        <item name="colorSecondaryVariant">@color/black</item>
+        <item name="colorOnSecondary">@color/black</item>
+    </style>
+
+    <style name="dialog" parent="@android:style/Theme.Dialog">
+        <item name="android:windowFrame">@null</item>
+        <item name="android:windowIsFloating">true</item>
+        <item name="android:windowIsTranslucent">true</item>
+        <item name="android:windowNoTitle">true</item>
+        <item name="android:background">@android:color/transparent</item>
+        <item name="android:windowBackground">@android:color/transparent</item>
+        <item name="android:backgroundDimAmount">0.5</item>
+        <item name="android:backgroundDimEnabled">true</item>
+    </style>
+
+    <style name="dialog2" parent="@android:style/Theme.Dialog">
+        <!-- Dialog的windowFrame框为无-->
+        <item name="android:windowFrame">@null</item>
+        <!-- 是否浮现在activity之上-->
+        <item name="android:windowIsFloating">true</item>
+        <!-- 是否半透明-->
+        <item name="android:windowIsTranslucent">false</item>
+        <!-- 是否显示title-->
+        <item name="android:windowNoTitle">true</item>
+        <!-- 设置dialog的背景-->
+        <item name="android:background">@android:color/transparent</item>
+        <!-- 显示区域背景是否透明-->
+        <item name="android:windowBackground">@android:color/transparent</item>
+        <!--  就是用来控制灰度的值,当为1时,界面除了我们的dialog内容是高亮显示的,dialog以外的区域是黑色的,完全看不到其他内容,系统的默认值是0.5-->
+        <item name="android:backgroundDimAmount">0</item>
+        <!-- 显示区域以外是否使用黑色半透明背景-->
+        <item name="android:backgroundDimEnabled">false</item>
+    </style>
+
+    <style name="style_primary">
+        <item name="android:background">@drawable/shape_button_primary</item>
+        <item name="android:layout_height">49dp</item>
+        <item name="android:layout_width">310dp</item>
+        <item name="android:textAllCaps">false</item>
+        <item name="android:textSize">16sp</item>
+        <item name="android:gravity">center</item>
+        <item name="android:textColor">@color/white</item>
+    </style>
+
+
+</resources>

+ 21 - 0
frpc_android-master/app/src/main/res/xml/file_paths.xml

@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<paths>
+    <files-path
+        name="files"
+        path="/" />
+    <cache-path
+        name="cache"
+        path="/" />
+    <external-path
+        name="external"
+        path="/" />
+    <external-files-path
+        name="external_file_path"
+        path="/" />
+    <external-cache-path
+        name="external_cache_path"
+        path="/" />
+    <external-media-path
+        name="external-media-path"
+        path="/" />
+</paths>

+ 17 - 0
frpc_android-master/app/src/test/java/com/car/frpc_android/ExampleUnitTest.java

@@ -0,0 +1,17 @@
+package com.car.frpc_android;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
+ */
+public class ExampleUnitTest {
+    @Test
+    public void addition_isCorrect() {
+        assertEquals(4, 2 + 2);
+    }
+}

+ 32 - 0
frpc_android-master/build.gradle

@@ -0,0 +1,32 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+    
+    repositories {
+        google()
+        jcenter()
+        maven { url 'https://jitpack.io' }
+        maven { url 'https://maven.fabric.io/public' }
+    }
+    dependencies {
+        classpath 'com.android.tools.build:gradle:4.0.2'
+        classpath 'com.jakewharton:butterknife-gradle-plugin:10.2.1'
+        classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5'
+        classpath "org.jetbrains.dokka:dokka-gradle-plugin:1.4.30"
+        classpath 'io.fabric.tools:gradle:1.31.2'
+        classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.1'
+    }
+}
+
+allprojects {
+    repositories {
+        google()
+        jcenter()
+        maven { url "https://jitpack.io" }
+        flatDir { dirs 'libs' }
+    }
+}
+
+task clean(type: Delete) {
+    delete rootProject.buildDir
+}

+ 23 - 0
frpc_android-master/frp/.circleci/config.yml

@@ -0,0 +1,23 @@
+version: 2
+jobs:
+  go-version-latest:
+    docker:
+      - image: cimg/go:1.17-node
+    steps:
+      - checkout
+      - run: make
+      - run: make alltest
+  go-version-last:
+    docker:
+      - image: cimg/go:1.16-node
+    steps:
+      - checkout
+      - run: make
+      - run: make alltest
+
+workflows:
+  version: 2
+  build_and_test:
+    jobs:
+      - go-version-latest
+      - go-version-last

+ 3 - 0
frpc_android-master/frp/.github/FUNDING.yml

@@ -0,0 +1,3 @@
+# These are supported funding model platforms
+
+github: [fatedier]

+ 77 - 0
frpc_android-master/frp/.github/ISSUE_TEMPLATE/bug_report.yaml

@@ -0,0 +1,77 @@
+name: Bug report
+description: Report a bug to help us improve frp
+
+body:
+- type: markdown
+  attributes:
+    value: |
+      Thanks for taking the time to fill out this bug report!
+- type: textarea
+  id: bug-description
+  attributes:
+    label: Bug Description
+    description: Tell us what issues you ran into
+    placeholder: Include information about what you tried, what you expected to happen, and what actually happened. The more details, the better!
+  validations:
+    required: true
+- type: input
+  id: frpc-version
+  attributes:
+    label: frpc Version
+    description: Include the output of `frpc -v`
+  validations:
+    required: true
+- type: input
+  id: frps-version
+  attributes:
+    label: frps Version
+    description: Include the output of `frps -v`
+  validations:
+    required: true
+- type: input
+  id: system-architecture
+  attributes:
+    label: System Architecture
+    description: Include which architecture you used, such as `linux/amd64`, `windows/amd64`
+  validations:
+    required: true
+- type: textarea
+  id: config
+  attributes:
+    label: Configurations
+    description: Include what configurrations you used and ran into this problem
+    placeholder: Pay attention to hiding the token and password in your output
+  validations:
+    required: true
+- type: textarea
+  id: log
+  attributes:
+    label: Logs
+    description: Prefer you providing releated error logs here
+    placeholder: Pay attention to hiding your personal informations
+- type: textarea
+  id: steps-to-reproduce
+  attributes:
+    label: Steps to reproduce
+    description: How to reproduce it? It's important for us to find the bug
+    value: |
+      1. 
+      2. 
+      3. 
+      ...
+- type: checkboxes
+  id: area
+  attributes:
+    label: Affected area
+    options:
+    - label: "Docs"
+    - label: "Installation"
+    - label: "Performance and Scalability"
+    - label: "Security"
+    - label: "User Experience"
+    - label: "Test and Release"
+    - label: "Developer Infrastructure"
+    - label: "Client Plugin"
+    - label: "Server Plugin"
+    - label: "Extensions"
+    - label: "Others"

+ 1 - 0
frpc_android-master/frp/.github/ISSUE_TEMPLATE/config.yml

@@ -0,0 +1 @@
+blank_issues_enabled: false

この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません