Ver código fonte

first commit

JaycePC 4 meses atrás
commit
8b9c5ecfb8
100 arquivos alterados com 1488 adições e 0 exclusões
  1. 76 0
      .gitignore
  2. 1 0
      app/.gitignore
  3. 67 0
      app/build.gradle
  4. BIN
      app/lib/sdkapi.jar
  5. 21 0
      app/proguard-rules.pro
  6. 26 0
      app/src/androidTest/java/xn/huaxue/update/ExampleInstrumentedTest.java
  7. 55 0
      app/src/main/AndroidManifest.xml
  8. 205 0
      app/src/main/java/xn/huaxue/update/MainActivity.java
  9. 86 0
      app/src/main/java/xn/huaxue/update/SettingActivity.java
  10. 102 0
      app/src/main/java/xn/huaxue/update/Tool.java
  11. 9 0
      app/src/main/java/xn/huaxue/update/constant/AppConstant.java
  12. 40 0
      app/src/main/java/xn/huaxue/update/http/HttpTool.java
  13. 61 0
      app/src/main/java/xn/huaxue/update/http/LogInterceptor.java
  14. 42 0
      app/src/main/java/xn/huaxue/update/http/OkHttpUtils.java
  15. 80 0
      app/src/main/java/xn/huaxue/update/http/bean/AllLogInterceptor.java
  16. 94 0
      app/src/main/java/xn/huaxue/update/http/bean/response/UpdateTask.java
  17. 18 0
      app/src/main/java/xn/huaxue/update/receiver/ProcessReceiver.java
  18. 186 0
      app/src/main/java/xn/huaxue/update/receiver/TimeTickReceiver.java
  19. 84 0
      app/src/main/java/xn/huaxue/update/service/TaskService.java
  20. 26 0
      app/src/main/java/xn/huaxue/update/works/TaskWork.java
  21. 14 0
      app/src/main/res/drawable-anydpi/ic_admin.xml
  22. 12 0
      app/src/main/res/drawable-anydpi/ic_back.xml
  23. 11 0
      app/src/main/res/drawable-anydpi/ic_celsius.xml
  24. 11 0
      app/src/main/res/drawable-anydpi/ic_cpu.xml
  25. 12 0
      app/src/main/res/drawable-anydpi/ic_eixt.xml
  26. 11 0
      app/src/main/res/drawable-anydpi/ic_et_err.xml
  27. 11 0
      app/src/main/res/drawable-anydpi/ic_folder.xml
  28. 11 0
      app/src/main/res/drawable-anydpi/ic_http.xml
  29. 11 0
      app/src/main/res/drawable-anydpi/ic_ip.xml
  30. 11 0
      app/src/main/res/drawable-anydpi/ic_mqtt.xml
  31. 11 0
      app/src/main/res/drawable-anydpi/ic_mqtt_account.xml
  32. 11 0
      app/src/main/res/drawable-anydpi/ic_mqtt_password.xml
  33. 14 0
      app/src/main/res/drawable-anydpi/ic_reboot.xml
  34. 11 0
      app/src/main/res/drawable-anydpi/ic_save.xml
  35. 11 0
      app/src/main/res/drawable-anydpi/ic_settings.xml
  36. 11 0
      app/src/main/res/drawable-anydpi/ic_sn.xml
  37. 14 0
      app/src/main/res/drawable-anydpi/ic_time.xml
  38. 11 0
      app/src/main/res/drawable-anydpi/ic_ver.xml
  39. BIN
      app/src/main/res/drawable-hdpi/ic_admin.png
  40. BIN
      app/src/main/res/drawable-hdpi/ic_back.png
  41. BIN
      app/src/main/res/drawable-hdpi/ic_celsius.png
  42. BIN
      app/src/main/res/drawable-hdpi/ic_cpu.png
  43. BIN
      app/src/main/res/drawable-hdpi/ic_eixt.png
  44. BIN
      app/src/main/res/drawable-hdpi/ic_et_err.png
  45. BIN
      app/src/main/res/drawable-hdpi/ic_folder.png
  46. BIN
      app/src/main/res/drawable-hdpi/ic_http.png
  47. BIN
      app/src/main/res/drawable-hdpi/ic_ip.png
  48. BIN
      app/src/main/res/drawable-hdpi/ic_mqtt.png
  49. BIN
      app/src/main/res/drawable-hdpi/ic_mqtt_account.png
  50. BIN
      app/src/main/res/drawable-hdpi/ic_mqtt_password.png
  51. BIN
      app/src/main/res/drawable-hdpi/ic_reboot.png
  52. BIN
      app/src/main/res/drawable-hdpi/ic_save.png
  53. BIN
      app/src/main/res/drawable-hdpi/ic_settings.png
  54. BIN
      app/src/main/res/drawable-hdpi/ic_sn.png
  55. BIN
      app/src/main/res/drawable-hdpi/ic_time.png
  56. BIN
      app/src/main/res/drawable-hdpi/ic_ver.png
  57. BIN
      app/src/main/res/drawable-mdpi/ic_admin.png
  58. BIN
      app/src/main/res/drawable-mdpi/ic_back.png
  59. BIN
      app/src/main/res/drawable-mdpi/ic_celsius.png
  60. BIN
      app/src/main/res/drawable-mdpi/ic_cpu.png
  61. BIN
      app/src/main/res/drawable-mdpi/ic_eixt.png
  62. BIN
      app/src/main/res/drawable-mdpi/ic_et_err.png
  63. BIN
      app/src/main/res/drawable-mdpi/ic_folder.png
  64. BIN
      app/src/main/res/drawable-mdpi/ic_http.png
  65. BIN
      app/src/main/res/drawable-mdpi/ic_ip.png
  66. BIN
      app/src/main/res/drawable-mdpi/ic_mqtt.png
  67. BIN
      app/src/main/res/drawable-mdpi/ic_mqtt_account.png
  68. BIN
      app/src/main/res/drawable-mdpi/ic_mqtt_password.png
  69. BIN
      app/src/main/res/drawable-mdpi/ic_reboot.png
  70. BIN
      app/src/main/res/drawable-mdpi/ic_save.png
  71. BIN
      app/src/main/res/drawable-mdpi/ic_settings.png
  72. BIN
      app/src/main/res/drawable-mdpi/ic_sn.png
  73. BIN
      app/src/main/res/drawable-mdpi/ic_time.png
  74. BIN
      app/src/main/res/drawable-mdpi/ic_ver.png
  75. BIN
      app/src/main/res/drawable-xhdpi/ic_admin.png
  76. BIN
      app/src/main/res/drawable-xhdpi/ic_back.png
  77. BIN
      app/src/main/res/drawable-xhdpi/ic_celsius.png
  78. BIN
      app/src/main/res/drawable-xhdpi/ic_cpu.png
  79. BIN
      app/src/main/res/drawable-xhdpi/ic_eixt.png
  80. BIN
      app/src/main/res/drawable-xhdpi/ic_et_err.png
  81. BIN
      app/src/main/res/drawable-xhdpi/ic_folder.png
  82. BIN
      app/src/main/res/drawable-xhdpi/ic_http.png
  83. BIN
      app/src/main/res/drawable-xhdpi/ic_ip.png
  84. BIN
      app/src/main/res/drawable-xhdpi/ic_mqtt.png
  85. BIN
      app/src/main/res/drawable-xhdpi/ic_mqtt_account.png
  86. BIN
      app/src/main/res/drawable-xhdpi/ic_mqtt_password.png
  87. BIN
      app/src/main/res/drawable-xhdpi/ic_reboot.png
  88. BIN
      app/src/main/res/drawable-xhdpi/ic_save.png
  89. BIN
      app/src/main/res/drawable-xhdpi/ic_settings.png
  90. BIN
      app/src/main/res/drawable-xhdpi/ic_sn.png
  91. BIN
      app/src/main/res/drawable-xhdpi/ic_time.png
  92. BIN
      app/src/main/res/drawable-xhdpi/ic_ver.png
  93. BIN
      app/src/main/res/drawable-xhdpi/logo.png
  94. BIN
      app/src/main/res/drawable-xxhdpi/ic_admin.png
  95. BIN
      app/src/main/res/drawable-xxhdpi/ic_back.png
  96. BIN
      app/src/main/res/drawable-xxhdpi/ic_celsius.png
  97. BIN
      app/src/main/res/drawable-xxhdpi/ic_cpu.png
  98. BIN
      app/src/main/res/drawable-xxhdpi/ic_eixt.png
  99. BIN
      app/src/main/res/drawable-xxhdpi/ic_et_err.png
  100. 0 0
      app/src/main/res/drawable-xxhdpi/ic_folder.png

