Browse Source

首页提交修改协议mg231

harla 3 weeks ago
commit
010dabe305
100 changed files with 224189 additions and 0 deletions
  1. 66 0
      .gitignore
  2. 1 0
      MultiLanguages/.gitignore
  3. 35 0
      MultiLanguages/build.gradle
  4. 0 0
      MultiLanguages/consumer-rules.pro
  5. 21 0
      MultiLanguages/proguard-rules.pro
  6. 26 0
      MultiLanguages/src/androidTest/java/com/example/multilanguages/ExampleInstrumentedTest.java
  7. 4 0
      MultiLanguages/src/main/AndroidManifest.xml
  8. 150 0
      MultiLanguages/src/main/java/com/example/multilanguages/ActivityLanguages.java
  9. 47 0
      MultiLanguages/src/main/java/com/example/multilanguages/ConfigurationObserver.java
  10. 106 0
      MultiLanguages/src/main/java/com/example/multilanguages/LanguagesConfig.java
  11. 125 0
      MultiLanguages/src/main/java/com/example/multilanguages/LanguagesUtils.java
  12. 78 0
      MultiLanguages/src/main/java/com/example/multilanguages/LocaleChangeReceiver.java
  13. 707 0
      MultiLanguages/src/main/java/com/example/multilanguages/LocaleContract.java
  14. 238 0
      MultiLanguages/src/main/java/com/example/multilanguages/MultiLanguages.java
  15. 28 0
      MultiLanguages/src/main/java/com/example/multilanguages/OnLanguageListener.java
  16. 17 0
      MultiLanguages/src/test/java/com/example/multilanguages/ExampleUnitTest.java
  17. 363 0
      README.md
  18. 1 0
      app/.gitignore
  19. 145 0
      app/build.gradle
  20. BIN
      app/libs/arm64-v8a/libjSerialComm.so
  21. BIN
      app/libs/arm64-v8a/libserial_port.so
  22. BIN
      app/libs/armeabi-v7a/libjSerialComm.so
  23. BIN
      app/libs/armeabi-v7a/libserial_port.so
  24. BIN
      app/libs/armeabi/libjSerialComm.so
  25. BIN
      app/libs/armeabi/libserial_port.so
  26. BIN
      app/libs/mips/libjSerialComm.so
  27. BIN
      app/libs/mips64/libjSerialComm.so
  28. BIN
      app/libs/x86/libjSerialComm.so
  29. BIN
      app/libs/x86/libserial_port.so
  30. BIN
      app/libs/x86_64/libjSerialComm.so
  31. BIN
      app/libs/x86_64/libserial_port.so
  32. 1 0
      app/multidexKeep.pro
  33. 115 0
      app/proguard-rules.pro
  34. 218735 0
      app/proguardMapping.txt
  35. 66 0
      app/src/main/AndroidManifest.xml
  36. 189 0
      app/src/main/java/com/sunzee/app/AppApplication.kt
  37. 30 0
      app/src/main/res/drawable-v24/ic_launcher_foreground.xml
  38. 170 0
      app/src/main/res/drawable/ic_launcher_background.xml
  39. 5 0
      app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
  40. 5 0
      app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
  41. BIN
      app/src/main/res/mipmap-xxhdpi/ic_launcher.png
  42. BIN
      app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
  43. 3 0
      app/src/main/res/values/strings.xml
  44. 8 0
      app/src/main/res/xml/network_security_config.xml
  45. 56 0
      base_lib.gradle
  46. 89 0
      base_module.gradle
  47. 8 0
      build.gradle.kts
  48. 2 0
      buildSrc/build.gradle.kts
  49. 172 0
      buildSrc/gradlew
  50. 84 0
      buildSrc/gradlew.bat
  51. 215 0
      buildSrc/src/main/kotlin/com/quyunshuo/androidbaseframemvvm/buildsrc/DependencyConfig.kt
  52. 31 0
      buildSrc/src/main/kotlin/com/quyunshuo/androidbaseframemvvm/buildsrc/ProjectBuildConfig.kt
  53. 14 0
      buildSrc/src/main/kotlin/com/quyunshuo/androidbaseframemvvm/buildsrc/SDKKeyConfig.kt
  54. 25 0
      gradle.properties
  55. 50 0
      gradle/libs.versions.toml
  56. BIN
      gradle/wrapper/gradle-wrapper.jar
  57. 6 0
      gradle/wrapper/gradle-wrapper.properties
  58. 172 0
      gradlew
  59. 84 0
      gradlew.bat
  60. BIN
      hboxs.jks
  61. BIN
      img/img2.png
  62. 1 0
      lib_base/.gitignore
  63. 112 0
      lib_base/build.gradle
  64. 0 0
      lib_base/consumer-rules.pro
  65. BIN
      lib_base/libs/ZtlApi.jar
  66. BIN
      lib_base/libs/xzjh_interface_20240731.jar
  67. 21 0
      lib_base/proguard-rules.pro
  68. 4 0
      lib_base/src/main/AndroidManifest.xml
  69. 83 0
      lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/BaseApplication.kt
  70. 34 0
      lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/addressenum/PayEnum.kt
  71. 39 0
      lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/addressenum/PlcCloseAddressEnum.kt
  72. 14 0
      lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/addressenum/PlcD2StatusEnum.kt
  73. 56 0
      lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/addressenum/PlcDebugAddressEnum.kt
  74. 54 0
      lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/addressenum/PlcDebugStataAddressEnum.kt
  75. 10 0
      lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/addressenum/PlcHomeAddressEnum.kt
  76. 135 0
      lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/addressenum/PlcParamAddressEnum.kt
  77. 7 0
      lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/addressenum/PlcSettingAddressEnum.kt
  78. 26 0
      lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/addressenum/ProTypeEnum.kt
  79. 51 0
      lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/app/ActivityLifecycleCallbacksImpl.kt
  80. 42 0
      lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/app/ApplicationLifecycle.kt
  81. 63 0
      lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/app/LoadModuleProxy.kt
  82. 11 0
      lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/bean/Global.kt
  83. 12 0
      lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/constant/DebugType.java
  84. 19 0
      lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/constant/VersionStatus.kt
  85. 157 0
      lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/factory/GsonFactory.java
  86. 41 0
      lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/factory/ParseExceptionCallback.java
  87. 26 0
      lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/factory/constructor/ConcurrentMapConstructor.java
  88. 25 0
      lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/factory/constructor/ConcurrentSkipListMapConstructor.java
  89. 39 0
      lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/factory/constructor/EnumMapConstructor.java
  90. 39 0
      lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/factory/constructor/EnumSetConstructor.java
  91. 24 0
      lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/factory/constructor/ExceptionConstructor.java
  92. 27 0
      lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/factory/constructor/InstanceCreatorConstructor.java
  93. 120 0
      lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/factory/constructor/KotlinDataClassDefaultValueConstructor.kt
  94. 25 0
      lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/factory/constructor/LinkedHashMapConstructor.java
  95. 25 0
      lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/factory/constructor/LinkedTreeMapConstructor.java
  96. 26 0
      lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/factory/constructor/ListConstructor.java
  97. 247 0
      lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/factory/constructor/MainConstructor.java
  98. 26 0
      lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/factory/constructor/QueueConstructor.java
  99. 55 0
      lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/factory/constructor/ReflectCreatorConstructor.java
  100. 0 0
      lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/factory/constructor/ReflectSafeCreatorConstructor.java

+ 66 - 0
.gitignore

@@ -0,0 +1,66 @@
+# Built application files
+*.apk
+*.ap_
+
+# Files for the ART/Dalvik VM
+*.dex
+
+# Java class files
+*.class
+
+# Generated files
+bin/
+gen/
+out/
+
+# Gradle files
+.gradle/
+build/
+
+# 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/tasks.xml
+.idea/gradle.xml
+.idea/assetWizardSettings.xml
+.idea/dictionaries
+.idea/libraries
+.idea/caches
+.idea/*
+
+# Keystore files
+# Uncomment the following line if you do not want to check your keystore files in.
+#*.jks
+
+# External native build folder generated in Android Studio 2.2 and later
+.externalNativeBuild
+
+# Google Services (e.g. APIs or Firebase)
+google-services.json
+
+# Freeline
+freeline.py
+freeline/
+freeline_project_description.json
+
+# fastlane
+fastlane/report.xml
+fastlane/Preview.html
+fastlane/screenshots
+fastlane/test_output
+fastlane/readme.md

+ 1 - 0
MultiLanguages/.gitignore

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

+ 35 - 0
MultiLanguages/build.gradle

@@ -0,0 +1,35 @@
+plugins {
+    alias(libs.plugins.library)
+}
+
+android {
+    namespace 'com.example.multilanguages'
+    compileSdk 34
+
+    defaultConfig {
+        minSdk 21
+
+        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+        consumerProguardFiles "consumer-rules.pro"
+    }
+
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+        }
+    }
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_8
+        targetCompatibility JavaVersion.VERSION_1_8
+    }
+}
+
+dependencies {
+
+    implementation libs.androidx.appcompat
+    implementation libs.material
+    testImplementation libs.junit
+    androidTestImplementation libs.androidx.junit
+    androidTestImplementation libs.androidx.espresso.core
+}

+ 0 - 0
MultiLanguages/consumer-rules.pro


+ 21 - 0
MultiLanguages/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
MultiLanguages/src/androidTest/java/com/example/multilanguages/ExampleInstrumentedTest.java

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

+ 4 - 0
MultiLanguages/src/main/AndroidManifest.xml

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+
+</manifest>

+ 150 - 0
MultiLanguages/src/main/java/com/example/multilanguages/ActivityLanguages.java

@@ -0,0 +1,150 @@
+package com.example.multilanguages;
+
+import android.app.Activity;
+import android.app.Application;
+import android.os.Bundle;
+
+/**
+ *    author : Android 轮子哥
+ *    github : https://github.com/getActivity/MultiLanguages
+ *    time   : 2021/01/21
+ *    desc   : Activity 语种注入
+ */
+final class ActivityLanguages implements Application.ActivityLifecycleCallbacks {
+
+    /*
+    这里解释一下,为什么要在 Activity 所有生命周期中刷新语种
+    这是因为发现有的手机厂商系统(例如 miui 系统)会偷摸修改 Activity 或 Application 绑定的语种
+    Github 地址:https://github.com/getActivity/MultiLanguages/issues/52
+     */
+
+    static void inject(Application application) {
+        application.registerActivityLifecycleCallbacks(new ActivityLanguages());
+    }
+
+    @Override
+    public void onActivityPreCreated(Activity activity, Bundle savedInstanceState) {
+        refreshActivityAndApplicationLanguage(activity);
+    }
+
+    @Override
+    public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
+        refreshActivityAndApplicationLanguage(activity);
+    }
+
+    @Override
+    public void onActivityPostCreated(Activity activity, Bundle savedInstanceState) {
+        refreshActivityAndApplicationLanguage(activity);
+    }
+
+    @Override
+    public void onActivityPreStarted(Activity activity) {
+        refreshActivityAndApplicationLanguage(activity);
+    }
+
+    @Override
+    public void onActivityStarted(Activity activity) {
+        refreshActivityAndApplicationLanguage(activity);
+    }
+
+    @Override
+    public void onActivityPostStarted(Activity activity) {
+        refreshActivityAndApplicationLanguage(activity);
+    }
+
+    @Override
+    public void onActivityPreResumed(Activity activity) {
+        refreshActivityAndApplicationLanguage(activity);
+    }
+
+    @Override
+    public void onActivityResumed(Activity activity) {
+        refreshActivityAndApplicationLanguage(activity);
+    }
+
+    @Override
+    public void onActivityPostResumed(Activity activity) {
+        refreshActivityAndApplicationLanguage(activity);
+    }
+
+    @Override
+    public void onActivityPrePaused(Activity activity) {
+        refreshActivityAndApplicationLanguage(activity);
+    }
+
+    @Override
+    public void onActivityPaused(Activity activity) {
+        refreshActivityAndApplicationLanguage(activity);
+    }
+
+    @Override
+    public void onActivityPostPaused(Activity activity) {
+        refreshActivityAndApplicationLanguage(activity);
+    }
+
+    @Override
+    public void onActivityPreStopped(Activity activity) {
+        refreshActivityAndApplicationLanguage(activity);
+    }
+
+    @Override
+    public void onActivityStopped(Activity activity) {
+        refreshActivityAndApplicationLanguage(activity);
+    }
+
+    @Override
+    public void onActivityPostStopped(Activity activity) {
+        refreshActivityAndApplicationLanguage(activity);
+    }
+
+    @Override
+    public void onActivityPreSaveInstanceState(Activity activity, Bundle outState) {
+        refreshActivityAndApplicationLanguage(activity);
+    }
+
+    @Override
+    public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
+        refreshActivityAndApplicationLanguage(activity);
+    }
+
+    @Override
+    public void onActivityPostSaveInstanceState(Activity activity, Bundle outState) {
+        refreshActivityAndApplicationLanguage(activity);
+    }
+
+    @Override
+    public void onActivityPreDestroyed(Activity activity) {
+        refreshActivityAndApplicationLanguage(activity);
+    }
+
+    @Override
+    public void onActivityDestroyed(Activity activity) {
+        refreshApplicationLanguage(activity.getApplication());
+    }
+
+    @Override
+    public void onActivityPostDestroyed(Activity activity) {
+        refreshApplicationLanguage(activity.getApplication());
+    }
+
+    /**
+     * 刷新 Activity 和 Application 的语种
+     */
+    private void refreshActivityAndApplicationLanguage(Activity activity) {
+        if (activity == null) {
+            return;
+        }
+        MultiLanguages.updateAppLanguage(activity);
+        refreshApplicationLanguage(activity.getApplication());
+    }
+
+    /**
+     * 刷新 Application 的语种
+     */
+    private void refreshApplicationLanguage(Application application) {
+        if (application == null) {
+            return;
+        }
+        MultiLanguages.updateAppLanguage(application);
+    }
+}

+ 47 - 0
MultiLanguages/src/main/java/com/example/multilanguages/ConfigurationObserver.java

@@ -0,0 +1,47 @@
+package com.example.multilanguages;
+
+import android.app.Application;
+import android.content.ComponentCallbacks;
+import android.content.res.Configuration;
+
+/**
+ *    author : Android 轮子哥
+ *    github : https://github.com/getActivity/MultiLanguages
+ *    time   : 2019/05/06
+ *    desc   : 手机配置变化监听
+ */
+final class ConfigurationObserver implements ComponentCallbacks {
+
+    /**
+     * 注册系统语种变化监听
+     */
+    static void register(Application application) {
+        ConfigurationObserver configurationObserver = new ConfigurationObserver(application);
+        application.registerComponentCallbacks(configurationObserver);
+    }
+
+    private final Application mApplication;
+
+    private ConfigurationObserver(Application application) {
+        mApplication = application;
+    }
+
+    /**
+     * 手机的配置发生了变化
+     */
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        if (newConfig == null) {
+            return;
+        }
+        // 如果当前是跟随系统语种,就则不往下执行
+        if (MultiLanguages.isSystemLanguage(mApplication)) {
+            return;
+        }
+        // 更新 Application 的配置,否则会出现横竖屏切换之后 Application 的 orientation 没有随之变化的问题
+        LanguagesUtils.updateConfigurationChanged(mApplication, newConfig, MultiLanguages.getAppLanguage(mApplication));
+    }
+
+    @Override
+    public void onLowMemory() {}
+}

+ 106 - 0
MultiLanguages/src/main/java/com/example/multilanguages/LanguagesConfig.java

@@ -0,0 +1,106 @@
+package com.example.multilanguages;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.text.TextUtils;
+import java.util.Locale;
+
+/**
+ *    author : Android 轮子哥
+ *    github : https://github.com/getActivity/MultiLanguages
+ *    time   : 2019/05/03
+ *    desc   : 语种配置保存类
+ */
+final class LanguagesConfig {
+
+    private static final String KEY_LANGUAGE = "key_language";
+    private static final String KEY_COUNTRY = "key_country";
+
+    private static String sSharedPreferencesName = "language_setting";
+
+    /** 当前语种 */
+    private static volatile Locale sCurrentLocale;
+
+    /** 默认语种 */
+    private static volatile Locale sDefaultLocale;
+
+    static void setSharedPreferencesName(String name) {
+        sSharedPreferencesName = name;
+    }
+
+    private static SharedPreferences getSharedPreferences(Context context) {
+        return context.getSharedPreferences(sSharedPreferencesName, Context.MODE_PRIVATE);
+    }
+
+    /**
+     * 读取 App 语种
+     */
+    static Locale readAppLanguageSetting(Context context) {
+        if (sCurrentLocale != null) {
+            return sCurrentLocale;
+        }
+
+        String language = getSharedPreferences(context).getString(KEY_LANGUAGE, "");
+        String country = getSharedPreferences(context).getString(KEY_COUNTRY, "");
+
+        if (!TextUtils.isEmpty(language)) {
+            sCurrentLocale = new Locale(language, country);
+            return sCurrentLocale;
+        }
+
+        if (sDefaultLocale != null) {
+            sCurrentLocale = sDefaultLocale;
+            return sCurrentLocale;
+        }
+
+        sCurrentLocale = LanguagesUtils.getLocale(context);
+
+        return sCurrentLocale;
+    }
+
+    /**
+     * 保存 App 语种设置
+     */
+    static void saveAppLanguageSetting(Context context, Locale locale) {
+        sCurrentLocale = locale;
+        getSharedPreferences(context).edit()
+                .putString(KEY_LANGUAGE, locale.getLanguage())
+                .putString(KEY_COUNTRY, locale.getCountry())
+                .apply();
+    }
+
+    /**
+     * 清除语种设置
+     */
+    static void clearLanguageSetting(Context context) {
+        sCurrentLocale = MultiLanguages.getSystemLanguage(context);
+        getSharedPreferences(context).edit()
+                .remove(KEY_LANGUAGE)
+                .remove(KEY_COUNTRY)
+                .apply();
+    }
+
+    /**
+     * 是否跟随系统
+     */
+    public static boolean isSystemLanguage(Context context) {
+        if (sDefaultLocale != null) {
+            return false;
+        }
+
+        String language = getSharedPreferences(context).getString(KEY_LANGUAGE, "");
+        return TextUtils.isEmpty(language);
+    }
+
+    /**
+     * 设置默认的语种
+     */
+    public static void setDefaultLanguage(Locale locale) {
+        if (sCurrentLocale != null) {
+            // 这个 API 需要越早调用越好,建议放在 Application 静态代码块中初始化
+            // 当然也可以在 Application 调用 super.attachBaseContext 方法之前
+            throw new IllegalStateException("Please set this before application initialization");
+        }
+        sDefaultLocale = locale;
+    }
+}

+ 125 - 0
MultiLanguages/src/main/java/com/example/multilanguages/LanguagesUtils.java

@@ -0,0 +1,125 @@
+package com.example.multilanguages;
+
+import android.app.LocaleManager;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.os.Build;
+import android.os.LocaleList;
+import java.util.Locale;
+
+/**
+ *    author : Android 轮子哥
+ *    github : https://github.com/getActivity/MultiLanguages
+ *    time   : 2019/05/03
+ *    desc   : 国际化工具类
+ */
+@SuppressWarnings("deprecation")
+final class LanguagesUtils {
+
+    /**
+     * 获取语种对象
+     */
+    static Locale getLocale(Context context) {
+        return getLocale(context.getResources().getConfiguration());
+    }
+
+    static Locale getLocale(Configuration config) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+            return config.getLocales().get(0);
+        } else  {
+            return config.locale;
+        }
+    }
+
+    /**
+     * 设置语种对象
+     */
+    static void setLocale(Configuration config, Locale locale) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+            LocaleList localeList = new LocaleList(locale);
+            config.setLocales(localeList);
+        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+            config.setLocale(locale);
+        } else {
+            config.locale = locale;
+        }
+    }
+
+    /**
+     * 获取系统的语种对象
+     */
+    static Locale getSystemLocale(Context context) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+            // 在 Android 13 上,不能用 Resources.getSystem() 来获取系统语种了
+            // Android 13 上面新增了一个 LocaleManager 的语种管理类
+            // 因为如果调用 LocaleManager.setApplicationLocales 会影响获取到的结果不准确
+            // 所以应该得用 LocaleManager.getSystemLocales 来获取会比较精准
+            LocaleManager localeManager = context.getSystemService(LocaleManager.class);
+            if (localeManager != null) {
+                return localeManager.getSystemLocales().get(0);
+            }
+        }
+
+        return LanguagesUtils.getLocale(Resources.getSystem().getConfiguration());
+    }
+
+    /**
+     * 设置默认的语种环境(日期格式化会用到)
+     */
+    static void setDefaultLocale(Context context) {
+        Configuration configuration = context.getResources().getConfiguration();
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+            LocaleList.setDefault(configuration.getLocales());
+        } else {
+            Locale.setDefault(configuration.locale);
+        }
+    }
+
+    /**
+     * 绑定当前 App 的语种
+     */
+    static Context attachLanguages(Context context, Locale locale) {
+        Resources resources = context.getResources();
+        Configuration config = new Configuration(resources.getConfiguration());
+        setLocale(config, locale);
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+            context = context.createConfigurationContext(config);
+        }
+        resources.updateConfiguration(config, resources.getDisplayMetrics());
+        return context;
+    }
+
+    /**
+     * 更新 Resources 语种
+     */
+    static void updateLanguages(Resources resources, Locale locale) {
+        Configuration config = resources.getConfiguration();
+        setLocale(config, locale);
+        resources.updateConfiguration(config, resources.getDisplayMetrics());
+    }
+
+    /**
+     * 更新手机配置信息变化
+     */
+    static void updateConfigurationChanged(Context context, Configuration newConfig, Locale appLanguage) {
+        Configuration config = new Configuration(newConfig);
+        // 绑定当前语种到这个新的配置对象中
+        setLocale(config, appLanguage);
+        Resources resources = context.getResources();
+        // 更新上下文的配置信息
+        resources.updateConfiguration(config, resources.getDisplayMetrics());
+    }
+
+    /**
+     * 生成某个语种下的 Resources 对象
+     */
+    static Resources generateLanguageResources(Context context, Locale locale) {
+        Configuration config = new Configuration();
+        setLocale(config, locale);
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+            return context.createConfigurationContext(config).getResources();
+        }
+        return new Resources(context.getAssets(), context.getResources().getDisplayMetrics(), config);
+    }
+}