+ 76 - 0
.gitignore

@@ -0,0 +1,76 @@
+# Built application files
+*.apk
+*.ap_
+
+# Files for the ART/Dalvik VM
+*.dex
+
+# Java class files
+*.class
+
+# Generated files
+bin/
+gen/
+out/
+build/
+
+# Gradle files
+.gradle/
+
+# Local configuration file (sdk path, etc)
+local.properties
+
+# Proguard folder generated by Eclipse
+proguard/
+
+# Log Files
+*.log
+
+# Android Studio Navigation editor temp files
+.navigation/
+
+# Android Studio captures folder
+captures/
+
+# IntelliJ
+*.iml
+.idea/workspace.xml
+.idea/libraries
+
+# Keystore files
+*.jks
+
+# External native build folder generated in Android Studio 2.2 and later
+.externalNativeBuild
+
+# Google Services (gcm, firebase)
+google-services.json
+
+# Freeline
+freeline/
+freeline_project_description.json
+
+# fastlane
+fastlane/report.xml
+fastlane/Preview.html
+fastlane/screenshots
+fastlane/test_output
+fastlane/readme.md
+
+# Version control
+.svn/
+
+# lint reports
+lint-results*.xml
+lint-results*.html
+
+# IDEA
+*.iws
+*.ipr
+*.idea/
+
+# Mac OS
+.DS_Store
+
+# Windows thumbnail db
+Thumbs.db

+ 1 - 0
app/.gitignore

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

+ 67 - 0
app/build.gradle

@@ -0,0 +1,67 @@
+plugins {
+    alias(libs.plugins.android.application)
+}
+
+android {
+    namespace 'xn.huaxue.update'
+    compileSdk 34
+
+    defaultConfig {
+        applicationId "xn.huaxue.update"
+        minSdk 24
+        //noinspection ExpiredTargetSdkVersion
+        targetSdk 28
+        versionCode 1
+        versionName "1.0"
+
+        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+    }
+
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+        }
+    }
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_8
+        targetCompatibility JavaVersion.VERSION_1_8
+    }
+    buildFeatures {
+        viewBinding true
+    }
+
+    applicationVariants.configureEach { variant ->
+        variant.outputs.configureEach { output ->
+            def formattedDate = new Date().format('yyyyMMddHHmm')
+            outputFileName = "xn_huaxue_update_${variant.versionName}_${formattedDate}.apk"
+        }
+    }
+}
+
+dependencies {
+
+    implementation libs.appcompat
+    implementation libs.material
+    implementation libs.activity
+    implementation libs.constraintlayout
+    implementation files('lib/sdkapi.jar')
+    testImplementation libs.junit
+    androidTestImplementation libs.ext.junit
+    androidTestImplementation libs.espresso.core
+
+    //noinspection UseTomlInstead
+    implementation 'com.blankj:utilcodex:1.31.1'
+    //noinspection UseTomlInstead
+    implementation 'com.github.getActivity:XXPermissions:20.0'
+    //noinspection UseTomlInstead
+    implementation 'com.google.android.flexbox:flexbox:3.0.0'
+    //noinspection UseTomlInstead
+    implementation 'com.squareup.okhttp3:okhttp:4.12.0'
+    //noinspection UseTomlInstead,GradleDependency
+    implementation "androidx.work:work-runtime:2.9.1"
+    //noinspection UseTomlInstead
+    implementation 'com.github.li-xiaojun:XPopup:2.10.0'
+    //noinspection UseTomlInstead
+    implementation 'com.github.li-xiaojun:XPopupExt:1.0.1'
+}

BIN
app/lib/sdkapi.jar


+ 21 - 0
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
app/src/androidTest/java/xn/huaxue/update/ExampleInstrumentedTest.java

@@ -0,0 +1,26 @@
+package xn.huaxue.update;
+
+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("xn.huaxue.update", appContext.getPackageName());
+    }
+}

+ 55 - 0
app/src/main/AndroidManifest.xml

@@ -0,0 +1,55 @@
+<?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" />
+    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.NOTIFICATION_SERVICE" />
+    <uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" />
+
+    <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:networkSecurityConfig="@xml/network_security_config"
+        android:requestLegacyExternalStorage="true"
+        android:roundIcon="@mipmap/ic_launcher_round"
+        android:supportsRtl="true"
+        android:theme="@style/Theme.西农化学更新"
+        tools:targetApi="31">
+        <activity
+            android:name=".SettingActivity"
+            android:exported="false" />
+        <activity
+            android:name=".MainActivity"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.intent.category.HOME" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <receiver
+            android:name=".receiver.ProcessReceiver"
+            android:exported="true"
+            tools:ignore="ExportedReceiver">
+            <intent-filter>
+                <action android:name="XN_ACTION" />
+            </intent-filter>
+        </receiver>
+
+        <service
+            android:name=".service.TaskService"
+            android:exported="true"
+            tools:ignore="ExportedService" />
+    </application>
+
+</manifest>

+ 205 - 0
app/src/main/java/xn/huaxue/update/MainActivity.java

@@ -0,0 +1,205 @@
+package xn.huaxue.update;
+
+import android.annotation.SuppressLint;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.View;
+import android.widget.EditText;
+
+import androidx.activity.EdgeToEdge;
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.core.graphics.Insets;
+import androidx.core.util.Pair;
+import androidx.core.view.ViewCompat;
+import androidx.core.view.WindowInsetsCompat;
+import androidx.work.ExistingPeriodicWorkPolicy;
+import androidx.work.PeriodicWorkRequest;
+import androidx.work.WorkManager;
+
+import com.blankj.utilcode.util.ActivityUtils;
+import com.blankj.utilcode.util.AppUtils;
+import com.blankj.utilcode.util.LogUtils;
+import com.blankj.utilcode.util.NetworkUtils;
+import com.blankj.utilcode.util.SPUtils;
+import com.blankj.utilcode.util.ThreadUtils;
+import com.blankj.utilcode.util.ToastUtils;
+import com.hjq.permissions.OnPermissionCallback;
+import com.hjq.permissions.Permission;
+import com.hjq.permissions.XXPermissions;
+import com.lxj.xpopup.XPopup;
+import com.lxj.xpopup.impl.InputConfirmPopupView;
+
+import org.json.JSONObject;
+
+import java.util.List;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.TimeUnit;
+
+import okhttp3.Response;
+import xn.huaxue.update.constant.AppConstant;
+import xn.huaxue.update.databinding.ActivityMainBinding;
+import xn.huaxue.update.http.HttpTool;
+import xn.huaxue.update.works.TaskWork;
+
+public class MainActivity extends AppCompatActivity {
+    private ActivityMainBinding binding;
+    private InputConfirmPopupView inputConfirmPopupView;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        initDev();
+        PeriodicWorkRequest taskRequest = new PeriodicWorkRequest.Builder(TaskWork.class, 15, TimeUnit.MINUTES).build();
+        WorkManager.getInstance(getApplicationContext()).enqueueUniquePeriodicWork("Task", ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE, taskRequest);
+
+        EdgeToEdge.enable(this);
+        binding = ActivityMainBinding.inflate(getLayoutInflater());
+        setContentView(binding.getRoot());
+        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
+            Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
+            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
+            return insets;
+        });
+        binding.versionNameTV.setText(AppUtils.getAppVersionName());
+        inputConfirmPopupView = new XPopup.Builder(this).autoDismiss(false).asInputConfirm("Tips", "请输入管理员密码",
+                text -> {
+                    if (text.equals(SPUtils.getInstance().getString(AppConstant.ADMIN_PASSWORD, "admin@098&"))) {
+                        ActivityUtils.startActivity(SettingActivity.class);
+                        inputConfirmPopupView.dismiss();
+                    } else {
+                        ToastUtils.showLong("密码不正确,请重新输入!");
+                    }
+                });
+        binding.logoIV.setOnLongClickListener(new View.OnLongClickListener() {
+            @Override
+            public boolean onLongClick(View v) {
+                if (AppUtils.isAppDebug()) {
+                    ActivityUtils.startActivity(SettingActivity.class);
+                } else {
+                    EditText editText = inputConfirmPopupView.getEditText();
+                    if (null != editText) {
+                        editText.setText("");
+                    }
+                    inputConfirmPopupView.show();
+                }
+                return true;
+            }
+        });
+        requestPermission();
+    }
+
+    private void initDev() {
+        Tool.INSTANCE.startMasterApp();
+        Tool.INSTANCE.openAdb();
+        Tool.INSTANCE.setLauncher();
+        SPUtils.getInstance().put("IP", NetworkUtils.getIPAddress(true));
+    }
+
+    private void requestPermission() {
+        // 全量权限申请
+        XXPermissions.with(this)
+                .permission(Permission.READ_EXTERNAL_STORAGE)
+                .permission(Permission.WRITE_EXTERNAL_STORAGE)
+                // 通知栏权限
+                .permission(Permission.NOTIFICATION_SERVICE)
+                .request(new OnPermissionCallback() {
+                    @Override
+                    public void onGranted(@NonNull List<String> permissions, boolean allGranted) {
+                        if (allGranted) {
+                            terminalAuth();
+                        } else {
+                            new XPopup.Builder(MainActivity.this)
+                                    .dismissOnBackPressed(false)
+                                    .dismissOnTouchOutside(false)
+                                    .asConfirm("Tips", "您必须同意所有权限才可以继续使用", ()
+                                            -> requestPermission())
+                                    .show();
+                        }
+                    }
+
+                    @Override
+                    public void onDenied(@NonNull List<String> permissions, boolean doNotAskAgain) {
+                        OnPermissionCallback.super.onDenied(permissions, doNotAskAgain);
+                        if (doNotAskAgain) {
+                            new XPopup.Builder(MainActivity.this)
+                                    .dismissOnBackPressed(false)
+                                    .dismissOnTouchOutside(false)
+                                    .asConfirm("Tips", "您必须同意所有权限才可以继续使用", ()
+                                            -> XXPermissions.startPermissionActivity(MainActivity.this, permissions))
+                                    .show();
+                        } else {
+                            new XPopup.Builder(MainActivity.this)
+                                    .dismissOnBackPressed(false)
+                                    .dismissOnTouchOutside(false)
+                                    .asConfirm("Tips", "您必须同意所有权限才可以继续使用", ()
+                                            -> requestPermission())
+                                    .show();
+                        }
+                    }
+                });
+    }
+
+
+    ThreadUtils.SimpleTask<Boolean> simpleTask = new ThreadUtils.SimpleTask<Boolean>() {
+        @Override
+        public Boolean doInBackground() throws Throwable {
+            return NetworkUtils.isAvailableByPing(Tool.INSTANCE.getBaseUrl().host());
+        }
+
+        @Override
+        public void onSuccess(Boolean result) {
+            if (result) {
+                binding.tipsTV.setText("鉴权中...");
+                ThreadUtils.executeByCached(new ThreadUtils.SimpleTask<Pair<Boolean, String>>() {
+                    @Override
+                    public Pair<Boolean, String> doInBackground() throws Throwable {
+                        try {
+                            SPUtils.getInstance().put("TerminalAuth", "");
+                            Response response = HttpTool.INSTANCE.terminalAuth();
+                            if (response.isSuccessful()) {
+                                String json = response.body().string();
+                                JSONObject jsonObject = new JSONObject(json);
+                                int code = jsonObject.getInt("code");
+                                if (200 == code) {
+                                    String data = jsonObject.getString("data");
+                                    SPUtils.getInstance().put("TerminalAuth", TextUtils.isEmpty(data) ? "" : data);
+                                    return Pair.create(true, "");
+                                } else {
+                                    return Pair.create(false, jsonObject.getString("message"));
+                                }
+                            }
+                        } catch (Exception e) {
+                            LogUtils.e(Log.getStackTraceString(e));
+                        }
+                        return Pair.create(false, "鉴权异常,请联系管理员!");
+                    }
+
+                    @Override
+                    public void onSuccess(Pair<Boolean, String> result) {
+                        Tool.INSTANCE.startTaskService();
+                        if (result.first) {
+                            binding.tipsTV.setText(result.second);
+                            ThreadUtils.cancel(simpleTask);
+                        } else {
+                            binding.tipsTV.setText(result.second);
+                        }
+                    }
+                });
+            } else {
+                binding.tipsTV.setText("无法连接到服务器,请联系管理员!");
+            }
+        }
+    };
+
+    private void terminalAuth() {
+        ThreadUtils.executeByCachedAtFixRate(simpleTask, ThreadLocalRandom.current().nextInt(10, 20), TimeUnit.SECONDS);
+    }
+
+    @SuppressLint("MissingSuperCall")
+    @Override
+    public void onBackPressed() {
+    }
+
+}

+ 86 - 0
app/src/main/java/xn/huaxue/update/SettingActivity.java

@@ -0,0 +1,86 @@
+package xn.huaxue.update;
+
+import android.annotation.SuppressLint;
+import android.os.Bundle;
+import android.text.Editable;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.View;
+import android.widget.CompoundButton;
+
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+
+import com.blankj.utilcode.util.AppUtils;
+import com.blankj.utilcode.util.LogUtils;
+import com.blankj.utilcode.util.NetworkUtils;
+import com.blankj.utilcode.util.SPUtils;
+import com.blankj.utilcode.util.ToastUtils;
+
+import xn.huaxue.update.constant.AppConstant;
+import xn.huaxue.update.databinding.ActivitySettingBinding;
+
+public class SettingActivity extends AppCompatActivity {
+    private ActivitySettingBinding binding;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        binding = ActivitySettingBinding.inflate(getLayoutInflater());
+        setContentView(binding.getRoot());
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+
+    }
+
+    @Override
+    protected void onPostCreate(@Nullable Bundle savedInstanceState) {
+        super.onPostCreate(savedInstanceState);
+        binding.varBT.setText("版本:" + AppUtils.getAppVersionName());
+        binding.snBT.setText("SN:" + Tool.INSTANCE.getSerialNumber());
+        binding.ipBT.setText(NetworkUtils.getIPAddress(true));
+        binding.rebootBT.setOnClickListener(v -> Tool.INSTANCE.cmd("reboot"));
+        binding.fileBrowserBT.setOnClickListener(v -> Tool.INSTANCE.openFileBrowser());
+        binding.settingBT.setOnClickListener(v -> Tool.INSTANCE.openSetting());
+        binding.barSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> Tool.INSTANCE.showBar(isChecked));
+        binding.httpUriET.setText(Tool.INSTANCE.getBaseUrl().toString());
+
+        // 测试地址输入
+        binding.httpUriTestBT.setOnClickListener(v -> binding.httpUriET.setText("http://192.168.1.8/api/"));
+        // 西农地址输入
+        binding.httpUriXnBT.setOnClickListener(v -> binding.httpUriET.setText("http://172.16.0.65/api/"));
+        // 管理员密码
+        binding.oldPasswordET.setText(SPUtils.getInstance().getString(AppConstant.ADMIN_PASSWORD, "admin@098&"));
+        // 保存
+        binding.save.setOnClickListener(v -> save());
+    }
+
+    private void save() {
+        try {
+            Editable httpUriETText = binding.httpUriET.getText();
+            if (null == httpUriETText || TextUtils.isEmpty(httpUriETText)) {
+                binding.httpUriET.setError("请输入Http服务地址");
+                return;
+            }
+            Editable oldPasswordETText = binding.oldPasswordET.getText();
+            if (null == oldPasswordETText || TextUtils.isEmpty(oldPasswordETText)) {
+                binding.oldPasswordET.setError("请输入管理员密码");
+                return;
+            }
+            SPUtils.getInstance().put(AppConstant.BASE_URL, httpUriETText.toString());
+            SPUtils.getInstance().put(AppConstant.ADMIN_PASSWORD, oldPasswordETText.toString());
+            finish();
+        } catch (Exception e) {
+            ToastUtils.showLong("参数异常无法保存,请核对参数!");
+            LogUtils.e(Log.getStackTraceString(e));
+        }
+    }
+
+    @SuppressLint("MissingSuperCall")
+    @Override
+    public void onBackPressed() {
+    }
+}