+ 78 - 0
MultiLanguages/src/main/java/com/example/multilanguages/LocaleChangeReceiver.java

@@ -0,0 +1,78 @@
+package com.example.multilanguages;
+
+import android.app.Application;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import java.util.Locale;
+
+/**
+ *    author : Android 轮子哥
+ *    github : https://github.com/getActivity/MultiLanguages
+ *    time   : 2023/11/24
+ *    desc   : 语种变化广播
+ */
+final class LocaleChangeReceiver extends BroadcastReceiver {
+
+    /** 系统语种 */
+    private static volatile Locale sSystemLanguage;
+
+    static void register(Application application) {
+        sSystemLanguage = LanguagesUtils.getSystemLocale(application);
+        IntentFilter filter = new IntentFilter(Intent.ACTION_LOCALE_CHANGED);
+        application.registerReceiver(new LocaleChangeReceiver(application), filter);
+    }
+
+    private final Application mApplication;
+
+    public LocaleChangeReceiver(Application application) {
+        mApplication = application;
+    }
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        if (intent == null) {
+            return;
+        }
+
+        String action = intent.getAction();
+
+        if (action == null) {
+            return;
+        }
+
+        if (!Intent.ACTION_LOCALE_CHANGED.equals(action)) {
+            return;
+        }
+
+        if (sSystemLanguage == null) {
+            return;
+        }
+
+        Locale latestSystemLocale = MultiLanguages.getSystemLanguage(mApplication);
+        if (MultiLanguages.equalsCountry(latestSystemLocale, sSystemLanguage)) {
+            return;
+        }
+
+        notifySystemLocaleChange(sSystemLanguage, latestSystemLocale);
+    }
+
+    /**
+     * 通知系统语种发生变化
+     */
+    public void notifySystemLocaleChange(Locale oldLocale, Locale newLocale) {
+        sSystemLanguage = newLocale;
+
+        // 如果当前的语种是跟随系统变化的,那么就需要重置一下当前 App 的语种
+        if (LanguagesConfig.isSystemLanguage(mApplication)) {
+            LanguagesConfig.clearLanguageSetting(mApplication);
+        }
+
+        OnLanguageListener listener = MultiLanguages.getOnLanguagesListener();
+        if (listener == null) {
+            return;
+        }
+        listener.onSystemLocaleChange(oldLocale, newLocale);
+    }
+}

+ 707 - 0
MultiLanguages/src/main/java/com/example/multilanguages/LocaleContract.java

@@ -0,0 +1,707 @@
+package com.example.multilanguages;
+
+import java.util.Locale;
+
+/**
+ *    author : Android 轮子哥
+ *    github : https://github.com/getActivity/MultiLanguages
+ *    time   : 2022/04/08
+ *    desc   : 语种契约类
+ *    doc    : https://blog.csdn.net/liuhhaiffeng/article/details/54706027
+ */
+public final class LocaleContract {
+
+   /** 中文 */
+   private static volatile Locale sChineseLocale;
+
+   public static Locale getChineseLocale() {
+      if (sChineseLocale == null) {
+         sChineseLocale = Locale.CHINESE;
+      }
+      return sChineseLocale;
+   }
+
+   /** 简体中文 */
+   private static volatile Locale sSimplifiedChineseLocale;
+
+   public static Locale getSimplifiedChineseLocale() {
+      if (sSimplifiedChineseLocale == null) {
+         sSimplifiedChineseLocale = Locale.SIMPLIFIED_CHINESE;
+      }
+      return sSimplifiedChineseLocale;
+   }
+
+   /** 繁体中文 */
+   private static volatile Locale sTraditionalChineseLocale;
+
+   public static Locale getTraditionalChineseLocale() {
+      if (sTraditionalChineseLocale == null) {
+         sTraditionalChineseLocale = Locale.TRADITIONAL_CHINESE;
+      }
+      return sTraditionalChineseLocale;
+   }
+
+   public static Locale getTaiWanLocale() {
+      return getTraditionalChineseLocale();
+   }
+
+   /** 新加坡 */
+   private static volatile Locale sSingaporeLocale ;
+
+   public static Locale getSingaporeLocale() {
+      if (sSimplifiedChineseLocale == null) {
+         sSingaporeLocale = new Locale("zh", "SG");
+      }
+      return sSingaporeLocale;
+   }
+
+   /** 英语 */
+   private static volatile Locale sEnglishLocale;
+
+   public static Locale getEnglishLocale() {
+      if (sEnglishLocale == null) {
+         sEnglishLocale = Locale.ENGLISH;
+      }
+      return sEnglishLocale;
+   }
+
+   /** 法语 */
+   private static volatile Locale sFrenchLocale;
+
+   public static Locale getFrenchLocale() {
+      if (sFrenchLocale == null) {
+         sFrenchLocale = Locale.FRENCH;
+      }
+      return sFrenchLocale;
+   }
+
+   /** 德语 */
+   private static volatile Locale sGermanLocale;
+
+   public static Locale getGermanLocale() {
+      if (sGermanLocale == null) {
+         sGermanLocale = Locale.GERMAN;
+      }
+      return sGermanLocale;
+   }
+
+   /** 意大利语 */
+   private static volatile Locale sItalianLocale;
+
+   public static Locale getItalianLocale() {
+      if (sItalianLocale == null) {
+         sItalianLocale = Locale.ITALIAN;
+      }
+      return sItalianLocale;
+   }
+
+   /** 日语 */
+   private static volatile Locale sJapaneseLocale;
+
+   public static Locale getJapaneseLocale() {
+      if (sJapaneseLocale == null) {
+         sJapaneseLocale = Locale.JAPANESE;
+      }
+      return sJapaneseLocale;
+   }
+
+   /** 韩语 */
+   private static volatile Locale sKoreanLocale;
+
+   public static Locale getKoreanLocale() {
+      if (sKoreanLocale == null) {
+         sKoreanLocale = Locale.KOREAN;
+      }
+      return sKoreanLocale;
+   }
+
+   /** 越南语 */
+   private static volatile Locale sVietnameseLocale;
+
+   public static Locale getVietnameseLocale() {
+      if (sVietnameseLocale == null) {
+         sVietnameseLocale = new Locale("vi");
+      }
+      return sVietnameseLocale;
+   }
+
+   /** 荷兰语 */
+   private static volatile Locale sDutchLocale;
+
+   public static Locale getDutchLocale() {
+      if (sDutchLocale == null) {
+         sDutchLocale = new Locale("af");
+      }
+      return sDutchLocale;
+   }
+
+   /** 阿尔巴尼亚语 */
+   private static volatile Locale sAlbanianLocale;
+
+   public static Locale getAlbanianLocale() {
+      if (sAlbanianLocale == null) {
+         sAlbanianLocale = new Locale("sq");
+      }
+      return sAlbanianLocale;
+   }
+
+   /** 阿拉伯语 */
+   private static volatile Locale sArabicLocale;
+
+   public static Locale getArabicLocale() {
+      if (sArabicLocale == null) {
+         sArabicLocale = new Locale("ar");
+      }
+      return sArabicLocale;
+   }
+
+   /** 亚美尼亚语 */
+   private static volatile Locale sArmenianLocale;
+
+   public static Locale getArmenianLocale() {
+      if (sArmenianLocale == null) {
+         sArmenianLocale = new Locale("hy");
+      }
+      return sArmenianLocale;
+   }
+
+   /** 阿塞拜疆语 */
+   private static volatile Locale sAzerbaijaniLocale;
+
+   public static Locale getAzerbaijaniLocale() {
+      if (sAzerbaijaniLocale == null) {
+         sAzerbaijaniLocale = new Locale("az");
+      }
+      return sAzerbaijaniLocale;
+   }
+
+   /** 巴斯克语 */
+   private static volatile Locale sBasqueLocale;
+
+   public static Locale getBasqueLocale() {
+      if (sBasqueLocale == null) {
+         sBasqueLocale = new Locale("eu");
+      }
+      return sBasqueLocale;
+   }
+
+   /** 白俄罗斯 */
+   private static volatile Locale sBelarusianLocale;
+
+   public static Locale getBelarusianLocale() {
+      if (sBelarusianLocale == null) {
+         sBelarusianLocale = new Locale("be");
+      }
+      return sBelarusianLocale;
+   }
+
+   /** 保加利亚 */
+   private static volatile Locale sBulgariaLocale;
+
+   public static Locale getBulgariaLocale() {
+      if (sBulgariaLocale == null) {
+         sBulgariaLocale = new Locale("bg");
+      }
+      return sBulgariaLocale;
+   }
+
+   /** 加泰罗尼亚 */
+   private static volatile Locale sCatalonianLocale;
+
+   public static Locale getCatalonianLocale() {
+      if (sCatalonianLocale == null) {
+         sCatalonianLocale = new Locale("ca");
+      }
+      return sCatalonianLocale;
+   }
+
+   /** 克罗埃西亚 */
+   private static volatile Locale sCroatiaLocale;
+
+   public static Locale getCroatiaLocale() {
+      if (sCroatiaLocale == null) {
+         sCroatiaLocale = new Locale("hr");
+      }
+      return sCroatiaLocale;
+   }
+
+   /** 捷克 */
+   private static volatile Locale sCzechRepublicLocale;
+
+   public static Locale getCzechRepublicLocale() {
+      if (sCzechRepublicLocale == null) {
+         sCzechRepublicLocale = new Locale("cs");
+      }
+      return sCzechRepublicLocale;
+   }
+
+   /** 丹麦文 */
+   private static volatile Locale sDanishLocale;
+
+   public static Locale getDanishLocale() {
+      if (sDanishLocale == null) {
+         sDanishLocale = new Locale("da");
+      }
+      return sDanishLocale;
+   }
+
+   /** 迪维希语 */
+   private static volatile Locale sDhivehiLocale;
+
+   public static Locale getDhivehiLocale() {
+      if (sDhivehiLocale == null) {
+         sDhivehiLocale = new Locale("div");
+      }
+      return sDhivehiLocale;
+   }
+
+   /** 荷兰 */
+   private static volatile Locale sNetherlandsLocale;
+
+   public static Locale getNetherlandsLocale() {
+      if (sNetherlandsLocale == null) {
+         sNetherlandsLocale = new Locale("nl");
+      }
+      return sNetherlandsLocale;
+   }
+
+   /** 法罗语 */
+   private static volatile Locale sFaroeseLocale;
+
+   public static Locale getFaroeseLocale() {
+      if (sFaroeseLocale == null) {
+         sFaroeseLocale = new Locale("fo");
+      }
+      return sFaroeseLocale;
+   }
+
+   /** 爱沙尼亚 */
+   private static volatile Locale sEstoniaLocale;
+
+   public static Locale getEstoniaLocale() {
+      if (sEstoniaLocale == null) {
+         sEstoniaLocale = new Locale("et");
+      }
+      return sEstoniaLocale;
+   }
+
+   /** 波斯语 */
+   private static volatile Locale sFarsiLocale;
+
+   public static Locale getFarsiLocale() {
+      if (sFarsiLocale == null) {
+         sFarsiLocale = new Locale("fa");
+      }
+      return sFarsiLocale;
+   }
+
+   /** 芬兰语 */
+   private static volatile Locale sFinnishLocale;
+
+   public static Locale getFinnishLocale() {
+      if (sFinnishLocale == null) {
+         sFinnishLocale = new Locale("fi");
+      }
+      return sFinnishLocale;
+   }
+
+   /** 加利西亚 */
+   private static volatile Locale sGaliciaLocale;
+
+   public static Locale getGaliciaLocale() {
+      if (sGaliciaLocale == null) {
+         sGaliciaLocale = new Locale("gl");
+      }
+      return sGaliciaLocale;
+   }
+
+   /** 格鲁吉亚州 */
+   private static volatile Locale sGeorgiaLocale;
+
+   public static Locale getGeorgiaLocale() {
+      if (sGeorgiaLocale == null) {
+         sGeorgiaLocale = new Locale("ka");
+      }
+      return sGeorgiaLocale;
+   }
+
+   /** 希腊 */
+   private static volatile Locale sGreeceLocale;
+
+   public static Locale getGreeceLocale() {
+      if (sGreeceLocale == null) {
+         sGreeceLocale = new Locale("el");
+      }
+      return sGreeceLocale;
+   }
+
+   /** 古吉拉特语 */
+   private static volatile Locale sGujaratiLocale;
+
+   public static Locale getGujaratiLocale() {
+      if (sGujaratiLocale == null) {
+         sGujaratiLocale = new Locale("gu");
+      }
+      return sGujaratiLocale;
+   }
+
+   /** 希伯来 */
+   private static volatile Locale sHebrewLocale;
+
+   public static Locale getHebrewLocale() {
+      if (sHebrewLocale == null) {
+         sHebrewLocale = new Locale("he");
+      }
+      return sHebrewLocale;
+   }
+
+   /** 北印度语 */
+   private static volatile Locale sHindiLocale;
+
+   public static Locale getHindiLocale() {
+      if (sHindiLocale == null) {
+         sHindiLocale = new Locale("hi");
+      }
+      return sHindiLocale;
+   }
+
+   /** 匈牙利 */
+   private static volatile Locale sHungaryLocale;
+
+   public static Locale getHungaryLocale() {
+      if (sHungaryLocale == null) {
+         sHungaryLocale = new Locale("hu");
+      }
+      return sHungaryLocale;
+   }
+
+   /** 冰岛语 */
+   private static volatile Locale sIcelandicLocale;
+
+   public static Locale getIcelandicLocale() {
+      if (sIcelandicLocale == null) {
+         sIcelandicLocale = new Locale("is");
+      }
+      return sIcelandicLocale;
+   }
+
+   /** 印尼 */
+   private static volatile Locale sIndonesiaLocale;
+
+   public static Locale getIndonesiaLocale() {
+      if (sIndonesiaLocale == null) {
+         sIndonesiaLocale = new Locale("id");
+      }
+      return sIndonesiaLocale;
+   }
+
+   /** 卡纳达语 */
+   private static volatile Locale sKannadaLocale;
+
+   public static Locale getKannadaLocale() {
+      if (sKannadaLocale == null) {
+         sKannadaLocale = new Locale("kn");
+      }
+      return sKannadaLocale;
+   }
+
+   /** 哈萨克语 */
+   private static volatile Locale sKazakhLocale;
+
+   public static Locale getKazakhLocale() {
+      if (sKazakhLocale == null) {
+         sKazakhLocale = new Locale("kk");
+      }
+      return sKazakhLocale;
+   }
+
+   /** 贡根文 */
+   private static volatile Locale sKonkaniLocale;
+
+   public static Locale getKonkaniLocale() {
+      if (sKonkaniLocale == null) {
+         sKonkaniLocale = new Locale("kok");
+      }
+      return sKonkaniLocale;
+   }
+
+   /** 吉尔吉斯斯坦 */
+   private static volatile Locale sKyrgyzLocale;
+
+   public static Locale getKyrgyzLocale() {
+      if (sKyrgyzLocale == null) {
+         sKyrgyzLocale = new Locale("ky");
+      }
+      return sKyrgyzLocale;
+   }
+
+   /** 拉脱维亚 */
+   private static volatile Locale sLatviaLocale;
+
+   public static Locale getLatviaLocale() {
+      if (sLatviaLocale == null) {
+         sLatviaLocale = new Locale("lv");
+      }
+      return sLatviaLocale;
+   }
+
+   /** 立陶宛 */
+   private static volatile Locale sLithuaniaLocale;
+
+   public static Locale getLithuaniaLocale() {
+      if (sLithuaniaLocale == null) {
+         sLithuaniaLocale = new Locale("lt");
+      }
+      return sLithuaniaLocale;
+   }
+
+   /** 马其顿 */
+   private static volatile Locale sMacedoniaLocale;
+
+   public static Locale getMacedoniaLocale() {
+      if (sMacedoniaLocale == null) {
+         sMacedoniaLocale = new Locale("mk");
+      }
+      return sMacedoniaLocale;
+   }
+
+   /** 马来 */
+   private static volatile Locale sMalayLocale;
+
+   public static Locale getMalayLocale() {
+      if (sMalayLocale == null) {
+         sMalayLocale = new Locale("ms");
+      }
+      return sMalayLocale;
+   }
+
+   /** 马拉地语 */
+   private static volatile Locale sMarathiLocale;
+
+   public static Locale getMarathiLocale() {
+      if (sMarathiLocale == null) {
+         sMarathiLocale = new Locale("mr");
+      }
+      return sMarathiLocale;
+   }
+
+   /** 蒙古 */
+   private static volatile Locale sMongoliaLocale;
+
+   public static Locale getMongoliaLocale() {
+      if (sMongoliaLocale == null) {
+         sMongoliaLocale = new Locale("mn");
+      }
+      return sMongoliaLocale;
+   }
+
+   /** 挪威 */
+   private static volatile Locale sNorwayLocale;
+
+   public static Locale getNorwayLocale() {
+      if (sNorwayLocale == null) {
+         sNorwayLocale = new Locale("no");
+      }
+      return sNorwayLocale;
+   }
+
+   /** 波兰 */
+   private static volatile Locale sPolandLocale;
+
+   public static Locale getPolandLocale() {
+      if (sPolandLocale == null) {
+         sPolandLocale = new Locale("pl");
+      }
+      return sPolandLocale;
+   }
+
+   /** 葡萄牙语 */
+   private static volatile Locale sPortugueseLocale;
+
+   public static Locale getPortugalLocale() {
+      if (sPortugueseLocale == null) {
+         sPortugueseLocale = new Locale("pt");
+      }
+      return sPortugueseLocale;
+   }
+
+   /** 旁遮普语(印度和巴基斯坦语) */
+   private static volatile Locale sPunjabLocale;
+
+   public static Locale getPunjabLocale() {
+      if (sPunjabLocale == null) {
+         sPunjabLocale = new Locale("pa");
+      }
+      return sPunjabLocale;
+   }
+
+   /** 罗马尼亚语 */
+   private static volatile Locale sRomanianLocale;
+
+   public static Locale getRomanianLocale() {
+      if (sRomanianLocale == null) {
+         sRomanianLocale = new Locale("ro");
+      }
+      return sRomanianLocale;
+   }
+
+   /** 俄国 */
+   private static volatile Locale sRussiaLocale;
+
+   public static Locale getRussiaLocale() {
+      if (sRussiaLocale == null) {
+         sRussiaLocale = new Locale("ru");
+      }
+      return sRussiaLocale;
+   }
+
+   /** 梵文 */
+   private static volatile Locale sSanskritLocale;
+
+   public static Locale getSanskritLocale() {
+      if (sSanskritLocale == null) {
+         sSanskritLocale = new Locale("sa");
+      }
+      return sSanskritLocale;
+   }
+
+   /** 斯洛伐克 */
+   private static volatile Locale sSlovakiaLocale;
+
+   public static Locale getSlovakiaLocale() {
+      if (sSlovakiaLocale == null) {
+         sSlovakiaLocale = new Locale("sk");
+      }
+      return sSlovakiaLocale;
+   }
+
+   /** 斯洛文尼亚 */
+   private static volatile Locale sSloveniaLocale;
+
+   public static Locale getSloveniaLocale() {
+      if (sSloveniaLocale == null) {
+         sSloveniaLocale = new Locale("sl");
+      }
+      return sSloveniaLocale;
+   }
+
+   /** 西班牙语 */
+   private static volatile Locale sSpanishLocale;
+
+   public static Locale getSpainLocale() {
+      if (sSpanishLocale == null) {
+         sSpanishLocale = new Locale("es");
+      }
+      return sSpanishLocale;
+   }
+
+   /** 斯瓦希里语 */
+   private static volatile Locale sSwahiliLocale;
+
+   public static Locale getSwahiliLocale() {
+      if (sSwahiliLocale == null) {
+         sSwahiliLocale = new Locale("sw");
+      }
+      return sSwahiliLocale;
+   }
+
+   /** 瑞典 */
+   private static volatile Locale sSwedenLocale;
+
+   public static Locale getSwedenLocale() {
+      if (sSwedenLocale == null) {
+         sSwedenLocale = new Locale("sv");
+      }
+      return sSwedenLocale;
+   }
+
+   /** 叙利亚语 */
+   private static volatile Locale sSyriacLocale;
+
+   public static Locale getSyriacLocale() {
+      if (sSyriacLocale == null) {
+         sSyriacLocale = new Locale("syr");
+      }
+      return sSyriacLocale;
+   }
+
+   /** 坦米尔 */
+   private static volatile Locale sTamilLocale;
+
+   public static Locale getTamilLocale() {
+      if (sTamilLocale == null) {
+         sTamilLocale = new Locale("ta");
+      }
+      return sTamilLocale;
+   }
+
+   /** 泰语 */
+   private static volatile Locale sThailandLocale;
+
+   public static Locale getThailandLocale() {
+      if (sThailandLocale == null) {
+         sThailandLocale = new Locale("th");
+      }
+      return sThailandLocale;
+   }
+
+   /** 鞑靼语 */
+   private static volatile Locale sTatarLocale;
+
+   public static Locale getTatarLocale() {
+      if (sTatarLocale == null) {
+         sTatarLocale = new Locale("tt");
+      }
+      return sTatarLocale;
+   }
+
+   /** 泰卢固语 */
+   private static volatile Locale sTeluguLocale;
+
+   public static Locale getTeluguLocale() {
+      if (sTeluguLocale == null) {
+         sTeluguLocale = new Locale("te");
+      }
+      return sTeluguLocale;
+   }
+
+   /** 土耳其语 */
+   private static volatile Locale sTurkishLocale;
+
+   public static Locale getTurkishLocale() {
+      if (sTurkishLocale == null) {
+         sTurkishLocale = new Locale("tr");
+      }
+      return sTurkishLocale;
+   }
+
+   /** 乌克兰 */
+   private static volatile Locale sUkraineLocale;
+
+   public static Locale getUkraineLocale() {
+      if (sUkraineLocale == null) {
+         sUkraineLocale = new Locale("uk");
+      }
+      return sUkraineLocale;
+   }
+
+   /** 乌尔都语 */
+   private static volatile Locale sUrduLocale;
+
+   public static Locale getUrduLocale() {
+      if (sUrduLocale == null) {
+         sUrduLocale = new Locale("ur");
+      }
+      return sUrduLocale;
+   }
+
+   /** 乌兹别克语 */
+   private static volatile Locale sUzbekLocale;
+
+   public static Locale getUzbekLocale() {
+      if (sUzbekLocale == null) {
+         sUzbekLocale = new Locale("uz");
+      }
+      return sUzbekLocale;
+   }
+}