+ 102 - 0
app/src/main/java/xn/huaxue/update/Tool.java

@@ -0,0 +1,102 @@
+package xn.huaxue.update;
+
+import android.net.Uri;
+import android.util.Log;
+
+import com.blankj.utilcode.util.AppUtils;
+import com.blankj.utilcode.util.LogUtils;
+import com.blankj.utilcode.util.SPUtils;
+import com.blankj.utilcode.util.ShellUtils;
+import com.blankj.utilcode.util.Utils;
+import com.lztek.toolkit.Lztek;
+
+import java.util.Locale;
+
+import okhttp3.HttpUrl;
+import xn.huaxue.update.constant.AppConstant;
+
+public enum Tool {
+    INSTANCE;
+    private Lztek lztek;
+
+    private Tool() {
+        lztek = Lztek.create(Utils.getApp());
+        boolean enable = lztek.getEthEnable();
+        LogUtils.d("九畅SDK是否可用", enable);
+        // 打开有线网
+        lztek.setEthEnable(true);
+        showBar(false);
+    }
+
+    public void cmd(String cmd) {
+        LogUtils.d(cmd);
+        lztek.suExec(cmd);
+    }
+
+    public void showBar(boolean isShow) {
+        lztek.navigationBarSlideShow(isShow);
+    }
+
+    public void reStartApp() {
+        exitApp();
+        cmd("monkey -p " + AppUtils.getAppPackageName() + " -c android.intent.category.LAUNCHER 1");
+    }
+
+    public void openFileBrowser() {
+        openApp("com.android.documentsui");
+    }
+
+    public void exitApp() {
+        cmd("am force-stop " + AppUtils.getAppPackageName());
+    }
+
+    public void openSetting() {
+        openApp("com.android.settings");
+    }
+
+    public void openApp(String packageName) {
+        cmd("monkey -p " + packageName + " -c android.intent.category.LAUNCHER 1");
+    }
+
+    public void openAdb() {
+        cmd("setprop persist.internet_adb_enable 1");
+    }
+
+    public ShellUtils.CommandResult stopApp(String packageName) {
+        return ShellUtils.execCmd("am force-stop " + packageName, true);
+    }
+
+    public void setLauncher() {
+        cmd("pm set-home-activity " + AppUtils.getAppPackageName());
+    }
+
+    public void startMasterApp() {
+        openApp(SPUtils.getInstance().getString("masterApp", "com.zhong.che"));
+    }
+
+    public void startTaskService() {
+        cmd("am startservice -n xn.huaxue.update/xn.huaxue.update.service.TaskService");
+    }
+
+    public String getSerialNumber() {
+        String mac = lztek.getEthMac().toUpperCase(Locale.getDefault());
+        return mac.replace(":", "");
+    }
+
+    public HttpUrl getBaseUrl() {
+        return HttpUrl.get(SPUtils.getInstance().getString(AppConstant.BASE_URL, "http://172.16.0.65/api/"));
+    }
+
+    public String checkUrl(String url) {
+        try {
+            String uriStr = url;
+            if (!url.matches("https?://.*") && !url.matches("http?://.*")) {
+                uriStr = Uri.parse(getBaseUrl() + url).toString();
+            }
+            return uriStr;
+        } catch (Exception e) {
+            LogUtils.e(Log.getStackTraceString(e));
+            return url;
+        }
+    }
+}

+ 9 - 0
app/src/main/java/xn/huaxue/update/constant/AppConstant.java

@@ -0,0 +1,9 @@
+package xn.huaxue.update.constant;
+
+public final class AppConstant {
+    private AppConstant() {
+    }
+
+    public static final String ADMIN_PASSWORD = "admin_password";
+    public static final String BASE_URL = "base_url";
+}

+ 40 - 0
app/src/main/java/xn/huaxue/update/http/HttpTool.java

@@ -0,0 +1,40 @@
+package xn.huaxue.update.http;
+
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.IOException;
+
+import okhttp3.Response;
+import xn.huaxue.update.Tool;
+
+public enum HttpTool {
+    INSTANCE;
+
+    public Response terminalAuth() throws IOException, JSONException {
+        JSONObject jsonObject = new JSONObject();
+        jsonObject.put("deviceCode", Tool.INSTANCE.getSerialNumber());
+        jsonObject.put("code", "aio_chemical");
+        return OkHttpUtils.INSTANCE.postSync(Tool.INSTANCE.getBaseUrl() + "terminal/authorize", jsonObject.toString());
+    }
+
+    public Response update() throws IOException, JSONException {
+        JSONObject jsonObject = new JSONObject();
+        jsonObject.put("deviceCode", Tool.INSTANCE.getSerialNumber());
+        return OkHttpUtils.INSTANCE.postSync(Tool.INSTANCE.getBaseUrl() + "terminal/upgrade/device/task", jsonObject.toString());
+    }
+
+    public Response updateCallBack(String taskId, String deviceCode, boolean isDownload, boolean isInstall) throws IOException, JSONException {
+        JSONObject jsonObject = new JSONObject();
+        jsonObject.put("taskId", taskId);
+        jsonObject.put("deviceCode", deviceCode);
+        if (isDownload) {
+            jsonObject.put("status", "downloaded");
+        }
+        if (isInstall) {
+            jsonObject.put("status", "success");
+        }
+        return OkHttpUtils.INSTANCE.postSync(Tool.INSTANCE.getBaseUrl() + "terminal/upgrade/device/callback", jsonObject.toString());
+    }
+}

+ 61 - 0
app/src/main/java/xn/huaxue/update/http/LogInterceptor.java

@@ -0,0 +1,61 @@
+package xn.huaxue.update.http;
+
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import com.blankj.utilcode.util.GsonUtils;
+import com.blankj.utilcode.util.LogUtils;
+import com.blankj.utilcode.util.SPUtils;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+import okhttp3.Interceptor;
+import okhttp3.Request;
+import okhttp3.Response;
+import okhttp3.ResponseBody;
+import okio.Buffer;
+import okio.BufferedSource;
+import xn.huaxue.update.Tool;
+
+public class LogInterceptor implements Interceptor {
+    @NonNull
+    @Override
+    public Response intercept(@NonNull Chain chain) throws IOException {
+        try {
+            Request oldRequest = chain.request();
+            Request.Builder newRequestBuilder = oldRequest.newBuilder();
+            String terminalAuth = SPUtils.getInstance().getString("TerminalAuth", "");
+            if (null != terminalAuth && !TextUtils.isEmpty(terminalAuth)) {
+                newRequestBuilder.header("TerminalAuth", "Bearer " + terminalAuth);
+            }
+            newRequestBuilder.header("SN", Tool.INSTANCE.getSerialNumber());
+            newRequestBuilder.header("IP", SPUtils.getInstance().getString("IP", "127.0.0.1"));
+
+            Request newRequest = newRequestBuilder.build();
+            // 记录请求报文
+            String requestLog = "--> " + newRequest.method() + " " + newRequest.url() + "\n"
+                    + newRequest.headers() + "\n"
+                    + (newRequest.body() != null ? GsonUtils.toJson(newRequest.body()) : ""); // 请求体可能为空
+            LogUtils.d(requestLog);
+
+            Response response = chain.proceed(newRequest);
+            ResponseBody responseBody = response.body();
+            BufferedSource source = responseBody.source();
+            source.request(Long.MAX_VALUE);
+            Buffer buffer = source.getBuffer();
+            String responseBodyString = buffer.clone().readString(StandardCharsets.UTF_8);
+            // 记录响应报文
+            String responseLog = "<-- " + response.code() + " " + response.message() + " " + response.request().url() + "\n"
+                    + response.headers() + "\n"
+                    + (response.body() != null ? responseBodyString : ""); // 响应体可能为空
+            LogUtils.d(responseLog);
+            return response.newBuilder().body(ResponseBody.create(responseBody.contentType(), responseBodyString)).build();
+        } catch (Exception e) {
+            LogUtils.e(Log.getStackTraceString(e));
+        }
+        return chain.proceed(chain.request());
+    }
+}

+ 42 - 0
app/src/main/java/xn/huaxue/update/http/OkHttpUtils.java

@@ -0,0 +1,42 @@
+package xn.huaxue.update.http;
+
+import java.io.IOException;
+
+import okhttp3.Call;
+import okhttp3.MediaType;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.RequestBody;
+import okhttp3.Response;
+import xn.huaxue.update.http.bean.AllLogInterceptor;
+
+public enum OkHttpUtils {
+    INSTANCE;
+    private final OkHttpClient client = new OkHttpClient.Builder()
+            .addInterceptor(new AllLogInterceptor())
+            .build();
+    private final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
+
+    private OkHttpUtils() {
+    }
+
+    // 同步 GET 请求
+    public Response getSync(String url) throws IOException {
+        Request request = new Request.Builder()
+                .url(url)
+                .build();
+        Call call = client.newCall(request);
+        return call.execute();
+    }
+
+    // 同步 POST 请求
+    public Response postSync(String url, String json) throws IOException {
+        RequestBody body = RequestBody.create(JSON, json);
+        Request request = new Request.Builder()
+                .url(url)
+                .post(body)
+                .build();
+        Call call = client.newCall(request);
+        return call.execute();
+    }
+}

+ 80 - 0
app/src/main/java/xn/huaxue/update/http/bean/AllLogInterceptor.java

@@ -0,0 +1,80 @@
+package xn.huaxue.update.http.bean;
+
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import com.blankj.utilcode.util.LogUtils;
+import com.blankj.utilcode.util.SPUtils;
+
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+
+import okhttp3.Interceptor;
+import okhttp3.Request;
+import okhttp3.RequestBody;
+import okhttp3.Response;
+import okhttp3.ResponseBody;
+import okio.Buffer;
+import okio.BufferedSource;
+import xn.huaxue.update.Tool;
+
+public class AllLogInterceptor implements Interceptor {
+    @NonNull
+    @Override
+    public Response intercept(@NonNull Chain chain) throws IOException {
+        try {
+            Request request = chain.request();
+            Request.Builder newRequestBuilder = request.newBuilder();
+            String terminalAuth = SPUtils.getInstance().getString("TerminalAuth", "");
+            if (null != terminalAuth && !TextUtils.isEmpty(terminalAuth)) {
+                newRequestBuilder.header("TerminalAuth", "Bearer " + terminalAuth);
+            }
+            newRequestBuilder.header("SN", Tool.INSTANCE.getSerialNumber());
+            newRequestBuilder.header("IP", SPUtils.getInstance().getString("IP", "127.0.0.1"));
+            request = newRequestBuilder.build();
+            long t1 = System.nanoTime();
+            // 记录请求信息
+            StringBuilder requestLog = new StringBuilder();
+            requestLog.append("--> ").append(request.method()).append(" ").append(request.url()).append("\n");
+            requestLog.append(request.headers()).append("\n");
+            // 记录请求体
+            RequestBody requestBody = request.body();
+            if (requestBody != null) {
+                Buffer buffer = new Buffer();
+                requestBody.writeTo(buffer);
+                Charset charset = requestBody.contentType() != null ? requestBody.contentType().charset(StandardCharsets.UTF_8) : StandardCharsets.UTF_8;
+                requestLog.append(buffer.readString(charset)).append("\n");
+            }
+            // 打印请求信息
+            LogUtils.d(requestLog);
+            // 执行请求
+            Response response = chain.proceed(request);
+            long t2 = System.nanoTime();
+            // 记录响应信息
+            StringBuilder responseLog = new StringBuilder();
+            responseLog.append("<-- ").append(response.code()).append(" ").append(response.message()).append(" ").append(response.request().url()).append(" (").append((t2 - t1) / 1e6d).append("ms)\n\n");
+//            responseLog.append(response.headers()).append("\n");
+            // 记录响应体
+            ResponseBody responseBody = response.body();
+            if (responseBody != null) {
+                BufferedSource source = responseBody.source();
+                source.request(Long.MAX_VALUE); // Buffer the entire body.
+                Buffer buffer = source.getBuffer();
+                Charset charset = responseBody.contentType() != null ? responseBody.contentType().charset(StandardCharsets.UTF_8) : StandardCharsets.UTF_8;
+                responseLog.append(buffer.clone().readString(charset)).append("\n"); // 使用 clone() 避免消耗响应体
+                response = response.newBuilder()
+                        .body(ResponseBody.create(responseBody.contentType(), buffer.size(), buffer))
+                        .build();
+            }
+            // 打印响应信息
+            LogUtils.d(responseLog);
+            return response;
+        } catch (Exception e) {
+            LogUtils.e(Log.getStackTraceString(e));
+        }
+        return chain.proceed(chain.request());
+    }
+}

+ 94 - 0
app/src/main/java/xn/huaxue/update/http/bean/response/UpdateTask.java

@@ -0,0 +1,94 @@
+package xn.huaxue.update.http.bean.response;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class UpdateTask {
+
+
+    private int code;
+    private String message;
+    private List<Task> data = new ArrayList<>();
+
+    public int getCode() {
+        return code;
+    }
+
+    public void setCode(int code) {
+        this.code = code;
+    }
+
+    public String getMessage() {
+        return message;
+    }
+
+    public void setMessage(String message) {
+        this.message = message;
+    }
+
+    public List<Task> getData() {
+        return data;
+    }
+
+    public void setData(List<Task> data) {
+        this.data = data;
+    }
+
+    public static class Task {
+
+        private String taskId;
+        private String deviceCode;
+        private String apkUrl;
+        private String versionName;
+        private String startLaunchPackage;
+        private boolean isForceUpdate;
+
+        public String getTaskId() {
+            return taskId;
+        }
+
+        public void setTaskId(String taskId) {
+            this.taskId = taskId;
+        }
+
+        public String getDeviceCode() {
+            return deviceCode;
+        }
+
+        public void setDeviceCode(String deviceCode) {
+            this.deviceCode = deviceCode;
+        }
+
+        public String getApkUrl() {
+            return apkUrl;
+        }
+
+        public void setApkUrl(String apkUrl) {
+            this.apkUrl = apkUrl;
+        }
+
+        public String getVersionName() {
+            return versionName;
+        }
+
+        public void setVersionName(String versionName) {
+            this.versionName = versionName;
+        }
+
+        public String getStartLaunchPackage() {
+            return startLaunchPackage;
+        }
+
+        public void setStartLaunchPackage(String startLaunchPackage) {
+            this.startLaunchPackage = startLaunchPackage;
+        }
+
+        public boolean isIsForceUpdate() {
+            return isForceUpdate;
+        }
+
+        public void setIsForceUpdate(boolean isForceUpdate) {
+            this.isForceUpdate = isForceUpdate;
+        }
+    }
+}

+ 18 - 0
app/src/main/java/xn/huaxue/update/receiver/ProcessReceiver.java

@@ -0,0 +1,18 @@
+package xn.huaxue.update.receiver;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+import com.blankj.utilcode.util.AppUtils;
+import com.blankj.utilcode.util.LogUtils;
+
+public class ProcessReceiver extends BroadcastReceiver {
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        // 其他应用发送过来的信息
+        if (!AppUtils.getAppPackageName().equals(intent.getPackage())) {
+            LogUtils.d(intent, intent.getAction(), intent.getBooleanExtra("isDebug", false));
+        }
+    }
+}

+ 186 - 0
app/src/main/java/xn/huaxue/update/receiver/TimeTickReceiver.java