+ 238 - 0
MultiLanguages/src/main/java/com/example/multilanguages/MultiLanguages.java

@@ -0,0 +1,238 @@
+package com.example.multilanguages;
+
+import android.app.Application;
+import android.app.LocaleManager;
+import android.content.Context;
+import android.content.res.Resources;
+import android.os.Build;
+import android.os.LocaleList;
+import android.os.Looper;
+import android.os.MessageQueue;
+import android.text.TextUtils;
+import java.util.Locale;
+
+/**
+ *    author : Android 轮子哥
+ *    github : https://github.com/getActivity/MultiLanguages
+ *    time   : 2019/05/03
+ *    desc   : 语种切换框架
+ */
+@SuppressWarnings("unused")
+public final class MultiLanguages {
+
+    /** 应用上下文对象 */
+    private static Application sApplication;
+
+    /** 语种变化监听对象 */
+    private static OnLanguageListener sLanguageListener;
+
+    /**
+     * 初始化多语种框架
+     */
+    public static void init(Application application) {
+        init(application, true);
+    }
+
+    public static void init(final Application application, boolean inject) {
+        if (sApplication != null) {
+            // 如果框架已经初始化过了,则不往下执行
+            return;
+        }
+        sApplication = application;
+        LanguagesUtils.setDefaultLocale(application);
+        if (inject) {
+            ActivityLanguages.inject(application);
+        }
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+            LocaleManager localeManager = application.getSystemService(LocaleManager.class);
+            if (localeManager != null) {
+                if (isSystemLanguage(application)) {
+                    // 在没有设置过 setApplicationLocales 方法,里面的默认值会为 LocaleList.getEmptyLocaleList()
+                    // 当设置过一次的 setApplicationLocales 为其他值,在 Android 13 的手机设置中修改语种(系统或者应用)的时候
+                    // 就会导致 context.registerComponentCallbacks 中的 onConfigurationChanged 监听方法没有触发到
+                    // 如果当前应用指定了某种语种,监听方法没有回调是正常的,但是如果当前语种是跟随系统的模式,那么不回调就是有问题的了
+                    // 这是因为系统对 setApplicationLocales 的结果进行了持久化操作,所以这里要重新设置一下,并传入 LocaleList.getEmptyLocaleList()
+                    // Github issue:https://github.com/getActivity/MultiLanguages/issues/37
+                    localeManager.setApplicationLocales(LocaleList.getEmptyLocaleList());
+                } else {
+                    localeManager.setApplicationLocales(new LocaleList(getAppLanguage(application)));
+                }
+            }
+        }
+        // 等所有的任务都执行完了,再设置对系统语种的监听,用户不可能在这点间隙的时间完成切换语言的
+        // 经过实践证明 IdleHandler 会在第一个 Activity attachBaseContext 之后调用的,所以没有什么问题
+        Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
+            @Override
+            public boolean queueIdle() {
+                ConfigurationObserver.register(application);
+                LocaleChangeReceiver.register(application);
+                return false;
+            }
+        });
+    }
+
+    /**
+     * 在上下文的子类中重写 attachBaseContext 方法(用于更新 Context 的语种)
+     */
+    public static Context attach(Context context) {
+        Locale locale = getAppLanguage(context);
+        if (LanguagesUtils.getLocale(context).equals(locale)) {
+            return context;
+        }
+        return LanguagesUtils.attachLanguages(context, locale);
+    }
+
+    /**
+     * 更新 Context 的语种
+     */
+    public static void updateAppLanguage(Context context) {
+        updateAppLanguage(context, context.getResources());
+    }
+
+    /**
+     * 更新 Resources 的语种
+     */
+    public static void updateAppLanguage(Context context, Resources resources) {
+        if (resources == null) {
+            return;
+        }
+        if (LanguagesUtils.getLocale(resources.getConfiguration()).equals(getAppLanguage(context))) {
+            return;
+        }
+        LanguagesUtils.updateLanguages(resources, getAppLanguage(context));
+    }
+
+    /**
+     * 获取 App 的语种
+     */
+    public static Locale getAppLanguage(Context context) {
+        if (isSystemLanguage(context)) {
+            return getSystemLanguage(context);
+        } else {
+            return LanguagesConfig.readAppLanguageSetting(context);
+        }
+    }
+
+    /**
+     * 设置 App 的语种
+     *
+     * @return              语种是否发生改变了
+     */
+    public static boolean setAppLanguage(Context context, Locale newLocale) {
+        //if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+        //      context.getSystemService(LocaleManager.class).setApplicationLocales(new LocaleList(newLocale));
+        //}
+        // 这里解释一下,在 Android 13 上为什么不用 LocaleManager.setApplicationLocales 来设置语种,原因如下:
+        // 1. 调用此 API 会自动重启 Activity,而框架是将重启操作放到了外层给开发者去重启
+        // 2. 上面说了,调用此 API 会重启 Activity,重启也就算了,还顺带闪了一下,这个不能忍
+        LanguagesConfig.saveAppLanguageSetting(context, newLocale);
+        if (LanguagesUtils.getLocale(context).equals(newLocale)) {
+            return false;
+        }
+
+        Locale oldLocale = LanguagesUtils.getLocale(context);
+        // 更新 Context 的语种
+        LanguagesUtils.updateLanguages(context.getResources(), newLocale);
+        if (context != sApplication) {
+            // 更新 Application 的语种
+            LanguagesUtils.updateLanguages(sApplication.getResources(), newLocale);
+        }
+
+        LanguagesUtils.setDefaultLocale(context);
+        if (sLanguageListener != null) {
+            sLanguageListener.onAppLocaleChange(oldLocale, newLocale);
+        }
+        return true;
+    }
+
+    /**
+     * 获取系统的语种
+     */
+    public static Locale getSystemLanguage(Context context) {
+        return LanguagesUtils.getSystemLocale(context);
+    }
+
+    /**
+     * 是否跟随系统的语种
+     */
+    public static boolean isSystemLanguage(Context context) {
+        return LanguagesConfig.isSystemLanguage(context);
+    }
+
+    /**
+     * 跟随系统语种
+     *
+     * @return              语种是否发生改变了
+     */
+    public static boolean clearAppLanguage(Context context) {
+        LanguagesConfig.clearLanguageSetting(context);
+        if (LanguagesUtils.getLocale(context).equals(getSystemLanguage(sApplication))) {
+            return false;
+        }
+
+        LanguagesUtils.updateLanguages(context.getResources(), getSystemLanguage(sApplication));
+        LanguagesUtils.setDefaultLocale(context);
+        if (context != sApplication) {
+            // 更新 Application 的语种
+            LanguagesUtils.updateLanguages(sApplication.getResources(), getSystemLanguage(sApplication));
+        }
+        return true;
+    }
+
+    /**
+     * 设置默认的语种(越早设置越好)
+     */
+    public static void setDefaultLanguage(Locale locale) {
+        LanguagesConfig.setDefaultLanguage(locale);
+    }
+
+    /**
+     * 对比两个语言是否是同一个语种(比如:中文有简体和繁体,但是它们都属于同一个语种)
+     */
+    public static boolean equalsLanguage(Locale locale1, Locale locale2) {
+        return TextUtils.equals(locale1.getLanguage(), locale2.getLanguage());
+    }
+
+    /**
+     * 对比两个语言是否是同一个地方的(比如:中国大陆用的中文简体,中国台湾用的中文繁体)
+     */
+    public static boolean equalsCountry(Locale locale1, Locale locale2) {
+        return equalsLanguage(locale1, locale2) &&
+                TextUtils.equals(locale1.getCountry(), locale2.getCountry());
+    }
+
+    /**
+     * 获取某个语种下的 String
+     */
+    public static String getLanguageString(Context context, Locale locale, int id) {
+        return generateLanguageResources(context, locale).getString(id);
+    }
+
+    /**
+     * 生成某个语种下的 Resources 对象
+     */
+    public static Resources generateLanguageResources(Context context, Locale locale) {
+        return LanguagesUtils.generateLanguageResources(context, locale);
+    }
+
+    /**
+     * 设置语种变化监听器
+     */
+    public static void setOnLanguageListener(OnLanguageListener listener) {
+        sLanguageListener = listener;
+    }
+
+    /**
+     * 设置保存的 SharedPreferences 文件名(请在 Application 初始化之前设置,可以放在 Application 中的代码块或者静态代码块)
+     */
+    public static void setSharedPreferencesName(String name) {
+        LanguagesConfig.setSharedPreferencesName(name);
+    }
+
+    /**
+     * 获取语种变化监听对象
+     */
+    static OnLanguageListener getOnLanguagesListener() {
+        return sLanguageListener;
+    }
+}

+ 28 - 0
MultiLanguages/src/main/java/com/example/multilanguages/OnLanguageListener.java

@@ -0,0 +1,28 @@
+package com.example.multilanguages;
+
+import java.util.Locale;
+
+/**
+ *    author : Android 轮子哥
+ *    github : https://github.com/getActivity/MultiLanguages
+ *    time   : 2021/01/18
+ *    desc   : 语种变化监听器
+ */
+public interface OnLanguageListener {
+
+    /**
+     * 当前应用语种发生变化时回调
+     *
+     * @param oldLocale         旧语种
+     * @param newLocale         新语种
+     */
+    void onAppLocaleChange(Locale oldLocale, Locale newLocale);
+
+    /**
+     * 手机系统语种发生变化时回调
+     *
+     * @param oldLocale         旧语种
+     * @param newLocale         新语种
+     */
+    void onSystemLocaleChange(Locale oldLocale, Locale newLocale);
+}

+ 17 - 0
MultiLanguages/src/test/java/com/example/multilanguages/ExampleUnitTest.java

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

File diff suppressed because it is too large
+ 363 - 0
README.md


+ 1 - 0
app/.gitignore

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

+ 145 - 0
app/build.gradle

@@ -0,0 +1,145 @@
+//****************************************
+//************ app 壳的配置文件 ************
+//****************************************
+
+import com.quyunshuo.androidbaseframemvvm.buildsrc.*
+
+plugins {
+    alias(libs.plugins.application)
+    alias(libs.plugins.kotlin)
+    alias(libs.plugins.hilt)
+    id "kotlin-kapt"
+}
+
+android {
+    signingConfigs {
+        release {
+            keyAlias 'hboxs'
+            keyPassword 'android'
+            storeFile file('../hboxs.jks')
+            storePassword 'android'
+        }
+    }
+    namespace 'com.quyunshuo.androidbaseframemvvm'
+    compileSdk ProjectBuildConfig.compileSdkVersion
+
+    defaultConfig {
+        applicationId ProjectBuildConfig.applicationId
+        minSdk ProjectBuildConfig.minSdkVersion
+        targetSdk ProjectBuildConfig.targetSdkVersion
+        versionCode ProjectBuildConfig.versionCode
+        versionName ProjectBuildConfig.versionName
+        flavorDimensions "default"
+        testInstrumentationRunner DependencyConfig.AndroidX.AndroidJUnitRunner
+        multiDexKeepProguard file("multidexKeep.pro")
+
+        manifestPlaceholders = [
+                GETUI_APP_ID    : "GKa6qa12heALjEXZlAn1U3",
+                GETUI_APP_KEY   : "89WV8dfjRg6RlxEchgmnS6",
+                GETUI_APP_SECRET: "fLvPjR8hni7VFMkgjh8lx2"
+        ]
+        ndk {
+            // 设置支持的SO库架构
+//            abiFilters 'armeabi', 'x86', 'armeabi-v7a', 'x86_64', 'arm64-v8a'
+            abiFilters 'armeabi-v7a', 'arm64-v8a'
+        }
+
+
+
+        packagingOptions {
+            exclude 'META-INF/io.netty.versions.properties'
+        }
+    }
+    configurations.all {
+        resolutionStrategy {
+            force 'commons-codec:commons-codec:1.15'
+        }
+    }
+    signingConfigs {
+        release {
+            keyAlias 'sc'
+            keyPassword '1234567890'
+            storeFile file('../snowcone.jks')
+            storePassword '1234567890'
+        }
+    }
+    sourceSets {
+        main {
+            jniLibs.srcDirs = ['libs']
+        }
+    }
+    buildTypes {
+        // 对应 ALPHA 版本
+        debug {
+            buildConfigField "String", "VERSION_TYPE", "\"${ProjectBuildConfig.Version.ALPHA}\""
+//            signingConfig signingConfigs.releaseConfig
+            minifyEnabled false //为true,则对代码进行混淆和压缩
+//            shrinkResources false //为true, 则对资源进行缩减
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+
+        }
+        beta {
+            buildConfigField "String", "VERSION_TYPE", "\"${ProjectBuildConfig.Version.BETA}\""
+//            signingConfig signingConfigs.releaseConfig
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+        }
+        release {
+            buildConfigField "String", "VERSION_TYPE", "\"${ProjectBuildConfig.Version.RELEASE}\""
+//            signingConfig signingConfigs.releaseConfig
+            minifyEnabled false
+//            shrinkResources true //为true, 则对资源进行缩减
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+            signingConfig signingConfigs.release
+        }
+
+    }
+
+    // 自定义打包apk的文件名
+    android.applicationVariants.all { variant ->
+        variant.outputs.all { output ->
+            if (outputFileName != null && outputFileName.endsWith('.apk')) {
+                outputFileName = "${ProjectBuildConfig.applicationId}" +
+                        "_${ProjectBuildConfig.versionCode}" +
+                        "_${ProjectBuildConfig.versionName}" +
+                        "_${variant.buildType.name}" +
+                        ".apk"
+            }
+        }
+    }
+    productFlavors {
+        china {
+            buildConfigField("boolean", "APP_TYPE", "true")
+        }
+        abroad {
+            buildConfigField("boolean", "APP_TYPE", "false")
+        }
+    }
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_17
+        targetCompatibility JavaVersion.VERSION_17
+    }
+
+    kotlinOptions {
+        jvmTarget = '17'
+    }
+}
+
+dependencies {
+    implementation fileTree(dir: "libs", include: ["*.jar"])
+
+    if (!ProjectBuildConfig.isAppMode) {
+        implementation project(path: ':module_home')
+        implementation project(path: ':module_backstage')
+        implementation project(path: ':module_pay')
+        implementation project(path: ':module_database')
+        implementation project(path: ':serialport-api')
+    } else {
+        implementation project(path: ':lib_common')
+    }
+    implementation DependencyConfig.JetPack.HiltCore
+    implementation project(':lib_base')
+
+    kapt DependencyConfig.GitHub.AutoServiceAnnotations
+    kapt DependencyConfig.JetPack.HiltApt
+}

BIN
app/libs/arm64-v8a/libjSerialComm.so


BIN
app/libs/arm64-v8a/libserial_port.so


BIN
app/libs/armeabi-v7a/libjSerialComm.so


BIN
app/libs/armeabi-v7a/libserial_port.so


BIN
app/libs/armeabi/libjSerialComm.so


BIN
app/libs/armeabi/libserial_port.so


BIN
app/libs/mips/libjSerialComm.so


BIN
app/libs/mips64/libjSerialComm.so


BIN
app/libs/x86/libjSerialComm.so


BIN
app/libs/x86/libserial_port.so


BIN
app/libs/x86_64/libjSerialComm.so


BIN
app/libs/x86_64/libserial_port.so


+ 1 - 0
app/multidexKeep.pro

@@ -0,0 +1 @@
+-keep public class com.tencent.bugly.**{*;}

+ 115 - 0
app/proguard-rules.pro

@@ -0,0 +1,115 @@
+# AGP 8.x 警告生成
+# Please add these rules to your existing keep rules in order to suppress warnings.
+# This is generated automatically by the Android Gradle plugin.
+-dontwarn dalvik.system.VMStack
+-dontwarn javax.lang.model.element.Element
+-dontwarn org.bouncycastle.jsse.BCSSLParameters
+-dontwarn org.bouncycastle.jsse.BCSSLSocket
+-dontwarn org.bouncycastle.jsse.provider.BouncyCastleJsseProvider
+-dontwarn org.conscrypt.Conscrypt$Version
+-dontwarn org.conscrypt.Conscrypt
+-dontwarn org.conscrypt.ConscryptHostnameVerifier
+-dontwarn org.openjsse.javax.net.ssl.SSLParameters
+-dontwarn org.openjsse.javax.net.ssl.SSLSocket
+-dontwarn org.openjsse.net.ssl.OpenJSSE
+
+# R8的
+# Please add these rules to your existing keep rules in order to suppress warnings.
+# This is generated automatically by the Android Gradle plugin.
+-dontwarn android.os.SystemProperties
+-dontwarn org.slf4j.impl.StaticLoggerBinder
+-dontwarn reactor.blockhound.integration.BlockHoundIntegration
+
+
+#-----------基本配置--------------
+# 代码混淆压缩比,在0~7之间,默认为5,一般不需要改
+-optimizationpasses 5
+
+# 混淆时不使用大小写混合,混淆后的类名为小写
+-dontusemixedcaseclassnames
+
+# 指定不去忽略非公共的库的类
+-dontskipnonpubliclibraryclasses
+
+# 指定不去忽略非公共的库的类的成员
+-dontskipnonpubliclibraryclassmembers
+
+# 不做预校验,可加快混淆速度
+# preverify是proguard的4个步骤之一
+# Android不需要preverify,去掉这一步可以加快混淆速度
+-dontpreverify
+
+# 不优化输入的类文件
+-dontoptimize
+
+# 混淆时生成日志文件,即映射文件
+-verbose
+
+# 指定映射文件的名称
+-printmapping proguardMapping.txt
+
+#混淆时所采用的算法
+-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
+
+# 保护代码中的Annotation不被混淆
+-keepattributes *Annotation*
+
+# 忽略警告
+-ignorewarnings
+
+# 保护泛型不被混淆
+-keepattributes Signature
+
+# 抛出异常时保留代码行号
+-keepattributes SourceFile,LineNumberTable
+
+#------------------其他配置-------------------------
+
+#eventbus
+-keepattributes *Annotation*
+
+-keepclassmembers class ** {
+    @de.greenrobot.event.Subscribe <methods>;
+}
+-keep enum org.greenrobot.eventbus.ThreadMode { *; }
+-keepclassmembers class ** {
+    @org.greenrobot.eventbus.Subscribe <methods>;
+}
+-keep enum de.greenrobot.event.ThreadMode { *; }
+
+
+# 不混淆资源类 不增加,会出现0xffffffff资源找不到报错
+-keepclassmembers class **.R$* { *; }
+
+# arouter 混淆 不增加,会出现报错,找不到路径。
+-keep class com.alibaba.android.arouter.** { *; }
+-keep class * implements com.alibaba.android.arouter.facade.template.ISyringe{*;}
+
+#3D 地图 V5.0.0之前:
+-keep   class com.amap.api.maps.**{*;}
+-keep   class com.autonavi.amap.mapcore.*{*;}
+-keep   class com.amap.api.trace.**{*;}
+
+# 3D 地图 V5.0.0之后:
+-keep   class com.amap.api.maps.**{*;}
+-keep   class com.autonavi.**{*;}
+-keep   class com.amap.api.trace.**{*;}
+
+# 定位
+-keep class com.amap.api.location.**{*;}
+-keep class com.amap.api.fence.**{*;}
+-keep class com.loc.**{*;}
+-keep class com.autonavi.aps.amapapi.model.**{*;}
+
+#搜索
+-keep   class com.amap.api.services.**{*;}
+
+#  2D地图
+-keep class com.amap.api.maps2d.**{*;}
+-keep class com.amap.api.mapcore2d.**{*;}
+
+# 导航
+-keep class com.amap.api.navi.**{*;}
+-keep class com.autonavi.**{*;}
+# 保留 Apache Commons Codec 的所有类和方法
+-keep class org.apache.commons.codec.** { *; }

File diff suppressed because it is too large
+ 218735 - 0
app/proguardMapping.txt


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

@@ -0,0 +1,66 @@
+<?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.ACCESS_COARSE_LOCATION"></uses-permission>
+    <!--用于访问GPS定位-->
+    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"></uses-permission>
+    <!--用于获取运营商信息,用于支持提供运营商信息相关的接口-->
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission>
+    <!--用于访问wifi网络信息,wifi信息会用于进行网络定位-->
+    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"></uses-permission>
+    <!--用于获取wifi的获取权限,wifi信息会用来进行网络定位-->
+    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"></uses-permission>
+    <!--用于访问网络,网络定位需要上网-->
+    <uses-permission android:name="android.permission.INTERNET"></uses-permission>
+    <!--用于写入缓存数据到扩展存储卡-->
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
+    <!--用于申请调用A-GPS模块-->
+    <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS"></uses-permission>
+    <!--如果设置了target >= 28 如果需要启动后台定位则必须声明这个权限-->
+    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
+    <!--如果您的应用需要后台定位权限,且有可能运行在Android Q设备上,并且设置了target>28,必须增加这个权限声明-->
+    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
+    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
+    <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.SYSTEM_ALERT_WINDOW" /> <!-- ⾃定义权限  全局对话框 -->
+    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
+    <uses-permission
+        android:name="android.permission.WRITE_SETTINGS"
+        tools:ignore="ProtectedPermissions" />
+    <uses-permission
+        android:name="android.permission.READ_LOGS"
+        tools:ignore="ProtectedPermissions" />
+
+    <application
+        android:name="com.sunzee.app.AppApplication"
+        android:allowBackup="false"
+        android:icon="@drawable/pic_sc_01"
+        android:label="@string/app_name"
+        android:networkSecurityConfig="@xml/network_security_config"
+        android:roundIcon="@mipmap/ic_launcher_round"
+        android:supportsRtl="true"
+        android:theme="@style/base_AppTheme"
+        tools:replace="android:allowBackup"
+        android:requestLegacyExternalStorage="true"
+        tools:ignore="UnusedAttribute">
+        <service
+            android:name="androidx.appcompat.app.AppLocalesMetadataHolderService"
+            android:enabled="false"
+            android:exported="false">
+            <meta-data
+                android:name="autoStoreLocales"
+                android:value="true" />
+        </service>
+
+        <service android:name="com.quyunshuo.module.home.service.GlobalService"/>
+        <service android:name="com.quyunshuo.module.home.service.MqService" />
+        <service android:name="com.quyunshuo.module.home.service.ForceBackToAppService"
+            android:process=":my_service_process"/>
+
+    </application>
+
+</manifest>

+ 189 - 0
app/src/main/java/com/sunzee/app/AppApplication.kt

@@ -0,0 +1,189 @@
+package com.sunzee.app
+
+import android.app.ActivityManager
+import android.content.Context
+import android.content.Intent
+import android.os.Build
+import android.os.Environment
+import android.os.Process
+import android.text.TextUtils
+
+import com.hjq.http.EasyConfig
+import com.hjq.http.config.IRequestHandler
+import com.hjq.http.request.HttpRequest
+import com.hjq.toast.Toaster
+import com.quyunshuo.androidbaseframemvvm.base.BaseApplication
+import com.quyunshuo.androidbaseframemvvm.base.BuildConfig
+import com.quyunshuo.androidbaseframemvvm.base.utils.LanguageUtilsObject
+import com.quyunshuo.androidbaseframemvvm.base.utils.multilanguages.MultiLanguages
+import com.quyunshuo.androidbaseframemvvm.common.constant.Heartbeat
+import com.quyunshuo.androidbaseframemvvm.common.util.AppTypeManger
+import com.quyunshuo.androidbaseframemvvm.common.util.FileUtil
+import com.quyunshuo.androidbaseframemvvm.common.util.XLogUtil
+import com.quyunshuo.module.home.service.ForceBackToAppService
+import com.quyunshuo.module.home.service.GlobalService
+import com.quyunshuo.module.home.service.MqService
+import dagger.hilt.android.HiltAndroidApp
+import okhttp3.OkHttpClient
+import okhttp3.Response
+import org.greenrobot.eventbus.EventBus
+import java.io.File
+import java.lang.reflect.Type
+
+
+/**
+ * App壳
+ *
+ * @author Qu Yunshuo
+ * @since 4/23/21 6:08 PM
+ */
+@HiltAndroidApp
+class AppApplication : BaseApplication() {
+    private val TAG = "AppApplication"
+    private var instances: AppApplication? = null
+
+
+    override fun onCreate() {
+        instances = this
+      //  MultiLanguages.init(this)
+       // LanguageUtilsObject.applyOnCreate(this)
+
+        AppTypeManger.getInstance().init(com.quyunshuo.androidbaseframemvvm.BuildConfig.APP_TYPE)
+       // initLog()
+        if (isAppMainProcess()) {
+
+            XLogUtil.init()
+            // 开启EventBusAPT,优化反射效率 当组件作为App运行时需要将添加的Index注释掉 因为找不到对应的类了
+            EventBus
+                .builder()
+//            .addIndex(MainEventIndex())
+                .installDefaultEventBus()
+
+            // 初始化 Toast 框架
+            Toaster.init(this)
+            //打开串口
+//            connectDevice()
+            //创建设备编号
+           // Heartbeat.deviceId = FileUtil.getDeviceId()
+            Heartbeat.deviceId = XLogUtil.getDeviceId()
+            initEasyOkHttp()
+            val intent = Intent(this, GlobalService::class.java)
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+                startForegroundService(intent)
+            } else {
+                startService(intent)
+            }
+            //强制自恢复App
+            val forceBackToAppIntent = Intent(this, ForceBackToAppService::class.java)
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+                startForegroundService(forceBackToAppIntent)
+            } else {
+                startService(forceBackToAppIntent)
+            }
+
+            //开启MQ
+            startService(Intent(this, MqService::class.java))
+
+        }
+
+
+        super.onCreate()
+    }
+
+/*    override fun attachBaseContext(base: Context) {
+        super.attachBaseContext(MultiLanguages.attach(base));
+    }*/
+
+
+    private fun initLog() {
+//        initLeakCanary();//内存泄漏检测库
+        FileUtil.createLogFile() //日志初始化
+        FileUtil.createPhoneFile() //创建电话日志文件
+        FileUtil.pastDueLog() //删除过期日志
+        //创建文件夹
+        val externalFilesDir =
+            File(Environment.getExternalStorageDirectory().absolutePath + "/promptAudio")
+        if (externalFilesDir == null || !externalFilesDir.exists()) {
+            externalFilesDir!!.mkdir()
+        }
+    }
+
+    private fun initEasyOkHttp() {
+
+        /**
+         * 初始哈EasyHttp
+         */
+        var okHttpClient: OkHttpClient? = OkHttpClient.Builder().build()
+
+        // 网络请求框架初始化
+        EasyConfig.with(okHttpClient)
+            //  http://app.sunzee.com.cn/
+            .setServer { "https://sz.sunzee.com.cn/" } // 设置服务器配置(必须设置)
+            .setHandler(object : IRequestHandler {
+                override fun requestSuccess(p0: HttpRequest<*>, result: Response, p2: Type): Any {
+                    return result
+                }
+
+                override fun requestFail(p0: HttpRequest<*>, p1: Throwable): Throwable {
+                    return p1
+                }
+            })
+            .setRetryCount(1) // 设置请求重试次数
+            .setRetryTime(2000) // 设置请求重试时间
+            .into()
+
+    }
+
+//    private fun connectDevice() {
+//        //对plc进行连接
+////        val device = SerialPortDevice("/dev/ttyS4", "9600")
+////        try {
+////            SerialPortManager.getInstance().open(device)
+////        } catch (e: Exception) {
+////            Log.d(TAG, "connectDevice 串口打开失败: ")
+////        }
+////        Log.d(TAG, "connectDevice 串口打开成功: ")
+//
+//        //对单片机进行连接
+//        val device: VboxSerialPortDevice = VboxSerialPortDevice("dev/"+SpUtils.getString(MMKVName.TTYS_PLC,"ttyS7"), "115200")
+//        try {
+//            VboxSerialPortManager.getInstance().open(device)
+//        } catch (e: Exception) {
+//            Log.d(TAG, "connectDevice 串口打开失败: ")
+//        }
+//        Log.d(TAG, "connectDevice 串口打开成功: ")
+//    }
+
+    /**
+     * 判断是不是UI主进程,因为有些东西只能在UI主进程初始化
+     */
+    fun isAppMainProcess(): Boolean {
+        try {
+            val pid = Process.myPid()
+            val process: String = getAppNameByPID(instances!!.baseContext, pid)
+            return if (TextUtils.isEmpty(process)) {
+                true
+            } else if ("com.quyunshuo.androidbaseframemvvm".equals(process, ignoreCase = true)) {
+                true
+            } else {
+                false
+            }
+        } catch (e: java.lang.Exception) {
+            e.printStackTrace()
+            return true
+        }
+    }
+
+    /**
+     * 根据Pid得到进程名
+     */
+    fun getAppNameByPID(context: Context, pid: Int): String {
+        val manager = context.getSystemService(ACTIVITY_SERVICE) as ActivityManager
+        for (processInfo in manager.runningAppProcesses) {
+            if (processInfo.pid == pid) {
+                return processInfo.processName
+            }
+        }
+        return ""
+    }
+}

File diff suppressed because it is too large
+ 30 - 0
app/src/main/res/drawable-v24/ic_launcher_foreground.xml


+ 170 - 0
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>

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

@@ -0,0 +1,5 @@
+<?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" />
+</adaptive-icon>

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

@@ -0,0 +1,5 @@
+<?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" />
+</adaptive-icon>

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


BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png


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

@@ -0,0 +1,3 @@
+<resources>
+    <string name="app_name">MG231</string>
+</resources>

+ 8 - 0
app/src/main/res/xml/network_security_config.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+    <domain-config cleartextTrafficPermitted="true">
+        <domain includeSubdomains="true">android.bugly.qq.com</domain>
+        <domain includeSubdomains="true">sz.sunzee.com.cn</domain>
+        <domain includeSubdomains="true">qiniuyun.sunzee.com.cn</domain>
+    </domain-config>
+</network-security-config>

+ 56 - 0
base_lib.gradle

@@ -0,0 +1,56 @@
+//****************************************
+//********* lib 模块的公共脚本配置 **********
+//****************************************
+
+import com.quyunshuo.androidbaseframemvvm.buildsrc.*
+
+android {
+    compileSdkVersion ProjectBuildConfig.compileSdkVersion
+
+    defaultConfig {
+        minSdkVersion ProjectBuildConfig.minSdkVersion
+        targetSdkVersion ProjectBuildConfig.targetSdkVersion
+
+        consumerProguardFiles "consumer-rules.pro"
+
+        ndk {
+            // 设置支持的SO库架构
+            abiFilters 'armeabi', 'x86', 'armeabi-v7a', 'x86_64', 'arm64-v8a'
+//            abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86'
+        }
+    }
+
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_17
+        targetCompatibility JavaVersion.VERSION_17
+    }
+
+    kotlinOptions {
+        jvmTarget = "17"
+    }
+
+    buildTypes {
+        // 对应 ALPHA 版本
+        debug {
+            buildConfigField "String", "VERSION_TYPE", "\"${ProjectBuildConfig.Version.ALPHA}\""
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+        }
+        beta {
+            buildConfigField "String", "VERSION_TYPE", "\"${ProjectBuildConfig.Version.BETA}\""
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+        }
+        release {
+            buildConfigField "String", "VERSION_TYPE", "\"${ProjectBuildConfig.Version.RELEASE}\""
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+        }
+    }
+}
+
+kapt {
+    arguments {
+        arg("AROUTER_MODULE_NAME", project.getName())
+    }
+}

+ 89 - 0
base_module.gradle

@@ -0,0 +1,89 @@
+//****************************************
+//******** module模块的公共脚本配置 *********
+//****************************************
+
+import com.quyunshuo.androidbaseframemvvm.buildsrc.*
+
+android {
+    compileSdk ProjectBuildConfig.compileSdkVersion
+
+    defaultConfig {
+        minSdk ProjectBuildConfig.minSdkVersion
+        targetSdk ProjectBuildConfig.targetSdkVersion
+        versionCode ProjectBuildConfig.versionCode
+        versionName ProjectBuildConfig.versionName
+        testInstrumentationRunner DependencyConfig.AndroidX.AndroidJUnitRunner
+
+        ndk {
+            // 设置支持的SO库架构
+            //abiFilters 'armeabi', 'x86', 'armeabi-v7a', 'x86_64', 'arm64-v8a'
+            abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86'
+        }
+    }
+
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_17
+        targetCompatibility JavaVersion.VERSION_17
+    }
+
+    kotlinOptions {
+        jvmTarget = "17"
+    }
+
+    buildFeatures {
+        viewBinding = true
+    }
+
+    sourceSets {
+        main {
+            manifest.srcFile 'src/main/AndroidManifest.xml'
+            java {
+                //排除debug文件夹下的所有文件
+                exclude 'debug/**'
+            }
+        }
+    }
+
+    buildTypes {
+        // 对应 ALPHA 版本
+        debug {
+            buildConfigField "String", "VERSION_TYPE", "\"${ProjectBuildConfig.Version.ALPHA}\""
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+        }
+        beta {
+            buildConfigField "String", "VERSION_TYPE", "\"${ProjectBuildConfig.Version.BETA}\""
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+        }
+        release {
+            buildConfigField "String", "VERSION_TYPE", "\"${ProjectBuildConfig.Version.RELEASE}\""
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+        }
+    }
+}
+
+kapt {
+    arguments {
+        arg("AROUTER_MODULE_NAME", project.name)
+        arg("eventBusIndex", "${ProjectBuildConfig.applicationId}.eventbus.index.${project.name}EventIndex")
+    }
+}
+
+dependencies {
+    implementation fileTree(dir: 'libs', include: ['*.jar'])
+
+    api project(path: ':lib_common')
+
+    testImplementation DependencyConfig.Android.Junit
+    androidTestImplementation DependencyConfig.AndroidX.TestExtJunit
+    androidTestImplementation DependencyConfig.AndroidX.TestEspresso
+    implementation DependencyConfig.JetPack.HiltCore
+
+    kapt DependencyConfig.GitHub.ARouteCompiler
+    kapt DependencyConfig.GitHub.EventBusAPT
+    kapt DependencyConfig.GitHub.AutoServiceAnnotations
+    kapt DependencyConfig.JetPack.HiltApt
+    kapt DependencyConfig.JetPack.LifecycleCompilerAPT
+}

+ 8 - 0
build.gradle.kts

@@ -0,0 +1,8 @@
+@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed
+plugins {
+    alias(libs.plugins.application) apply false
+    alias(libs.plugins.library) apply false
+    alias(libs.plugins.kotlin) apply false
+    alias(libs.plugins.hilt) apply false
+}
+true // Needed to make the Suppress annotation work for the plugins block

+ 2 - 0
buildSrc/build.gradle.kts

@@ -0,0 +1,2 @@
+plugins { `kotlin-dsl` }
+repositories { mavenCentral() }

+ 172 - 0
buildSrc/gradlew