@@ -0,0 +1,186 @@
+package xn.huaxue.update.receiver;
+
+import android.annotation.SuppressLint;
+import android.app.DownloadManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Environment;
+import android.util.Log;
+
+import com.blankj.utilcode.util.ActivityUtils;
+import com.blankj.utilcode.util.AppUtils;
+import com.blankj.utilcode.util.FileUtils;
+import com.blankj.utilcode.util.GsonUtils;
+import com.blankj.utilcode.util.LogUtils;
+import com.blankj.utilcode.util.SPUtils;
+import com.blankj.utilcode.util.ShellUtils;
+import com.blankj.utilcode.util.ThreadUtils;
+
+import org.json.JSONException;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.List;
+
+import okhttp3.Response;
+import xn.huaxue.update.Tool;
+import xn.huaxue.update.http.HttpTool;
+import xn.huaxue.update.http.bean.response.UpdateTask;
+
+public class TimeTickReceiver extends BroadcastReceiver {
+    private volatile boolean isRunning = false;
+    private DownloadManager downloadManager;
+    private BroadcastReceiver downloadApkReceiver;
+    private long downloadId;
+    @SuppressLint("SdCardPath")
+    private final String apkPath = "/sdcard/Download/apk/";
+    private final String apkName = "app.apk";
+    private UpdateTask.Task task;
+
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        // 保活用
+        Tool.INSTANCE.startTaskService();
+        Intent newIntent = new Intent("XN_ACTION");
+        intent.putExtra("heartbeat", "heartbeat");
+        context.sendBroadcast(newIntent);
+        LogUtils.d("分钟", isRunning, ActivityUtils.getActivityList());
+        if (!isRunning) {
+            if (null == downloadManager) {
+                downloadManager = (DownloadManager) context.getApplicationContext().getSystemService(Context.DOWNLOAD_SERVICE);
+                downloadApkReceiver = new BroadcastReceiver() {
+                    @Override
+                    public void onReceive(Context context, Intent intent) {
+                        long id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
+                        if (downloadId == id) {
+                            if (null != task) {
+                                AsyncTask.execute(() -> {
+                                    try {
+                                        HttpTool.INSTANCE.updateCallBack(task.getTaskId(), task.getDeviceCode(), true, false);
+                                    } catch (IOException | JSONException e) {
+                                        LogUtils.e(Log.getStackTraceString(e));
+                                    }
+                                });
+                                if (task.getStartLaunchPackage().equals(AppUtils.getAppPackageName())) {
+                                    AsyncTask.execute(() -> {
+                                        try {
+                                            HttpTool.INSTANCE.updateCallBack(task.getTaskId(), task.getDeviceCode(), true, true);
+                                            Tool.INSTANCE.cmd("pm install -d -g " + apkPath + apkName);
+                                            Tool.INSTANCE.openApp(task.getStartLaunchPackage());
+                                        } catch (IOException | JSONException e) {
+                                            LogUtils.e(Log.getStackTraceString(e));
+                                        }
+                                    });
+                                } else {
+                                    ShellUtils.CommandResult commandResult = ShellUtils.execCmd("pm install -d -g " + apkPath + apkName, true);
+                                    if (commandResult.result == 0) {
+                                        AsyncTask.execute(() -> {
+                                            try {
+                                                HttpTool.INSTANCE.updateCallBack(task.getTaskId(), task.getDeviceCode(), true, true);
+                                            } catch (IOException | JSONException e) {
+                                                LogUtils.e(Log.getStackTraceString(e));
+                                            }
+                                        });
+                                        Tool.INSTANCE.startMasterApp();
+                                    } else {
+                                        LogUtils.e(commandResult.errorMsg);
+                                    }
+                                }
+                            }
+                            isRunning = false;
+                        }
+                    }
+                };
+            }
+            ThreadUtils.executeByCached(new ThreadUtils.SimpleTask<Boolean>() {
+                @SuppressLint({"UnspecifiedRegisterReceiverFlag", "SdCardPath"})
+                @Override
+                public Boolean doInBackground() throws Throwable {
+                    isRunning = true;
+                    try {
+                        Response response = HttpTool.INSTANCE.update();
+                        if (response.isSuccessful()) {
+                            String json = response.body().string();
+                            UpdateTask updateTask = GsonUtils.fromJson(json, UpdateTask.class);
+                            if (updateTask.getCode() == 200) {
+                                List<UpdateTask.Task> updateTaskData = updateTask.getData();
+                                if (null != updateTaskData && !updateTaskData.isEmpty()) {
+                                    task = updateTaskData.get(0);
+                                    FileUtils.createOrExistsDir(apkPath);
+                                    FileUtils.deleteAllInDir(apkPath);
+                                    if (!AppUtils.getAppPackageName().equals(task.getStartLaunchPackage())) {
+                                        SPUtils.getInstance().put("masterApp", task.getStartLaunchPackage());
+                                    }
+                                    DownloadManager.Request request = new DownloadManager.Request(Uri.parse(Tool.INSTANCE.checkUrl(task.getApkUrl())));
+                                    request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "apk/" + apkName);
+                                    request.setMimeType("application/vnd.android.package-archive");
+                                    request.setVisibleInDownloadsUi(false);
+                                    request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_HIDDEN);
+                                    context.registerReceiver(downloadApkReceiver, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
+                                    downloadId = downloadManager.enqueue(request);
+                                    return true;
+                                }
+                            } else {
+                                LogUtils.e(updateTask.getMessage());
+                            }
+                        }
+                    } catch (Exception e) {
+                        LogUtils.e(Log.getStackTraceString(e));
+                    }
+                    isRunning = false;
+                    return false;
+                }
+
+                @Override
+                public void onSuccess(Boolean result) {
+                }
+            });
+
+        }
+
+        if (!SPUtils.getInstance().getBoolean("isDebug")) {
+            ThreadUtils.executeByCached(new ThreadUtils.SimpleTask<Object>() {
+                @Override
+                public Object doInBackground() throws Throwable {
+                    try {
+                        ShellUtils.CommandResult commandResult = ShellUtils.execCmd("dumpsys activity activities", true);
+                        BufferedReader reader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(commandResult.successMsg.getBytes())));
+                        String line;
+                        while ((line = reader.readLine()) != null) {
+                            if (line.contains("mResumedActivity") || line.contains("mCurrentFocus")) {
+                                String[] itemArray = line.split(" ", -1);
+                                for (String item : itemArray) {
+                                    if (item.contains("/")) {
+                                        String packageName = item.substring(0, item.indexOf("/"));
+                                        if (!"com.zhong.che".equals(packageName)) {
+                                            LogUtils.d("定时拉起主应用");
+                                            Tool.INSTANCE.stopApp("com.zhong.che");
+                                            Tool.INSTANCE.startMasterApp();
+                                        }
+                                        break;
+                                    }
+                                }
+                                break;
+                            }
+                        }
+                    } catch (Exception e) {
+                        LogUtils.e(Log.getStackTraceString(e));
+                    }
+                    return null;
+                }
+
+                @Override
+                public void onSuccess(Object result) {
+
+                }
+            });
+        }
+    }
+}

+ 84 - 0
app/src/main/java/xn/huaxue/update/service/TaskService.java

@@ -0,0 +1,84 @@
+package xn.huaxue.update.service;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.Service;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Build;
+import android.os.IBinder;
+
+import androidx.annotation.Nullable;
+import androidx.core.app.NotificationCompat;
+
+import com.blankj.utilcode.util.LogUtils;
+
+import xn.huaxue.update.R;
+import xn.huaxue.update.receiver.TimeTickReceiver;
+
+public class TaskService extends Service {
+
+    private TimeTickReceiver timeTickReceiver;
+
+    @Nullable
+    @Override
+    public IBinder onBind(Intent intent) {
+        return null;
+    }
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        createNotificationChannel();
+        LogUtils.d(getClass().getName(), "onCreate");
+        timeTickReceiver = new TimeTickReceiver();
+        // 监听分钟广播
+        registerReceiver(timeTickReceiver, new IntentFilter(Intent.ACTION_TIME_TICK));
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        LogUtils.d(getClass().getName(), "onStartCommand");
+        Notification notification = createNotification();
+        startForeground(1, notification);
+        return START_REDELIVER_INTENT;
+    }
+
+    @Override
+    public void onTaskRemoved(Intent rootIntent) {
+        super.onTaskRemoved(rootIntent);
+        LogUtils.d(getClass().getName(), "onTaskRemoved");
+        stopSelf();
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        if (null != timeTickReceiver) {
+            unregisterReceiver(timeTickReceiver);
+            timeTickReceiver = null;
+        }
+        LogUtils.d(getClass().getName(), "onDestroy");
+    }
+
+    private void createNotificationChannel() {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+            int importance = NotificationManager.IMPORTANCE_DEFAULT;
+            NotificationChannel channel = new NotificationChannel("task", "task", importance);
+            NotificationManager notificationManager = getSystemService(NotificationManager.class);
+            notificationManager.createNotificationChannel(channel);
+        }
+    }
+
+    private Notification createNotification() {
+        NotificationCompat.Builder builder = new NotificationCompat.Builder(this, "task")
+                .setContentTitle("Task")
+                .setContentText("Task is running")
+                .setSmallIcon(R.mipmap.ic_launcher)
+                .setContentIntent(null)
+                .setSilent(true)
+                .setPriority(NotificationCompat.PRIORITY_DEFAULT);
+        return builder.build();
+    }
+}