@@ -0,0 +1,172 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+##  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=""
+
+# 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, switch paths to Windows format before running java
+if $cygwin ; 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=$((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"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+  cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"

+ 84 - 0
buildSrc/gradlew.bat

@@ -0,0 +1,84 @@
+@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 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=
+
+@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 init
+
+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 init
+
+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
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+: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 %CMD_LINE_ARGS%
+
+: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

+ 215 - 0
buildSrc/src/main/kotlin/com/quyunshuo/androidbaseframemvvm/buildsrc/DependencyConfig.kt

@@ -0,0 +1,215 @@
+package com.quyunshuo.androidbaseframemvvm.buildsrc
+
+/**
+ * 项目依赖版本统一管理
+ *
+ * @author Qu Yunshuo
+ * @since 4/24/21 4:00 PM
+ */
+object DependencyConfig {
+
+    /**
+     * 依赖版本号
+     *
+     * @author Qu Yunshuo
+     * @since 4/24/21 4:01 PM
+     */
+    object Version {
+
+
+        // AndroidX--------------------------------------------------------------
+        const val AppCompat = "1.3.1"
+        const val CoreKtx = "1.7.0"
+        const val ConstraintLayout = "2.1.3"                // 约束布局
+        const val TestExtJunit = "1.1.2"
+        const val TestEspresso = "3.3.0"
+        const val ActivityKtx = "1.5.1"
+        const val FragmentKtx = "1.5.2"
+        const val MultiDex = "2.0.1"
+        const val Navigation = "2.3.0"
+        const val Room = "2.6.0"
+        const val Work = "2.8.1"                           //后台管理器
+
+        // Android---------------------------------------------------------------
+        const val Junit = "4.13"
+        const val Material = "1.2.0"                        // 材料设计UI套件
+
+        // Kotlin----------------------------------------------------------------
+        const val Kotlin = "1.6.21"
+        const val Coroutines = "1.6.1"                      // 协程
+        const val KotlinSerialization = "1.7.3"             // kotlin 系列化
+
+        // JetPack---------------------------------------------------------------
+        const val Lifecycle = "2.4.1"                       // Lifecycle
+        const val Hilt = "2.44"                             // DI框架-Hilt
+
+        // GitHub----------------------------------------------------------------
+        const val OkHttp = "4.9.0"                          // OkHttp
+        const val OkHttpInterceptorLogging = "4.9.1"        // OkHttp 请求Log拦截器
+        const val Retrofit = "2.9.0"                        // Retrofit
+        const val RetrofitConverterGson = "2.9.0"           // Retrofit Gson 转换器
+        const val RetrofitConverterScalars = "2.9.0"           // Retrofit String 转换器
+        const val Gson = "2.10.1"                            // Gson
+        const val MMKV = "1.2.9"                            // 腾讯 MMKV 替代SP
+        const val AutoSize = "v1.2.1"                        // 屏幕适配
+        const val ARoute = "1.5.2"                          // 阿里路由
+        const val ARouteCompiler = "1.5.2"                  // 阿里路由 APT
+        const val RecyclerViewAdapter = "3.0.4"             // RecyclerViewAdapter
+        const val EventBus = "3.2.0"                        // 事件总线
+        const val PermissionX = "1.8.0"                     // 权限申请
+        const val LeakCanary = "2.7"                        // 检测内存泄漏
+        const val AutoService = "1.0"                       // 自动生成SPI暴露服务文件
+        const val Coil = "1.3.0"                            // Kotlin图片加载框架
+        const val Toaster = "12.6"                            //Toast框架
+        const val MagicIndicator = "1.7.0"                            //导航条
+        const val Okhttputils =
+            "12.8"                     //下载更新apk https://github.com/getActivity/EasyHttp
+        const val MPAndroidChart = "v3.1.0"                  //图表
+        const val BaseRecyclerViewAdapterHelper4 = "4.1.4" //Rv适配器
+        const val Serialport = "1.0.0"                     //串口支付设备
+        const val XLog = "1.11.1"                          // xlog
+
+        // 第三方SDK--------------------------------------------------------------
+        const val TencentBugly = "3.3.9"                    // 腾讯Bugly 异常上报
+        const val TencentBuglyNative = "3.8.0"              // Bugly native异常上报
+        const val TencentTBSX5 = "43939"                    // 腾讯X5WebView
+        const val GeTui = "2.13.1.0"                        // 个推
+        const val RabbitMq = "4.12.0"                       //RabbitMq
+        const val rxjava = "2.1.16"
+        const val rxandroid="2.0.2"
+    }
+
+    /**
+     * AndroidX相关依赖
+     *
+     * @author Qu Yunshuo
+     * @since 4/24/21 4:01 PM
+     */
+    object AndroidX {
+        const val AndroidJUnitRunner = "androidx.test.runner.AndroidJUnitRunner"
+        const val AppCompat = "androidx.appcompat:appcompat:${Version.AppCompat}"
+        const val CoreKtx = "androidx.core:core-ktx:${Version.CoreKtx}"
+        const val ConstraintLayout =
+            "androidx.constraintlayout:constraintlayout:${Version.ConstraintLayout}"
+        const val TestExtJunit = "androidx.test.ext:junit:${Version.TestExtJunit}"
+        const val TestEspresso = "androidx.test.espresso:espresso-core:${Version.TestEspresso}"
+        const val ActivityKtx = "androidx.activity:activity-ktx:${Version.ActivityKtx}"
+        const val FragmentKtx = "androidx.fragment:fragment-ktx:${Version.FragmentKtx}"
+        const val MultiDex = "androidx.multidex:multidex:${Version.MultiDex}"
+        const val Navigation = "androidx.navigation:navigation-fragment-ktx:${Version.Navigation}"
+        const val NavigationUI = "androidx.navigation:navigation-ui-ktx:${Version.Navigation}"
+        const val Room = "androidx.room:room-runtime:${Version.Room}"
+        const val RoomCompiler = "androidx.room:room-compiler:${Version.Room}"
+        const val RoomKtx = "androidx.room:room-ktx:${Version.Room}"
+        const val WorkKtx = "androidx.work:work-runtime-ktx:${Version.Work}"
+    }
+
+    /**
+     * Android相关依赖
+     *
+     * @author Qu Yunshuo
+     * @since 4/24/21 4:02 PM
+     */
+    object Android {
+        const val Junit = "junit:junit:${Version.Junit}"
+        const val Material = "com.google.android.material:material:${Version.Material}"
+    }
+
+    /**
+     * JetPack相关依赖
+     *
+     * @author Qu Yunshuo
+     * @since 4/24/21 4:02 PM
+     */
+    object JetPack {
+        const val ViewModel = "androidx.lifecycle:lifecycle-viewmodel-ktx:${Version.Lifecycle}"
+        const val ViewModelSavedState =
+            "androidx.lifecycle:lifecycle-viewmodel-savedstate:${Version.Lifecycle}"
+        const val LiveData = "androidx.lifecycle:lifecycle-livedata-ktx:${Version.Lifecycle}"
+        const val Lifecycle = "androidx.lifecycle:lifecycle-runtime-ktx:${Version.Lifecycle}"
+        const val LifecycleCompilerAPT =
+            "androidx.lifecycle:lifecycle-compiler:${Version.Lifecycle}"
+        const val HiltCore = "com.google.dagger:hilt-android:${Version.Hilt}"
+        const val HiltApt = "com.google.dagger:hilt-compiler:${Version.Hilt}"
+    }
+
+    /**
+     * Kotlin相关依赖
+     *
+     * @author Qu Yunshuo
+     * @since 4/24/21 4:02 PM
+     */
+    object Kotlin {
+        const val Kotlin = "org.jetbrains.kotlin:kotlin-stdlib:${Version.Kotlin}"
+        const val CoroutinesCore =
+            "org.jetbrains.kotlinx:kotlinx-coroutines-core:${Version.Coroutines}"
+        const val CoroutinesAndroid =
+            "org.jetbrains.kotlinx:kotlinx-coroutines-android:${Version.Coroutines}"
+        const val KotlinSerialization =
+            "org.jetbrains.kotlinx:kotlinx-serialization-json:${Version.KotlinSerialization}"
+    }
+
+    /**
+     * GitHub及其他相关依赖
+     *
+     * @author Qu Yunshuo
+     * @since 4/24/21 4:02 PM
+     */
+    object GitHub {
+        const val OkHttp = "com.squareup.okhttp3:okhttp:${Version.OkHttp}"
+        const val OkHttpInterceptorLogging =
+            "com.squareup.okhttp3:logging-interceptor:${Version.OkHttpInterceptorLogging}"
+        const val Retrofit = "com.squareup.retrofit2:retrofit:${Version.Retrofit}"
+        const val RetrofitConverterGson =
+            "com.squareup.retrofit2:converter-gson:${Version.RetrofitConverterGson}"
+        const val RetrofitConverterScalars =
+            "com.squareup.retrofit2:converter-scalars:${Version.RetrofitConverterScalars}"
+        const val Gson = "com.google.code.gson:gson:${Version.Gson}"
+        const val MMKV = "com.tencent:mmkv-static:${Version.MMKV}"
+        const val AutoSize = "com.github.JessYanCoding:AndroidAutoSize:${Version.AutoSize}"
+        const val ARoute = "com.alibaba:arouter-api:${Version.ARoute}"
+        const val ARouteCompiler = "com.alibaba:arouter-compiler:${Version.ARouteCompiler}"
+        const val RecyclerViewAdapter =
+            "com.github.CymChad:BaseRecyclerViewAdapterHelper:${Version.RecyclerViewAdapter}"
+        const val EventBus = "org.greenrobot:eventbus:${Version.EventBus}"
+        const val EventBusAPT = "org.greenrobot:eventbus-annotation-processor:${Version.EventBus}"
+        const val PermissionX = "com.guolindev.permissionx:permissionx:${Version.PermissionX}"
+        const val LeakCanary = "com.squareup.leakcanary:leakcanary-android:${Version.LeakCanary}"
+        const val AutoService = "com.google.auto.service:auto-service:${Version.AutoService}"
+        const val AutoServiceAnnotations =
+            "com.google.auto.service:auto-service-annotations:${Version.AutoService}"
+        const val Coil = "io.coil-kt:coil:${Version.Coil}"
+        const val CoilGIF = "io.coil-kt:coil-gif:${Version.Coil}"
+        const val CoilSVG = "io.coil-kt:coil-svg:${Version.Coil}"
+        const val CoilVideo = "io.coil-kt:coil-video:${Version.Coil}"
+        const val Toaster = "com.github.getActivity:Toaster:${Version.Toaster}"
+        const val MagicIndicator =
+            "com.github.hackware1993:MagicIndicator:${Version.MagicIndicator}"
+        const val Okhttputils = "com.github.getActivity:EasyHttp:${Version.Okhttputils}"
+        const val MPAndroidChart = "com.github.PhilJay:MPAndroidChart:${Version.MPAndroidChart}"
+        const val BaseRecyclerViewAdapterHelper4 =
+            "io.github.cymchad:BaseRecyclerViewAdapterHelper4:${Version.BaseRecyclerViewAdapterHelper4}"
+        const val Serialport = "com.azhon:serialport:${Version.Serialport}"
+        const val XLog = "com.elvishew:xlog:${Version.XLog}"
+        const val rxjava = "io.reactivex.rxjava2:rxjava:${Version.rxjava}"
+        const val rxandroid="io.reactivex.rxjava2:rxandroid:${Version.rxandroid}"
+        const val GsonFactory="com.github.getActivity:GsonFactory:6.3"
+    }
+
+    /**
+     * SDK相关依赖
+     *
+     * @author Qu Yunshuo
+     * @since 4/24/21 4:02 PM
+     */
+    object SDK {
+        const val TencentBugly = "com.tencent.bugly:crashreport:${Version.TencentBugly}"
+        const val TencentBuglyNative =
+            "com.tencent.bugly:nativecrashreport:${Version.TencentBuglyNative}"
+        const val TencentTBSX5 = "com.tencent.tbs.tbssdk:sdk:${Version.TencentTBSX5}"
+        const val GeTui = "com.getui:sdk:${Version.GeTui}"
+        const val RabbitMq = "com.rabbitmq:amqp-client:${Version.RabbitMq}"
+
+
+    }
+}

+ 31 - 0
buildSrc/src/main/kotlin/com/quyunshuo/androidbaseframemvvm/buildsrc/ProjectBuildConfig.kt

@@ -0,0 +1,31 @@
+package com.quyunshuo.androidbaseframemvvm.buildsrc
+
+/**
+ * 项目相关参数配置
+ *
+ * @author Qu Yunshuo
+ * @since 4/24/21 5:56 PM
+ */
+object ProjectBuildConfig {
+    const val compileSdkVersion = 34
+    const val applicationId = "com.quyunshuo.androidbaseframemvvm"
+    const val minSdkVersion = 21
+    const val targetSdkVersion = 29
+    const val versionCode = 1
+    const val versionName = "1.0"
+    const val isAppMode = false
+
+    /**
+     * 项目当前的版本状态
+     * 该状态直接反映当前App是测试版 还是正式版 或者预览版
+     * 正式版:RELEASE、预览版(α)-内部测试版:ALPHA、测试版(β)-公开测试版:BETA
+     */
+    object Version {
+
+        const val RELEASE = "VERSION_STATUS_RELEASE"
+
+        const val ALPHA = "VERSION_STATUS_ALPHA"
+
+        const val BETA = "VERSION_STATUS_BETA"
+    }
+}

+ 14 - 0
buildSrc/src/main/kotlin/com/quyunshuo/androidbaseframemvvm/buildsrc/SDKKeyConfig.kt

@@ -0,0 +1,14 @@
+package com.quyunshuo.androidbaseframemvvm.buildsrc
+
+/**
+ * 存放需要存在本地的SDK的密钥
+ * 这种方式并不安全
+ */
+object SDKKeyConfig {
+
+    /**
+     * Bugly APP_ID
+     * 正式环境需要与测试环境分开 正式ID:""、测试ID:""
+     */
+    const val BUGLY_APP_ID = "1f084d947d"
+}

+ 25 - 0
gradle.properties

@@ -0,0 +1,25 @@
+# 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
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec: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
+# Automatically convert third-party libraries to use AndroidX
+android.enableJetifier=true
+# Kotlin code style for this project: "official" or "obsolete":
+kotlin.code.style=official
+android.defaults.buildfeatures.buildconfig=true
+android.nonTransitiveRClass=false
+android.nonFinalResIds=false
+android.enableR8.fullMode=false

+ 50 - 0
gradle/libs.versions.toml

@@ -0,0 +1,50 @@
+[versions]
+# plugin
+agp = "8.4.0"
+kotlin-android = "1.8.0"
+hilt = "2.44"
+core-ktx = "1.10.1"
+junit = "4.13.2"
+junit-version = "1.1.5"
+espresso-core = "3.5.1"
+lifecycle-runtime-ktx = "2.6.1"
+activity-compose = "1.8.0"
+compose-bom = "2023.08.00"
+serialization = "1.9.24"
+appcompat = "1.6.1"
+material = "1.10.0"
+activity = "1.8.0"
+constraintlayout = "2.1.4"
+
+#lib
+
+
+[libraries]
+androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "core-ktx" }
+junit = { group = "junit", name = "junit", version.ref = "junit" }
+androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junit-version" }
+androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espresso-core" }
+androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycle-runtime-ktx" }
+androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activity-compose" }
+androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" }
+androidx-ui = { group = "androidx.compose.ui", name = "ui" }
+androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
+androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
+androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
+androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
+androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
+androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
+androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
+material = { group = "com.google.android.material", name = "material", version.ref = "material" }
+androidx-activity = { group = "androidx.activity", name = "activity", version.ref = "activity" }
+androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
+
+
+[plugins]
+application = { id = "com.android.application", version.ref = "agp" }
+library = { id = "com.android.library", version.ref = "agp" }
+kotlin = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin-android" }
+hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" }
+serialization = { id = "org.jetbrains.kotlin.plugin.serialization",version.ref="serialization"}
+
+[bundles]

BIN
gradle/wrapper/gradle-wrapper.jar


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

@@ -0,0 +1,6 @@
+#Sat May 22 08:57:40 CST 2021
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-8.6-bin.zip

+ 172 - 0
gradlew

@@ -0,0 +1,172 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+##  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=""
+
+# 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, switch paths to Windows format before running java
+if $cygwin ; 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=$((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"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+  cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"

+ 84 - 0
gradlew.bat

@@ -0,0 +1,84 @@
+@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 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=
+
+@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 init
+
+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 init
+
+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
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+: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 %CMD_LINE_ARGS%
+
+: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

BIN
hboxs.jks


BIN
img/img2.png


+ 1 - 0
lib_base/.gitignore

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

+ 112 - 0
lib_base/build.gradle

@@ -0,0 +1,112 @@
+//****************************************
+//********** lib_base 的配置文件 ***********
+//****************************************
+
+plugins {
+    alias(libs.plugins.library)
+    alias(libs.plugins.kotlin)
+    alias(libs.plugins.hilt)
+    id "kotlin-kapt"
+//    alias(libs.plugins.serialization)
+}
+
+apply from: '../base_lib.gradle'
+
+import com.quyunshuo.androidbaseframemvvm.buildsrc.*
+
+android {
+
+    buildFeatures {
+        viewBinding = true
+    }
+
+   // resourcePrefix "base_"
+    namespace 'com.quyunshuo.androidbaseframemvvm.base'
+
+}
+
+dependencies {
+    implementation fileTree(dir: "libs", include: ["*.jar"])
+
+    api DependencyConfig.AndroidX.CoreKtx
+    api DependencyConfig.AndroidX.AppCompat
+    api DependencyConfig.AndroidX.ConstraintLayout
+    api DependencyConfig.AndroidX.ActivityKtx
+    api DependencyConfig.AndroidX.FragmentKtx
+    api DependencyConfig.AndroidX.MultiDex
+    api DependencyConfig.AndroidX.Navigation
+    api DependencyConfig.AndroidX.NavigationUI
+
+
+    api DependencyConfig.Android.Material
+
+    api DependencyConfig.Kotlin.Kotlin
+    api DependencyConfig.Kotlin.CoroutinesCore
+//    api DependencyConfig.Kotlin.KotlinSerialization
+    api DependencyConfig.Kotlin.CoroutinesAndroid
+
+    api DependencyConfig.JetPack.ViewModel
+    api DependencyConfig.JetPack.ViewModelSavedState
+    api DependencyConfig.JetPack.LiveData
+    api DependencyConfig.JetPack.Lifecycle
+    api DependencyConfig.JetPack.HiltCore
+
+    api DependencyConfig.GitHub.Gson
+    api DependencyConfig.GitHub.MMKV
+    api DependencyConfig.GitHub.AutoSize
+    api DependencyConfig.GitHub.ARoute
+    api DependencyConfig.GitHub.RecyclerViewAdapter
+    api DependencyConfig.GitHub.EventBus
+    api DependencyConfig.GitHub.PermissionX
+    api DependencyConfig.GitHub.AutoService
+    api DependencyConfig.GitHub.OkHttp
+    api DependencyConfig.GitHub.OkHttpInterceptorLogging
+    api DependencyConfig.GitHub.Retrofit
+    api DependencyConfig.GitHub.RetrofitConverterGson
+    api DependencyConfig.GitHub.RetrofitConverterScalars
+    api DependencyConfig.GitHub.Coil
+    api DependencyConfig.GitHub.CoilGIF
+    api DependencyConfig.GitHub.CoilSVG
+    api DependencyConfig.GitHub.CoilVideo
+    api DependencyConfig.GitHub.Toaster
+    api DependencyConfig.GitHub.MagicIndicator
+    api DependencyConfig.GitHub.Okhttputils
+    api DependencyConfig.GitHub.MPAndroidChart
+    api DependencyConfig.GitHub.BaseRecyclerViewAdapterHelper4
+    api DependencyConfig.GitHub.Serialport
+    api DependencyConfig.GitHub.XLog
+    api DependencyConfig.GitHub.rxjava
+    api DependencyConfig.GitHub.rxandroid
+
+    api DependencyConfig.SDK.TencentBugly
+    api DependencyConfig.SDK.TencentBuglyNative
+//    api DependencyConfig.SDK.TencentTBSX5
+    api DependencyConfig.SDK.GeTui
+    api DependencyConfig.SDK.RabbitMq
+    api files('libs\\ZtlApi.jar')
+
+    api "com.github.weilinhu:mylibaray:1.0" //https://github.com/weilinhu/mylibaray //监听网络情况。
+    api 'com.github.ldt-libs:SpringBackLayout:1.0' //回弹效果
+    api 'com.github.kabouzeid:RecyclerView-FastScroll:1.0.16-kmod'
+    api files('libs\\xzjh_interface_20240731.jar')
+
+    //api project(':lib_common')
+
+
+    kapt DependencyConfig.GitHub.ARouteCompiler
+    kapt DependencyConfig.GitHub.EventBusAPT
+    kapt DependencyConfig.GitHub.AutoServiceAnnotations
+    kapt DependencyConfig.JetPack.HiltApt
+    kapt DependencyConfig.JetPack.LifecycleCompilerAPT
+    // implementation 'commons-codec:commons-codec:1.16.1'
+    debugApi DependencyConfig.GitHub.LeakCanary
+
+    api 'org.jetbrains.kotlin:kotlin-reflect:1.5.10'
+    api 'com.google.code.gson:gson:2.10.1'
+    api ('com.qiniu:qiniu-android-sdk:8.8.+'){
+        exclude (group: 'com.squareup.okhttp3', module: 'okhttp')
+    }
+    api 'commons-codec:commons-codec:1.9'
+
+
+}

+ 0 - 0
lib_base/consumer-rules.pro


BIN
lib_base/libs/ZtlApi.jar


BIN
lib_base/libs/xzjh_interface_20240731.jar


+ 21 - 0
lib_base/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

+ 4 - 0
lib_base/src/main/AndroidManifest.xml

@@ -0,0 +1,4 @@
+<manifest >
+
+
+</manifest>

+ 83 - 0
lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/BaseApplication.kt

@@ -0,0 +1,83 @@
+package com.quyunshuo.androidbaseframemvvm.base
+
+import ZtlApi.ZtlManager
+import android.annotation.SuppressLint
+import android.content.Context
+import android.util.Log
+import androidx.multidex.MultiDexApplication
+import com.quyunshuo.androidbaseframemvvm.base.app.ActivityLifecycleCallbacksImpl
+import com.quyunshuo.androidbaseframemvvm.base.app.LoadModuleProxy
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.MainScope
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.launch
+import kotlin.system.measureTimeMillis
+
+/**
+ * Application 基类
+ *
+ * @author Qu Yunshuo
+ * @since 4/24/21 5:30 PM
+ */
+open class BaseApplication : MultiDexApplication() {
+
+    private val mCoroutineScope by lazy(mode = LazyThreadSafetyMode.NONE) { MainScope() }
+
+    private val mLoadModuleProxy by lazy(mode = LazyThreadSafetyMode.NONE) { LoadModuleProxy() }
+
+    companion object {
+        // 全局Context
+        @SuppressLint("StaticFieldLeak")
+        lateinit var context: Context
+
+        @SuppressLint("StaticFieldLeak")
+        lateinit var application: BaseApplication
+    }
+
+    override fun attachBaseContext(base: Context) {
+        super.attachBaseContext(base)
+        context = base
+        application = this
+        mLoadModuleProxy.onAttachBaseContext(base)
+    }
+
+    override fun onCreate() {
+        super.onCreate()
+        ZtlManager.GetInstance().setContext(applicationContext) //屏幕厂家数据初始化
+
+        // 全局监听 Activity 生命周期
+        registerActivityLifecycleCallbacks(ActivityLifecycleCallbacksImpl())
+
+        mLoadModuleProxy.onCreate(this)
+
+        // 策略初始化第三方依赖
+        initDepends()
+    }
+
+    /**
+     * 初始化第三方依赖
+     */
+    private fun initDepends() {
+        // 开启一个 Default Coroutine 进行初始化不会立即使用的第三方
+        mCoroutineScope.launch(Dispatchers.Default) {
+            mLoadModuleProxy.initByBackstage()
+        }
+
+        // 前台初始化
+        val allTimeMillis = measureTimeMillis {
+            val depends = mLoadModuleProxy.initByFrontDesk()
+            var dependInfo: String
+            depends.forEach {
+                val dependTimeMillis = measureTimeMillis { dependInfo = it() }
+                Log.d("BaseApplication", "initDepends: $dependInfo : $dependTimeMillis ms")
+            }
+        }
+        Log.d("BaseApplication", "初始化完成 $allTimeMillis ms")
+    }
+
+    override fun onTerminate() {
+        super.onTerminate()
+        mLoadModuleProxy.onTerminate(this)
+        mCoroutineScope.cancel()
+    }
+}

+ 34 - 0
lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/addressenum/PayEnum.kt

@@ -0,0 +1,34 @@
+package com.quyunshuo.androidbaseframemvvm.base.addressenum
+
+import com.quyunshuo.androidbaseframemvvm.base.R
+
+/**
+ * 支付枚举
+ * code:唯一标识
+ * remark:中文名称
+ * nameId:名字id
+ * imgId:图片id
+ * navId:对应的导航
+ */
+enum class PayEnum(val code:String,val remark:String,val nameId:Int,val imgId:Int,val payType:Int) {
+    TWO_CODE("TwoCode","微信支付宝", R.string.base_two_code,R.drawable.quibinary,99),
+    BILL_COIN("BillCoin","纸币硬币",R.string.base_bill_coin,R.drawable.pic_toubi_02,3),
+    NAYAX("Nayax","Nayax",R.string.base_nayax,R.drawable.pic_shuakazhifu,4),//闲置模式,非闲置模式
+    MDB_NO_CASH("MdbNoCash","MDB信用卡",R.string.base_mdb_no_cash,R.drawable.pic_shuakazhifu,4),//级别2,级别3
+    SOEPAY("Soepay","Soepay",R.string.base_soepay,R.drawable.pic_shuakazhifu,4),//Soepay
+    FREE("Free","免费制作", R.string.base_free_pay,R.drawable.pic_shuakazhifu,0);//无需支付
+
+
+    companion object {
+        fun getEnumByValue(code: String): PayEnum {
+
+            for (anEnum in PayEnum.values()) {
+                if (anEnum.code == code) {
+                    return anEnum
+                }
+            }
+            return PayEnum.TWO_CODE
+        }
+
+    }
+}

+ 39 - 0
lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/addressenum/PlcCloseAddressEnum.kt

@@ -0,0 +1,39 @@
+package com.quyunshuo.androidbaseframemvvm.base.addressenum
+
+import com.quyunshuo.androidbaseframemvvm.base.R
+
+enum class PlcCloseAddressEnum(
+    val pit: Int,
+    val address: String,
+    val aName: String,
+    val aNameId: Int
+) {
+    DATA1(
+        1,
+        "000000020001",
+        "关闭制冷时间",
+        R.string.base_plc_param_1
+    ),//FF020014为什么是14,就是读取data20,20的十六进制是14
+    DATA2(2, "000000020001", "抽口味时间", R.string.base_plc_param_2),
+
+    DATA3(3, "000000020001", "蒸发器温度到达才抽水", R.string.base_plc_param_3),
+    DATA4(4, "000000020001", "皮带1延时时间", R.string.base_plc_param_4),
+    DATA5(5, "000000020001", "拔针时间", R.string.base_plc_param_5),
+    DATA6(6, "000000020001", "一瓶饮料所抽的时间", R.string.base_plc_param_6),
+    DATA7(7, "000000020001", "制作一次的用量", R.string.base_plc_param_7),
+    DATA8(8, "000000020001", "蒸发器保温下限", R.string.base_plc_param_8),
+    DATA9(9, "000000020001", "蒸发器保温上限", R.string.base_plc_param_9),
+    DATA10(10, "000000020001", "蒸发器保温上限", R.string.base_plc_param_9),
+    DATA11(11, "000000020001", "蒸发器保温上限", R.string.base_plc_param_9),
+    DATA12(12, "000000020001", "蒸发器保温上限", R.string.base_plc_param_9),
+    DATA13(13, "000000020001", "蒸发器保温上限", R.string.base_plc_param_9),
+    DATA14(14, "000000020001", "蒸发器保温上限", R.string.base_plc_param_9),
+    DATA15(15, "000000020001", "蒸发器保温上限", R.string.base_plc_param_9),
+    DATA16(16, "000000020001", "蒸发器保温上限", R.string.base_plc_param_9),
+    DATA17(17, "000000020001", "蒸发器保温上限", R.string.base_plc_param_9),
+    DATA18(18, "000000020001", "蒸发器保温上限", R.string.base_plc_param_9),
+    DATA19(19, "000000020001", "蒸发器保温上限", R.string.base_plc_param_9),
+    DATA20(20, "000000020001", "蒸发器保温上限", R.string.base_plc_param_9),
+
+
+}

+ 14 - 0
lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/addressenum/PlcD2StatusEnum.kt

@@ -0,0 +1,14 @@
+package com.quyunshuo.androidbaseframemvvm.base.addressenum
+
+enum class PlcD2StatusEnum(val address: String, val aName: String) {
+    D2_99("99", "整机初次上电"),
+
+    D2_98("98", "开机完毕"),
+    D2_0("0", "开机中或待机"),
+
+    D2_1("1", "做冰沙中"),
+    D2_2("2", "冰沙制作完成"),
+
+    D2_3("3", "PLC报警"),
+
+}

+ 56 - 0
lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/addressenum/PlcDebugAddressEnum.kt

@@ -0,0 +1,56 @@
+package com.quyunshuo.androidbaseframemvvm.base.addressenum
+
+import com.quyunshuo.androidbaseframemvvm.base.R
+import com.quyunshuo.androidbaseframemvvm.base.constant.DebugType
+
+/**
+ * pit这个很重要,它对应着状态位置OUT_IO占3个字节24位,PWM1_IO占一个字节1e表示炉头低速,32表示中速,64表示高速
+ * */
+enum class PlcDebugAddressEnum(
+    val type: Int,
+    val pit: Int,
+    val address: String,
+    val aName: String,
+    val aNameId: Int
+) {
+    IO_SE_01(DebugType.OUT_IO, 25, "000100010019", "白糖", R.string.base_white_sugar),
+    IO_SE_02(DebugType.OUT_IO, 12, "0001000100", "红糖", R.string.base_red_sugar),
+    IO_SE_03(DebugType.OUT_IO, 13, "0001000100", "黄糖", R.string.base_brown_sugar),
+    IO_SE_04(DebugType.OUT_IO, 14, "0001000100", "蓝糖", R.string.base_blue_sugar),
+    IO_SE_05(DebugType.OUT_IO, 1, "0001000100", "无刷气泵", R.string.base_brushless_air_pump),
+    IO_SE_06(DebugType.OUT_IO, 2, "0001000100", "循环风机", R.string.base_circulating_fan),
+    IO_SE_07(DebugType.OUT_IO, 9, "0001000100", "炉头加热", R.string.base_furnace_head_heating),
+
+    PWM_SE_01(DebugType.PWM1_IO, 25, "01010100", "低速炉头", R.string.base_low_burner),
+    PWM_SE_02(DebugType.PWM2_IO, 25, "01010100", "中速炉头", R.string.base_medium_burner),
+    PWM_SE_03(DebugType.PWM3_IO, 25, "01010100", "高速炉头", R.string.base_high_burner),
+    PWM_CLEAN(DebugType.PWMCLEAN, 26, "10100111", "炉头清洗", R.string.base_clean_head_heating),
+    PWM_STUCK(DebugType.PWMSTUCK, 26, "10100102", "炉头卡死", R.string.base_stuck_head_heating),
+    PWM_XRESET(DebugType.PWMXRESET, 26, "10100101", "X轴复位", R.string.base_x_reset),
+
+
+}
+
+//    M111("M111", "蒸发器风机", R.string.base_plc_debug_1),
+//    M116("M116", "蒸发器压缩机",R.string.base_plc_debug_2),
+//    M166("M166", "蒸发器电机",R.string.base_plc_debug_3),
+//    M182("M182", "取餐门",R.string.base_plc_debug_4),
+//    M174("M174", "口味皮带1",R.string.base_plc_debug_5),
+//    M175("M175", "口味皮带2",R.string.base_plc_debug_6),
+//    M176("M176", "口味皮带3",R.string.base_plc_debug_7),
+//    M177("M177", "口味皮带4",R.string.base_plc_debug_8),
+//    M173("M173", "落杯",R.string.base_plc_debug_9),
+//    M171("M171", "转杯",R.string.base_plc_debug_10),
+//    M184("M184", "清洗蠕动泵",R.string.base_plc_debug_11),
+//    M178("M178", "口味泵1",R.string.base_plc_debug_12),
+//    M179("M179", "口味泵2",R.string.base_plc_debug_13),
+//    M180("M180", "口味泵3",R.string.base_plc_debug_14),
+//    M181("M181", "口味泵4",R.string.base_plc_debug_15),
+//    M199("M199", "拔针1",R.string.base_plc_debug_16),
+//    M198("M198", "插针1",R.string.base_plc_debug_17),
+//    M209("M209", "拔针2",R.string.base_plc_debug_18),
+//    M208("M208", "插针2",R.string.base_plc_debug_19),
+//    M219("M219", "拔针3",R.string.base_plc_debug_20),
+//    M218("M218", "插针3",R.string.base_plc_debug_21),
+//    M229("M229", "拔针4",R.string.base_plc_debug_22),
+//    M228("M228", "插针4",R.string.base_plc_debug_23)

+ 54 - 0
lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/addressenum/PlcDebugStataAddressEnum.kt

@@ -0,0 +1,54 @@
+package com.quyunshuo.androidbaseframemvvm.base.addressenum
+
+import com.quyunshuo.androidbaseframemvvm.base.R
+import com.quyunshuo.androidbaseframemvvm.base.constant.DebugType
+
+enum class PlcDebugStataAddressEnum(
+    val type: Int,
+    val pit: Int,
+    val aName: String,
+    val aNameId: Int
+) {
+    INPUT_01(DebugType.INPUT_IO, 1, "白糖", R.string.base_plc_debug_1),
+    INPUT_02(DebugType.INPUT_IO, 2, "红糖", R.string.base_plc_debug_2),
+    INPUT_03(DebugType.INPUT_IO, 3, "红糖", R.string.base_plc_debug_3),
+    INPUT_04(DebugType.INPUT_IO, 4, "蓝糖", R.string.base_plc_debug_4),
+    INPUT_05(DebugType.INPUT_IO, 5, "无刷气泵", R.string.base_plc_debug_5),
+    INPUT_06(DebugType.INPUT_IO, 6, "循环风机", R.string.base_plc_debug_6),
+    INPUT_07(DebugType.INPUT_IO, 7, "炉头加热", R.string.base_plc_debug_7),
+    INPUT_08(DebugType.INPUT_IO, 8, "炉头加热", R.string.base_plc_debug_7),
+    INPUT_09(DebugType.INPUT_IO, 9, "炉头加热", R.string.base_plc_debug_7),
+    INPUT_10(DebugType.INPUT_IO, 10, "炉头加热", R.string.base_plc_debug_7),
+    INPUT_11(DebugType.INPUT_IO, 11, "炉头加热", R.string.base_plc_debug_7),
+    INPUT_12(DebugType.INPUT_IO, 12, "炉头加热", R.string.base_plc_debug_7),
+    INPUT_13(DebugType.INPUT_IO, 13, "炉头加热", R.string.base_plc_debug_7),
+    INPUT_14(DebugType.INPUT_IO, 14, "炉头加热", R.string.base_plc_debug_7),
+    INPUT_15(DebugType.INPUT_IO, 15, "炉头加热", R.string.base_plc_debug_7),
+    INPUT_16(DebugType.INPUT_IO, 16, "炉头加热", R.string.base_plc_debug_7),
+
+
+}
+
+//    M111("M111", "蒸发器风机", R.string.base_plc_debug_1),
+//    M116("M116", "蒸发器压缩机",R.string.base_plc_debug_2),
+//    M166("M166", "蒸发器电机",R.string.base_plc_debug_3),
+//    M182("M182", "取餐门",R.string.base_plc_debug_4),
+//    M174("M174", "口味皮带1",R.string.base_plc_debug_5),
+//    M175("M175", "口味皮带2",R.string.base_plc_debug_6),
+//    M176("M176", "口味皮带3",R.string.base_plc_debug_7),
+//    M177("M177", "口味皮带4",R.string.base_plc_debug_8),
+//    M173("M173", "落杯",R.string.base_plc_debug_9),
+//    M171("M171", "转杯",R.string.base_plc_debug_10),
+//    M184("M184", "清洗蠕动泵",R.string.base_plc_debug_11),
+//    M178("M178", "口味泵1",R.string.base_plc_debug_12),
+//    M179("M179", "口味泵2",R.string.base_plc_debug_13),
+//    M180("M180", "口味泵3",R.string.base_plc_debug_14),
+//    M181("M181", "口味泵4",R.string.base_plc_debug_15),
+//    M199("M199", "拔针1",R.string.base_plc_debug_16),
+//    M198("M198", "插针1",R.string.base_plc_debug_17),
+//    M209("M209", "拔针2",R.string.base_plc_debug_18),
+//    M208("M208", "插针2",R.string.base_plc_debug_19),
+//    M219("M219", "拔针3",R.string.base_plc_debug_20),
+//    M218("M218", "插针3",R.string.base_plc_debug_21),
+//    M229("M229", "拔针4",R.string.base_plc_debug_22),
+//    M228("M228", "插针4",R.string.base_plc_debug_23)

+ 10 - 0
lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/addressenum/PlcHomeAddressEnum.kt

@@ -0,0 +1,10 @@
+package com.quyunshuo.androidbaseframemvvm.base.addressenum
+
+enum class PlcHomeAddressEnum(val address: String, val aName: String,) {
+    D170("D170","蒸汽温度"),
+    D2("D2","机器状态"),
+    D91("D91","口味"),
+    M1("M1","开始做冰沙"),
+    RM1("RM1","复位 开始做冰沙")
+
+}

+ 135 - 0
lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/addressenum/PlcParamAddressEnum.kt

@@ -0,0 +1,135 @@
+package com.quyunshuo.androidbaseframemvvm.base.addressenum
+
+import com.quyunshuo.androidbaseframemvvm.base.R
+
+enum class PlcParamAddressEnum(
+    val pit: Int,
+    val address: String,
+    val aName: String,
+    val aNameId: Int
+) {
+    DATA1(
+        1,
+        "000000020002",
+        "炉头待机转速",
+        R.string.base_plc_param_1
+    ),//FF020014为什么是14,就是读取data20,20的十六进制是14
+    DATA2(2, "000000020002", "炉头待机温度", R.string.base_plc_param_2),
+
+    DATA3(3, "000000020002", "X轴开机位置", R.string.base_plc_param_3),
+    DATA4(4, "000000020002", "插棍位置", R.string.base_plc_param_4),
+    DATA5(5, "000000020002", "插棍延时", R.string.base_plc_param_5),
+    DATA6(6, "000000020002", "制作温度", R.string.base_plc_param_6),
+    DATA7(7, "000000020002", "制作转速", R.string.base_plc_param_7),
+    DATA8(8, "000000020002", "加湿量", R.string.base_plc_param_8),
+    DATA9(9, "000000020002", "起始卷丝高度", R.string.base_plc_param_9),
+    DATA10(10, "000000020002", "收尾丝速度", R.string.base_plc_param_10),
+    DATA11(11, "000000020002", "收尾丝时间", R.string.base_plc_param_11),
+    DATA12(12, "000000020002", "单色取糖位置补偿", R.string.base_plc_param_12),
+    DATA13(13, "000000020002", "多色取糖位置补偿", R.string.base_plc_param_13),
+    DATA14(14, "000000020002", "取糖位置", R.string.base_plc_param_14),
+    DATA15(15, "000000020002", "取糖延时", R.string.base_plc_param_15),
+    DATA16(16, "000000020002", "自动清洁水量", R.string.base_plc_param_16),
+    DATA17(17, "000000020002", "自动清洁计数", R.string.base_plc_param_17),
+
+
+    COREDATA1(1, "FF020016", "抽口味时间", R.string.base_action_param_1),
+    COREDATA2(2, "FF020016", "抽口味时间", R.string.base_action_param_2),
+    COREDATA3(3, "FF020016", "抽口味时间", R.string.base_action_param_3),
+    COREDATA4(4, "FF020016", "抽口味时间", R.string.base_action_param_4),
+    COREDATA5(5, "FF020016", "抽口味时间", R.string.base_action_param_5),
+    COREDATA6(6, "FF020016", "抽口味时间", R.string.base_action_param_6),
+    COREDATA7(7, "FF020016", "抽口味时间", R.string.base_action_param_7),
+    COREDATA8(8, "FF020016", "抽口味时间", R.string.base_action_param_8),
+    COREDATA9(9, "FF020016", "抽口味时间", R.string.base_action_param_9),
+    COREDATA10(10, "FF020016", "抽口味时间", R.string.base_action_param_10),
+    COREDATA11(11, "FF020016", "抽口味时间", R.string.base_action_param_11),
+    COREDATA12(12, "FF020016", "抽口味时间", R.string.base_action_param_12),
+    COREDATA13(13, "FF020016", "抽口味时间", R.string.base_action_param_13),
+    COREDATA14(14, "FF020016", "抽口味时间", R.string.base_action_param_14),
+    COREDATA15(15, "FF020016", "抽口味时间", R.string.base_action_param_15),
+    COREDATA16(16, "FF020016", "抽口味时间", R.string.base_action_param_16),
+    COREDATA17(17, "FF020016", "抽口味时间", R.string.base_action_param_17),
+    COREDATA18(18, "FF020016", "抽口味时间", R.string.base_action_param_18),
+    COREDATA19(19, "FF020016", "抽口味时间", R.string.base_action_param_19),
+    COREDATA20(20, "FF020016", "抽口味时间", R.string.base_action_param_20),
+
+
+    ADVAMCE1(1, "FF02000B", "X轴电机倍率", R.string.base_adc_param_1),
+    ADVAMCE2(2, "FF02000B", "Y轴电机倍率", R.string.base_adc_param_2),
+    ADVAMCE3(3, "FF02000B", "Z轴电机倍率", R.string.base_adc_param_3),
+    ADVAMCE4(4, "FF02000B", "E0轴电机倍率", R.string.base_adc_param_4),
+    ADVAMCE5(5, "FF02000B", "备用轴电机倍率", R.string.base_adc_param_5),
+    ADVAMCE6(6, "FF02000B", "白糖电机倍率", R.string.base_adc_param_6),
+    ADVAMCE7(7, "FF02000B", "红糖电机倍率", R.string.base_adc_param_7),
+    ADVAMCE8(8, "FF02000B", "黄糖电机倍率", R.string.base_adc_param_8),
+    ADVAMCE9(9, "FF02000B", "蓝糖电机倍率", R.string.base_adc_param_9),
+    ADVAMCE10(10, "FF02000B", "备用糖电机倍率1", R.string.base_adc_param_10),
+    ADVAMCE11(11, "FF02000B", "备用糖电机倍率2", R.string.base_adc_param_11),
+    ADVAMCE12(12, "FF02000B", "备用电机倍率3", R.string.base_adc_param_12),
+    ADVAMCE13(13, "FF02000B", "备用电机倍率4", R.string.base_adc_param_13),
+    ADVAMCE14(14, "FF02000B", "备用电机倍率5", R.string.base_adc_param_14),
+    ADVAMCE15(15, "FF02000B", "备用电机倍率6", R.string.base_adc_param_15),
+    ADVAMCE16(16, "FF02000B", "炉头温度误差倍率", R.string.base_adc_param_16),
+    ADVAMCE17(17, "FF02000B", "蒸发器温度误差倍率", R.string.base_adc_param_17),
+    ADVAMCE21(21, "FF02000B", "脉冲1 翻转", R.string.base_adc_param_21),
+    ADVAMCE22(22, "FF02000B", "脉冲2 翻转", R.string.base_adc_param_22),
+    ADVAMCE23(23, "FF02000B", "脉冲3 翻转", R.string.base_adc_param_23),
+
+
+    ADVAMCE121(1, "FF02000C", "X轴复位超时", R.string.base_adc_param_121),
+    ADVAMCE122(2, "FF02000C", "Y轴复位超时", R.string.base_adc_param_122),
+    ADVAMCE123(3, "FF02000C", "Z轴复位超时", R.string.base_adc_param_123),
+    ADVAMCE126(6, "FF02000C", "炉头加热超时", R.string.base_adc_param_126),
+
+
+    SUGARWRITE1(1, "FF020015", "白糖-1", R.string.base_sugar_write_param_1),
+    SUGARWRITE2(2, "FF020015", "白糖-2", R.string.base_sugar_write_param_2),
+    SUGARWRITE3(3, "FF020015", "白糖-3", R.string.base_sugar_write_param_3),
+    SUGARWRITE4(4, "FF020015", "白糖-4", R.string.base_sugar_write_param_4),
+    SUGARWRITE5(5, "FF020015", "白糖-5", R.string.base_sugar_write_param_5),
+    SUGARWRITE6(6, "FF020015", "红糖-1", R.string.base_sugar_write_param_6),
+    SUGARWRITE7(7, "FF020015", "红糖-2", R.string.base_sugar_write_param_7),
+    SUGARWRITE8(8, "FF020015", "红糖-3", R.string.base_sugar_write_param_8),
+    SUGARWRITE9(9, "FF020015", "红糖-4", R.string.base_sugar_write_param_9),
+    SUGARWRITE10(10, "FF020015", "红糖-5", R.string.base_sugar_write_param_10),
+    SUGARWRITE11(11, "FF020015", "黄糖-1", R.string.base_sugar_write_param_11),
+    SUGARWRITE12(12, "FF020015", "黄糖-2", R.string.base_sugar_write_param_12),
+    SUGARWRITE13(13, "FF020015", "黄糖-3", R.string.base_sugar_write_param_13),
+    SUGARWRITE14(14, "FF020015", "黄糖-4", R.string.base_sugar_write_param_14),
+    SUGARWRITE15(15, "FF020015", "黄糖-5", R.string.base_sugar_write_param_15),
+    SUGARWRITE16(16, "FF020015", "蓝糖-1", R.string.base_sugar_write_param_16),
+    SUGARWRITE17(17, "FF020015", "蓝糖-2", R.string.base_sugar_write_param_17),
+    SUGARWRITE18(18, "FF020015", "蓝糖-3", R.string.base_sugar_write_param_18),
+    SUGARWRITE19(19, "FF020015", "蓝糖-4", R.string.base_sugar_write_param_19),
+    SUGARWRITE20(20, "FF020015", "蓝糖-5", R.string.base_sugar_write_param_20),
+    SUGARWRITE21(21, "FF020015", "彩糖-1", R.string.base_sugar_write_param_21),
+    SUGARWRITE22(22, "FF020015", "彩糖-2", R.string.base_sugar_write_param_22),
+    SUGARWRITE23(23, "FF020015", "彩糖-3", R.string.base_sugar_write_param_23),
+    SUGARWRITE24(24, "FF020015", "彩糖-4", R.string.base_sugar_write_param_24),
+    SUGARWRITE25(25, "FF020015", "彩糖-5", R.string.base_sugar_write_param_25),
+
+
+    R9OO8HG0ARAM1(1, "FF020017", "彩糖-5", R.string.base_rolling_param_1),
+    R9OO8HG0ARAM2(2, "FF020017", "彩糖-5", R.string.base_rolling_param_2),
+    R9OO8HG0ARAM3(3, "FF020017", "彩糖-5", R.string.base_rolling_param_3),
+    R9OO8HG0ARAM4(4, "FF020017", "彩糖-5", R.string.base_rolling_param_4),
+    R9OO8HG0ARAM5(5, "FF020017", "彩糖-5", R.string.base_rolling_param_5),
+    R9OO8HG0ARAM6(6, "FF020017", "彩糖-5", R.string.base_rolling_param_6),
+    R9OO8HG0ARAM7(7, "FF020017", "彩糖-5", R.string.base_rolling_param_7),
+    R9OO8HG0ARAM8(8, "FF020017", "彩糖-5", R.string.base_rolling_param_8),
+    R9OO8HG0ARAM9(9, "FF020017", "彩糖-5", R.string.base_rolling_param_9),
+    R9OO8HG0ARAM10(10, "FF020017", "彩糖-5", R.string.base_rolling_param_10),
+    R9OO8HG0ARAM11(11, "FF020017", "彩糖-5", R.string.base_rolling_param_11),
+    R9OO8HG0ARAM12(12, "FF020017", "彩糖-5", R.string.base_rolling_param_12),
+    R9OO8HG0ARAM13(13, "FF020017", "彩糖-5", R.string.base_rolling_param_13),
+    R9OO8HG0ARAM14(14, "FF020017", "彩糖-5", R.string.base_rolling_param_14),
+    R9OO8HG0ARAM15(15, "FF020017", "彩糖-5", R.string.base_rolling_param_15),
+    R9OO8HG0ARAM16(16, "FF020017", "彩糖-5", R.string.base_rolling_param_16),
+    R9OO8HG0ARAM17(17, "FF020017", "彩糖-5", R.string.base_rolling_param_17),
+    R9OO8HG0ARAM18(18, "FF020017", "彩糖-5", R.string.base_rolling_param_18),
+    R9OO8HG0ARAM19(19, "FF020017", "彩糖-5", R.string.base_rolling_param_19),
+    R9OO8HG0ARAM20(20, "FF020017", "彩糖-5", R.string.base_rolling_param_20),
+
+
+}

+ 7 - 0
lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/addressenum/PlcSettingAddressEnum.kt

@@ -0,0 +1,7 @@
+package com.quyunshuo.androidbaseframemvvm.base.addressenum
+
+enum class PlcSettingAddressEnum(val address: String, val aName: String,) {
+    M3("M3","开机复位"),
+    RM3("RM3","开机复位"),
+
+}

+ 26 - 0
lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/addressenum/ProTypeEnum.kt

@@ -0,0 +1,26 @@
+package com.quyunshuo.androidbaseframemvvm.base.addressenum
+
+/**
+ * 商品:
+ */
+enum class ProTypeEnum(var proValue: Int, var chineseName: String) {
+    PRO_1(1, "玫瑰精灵"),
+    PRO_2(2, "五彩缤纷"),
+    PRO_3(3, "小棉袄"),
+    PRO_4(4, "彩色精灵");
+
+    companion object {
+        fun getEnumByValue(chineseName: String): ProTypeEnum {
+
+            for (anEnum in ProTypeEnum.values()) {
+                if (anEnum.chineseName == chineseName) {
+                    return anEnum
+                }
+            }
+            return PRO_1
+        }
+
+    }
+
+
+}

+ 51 - 0
lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/app/ActivityLifecycleCallbacksImpl.kt

@@ -0,0 +1,51 @@
+package com.quyunshuo.androidbaseframemvvm.base.app
+
+import android.app.Activity
+import android.app.Application
+import android.os.Bundle
+import android.util.Log
+import com.quyunshuo.androidbaseframemvvm.base.utils.ActivityStackManager
+import com.quyunshuo.androidbaseframemvvm.base.utils.ForegroundBackgroundHelper
+
+/**
+ * Activity生命周期监听
+ *
+ * @author Qu Yunshuo
+ * @since 4/20/21 9:10 AM
+ */
+class ActivityLifecycleCallbacksImpl : Application.ActivityLifecycleCallbacks {
+
+    private val TAG = "ActivityLifecycle"
+
+    override fun onActivityCreated(activity: Activity, bundle: Bundle?) {
+        ActivityStackManager.addActivityToStack(activity)
+        Log.e(TAG, "${activity.javaClass.simpleName} --> onActivityCreated")
+    }
+
+    override fun onActivityStarted(activity: Activity) {
+        Log.e(TAG, "${activity.javaClass.simpleName} --> onActivityStarted")
+        ForegroundBackgroundHelper.onActivityStarted()
+    }
+
+    override fun onActivityResumed(activity: Activity) {
+        Log.e(TAG, "${activity.javaClass.simpleName} --> onActivityResumed")
+    }
+
+    override fun onActivityPaused(activity: Activity) {
+        Log.e(TAG, "${activity.javaClass.simpleName} --> onActivityPaused")
+    }
+
+    override fun onActivityStopped(activity: Activity) {
+        Log.e(TAG, "${activity.javaClass.simpleName} --> onActivityStopped")
+        ForegroundBackgroundHelper.onActivityStopped()
+    }
+
+    override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {
+        Log.e(TAG, "${activity.javaClass.simpleName} --> onActivitySaveInstanceState")
+    }
+
+    override fun onActivityDestroyed(activity: Activity) {
+        ActivityStackManager.popActivityToStack(activity)
+        Log.e(TAG, "${activity.javaClass.simpleName} --> onActivityDestroyed")
+    }
+}

+ 42 - 0
lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/app/ApplicationLifecycle.kt

@@ -0,0 +1,42 @@
+package com.quyunshuo.androidbaseframemvvm.base.app
+
+import android.app.Application
+import android.content.Context
+
+/**
+ * Application 生命周期 用于初始化各个组件
+ *
+ * @author Qu Yunshuo
+ * @since 4/23/21 5:22 PM
+ */
+interface ApplicationLifecycle {
+
+    /**
+     * 同[Application.attachBaseContext]
+     * @param context Context
+     */
+    fun onAttachBaseContext(context: Context)
+
+    /**
+     * 同[Application.onCreate]
+     * @param application Application
+     */
+    fun onCreate(application: Application)
+
+    /**
+     * 同[Application.onTerminate]
+     * @param application Application
+     */
+    fun onTerminate(application: Application)
+
+    /**
+     * 主线程前台初始化
+     * @return MutableList<() -> String> 初始化方法集合
+     */
+    fun initByFrontDesk(): MutableList<() -> String>
+
+    /**
+     * 不需要立即初始化的放在这里进行后台初始化
+     */
+    fun initByBackstage()
+}

+ 63 - 0
lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/app/LoadModuleProxy.kt

@@ -0,0 +1,63 @@
+package com.quyunshuo.androidbaseframemvvm.base.app
+
+import android.app.Application
+import android.content.Context
+import android.util.Log
+import java.util.*
+
+/**
+ * 加载组件代理类
+ * 组件初始化的工作将由该代理类代理实现
+ *
+ * @author Qu Yunshuo
+ * @since 4/23/21 5:37 PM
+ */
+class LoadModuleProxy : ApplicationLifecycle {
+
+    private var mLoader: ServiceLoader<ApplicationLifecycle> =
+        ServiceLoader.load(ApplicationLifecycle::class.java)
+
+    /**
+     * 同[Application.attachBaseContext]
+     * @param context Context
+     */
+    override fun onAttachBaseContext(context: Context) {
+        mLoader.forEach {
+            Log.d("ApplicationInit", it.toString())
+            it.onAttachBaseContext(context)
+        }
+    }
+
+    /**
+     * 同[Application.onCreate]
+     * @param application Application
+     */
+    override fun onCreate(application: Application) {
+        mLoader.forEach { it.onCreate(application) }
+    }
+
+    /**
+     * 同[Application.onTerminate]
+     * @param application Application
+     */
+    override fun onTerminate(application: Application) {
+        mLoader.forEach { it.onTerminate(application) }
+    }
+
+    /**
+     * 主线程前台初始化
+     * @return MutableList<() -> String> 初始化方法集合
+     */
+    override fun initByFrontDesk(): MutableList<() -> String> {
+        val list: MutableList<() -> String> = mutableListOf()
+        mLoader.forEach { list.addAll(it.initByFrontDesk()) }
+        return list
+    }
+
+    /**
+     * 不需要立即初始化的放在这里进行后台初始化
+     */
+    override fun initByBackstage() {
+        mLoader.forEach { it.initByBackstage() }
+    }
+}

+ 11 - 0
lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/bean/Global.kt

@@ -0,0 +1,11 @@
+package com.quyunshuo.androidbaseframemvvm.base.bean
+
+object Global {
+    var isNetWork: Boolean = false //自主检测里面来测试是否有网络
+
+    /**
+     * 记录通讯时间
+     */
+
+    var commTime: Long = System.currentTimeMillis()
+}

+ 12 - 0
lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/constant/DebugType.java

@@ -0,0 +1,12 @@
+package com.quyunshuo.androidbaseframemvvm.base.constant;
+
+public class DebugType {
+    public final static int OUT_IO = 0;
+    public final static int INPUT_IO = 1;
+    public final static int PWM1_IO = 2;//低速炉头
+    public final static int PWM2_IO = 3;//中速炉头
+    public final static int PWM3_IO = 4;//高速炉头
+    public final static int PWMCLEAN = 5;//清洗炉头
+    public final static int PWMSTUCK = 6;//测试炉头卡死
+    public final static int PWMXRESET = 7;//X轴复位
+}

+ 19 - 0
lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/constant/VersionStatus.kt

@@ -0,0 +1,19 @@
+package com.quyunshuo.androidbaseframemvvm.base.constant
+
+/**
+ * 版本状态
+ *
+ * @author Qu Yunshuo
+ * @since 4/20/21 9:05 AM
+ */
+object VersionStatus {
+
+    const val RELEASE = "VERSION_STATUS_RELEASE"
+
+    const val ALPHA = "VERSION_STATUS_ALPHA"
+
+    const val BETA = "VERSION_STATUS_BETA"
+
+    var networkState = true
+
+}

+ 157 - 0
lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/factory/GsonFactory.java

@@ -0,0 +1,157 @@
+package com.quyunshuo.androidbaseframemvvm.base.factory;
+
+import com.google.gson.FieldNamingPolicy;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.InstanceCreator;
+import com.google.gson.ReflectionAccessFilter;
+import com.google.gson.ToNumberStrategy;
+import com.google.gson.TypeAdapterFactory;
+import com.google.gson.internal.Excluder;
+import com.google.gson.internal.bind.TypeAdapters;
+import com.quyunshuo.androidbaseframemvvm.base.factory.constructor.MainConstructor;
+import com.quyunshuo.androidbaseframemvvm.base.factory.data.BigDecimalTypeAdapter;
+import com.quyunshuo.androidbaseframemvvm.base.factory.data.BooleanTypeAdapter;
+import com.quyunshuo.androidbaseframemvvm.base.factory.data.DoubleTypeAdapter;
+import com.quyunshuo.androidbaseframemvvm.base.factory.data.FloatTypeAdapter;
+import com.quyunshuo.androidbaseframemvvm.base.factory.data.IntegerTypeAdapter;
+import com.quyunshuo.androidbaseframemvvm.base.factory.data.JSONArrayTypeAdapter;
+import com.quyunshuo.androidbaseframemvvm.base.factory.data.JSONObjectTypeAdapter;
+import com.quyunshuo.androidbaseframemvvm.base.factory.data.LongTypeAdapter;
+import com.quyunshuo.androidbaseframemvvm.base.factory.data.StringTypeAdapter;
+import com.quyunshuo.androidbaseframemvvm.base.factory.element.CollectionTypeAdapterFactory;
+import com.quyunshuo.androidbaseframemvvm.base.factory.element.MapTypeAdapterFactory;
+import com.quyunshuo.androidbaseframemvvm.base.factory.element.ReflectiveTypeAdapterFactory;
+import com.quyunshuo.androidbaseframemvvm.base.factory.other.AutoToNumberStrategy;
+
+import java.lang.reflect.Type;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+/**
+ * author : Android 轮子哥
+ * github : https://github.com/getActivity/GsonFactory
+ * time   : 2020/11/10
+ * desc   : Gson 解析容错适配器
+ */
+@SuppressWarnings("unused")
+public final class GsonFactory {
+
+    private static final HashMap<Type, InstanceCreator<?>> INSTANCE_CREATORS = new HashMap<>(0);
+
+    private static final List<TypeAdapterFactory> TYPE_ADAPTER_FACTORIES = new ArrayList<>();
+
+    private static final List<ReflectionAccessFilter> REFLECTION_ACCESS_FILTERS = new ArrayList<>();
+
+    private static ToNumberStrategy sObjectToNumberStrategy = new AutoToNumberStrategy();
+
+    private static ParseExceptionCallback sParseExceptionCallback;
+
+    private static volatile Gson sGson;
+
+    private GsonFactory() {
+    }
+
+    /**
+     * 获取单例的 Gson 对象
+     */
+    public static Gson getSingletonGson() {
+        // 加入双重校验锁
+        if (sGson == null) {
+            synchronized (GsonFactory.class) {
+                if (sGson == null) {
+                    sGson = newGsonBuilder().create();
+                }
+            }
+        }
+        return sGson;
+    }
+
+    /**
+     * 设置单例的 Gson 对象
+     */
+    public static void setSingletonGson(Gson gson) {
+        sGson = gson;
+    }
+
+    /**
+     * 设置 Json 解析出错回调对象
+     */
+    public static void setParseExceptionCallback(ParseExceptionCallback callback) {
+        GsonFactory.sParseExceptionCallback = callback;
+    }
+
+    /**
+     * 获取 Json 解析出错回调对象(可能为空)
+     */
+    public static ParseExceptionCallback getParseExceptionCallback() {
+        return sParseExceptionCallback;
+    }
+
+    /**
+     * 注册类型解析适配器
+     */
+    public static void registerTypeAdapterFactory(TypeAdapterFactory factory) {
+        TYPE_ADAPTER_FACTORIES.add(factory);
+    }
+
+    /**
+     * 注册构造函数创建器
+     *
+     * @param type    对象类型
+     * @param creator 实例创建器
+     */
+    public static void registerInstanceCreator(Type type, InstanceCreator<?> creator) {
+        INSTANCE_CREATORS.put(type, creator);
+    }
+
+    /**
+     * 添加反射访问过滤器,同等于 {@link GsonBuilder#addReflectionAccessFilter(ReflectionAccessFilter)}
+     */
+    public static void addReflectionAccessFilter(ReflectionAccessFilter filter) {
+        if (filter == null) {
+            return;
+        }
+        REFLECTION_ACCESS_FILTERS.add(0, filter);
+    }
+
+    /**
+     * 设置自动转换数值类型的策略
+     */
+    public static void setObjectToNumberStrategy(ToNumberStrategy objectToNumberStrategy) {
+        GsonFactory.sObjectToNumberStrategy = objectToNumberStrategy;
+    }
+
+    /**
+     * 创建 Gson 构建对象
+     */
+    public static GsonBuilder newGsonBuilder() {
+        GsonBuilder gsonBuilder = new GsonBuilder();
+        MainConstructor mainConstructor = new MainConstructor(INSTANCE_CREATORS, true, REFLECTION_ACCESS_FILTERS);
+        if (sObjectToNumberStrategy != null) {
+            gsonBuilder.setObjectToNumberStrategy(sObjectToNumberStrategy);
+        }
+        gsonBuilder.registerTypeAdapterFactory(TypeAdapters.newFactory(String.class, new StringTypeAdapter()))
+                .registerTypeAdapterFactory(TypeAdapters.newFactory(boolean.class, Boolean.class, new BooleanTypeAdapter()))
+                .registerTypeAdapterFactory(TypeAdapters.newFactory(int.class, Integer.class, new IntegerTypeAdapter()))
+                .registerTypeAdapterFactory(TypeAdapters.newFactory(long.class, Long.class, new LongTypeAdapter()))
+                .registerTypeAdapterFactory(TypeAdapters.newFactory(float.class, Float.class, new FloatTypeAdapter()))
+                .registerTypeAdapterFactory(TypeAdapters.newFactory(double.class, Double.class, new DoubleTypeAdapter()))
+                .registerTypeAdapterFactory(TypeAdapters.newFactory(BigDecimal.class, new BigDecimalTypeAdapter()))
+                .registerTypeAdapterFactory(new CollectionTypeAdapterFactory(mainConstructor))
+                .registerTypeAdapterFactory(new ReflectiveTypeAdapterFactory(mainConstructor, FieldNamingPolicy.IDENTITY, Excluder.DEFAULT))
+                .registerTypeAdapterFactory(new MapTypeAdapterFactory(mainConstructor, false))
+                .registerTypeAdapterFactory(TypeAdapters.newFactory(JSONObject.class, new JSONObjectTypeAdapter()))
+                .registerTypeAdapterFactory(TypeAdapters.newFactory(JSONArray.class, new JSONArrayTypeAdapter()));
+        // 添加到自定义的类型解析适配器,因为在 GsonBuilder.create 方法中会对 List 进行反转,所以这里需要放到最后的位置上,这样就会优先解析
+        for (TypeAdapterFactory typeAdapterFactory : TYPE_ADAPTER_FACTORIES) {
+            gsonBuilder.registerTypeAdapterFactory(typeAdapterFactory);
+        }
+        return gsonBuilder;
+    }
+}

+ 41 - 0
lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/factory/ParseExceptionCallback.java

@@ -0,0 +1,41 @@
+package com.quyunshuo.androidbaseframemvvm.base.factory;
+
+import com.google.gson.reflect.TypeToken;
+import com.google.gson.stream.JsonToken;
+
+/**
+ *    author : Android 轮子哥
+ *    github : https://github.com/getActivity/GsonFactory
+ *    time   : 2021/02/02
+ *    desc   : Json 解析异常回调类
+ */
+public interface ParseExceptionCallback {
+
+    /**
+     * 对象类型解析异常
+     *
+     * @param typeToken             类型 Token
+     * @param fieldName             字段名称(可能为空)
+     * @param jsonToken             后台给定的类型
+     */
+    void onParseObjectException(TypeToken<?> typeToken, String fieldName, JsonToken jsonToken);
+
+    /**
+     * List item 类型解析异常
+     *
+     * @param typeToken             类型 Token
+     * @param fieldName             字段名称(可能为空)
+     * @param listItemJsonToken     List 条目类型(可能为空)
+     */
+    void onParseListItemException(TypeToken<?> typeToken, String fieldName, JsonToken listItemJsonToken);
+
+    /**
+     * Map item 类型解析异常
+     *
+     * @param typeToken             类型 Token
+     * @param fieldName             字段名称(可能为空)
+     * @param mapItemKey            Map 集合中的 key 值,如果等于为 "null" 字符串,则证明后端返回了错误类型的 key 过来
+     * @param mapItemJsonToken      Map 条目类型(可能为空)
+     */
+    void onParseMapItemException(TypeToken<?> typeToken, String fieldName, String mapItemKey, JsonToken mapItemJsonToken);
+}

+ 26 - 0
lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/factory/constructor/ConcurrentMapConstructor.java

@@ -0,0 +1,26 @@
+package com.quyunshuo.androidbaseframemvvm.base.factory.constructor;
+
+import com.google.gson.internal.ObjectConstructor;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ *    author : Android 轮子哥
+ *    github : https://github.com/getActivity/GsonFactory
+ *    time   : 2023/08/01
+ *    desc   : ConcurrentMap 创建器
+ */
+public final class ConcurrentMapConstructor implements ObjectConstructor<ConcurrentMap<?, ?>> {
+
+    private static final ConcurrentMapConstructor INSTANCE = new ConcurrentMapConstructor();
+
+    @SuppressWarnings("unchecked")
+    public static  <T extends ObjectConstructor<?>> T getInstance() {
+        return (T) INSTANCE;
+    }
+
+    @Override
+    public ConcurrentMap<?, ?> construct() {
+        return new ConcurrentHashMap<>();
+    }
+}

+ 25 - 0
lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/factory/constructor/ConcurrentSkipListMapConstructor.java

@@ -0,0 +1,25 @@
+package com.quyunshuo.androidbaseframemvvm.base.factory.constructor;
+
+import com.google.gson.internal.ObjectConstructor;
+import java.util.concurrent.ConcurrentSkipListMap;
+
+/**
+ *    author : Android 轮子哥
+ *    github : https://github.com/getActivity/GsonFactory
+ *    time   : 2023/08/01
+ *    desc   : ConcurrentSkipListMap 创建器
+ */
+public final class ConcurrentSkipListMapConstructor implements ObjectConstructor<ConcurrentSkipListMap<?, ?>> {
+
+    private static final ConcurrentSkipListMapConstructor INSTANCE = new ConcurrentSkipListMapConstructor();
+
+    @SuppressWarnings("unchecked")
+    public static  <T extends ObjectConstructor<?>> T getInstance() {
+        return (T) INSTANCE;
+    }
+
+    @Override
+    public ConcurrentSkipListMap<?, ?> construct() {
+        return new ConcurrentSkipListMap<>();
+    }
+}

+ 39 - 0
lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/factory/constructor/EnumMapConstructor.java

@@ -0,0 +1,39 @@
+package com.quyunshuo.androidbaseframemvvm.base.factory.constructor;
+
+import com.google.gson.JsonIOException;
+import com.google.gson.internal.ObjectConstructor;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.EnumMap;
+
+/**
+ *    author : Android 轮子哥
+ *    github : https://github.com/getActivity/GsonFactory
+ *    time   : 2023/08/01
+ *    desc   : EnumMap 创建器
+ */
+public final class EnumMapConstructor<T> implements ObjectConstructor<T> {
+
+    private final Type mType;
+
+    public EnumMapConstructor(Type type) {
+        mType = type;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public T construct() {
+        if (mType instanceof ParameterizedType) {
+            Type elementType = ((ParameterizedType) mType).getActualTypeArguments()[0];
+            if (elementType instanceof Class) {
+                @SuppressWarnings({"unchecked", "rawtypes"})
+                T map = (T) new EnumMap((Class) elementType);
+                return map;
+            } else {
+                throw new JsonIOException("Invalid EnumMap type: " + mType);
+            }
+        } else {
+            throw new JsonIOException("Invalid EnumMap type: " + mType.toString());
+        }
+    }
+}

+ 39 - 0
lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/factory/constructor/EnumSetConstructor.java

@@ -0,0 +1,39 @@
+package com.quyunshuo.androidbaseframemvvm.base.factory.constructor;
+
+import com.google.gson.JsonIOException;
+import com.google.gson.internal.ObjectConstructor;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.EnumSet;
+
+/**
+ *    author : Android 轮子哥
+ *    github : https://github.com/getActivity/GsonFactory
+ *    time   : 2023/08/01
+ *    desc   : EnumSet 创建器
+ */
+public final class EnumSetConstructor<T> implements ObjectConstructor<T> {
+
+    private final Type mType;
+
+    public EnumSetConstructor(Type type) {
+        mType = type;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public T construct() {
+        if (mType instanceof ParameterizedType) {
+            Type elementType = ((ParameterizedType) mType).getActualTypeArguments()[0];
+            if (elementType instanceof Class) {
+                @SuppressWarnings({"rawtypes"})
+                T set = (T) EnumSet.noneOf((Class)elementType);
+                return set;
+            } else {
+                throw new JsonIOException("Invalid EnumSet type: " + mType);
+            }
+        } else {
+            throw new JsonIOException("Invalid EnumSet type: " + mType.toString());
+        }
+    }
+}

+ 24 - 0
lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/factory/constructor/ExceptionConstructor.java

@@ -0,0 +1,24 @@
+package com.quyunshuo.androidbaseframemvvm.base.factory.constructor;
+
+import com.google.gson.JsonIOException;
+import com.google.gson.internal.ObjectConstructor;
+
+/**
+ *    author : Android 轮子哥
+ *    github : https://github.com/getActivity/GsonFactory
+ *    time   : 2023/08/01
+ *    desc   : 异常的构造器
+ */
+public final class ExceptionConstructor<T> implements ObjectConstructor<T> {
+
+    private final String mExceptionMessage;
+
+    public ExceptionConstructor(String exceptionMessage) {
+        mExceptionMessage = exceptionMessage;
+    }
+
+    @Override
+    public T construct() {
+        throw new JsonIOException(mExceptionMessage);
+    }
+}

+ 27 - 0
lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/factory/constructor/InstanceCreatorConstructor.java

@@ -0,0 +1,27 @@
+package com.quyunshuo.androidbaseframemvvm.base.factory.constructor;
+
+import com.google.gson.InstanceCreator;
+import com.google.gson.internal.ObjectConstructor;
+import java.lang.reflect.Type;
+
+/**
+ *    author : Android 轮子哥
+ *    github : https://github.com/getActivity/GsonFactory
+ *    time   : 2023/08/01
+ *    desc   : 自定义的创建器
+ */
+public final class InstanceCreatorConstructor<T> implements ObjectConstructor<T> {
+
+    private final InstanceCreator<T> mInstanceCreator;
+    private final Type mType;
+
+    public InstanceCreatorConstructor(InstanceCreator<T> instanceCreator, Type type) {
+        mInstanceCreator = instanceCreator;
+        mType = type;
+    }
+
+    @Override
+    public T construct() {
+        return mInstanceCreator.createInstance(mType);
+    }
+}

+ 120 - 0
lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/factory/constructor/KotlinDataClassDefaultValueConstructor.kt

@@ -0,0 +1,120 @@
+package com.hjq.gson.factory.constructor
+
+import com.google.gson.Gson
+import com.google.gson.internal.ObjectConstructor
+import com.google.gson.reflect.TypeToken
+import com.quyunshuo.androidbaseframemvvm.base.factory.constructor.MainConstructor
+import kotlin.reflect.KParameter
+import kotlin.reflect.KType
+import kotlin.reflect.full.primaryConstructor
+import kotlin.reflect.jvm.isAccessible
+import kotlin.reflect.jvm.javaType
+
+/**
+ *    author : Android 轮子哥
+ *    github : https://github.com/getActivity/GsonFactory
+ *    time   : 2023/11/25
+ *    desc   : Kotlin Data Class 创建器,用于处理反射创建 data class 类导致默认值不生效的问题
+ */
+class KotlinDataClassDefaultValueConstructor<T : Any>(private val mainConstructor: MainConstructor, private val gson: Gson, private val rawType: Class<*>) :  ObjectConstructor<T?> {
+
+    companion object {
+        /** 构造函数的字段的默认值 */
+        private val ABSENT_VALUE = Any()
+    }
+
+    override fun construct(): T? {
+        val rawTypeKotlin = rawType.kotlin
+        // 寻找 Kotlin 主构造函数,如果找不到就不往下执行
+        val constructor = rawTypeKotlin.primaryConstructor ?: return null
+        constructor.isAccessible = true
+
+        // 是否初始化构造函数中的全部参数
+        var initializedAllParameters = true
+        val constructorSize = constructor.parameters.size
+        val values = Array<Any?>(constructorSize) { ABSENT_VALUE }
+
+        for (i in 0 until constructorSize) {
+            if (values[i] !== ABSENT_VALUE) {
+                continue
+            }
+
+            val parameter = constructor.parameters[i]
+
+            // 判断这个参数是否携带了默认值
+            if (parameter.isOptional) {
+                initializedAllParameters = false
+                continue
+            }
+
+            // 判断这个参数是否标记为空的
+            if (parameter.type.isMarkedNullable) {
+                values[i] = null
+            } else {
+                // 如果这个参数没有标记为可空的,并且没有携带默认值
+                // 就需要赋一个默认值给它,否则会实例化构造函数会出现崩溃
+                // java.lang.IllegalArgumentException: method XxxBean.<init> argument 3 has type int, got java.lang.Object
+                // 如果是基本数据类型就一定会出现崩溃,如果是对象的话,需要同时满足以下条件才会出现崩溃
+                // 1. 后台给这个参数返回 null 的情况下(这种永远不会出现,因为框架内部处理了)
+                // 2. 获取对象的时候,如果没有做判空,也会出现异常
+                values[i] = getTypeDefaultValue(parameter.type)
+            }
+        }
+
+        // 判断构造函数上面所有的参数是否都携带了默认值
+        val result = if (initializedAllParameters) {
+            // 如果是的话,则传入元素全为 Any 的数组进去
+            constructor.call(*values)
+        } else {
+            // 如果不是的话,就传入自定义顺序的 Map 对象(Key 是参数名,Value 是参数值)进去
+            constructor.callBy(IndexedParameterMap(constructor.parameters, values))
+        }
+
+        return result as T
+    }
+
+    private fun getTypeDefaultValue(type: KType): Any? {
+        when (type.classifier) {
+            String::class -> return ""
+            Boolean::class -> return false
+            Int::class -> return 0
+            Long::class -> return 0L
+            Float::class -> return 0.0f
+            Double::class -> return 0.0
+            Short::class -> return 0.toShort()
+            Byte::class -> return 0.toByte()
+            Char::class -> return '\u0000'
+        }
+
+        val javaType = type.javaType
+        val typeToken = TypeToken.get(javaType) ?: return null
+        val objectConstructor = mainConstructor.get(gson, typeToken) ?: return null
+        return objectConstructor.construct()
+    }
+
+    /** 一个简单的 Map,它使用参数索引而不是排序或哈希。 */
+    class IndexedParameterMap(
+        private val parameterKeys: List<KParameter>,
+        private val parameterValues: Array<Any?>,
+    ) : AbstractMutableMap<KParameter, Any?>() {
+
+        override fun put(key: KParameter, value: Any?): Any? = null
+
+        override val entries: MutableSet<MutableMap.MutableEntry<KParameter, Any?>>
+            get() {
+                val allPossibleEntries = parameterKeys.mapIndexed { index, value ->
+                    SimpleEntry<KParameter, Any?>(value, parameterValues[index])
+                }
+                return allPossibleEntries.filterTo(mutableSetOf()) {
+                    it.value !== ABSENT_VALUE
+                }
+            }
+
+        override fun containsKey(key: KParameter) = parameterValues[key.index] !== ABSENT_VALUE
+
+        override fun get(key: KParameter): Any? {
+            val value = parameterValues[key.index]
+            return if (value !== ABSENT_VALUE) value else null
+        }
+    }
+}

+ 25 - 0
lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/factory/constructor/LinkedHashMapConstructor.java

@@ -0,0 +1,25 @@
+package com.quyunshuo.androidbaseframemvvm.base.factory.constructor;
+
+import com.google.gson.internal.ObjectConstructor;
+import java.util.LinkedHashMap;
+
+/**
+ *    author : Android 轮子哥
+ *    github : https://github.com/getActivity/GsonFactory
+ *    time   : 2023/08/01
+ *    desc   : LinkedHashMap 创建器
+ */
+public final class LinkedHashMapConstructor implements ObjectConstructor<LinkedHashMap<?, ?>> {
+
+    private static final LinkedHashMapConstructor INSTANCE = new LinkedHashMapConstructor();
+
+    @SuppressWarnings("unchecked")
+    public static  <T extends ObjectConstructor<?>> T getInstance() {
+        return (T) INSTANCE;
+    }
+
+    @Override
+    public LinkedHashMap<?, ?> construct() {
+        return new LinkedHashMap<>();
+    }
+}

+ 25 - 0
lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/factory/constructor/LinkedTreeMapConstructor.java

@@ -0,0 +1,25 @@
+package com.quyunshuo.androidbaseframemvvm.base.factory.constructor;
+
+import com.google.gson.internal.LinkedTreeMap;
+import com.google.gson.internal.ObjectConstructor;
+
+/**
+ *    author : Android 轮子哥
+ *    github : https://github.com/getActivity/GsonFactory
+ *    time   : 2023/08/01
+ *    desc   : LinkedTreeMap 创建器
+ */
+public final class LinkedTreeMapConstructor implements ObjectConstructor<LinkedTreeMap<?, ?>> {
+
+    private static final LinkedTreeMapConstructor INSTANCE = new LinkedTreeMapConstructor();
+
+    @SuppressWarnings("unchecked")
+    public static  <T extends ObjectConstructor<?>> T getInstance() {
+        return (T) INSTANCE;
+    }
+
+    @Override
+    public LinkedTreeMap<?, ?> construct() {
+        return new LinkedTreeMap<>();
+    }
+}

+ 26 - 0
lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/factory/constructor/ListConstructor.java

@@ -0,0 +1,26 @@
+package com.quyunshuo.androidbaseframemvvm.base.factory.constructor;
+
+import com.google.gson.internal.ObjectConstructor;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ *    author : Android 轮子哥
+ *    github : https://github.com/getActivity/GsonFactory
+ *    time   : 2023/08/01
+ *    desc   : List 创建器
+ */
+public final class ListConstructor implements ObjectConstructor<List<?>> {
+
+    private static final ListConstructor INSTANCE = new ListConstructor();
+
+    @SuppressWarnings("unchecked")
+    public static  <T extends ObjectConstructor<?>> T getInstance() {
+        return (T) INSTANCE;
+    }
+
+    @Override
+    public List<?> construct() {
+        return new ArrayList<>();
+    }
+}

+ 247 - 0
lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/factory/constructor/MainConstructor.java

@@ -0,0 +1,247 @@
+package com.quyunshuo.androidbaseframemvvm.base.factory.constructor;
+
+import com.google.gson.Gson;
+import com.google.gson.InstanceCreator;
+import com.google.gson.ReflectionAccessFilter;
+import com.google.gson.ReflectionAccessFilter.FilterResult;
+import com.google.gson.internal.ObjectConstructor;
+import com.google.gson.internal.ReflectionAccessFilterHelper;
+import com.google.gson.internal.reflect.ReflectionHelper;
+import com.google.gson.reflect.TypeToken;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.Collection;
+import java.util.EnumMap;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Queue;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.SortedSet;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ConcurrentNavigableMap;
+
+/**
+ *    author : Android 轮子哥
+ *    github : https://github.com/getActivity/GsonFactory
+ *    time   : 2022/03/30
+ *    desc   : 构造函数构造器,参考:{@link com.google.gson.internal.ConstructorConstructor}
+ */
+public final class MainConstructor {
+    private final Map<Type, InstanceCreator<?>> mInstanceCreators;
+    private final boolean mUseJdkUnsafe;
+    private final List<ReflectionAccessFilter> mReflectionFilters;
+
+    public MainConstructor(Map<Type, InstanceCreator<?>> instanceCreators, boolean useJdkUnsafe, List<ReflectionAccessFilter> reflectionFilters) {
+        mInstanceCreators = instanceCreators;
+        mUseJdkUnsafe = useJdkUnsafe;
+        mReflectionFilters = reflectionFilters;
+    }
+
+    /**
+     * Check if the class can be instantiated by Unsafe allocator. If the instance has interface or abstract modifiers
+     * return an exception message.
+     * @param c instance of the class to be checked
+     * @return if instantiable {@code null}, else a non-{@code null} exception message
+     */
+    static String checkInstantiable(Class<?> c) {
+        int modifiers = c.getModifiers();
+        if (Modifier.isInterface(modifiers)) {
+            return "Interfaces can't be instantiated! Register an InstanceCreator "
+                + "or a TypeAdapter for this type. Interface name: " + c.getName();
+        }
+        if (Modifier.isAbstract(modifiers)) {
+            return "Abstract classes can't be instantiated! Register an InstanceCreator "
+                + "or a TypeAdapter for this type. Class name: " + c.getName();
+        }
+        return null;
+    }
+
+    public <T> ObjectConstructor<T> get(Gson gson, TypeToken<T> typeToken) {
+        final Type type = typeToken.getType();
+        final Class<? super T> rawType = typeToken.getRawType();
+
+        // first try an instance creator
+
+        @SuppressWarnings("unchecked")
+        final InstanceCreator<T> typeCreator = (InstanceCreator<T>) mInstanceCreators.get(type);
+        if (typeCreator != null) {
+            return new InstanceCreatorConstructor<>(typeCreator, type);
+        }
+
+        // Next try raw type match for instance creators
+        @SuppressWarnings("unchecked") // types must agree
+        final InstanceCreator<T> rawTypeCreator =
+            (InstanceCreator<T>) mInstanceCreators.get(rawType);
+        if (rawTypeCreator != null) {
+            return new InstanceCreatorConstructor<>(rawTypeCreator, type);
+        }
+
+        // First consider special constructors before checking for no-args constructors
+        // below to avoid matching internal no-args constructors which might be added in
+        // future JDK versions
+        ObjectConstructor<T> specialConstructor = newSpecialCollectionConstructor(type, rawType);
+        if (specialConstructor != null) {
+            return specialConstructor;
+        }
+
+        ReflectionAccessFilter.FilterResult filterResult = ReflectionAccessFilterHelper.getFilterResult(mReflectionFilters, rawType);
+        ObjectConstructor<T> defaultConstructor = newDefaultConstructor(this, gson, rawType, filterResult);
+        if (defaultConstructor != null) {
+            return defaultConstructor;
+        }
+
+        ObjectConstructor<T> defaultImplementation = newDefaultImplementationConstructor(type, rawType);
+        if (defaultImplementation != null) {
+            return defaultImplementation;
+        }
+
+        // Check whether type is instantiable; otherwise ReflectionAccessFilter recommendation
+        // of adjusting filter suggested below is irrelevant since it would not solve the problem
+        final String exceptionMessage = checkInstantiable(rawType);
+        if (exceptionMessage != null) {
+            return new ExceptionConstructor<>(exceptionMessage);
+        }
+
+        // Consider usage of Unsafe as reflection, so don't use if BLOCK_ALL
+        // Additionally, since it is not calling any constructor at all, don't use if BLOCK_INACCESSIBLE
+        if (filterResult == ReflectionAccessFilter.FilterResult.ALLOW) {
+            // finally try unsafe
+            return newUnsafeAllocator(gson, rawType);
+        } else {
+            final String message = "Unable to create instance of " + rawType + "; ReflectionAccessFilter "
+                + "does not permit using reflection or Unsafe. Register an InstanceCreator or a TypeAdapter "
+                + "for this type or adjust the access filter to allow using reflection.";
+            return new ExceptionConstructor<>(message);
+        }
+    }
+
+    /**
+     * 为没有公共无参数构造函数的特殊 JDK 集合类型创建构造函数
+     */
+    private static <T> ObjectConstructor<T> newSpecialCollectionConstructor(final Type type, Class<? super T> rawType) {
+        if (EnumSet.class.isAssignableFrom(rawType)) {
+            return new EnumSetConstructor<>(type);
+        }
+        // Only support creation of EnumMap, but not of custom subtypes; for them type parameters
+        // and constructor parameter might have completely different meaning
+        else if (rawType == EnumMap.class) {
+            return new EnumMapConstructor<>(type);
+        }
+
+        return null;
+    }
+
+    private static <T> ObjectConstructor<T> newDefaultConstructor(MainConstructor mainConstructor, Gson gson, Class<? super T> rawType, FilterResult filterResult) {
+        // Cannot invoke constructor of abstract class
+        if (Modifier.isAbstract(rawType.getModifiers())) {
+            return null;
+        }
+
+        final Constructor<? super T> constructor;
+        try {
+            constructor = rawType.getDeclaredConstructor();
+        } catch (NoSuchMethodException e) {
+            return null;
+        }
+
+        boolean canAccess = filterResult == FilterResult.ALLOW || (ReflectionAccessFilterHelper.canAccess(constructor, null)
+            // Be a bit more lenient here for BLOCK_ALL; if constructor is accessible and public then allow calling it
+            && (filterResult != FilterResult.BLOCK_ALL || Modifier.isPublic(constructor.getModifiers())));
+
+        if (!canAccess) {
+            final String message = "Unable to invoke no-args constructor of " + rawType + "; "
+                + "constructor is not accessible and ReflectionAccessFilter does not permit making "
+                + "it accessible. Register an InstanceCreator or a TypeAdapter for this type, change "
+                + "the visibility of the constructor or adjust the access filter.";
+            return new ExceptionConstructor<>(message);
+        }
+
+        // Only try to make accessible if allowed; in all other cases checks above should
+        // have verified that constructor is accessible
+        if (filterResult == FilterResult.ALLOW) {
+            final String exceptionMessage = ReflectionHelper.tryMakeAccessible(constructor);
+            if (exceptionMessage != null) {
+                /*
+                 * Create ObjectConstructor which throws exception.
+                 * This keeps backward compatibility (compared to returning `null` which
+                 * would then choose another way of creating object).
+                 * And it supports types which are only serialized but not deserialized
+                 * (compared to directly throwing exception here), e.g. when runtime type
+                 * of object is inaccessible, but compile-time type is accessible.
+                 */
+
+                // New exception is created every time to avoid keeping reference
+                // to exception with potentially long stack trace, causing a
+                // memory leak
+                return new ExceptionConstructor<>(exceptionMessage);
+            }
+        }
+
+        return new ReflectCreatorConstructor<>(mainConstructor, gson, rawType, constructor);
+    }
+
+    /**
+     * Constructors for common interface types like Map and List and their
+     * subtypes.
+     */
+    private static <T> ObjectConstructor<T> newDefaultImplementationConstructor(
+        final Type type, Class<? super T> rawType) {
+
+        /*
+         * IMPORTANT: Must only create instances for classes with public no-args constructor.
+         * For classes with special constructors / factory methods (e.g. EnumSet)
+         * `newSpecialCollectionConstructor` defined above must be used, to avoid no-args
+         * constructor check (which is called before this method) detecting internal no-args
+         * constructors which might be added in a future JDK version
+         */
+
+        if (Collection.class.isAssignableFrom(rawType)) {
+            if (SortedSet.class.isAssignableFrom(rawType)) {
+                return SortedSetConstructor.getInstance();
+            } else if (Set.class.isAssignableFrom(rawType)) {
+                return SetConstructor.getInstance();
+            } else if (Queue.class.isAssignableFrom(rawType)) {
+                return QueueConstructor.getInstance();
+            } else {
+                return ListConstructor.getInstance();
+            }
+        }
+
+        if (Map.class.isAssignableFrom(rawType)) {
+            if (ConcurrentNavigableMap.class.isAssignableFrom(rawType)) {
+                return ConcurrentSkipListMapConstructor.getInstance();
+            } else if (ConcurrentMap.class.isAssignableFrom(rawType)) {
+                return ConcurrentMapConstructor.getInstance();
+            } else if (SortedMap.class.isAssignableFrom(rawType)) {
+                return SortedMapConstructor.getInstance();
+            } else if (type instanceof ParameterizedType && !(String.class.isAssignableFrom(
+                TypeToken.get(((ParameterizedType) type).getActualTypeArguments()[0]).getRawType()))) {
+                return LinkedHashMapConstructor.getInstance();
+            } else {
+                return LinkedTreeMapConstructor.getInstance();
+            }
+        }
+
+        return null;
+    }
+
+    private <T> ObjectConstructor<T> newUnsafeAllocator(Gson gson, final Class<? super T> rawType) {
+        if (mUseJdkUnsafe) {
+            return new ReflectSafeCreatorConstructor<>(this, gson, rawType);
+        } else {
+            final String exceptionMessage = "Unable to create instance of " + rawType + "; usage of JDK Unsafe "
+                + "is disabled. Registering an InstanceCreator or a TypeAdapter for this type, adding a no-args "
+                + "constructor, or enabling usage of JDK Unsafe may fix this problem.";
+            return new ExceptionConstructor<>(exceptionMessage);
+        }
+    }
+
+    @Override
+    public String toString() {
+        return mInstanceCreators.toString();
+    }
+}

+ 26 - 0
lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/factory/constructor/QueueConstructor.java

@@ -0,0 +1,26 @@
+package com.quyunshuo.androidbaseframemvvm.base.factory.constructor;
+
+import com.google.gson.internal.ObjectConstructor;
+import java.util.ArrayDeque;
+import java.util.Queue;
+
+/**
+ *    author : Android 轮子哥
+ *    github : https://github.com/getActivity/GsonFactory
+ *    time   : 2023/08/01
+ *    desc   : Queue 创建器
+ */
+public final class QueueConstructor implements ObjectConstructor<Queue<?>> {
+
+    private static final QueueConstructor INSTANCE = new QueueConstructor();
+
+    @SuppressWarnings("unchecked")
+    public static  <T extends ObjectConstructor<?>> T getInstance() {
+        return (T) INSTANCE;
+    }
+
+    @Override
+    public Queue<?> construct() {
+        return new ArrayDeque<>();
+    }
+}

+ 55 - 0
lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/factory/constructor/ReflectCreatorConstructor.java

@@ -0,0 +1,55 @@
+package com.quyunshuo.androidbaseframemvvm.base.factory.constructor;
+
+import com.google.gson.Gson;
+import com.google.gson.internal.ObjectConstructor;
+import com.google.gson.internal.reflect.ReflectionHelper;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ *    author : Android 轮子哥
+ *    github : https://github.com/getActivity/GsonFactory
+ *    time   : 2023/08/01
+ *    desc   : 反射创建器
+ */
+public final class ReflectCreatorConstructor<T> implements ObjectConstructor<T> {
+
+    private final ObjectConstructor<T> mKotlinDataClassDefaultValueConstructor;
+
+    private final Constructor<? super T> mConstructor;
+
+    public ReflectCreatorConstructor(MainConstructor mainConstructor, Gson gson, Class<? super T> rawType, Constructor<? super T> constructor) {
+        mConstructor = constructor;
+        mKotlinDataClassDefaultValueConstructor = new com.hjq.gson.factory.constructor.KotlinDataClassDefaultValueConstructor<>(mainConstructor, gson, rawType);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public T construct() {
+        T instance = mKotlinDataClassDefaultValueConstructor.construct();
+
+        if (instance != null) {
+            return instance;
+        }
+
+        try {
+            instance = (T) mConstructor.newInstance();
+            return instance;
+        }
+        // Note: InstantiationException should be impossible because check at start of method made sure
+        //   that class is not abstract
+        catch (InstantiationException e) {
+            throw new RuntimeException("Failed to invoke constructor '" + ReflectionHelper.constructorToString(
+                mConstructor) + "'"
+                + " with no args", e);
+        } catch (InvocationTargetException e) {
+            // don't wrap if cause is unchecked?
+            // JsonParseException ?
+            throw new RuntimeException("Failed to invoke constructor '" + ReflectionHelper.constructorToString(
+                mConstructor) + "'"
+                + " with no args", e.getCause());
+        } catch (IllegalAccessException e) {
+            throw ReflectionHelper.createExceptionForUnexpectedIllegalAccess(e);
+        }
+    }
+}

+ 0 - 0
lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/factory/constructor/ReflectSafeCreatorConstructor.java


Some files were not shown because too many files changed in this diff