+ 26 - 0
app/src/main/java/xn/huaxue/update/works/TaskWork.java

@@ -0,0 +1,26 @@
+package xn.huaxue.update.works;
+
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.work.Worker;
+import androidx.work.WorkerParameters;
+
+import com.blankj.utilcode.util.LogUtils;
+
+import xn.huaxue.update.Tool;
+
+
+public class TaskWork extends Worker {
+    public TaskWork(@NonNull Context context, @NonNull WorkerParameters workerParams) {
+        super(context, workerParams);
+    }
+
+    @NonNull
+    @Override
+    public Result doWork() {
+        LogUtils.d("work保活");
+        Tool.INSTANCE.startTaskService();
+        return Result.success();
+    }
+}

+ 14 - 0
app/src/main/res/drawable-anydpi/ic_admin.xml

@@ -0,0 +1,14 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24"
+    android:tint="#333333"
+    android:alpha="0.6">
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M17,11c0.34,0 0.67,0.04 1,0.09V6.27L10.5,3L3,6.27v4.91c0,4.54 3.2,8.79 7.5,9.82c0.55,-0.13 1.08,-0.32 1.6,-0.55C11.41,19.47 11,18.28 11,17C11,13.69 13.69,11 17,11z"/>
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M17,13c-2.21,0 -4,1.79 -4,4c0,2.21 1.79,4 4,4s4,-1.79 4,-4C21,14.79 19.21,13 17,13zM17,14.38c0.62,0 1.12,0.51 1.12,1.12s-0.51,1.12 -1.12,1.12s-1.12,-0.51 -1.12,-1.12S16.38,14.38 17,14.38zM17,19.75c-0.93,0 -1.74,-0.46 -2.24,-1.17c0.05,-0.72 1.51,-1.08 2.24,-1.08s2.19,0.36 2.24,1.08C18.74,19.29 17.93,19.75 17,19.75z"/>
+</vector>

+ 12 - 0
app/src/main/res/drawable-anydpi/ic_back.xml

@@ -0,0 +1,12 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24"
+    android:tint="#333333"
+    android:alpha="0.6"
+    android:autoMirrored="true">
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z"/>
+</vector>

+ 11 - 0
app/src/main/res/drawable-anydpi/ic_celsius.xml

@@ -0,0 +1,11 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24"
+    android:tint="#333333"
+    android:alpha="0.6">
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M6.76,4.84l-1.8,-1.79 -1.41,1.41 1.79,1.79 1.42,-1.41zM4,10.5L1,10.5v2h3v-2zM13,0.55h-2L11,3.5h2L13,0.55zM20.45,4.46l-1.41,-1.41 -1.79,1.79 1.41,1.41 1.79,-1.79zM17.24,18.16l1.79,1.8 1.41,-1.41 -1.8,-1.79 -1.4,1.4zM20,10.5v2h3v-2h-3zM12,5.5c-3.31,0 -6,2.69 -6,6s2.69,6 6,6 6,-2.69 6,-6 -2.69,-6 -6,-6zM11,22.45h2L13,19.5h-2v2.95zM3.55,18.54l1.41,1.41 1.79,-1.8 -1.41,-1.41 -1.79,1.8z"/>
+</vector>

+ 11 - 0
app/src/main/res/drawable-anydpi/ic_cpu.xml

@@ -0,0 +1,11 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24"
+    android:tint="#333333"
+    android:alpha="0.6">
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M15,9L9,9v6h6L15,9zM13,13h-2v-2h2v2zM21,11L21,9h-2L19,7c0,-1.1 -0.9,-2 -2,-2h-2L15,3h-2v2h-2L11,3L9,3v2L7,5c-1.1,0 -2,0.9 -2,2v2L3,9v2h2v2L3,13v2h2v2c0,1.1 0.9,2 2,2h2v2h2v-2h2v2h2v-2h2c1.1,0 2,-0.9 2,-2v-2h2v-2h-2v-2h2zM17,17L7,17L7,7h10v10z"/>
+</vector>

+ 12 - 0
app/src/main/res/drawable-anydpi/ic_eixt.xml

@@ -0,0 +1,12 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24"
+    android:tint="#333333"
+    android:alpha="0.6"
+    android:autoMirrored="true">
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M10.09,15.59L11.5,17l5,-5 -5,-5 -1.41,1.41L12.67,11H3v2h9.67l-2.58,2.59zM19,3H5c-1.11,0 -2,0.9 -2,2v4h2V5h14v14H5v-4H3v4c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2z"/>
+</vector>

+ 11 - 0
app/src/main/res/drawable-anydpi/ic_et_err.xml

@@ -0,0 +1,11 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24"
+    android:tint="#E53333"
+    android:alpha="0.8">
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,17h-2v-2h2v2zM13,13h-2L11,7h2v6z"/>
+</vector>

+ 11 - 0
app/src/main/res/drawable-anydpi/ic_folder.xml

@@ -0,0 +1,11 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24"
+    android:tint="#333333"
+    android:alpha="0.6">
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M10,4H4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V8c0,-1.1 -0.9,-2 -2,-2h-8l-2,-2z"/>
+</vector>

+ 11 - 0
app/src/main/res/drawable-anydpi/ic_http.xml

@@ -0,0 +1,11 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24"
+    android:tint="#333333"
+    android:alpha="0.6">
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M4.5,11h-2L2.5,9L1,9v6h1.5v-2.5h2L4.5,15L6,15L6,9L4.5,9v2zM7,10.5h1.5L8.5,15L10,15v-4.5h1.5L11.5,9L7,9v1.5zM12.5,10.5L14,10.5L14,15h1.5v-4.5L17,10.5L17,9h-4.5v1.5zM21.5,9L18,9v6h1.5v-2h2c0.8,0 1.5,-0.7 1.5,-1.5v-1c0,-0.8 -0.7,-1.5 -1.5,-1.5zM21.5,11.5h-2v-1h2v1z"/>
+</vector>

Diferenças do arquivo suprimidas por serem muito extensas
+ 11 - 0
app/src/main/res/drawable-anydpi/ic_ip.xml


Diferenças do arquivo suprimidas por serem muito extensas
+ 11 - 0
app/src/main/res/drawable-anydpi/ic_mqtt.xml


+ 11 - 0
app/src/main/res/drawable-anydpi/ic_mqtt_account.xml

@@ -0,0 +1,11 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24"
+    android:tint="#333333"
+    android:alpha="0.6">
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M21,18v1c0,1.1 -0.9,2 -2,2L5,21c-1.11,0 -2,-0.9 -2,-2L3,5c0,-1.1 0.89,-2 2,-2h14c1.1,0 2,0.9 2,2v1h-9c-1.11,0 -2,0.9 -2,2v8c0,1.1 0.89,2 2,2h9zM12,16h10L22,8L12,8v8zM16,13.5c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5 1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5z"/>
+</vector>

+ 11 - 0
app/src/main/res/drawable-anydpi/ic_mqtt_password.xml

@@ -0,0 +1,11 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24"
+    android:tint="#333333"
+    android:alpha="0.6">
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M2,17h20v2H2V17zM3.15,12.95L4,11.47l0.85,1.48l1.3,-0.75L5.3,10.72H7v-1.5H5.3l0.85,-1.47L4.85,7L4,8.47L3.15,7l-1.3,0.75L2.7,9.22H1v1.5h1.7L1.85,12.2L3.15,12.95zM9.85,12.2l1.3,0.75L12,11.47l0.85,1.48l1.3,-0.75l-0.85,-1.48H15v-1.5h-1.7l0.85,-1.47L12.85,7L12,8.47L11.15,7l-1.3,0.75l0.85,1.47H9v1.5h1.7L9.85,12.2zM23,9.22h-1.7l0.85,-1.47L20.85,7L20,8.47L19.15,7l-1.3,0.75l0.85,1.47H17v1.5h1.7l-0.85,1.48l1.3,0.75L20,11.47l0.85,1.48l1.3,-0.75l-0.85,-1.48H23V9.22z"/>
+</vector>

+ 14 - 0
app/src/main/res/drawable-anydpi/ic_reboot.xml

@@ -0,0 +1,14 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24"
+    android:tint="#333333"
+    android:alpha="0.6">
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M12,5V2L8,6l4,4V7c3.31,0 6,2.69 6,6c0,2.97 -2.17,5.43 -5,5.91v2.02c3.95,-0.49 7,-3.85 7,-7.93C20,8.58 16.42,5 12,5z"/>
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M6,13c0,-1.65 0.67,-3.15 1.76,-4.24L6.34,7.34C4.9,8.79 4,10.79 4,13c0,4.08 3.05,7.44 7,7.93v-2.02C8.17,18.43 6,15.97 6,13z"/>
+</vector>

+ 11 - 0
app/src/main/res/drawable-anydpi/ic_save.xml

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

Diferenças do arquivo suprimidas por serem muito extensas
+ 11 - 0
app/src/main/res/drawable-anydpi/ic_settings.xml


+ 11 - 0
app/src/main/res/drawable-anydpi/ic_sn.xml

@@ -0,0 +1,11 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24"
+    android:tint="#333333"
+    android:alpha="0.6">
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M17,1H7C5.9,1 5,1.9 5,3v18c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2V3C19,1.9 18.1,1 17,1zM7,18V6h10v12H7zM16,11V9.14C16,8.51 15.55,8 15,8H9C8.45,8 8,8.51 8,9.14l0,1.96c0.55,0 1,0.45 1,1c0,0.55 -0.45,1 -1,1l0,1.76C8,15.49 8.45,16 9,16h6c0.55,0 1,-0.51 1,-1.14V13c-0.55,0 -1,-0.45 -1,-1C15,11.45 15.45,11 16,11zM12.5,14.5h-1v-1h1V14.5zM12.5,12.5h-1v-1h1V12.5zM12.5,10.5h-1v-1h1V10.5z"/>
+</vector>

+ 14 - 0
app/src/main/res/drawable-anydpi/ic_time.xml

@@ -0,0 +1,14 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24"
+    android:tint="#333333"
+    android:alpha="0.6">
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8z"/>
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M12.5,7H11v6l5.25,3.15 0.75,-1.23 -4.5,-2.67z"/>
+</vector>

+ 11 - 0
app/src/main/res/drawable-anydpi/ic_ver.xml

@@ -0,0 +1,11 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24"
+    android:tint="#333333"
+    android:alpha="0.6">
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M7,20h4c0,1.1 -0.9,2 -2,2S7,21.1 7,20zM5,19h8v-2H5V19zM16.5,9.5c0,3.82 -2.66,5.86 -3.77,6.5H5.27C4.16,15.36 1.5,13.32 1.5,9.5C1.5,5.36 4.86,2 9,2S16.5,5.36 16.5,9.5zM21.37,7.37L20,8l1.37,0.63L22,10l0.63,-1.37L24,8l-1.37,-0.63L22,6L21.37,7.37zM19,6l0.94,-2.06L22,3l-2.06,-0.94L19,0l-0.94,2.06L16,3l2.06,0.94L19,6z"/>
+</vector>

BIN
app/src/main/res/drawable-hdpi/ic_admin.png


BIN
app/src/main/res/drawable-hdpi/ic_back.png


BIN
app/src/main/res/drawable-hdpi/ic_celsius.png


BIN
app/src/main/res/drawable-hdpi/ic_cpu.png


BIN
app/src/main/res/drawable-hdpi/ic_eixt.png


BIN
app/src/main/res/drawable-hdpi/ic_et_err.png


BIN
app/src/main/res/drawable-hdpi/ic_folder.png


BIN
app/src/main/res/drawable-hdpi/ic_http.png


BIN
app/src/main/res/drawable-hdpi/ic_ip.png


BIN
app/src/main/res/drawable-hdpi/ic_mqtt.png


BIN
app/src/main/res/drawable-hdpi/ic_mqtt_account.png


BIN
app/src/main/res/drawable-hdpi/ic_mqtt_password.png


BIN
app/src/main/res/drawable-hdpi/ic_reboot.png


BIN
app/src/main/res/drawable-hdpi/ic_save.png


BIN
app/src/main/res/drawable-hdpi/ic_settings.png


BIN
app/src/main/res/drawable-hdpi/ic_sn.png


BIN
app/src/main/res/drawable-hdpi/ic_time.png


BIN
app/src/main/res/drawable-hdpi/ic_ver.png


BIN
app/src/main/res/drawable-mdpi/ic_admin.png


BIN
app/src/main/res/drawable-mdpi/ic_back.png


BIN
app/src/main/res/drawable-mdpi/ic_celsius.png


BIN
app/src/main/res/drawable-mdpi/ic_cpu.png


BIN
app/src/main/res/drawable-mdpi/ic_eixt.png


BIN
app/src/main/res/drawable-mdpi/ic_et_err.png


BIN
app/src/main/res/drawable-mdpi/ic_folder.png


BIN
app/src/main/res/drawable-mdpi/ic_http.png


BIN
app/src/main/res/drawable-mdpi/ic_ip.png


BIN
app/src/main/res/drawable-mdpi/ic_mqtt.png


BIN
app/src/main/res/drawable-mdpi/ic_mqtt_account.png


BIN
app/src/main/res/drawable-mdpi/ic_mqtt_password.png


BIN
app/src/main/res/drawable-mdpi/ic_reboot.png


BIN
app/src/main/res/drawable-mdpi/ic_save.png


BIN
app/src/main/res/drawable-mdpi/ic_settings.png


BIN
app/src/main/res/drawable-mdpi/ic_sn.png


BIN
app/src/main/res/drawable-mdpi/ic_time.png


BIN
app/src/main/res/drawable-mdpi/ic_ver.png


BIN
app/src/main/res/drawable-xhdpi/ic_admin.png


BIN
app/src/main/res/drawable-xhdpi/ic_back.png


BIN
app/src/main/res/drawable-xhdpi/ic_celsius.png


BIN
app/src/main/res/drawable-xhdpi/ic_cpu.png


BIN
app/src/main/res/drawable-xhdpi/ic_eixt.png


BIN
app/src/main/res/drawable-xhdpi/ic_et_err.png


BIN
app/src/main/res/drawable-xhdpi/ic_folder.png


BIN
app/src/main/res/drawable-xhdpi/ic_http.png


BIN
app/src/main/res/drawable-xhdpi/ic_ip.png


BIN
app/src/main/res/drawable-xhdpi/ic_mqtt.png


BIN
app/src/main/res/drawable-xhdpi/ic_mqtt_account.png


BIN
app/src/main/res/drawable-xhdpi/ic_mqtt_password.png


BIN
app/src/main/res/drawable-xhdpi/ic_reboot.png


BIN
app/src/main/res/drawable-xhdpi/ic_save.png


BIN
app/src/main/res/drawable-xhdpi/ic_settings.png


BIN
app/src/main/res/drawable-xhdpi/ic_sn.png


BIN
app/src/main/res/drawable-xhdpi/ic_time.png


BIN
app/src/main/res/drawable-xhdpi/ic_ver.png


BIN
app/src/main/res/drawable-xhdpi/logo.png


BIN
app/src/main/res/drawable-xxhdpi/ic_admin.png


BIN
app/src/main/res/drawable-xxhdpi/ic_back.png


BIN
app/src/main/res/drawable-xxhdpi/ic_celsius.png


BIN
app/src/main/res/drawable-xxhdpi/ic_cpu.png


BIN
app/src/main/res/drawable-xxhdpi/ic_eixt.png


BIN
app/src/main/res/drawable-xxhdpi/ic_et_err.png


+ 0 - 0
app/src/main/res/drawable-xxhdpi/ic_folder.png


Alguns arquivos não foram mostrados porque muitos arquivos mudaram nesse diff