liukai 8 månader sedan
incheckning
4b88fd92fa
100 ändrade filer med 30465 tillägg och 0 borttagningar
  1. 18 0
      .gitignore
  2. 1 0
      app/.gitignore
  3. 20 0
      app/build.gradle.kts
  4. 8000 0
      app/dictionary/proguard-1il.txt
  5. 8000 0
      app/dictionary/proguard-o0O.txt
  6. 8000 0
      app/dictionary/proguard-socialism.txt
  7. 258 0
      app/proguard-rules.pro
  8. 24 0
      app/src/androidTest/java/com/newki/template/ExampleInstrumentedTest.kt
  9. 52 0
      app/src/main/AndroidManifest.xml
  10. 12 0
      app/src/main/java/vn/hongyegroup/yybusiness/App.kt
  11. 22 0
      app/src/main/java/vn/hongyegroup/yybusiness/di/ApplicationDIModule.kt
  12. 25 0
      app/src/main/java/vn/hongyegroup/yybusiness/ui/MainActivity.kt
  13. 170 0
      app/src/main/res/drawable/ic_launcher_background.xml
  14. 30 0
      app/src/main/res/drawable/ic_launcher_foreground.xml
  15. 194 0
      app/src/main/res/layout/activity_main.xml
  16. BIN
      app/src/main/res/mipmap-hdpi/ic_launcher.webp
  17. BIN
      app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
  18. BIN
      app/src/main/res/mipmap-xhdpi/ic_launcher.webp
  19. BIN
      app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
  20. BIN
      app/src/main/res/mipmap-xxhdpi/done.webp
  21. BIN
      app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
  22. BIN
      app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
  23. BIN
      app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
  24. BIN
      app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
  25. 10 0
      app/src/main/res/values/colors.xml
  26. 3 0
      app/src/main/res/values/strings.xml
  27. 13 0
      app/src/main/res/xml/backup_rules.xml
  28. 19 0
      app/src/main/res/xml/data_extraction_rules.xml
  29. 17 0
      app/src/test/java/com/newki/template/ExampleUnitTest.kt
  30. 25 0
      build.gradle.kts
  31. 34 0
      buildSrc/build.gradle.kts
  32. 264 0
      buildSrc/src/main/kotlin/DefaultGradlePlugin.kt
  33. 139 0
      buildSrc/src/main/kotlin/Dependencies.kt
  34. 31 0
      buildSrc/src/main/kotlin/DependencyHandleExt.kt
  35. 8 0
      buildSrc/src/main/kotlin/ModuleGradlePlugin.kt
  36. 46 0
      buildSrc/src/main/kotlin/ProjectConfig.kt
  37. 162 0
      buildSrc/src/main/kotlin/VersionAndroidX.kt
  38. 21 0
      buildSrc/src/main/kotlin/VersionKotlin.kt
  39. 10 0
      buildSrc/src/main/kotlin/VersionTesting.kt
  40. 97 0
      buildSrc/src/main/kotlin/VersionThirdPart.kt
  41. 1 0
      cs-baselib/.gitignore
  42. 27 0
      cs-baselib/build.gradle.kts
  43. 0 0
      cs-baselib/consumer-rules.pro
  44. 21 0
      cs-baselib/proguard-rules.pro
  45. 24 0
      cs-baselib/src/androidTest/java/com/base/lib/ExampleInstrumentedTest.kt
  46. 84 0
      cs-baselib/src/main/AndroidManifest.xml
  47. 26 0
      cs-baselib/src/main/java/com/android/basiclib/adapter/MyPager2Adapter.kt
  48. 95 0
      cs-baselib/src/main/java/com/android/basiclib/adapter/ViewPagerFragmentAdapter.kt
  49. 19 0
      cs-baselib/src/main/java/com/android/basiclib/annotation/NetWork.java
  50. 79 0
      cs-baselib/src/main/java/com/android/basiclib/base/BaseApplication.kt
  51. 90 0
      cs-baselib/src/main/java/com/android/basiclib/base/BaseRetrofitClient.kt
  52. 188 0
      cs-baselib/src/main/java/com/android/basiclib/base/activity/AbsActivity.kt
  53. 89 0
      cs-baselib/src/main/java/com/android/basiclib/base/activity/BaseVMActivity.kt
  54. 105 0
      cs-baselib/src/main/java/com/android/basiclib/base/activity/BaseVMLoadingActivity.kt
  55. 47 0
      cs-baselib/src/main/java/com/android/basiclib/base/activity/BaseVVDActivity.kt
  56. 47 0
      cs-baselib/src/main/java/com/android/basiclib/base/activity/BaseVVDLoadingActivity.kt
  57. 94 0
      cs-baselib/src/main/java/com/android/basiclib/base/fragment/AbsFragment.kt
  58. 109 0
      cs-baselib/src/main/java/com/android/basiclib/base/fragment/BaseVMFragment.kt
  59. 191 0
      cs-baselib/src/main/java/com/android/basiclib/base/fragment/BaseVMLazyLoadingFragment.kt
  60. 127 0
      cs-baselib/src/main/java/com/android/basiclib/base/fragment/BaseVMLoadingFragment.kt
  61. 46 0
      cs-baselib/src/main/java/com/android/basiclib/base/fragment/BaseVVDFragment.kt
  62. 46 0
      cs-baselib/src/main/java/com/android/basiclib/base/fragment/BaseVVDLazyLoadingFragment.kt
  63. 46 0
      cs-baselib/src/main/java/com/android/basiclib/base/fragment/BaseVVDLoadingFragment.kt
  64. 31 0
      cs-baselib/src/main/java/com/android/basiclib/base/mvi/BaseEISViewModel.kt
  65. 61 0
      cs-baselib/src/main/java/com/android/basiclib/base/mvi/BaseISViewModel.kt
  66. 11 0
      cs-baselib/src/main/java/com/android/basiclib/base/mvi/IUIEffect.kt
  67. 11 0
      cs-baselib/src/main/java/com/android/basiclib/base/mvi/IUiIntent.kt
  68. 11 0
      cs-baselib/src/main/java/com/android/basiclib/base/mvi/IUiState.kt
  69. 64 0
      cs-baselib/src/main/java/com/android/basiclib/base/mvi/LiveDataExt.kt
  70. 81 0
      cs-baselib/src/main/java/com/android/basiclib/base/vm/BaseRepository.kt
  71. 61 0
      cs-baselib/src/main/java/com/android/basiclib/base/vm/BaseViewModel.kt
  72. 7 0
      cs-baselib/src/main/java/com/android/basiclib/base/vm/EmptyViewModel.kt
  73. 49 0
      cs-baselib/src/main/java/com/android/basiclib/bean/BaseBean.java
  74. 15 0
      cs-baselib/src/main/java/com/android/basiclib/bean/ErrorBean.java
  75. 18 0
      cs-baselib/src/main/java/com/android/basiclib/bean/LoadAction.kt
  76. 44 0
      cs-baselib/src/main/java/com/android/basiclib/bean/OkResult.kt
  77. 25 0
      cs-baselib/src/main/java/com/android/basiclib/bean/Optional.java
  78. 17 0
      cs-baselib/src/main/java/com/android/basiclib/bean/PageInfo.java
  79. 817 0
      cs-baselib/src/main/java/com/android/basiclib/cache/ACache.java
  80. 166 0
      cs-baselib/src/main/java/com/android/basiclib/cache/BaseLockCaches.java
  81. 100 0
      cs-baselib/src/main/java/com/android/basiclib/core/BaseLibCore.kt
  82. 83 0
      cs-baselib/src/main/java/com/android/basiclib/engine/dialog/DialogExt.kt
  83. 54 0
      cs-baselib/src/main/java/com/android/basiclib/engine/dialog/DialogType.kt
  84. 5 0
      cs-baselib/src/main/java/com/android/basiclib/engine/dialog/callback/IPopupController.kt
  85. 89 0
      cs-baselib/src/main/java/com/android/basiclib/engine/dialog/creator/AttachPopupViewCreator.kt
  86. 83 0
      cs-baselib/src/main/java/com/android/basiclib/engine/dialog/creator/BottomPopupViewCreator.kt
  87. 83 0
      cs-baselib/src/main/java/com/android/basiclib/engine/dialog/creator/CenterPopupViewCreator.kt
  88. 57 0
      cs-baselib/src/main/java/com/android/basiclib/engine/dialog/creator/FullScreenPopupViewCreator.kt
  89. 10 0
      cs-baselib/src/main/java/com/android/basiclib/engine/dialog/creator/IPopupViewCreator.kt
  90. 74 0
      cs-baselib/src/main/java/com/android/basiclib/engine/dialog/creator/PartPopupViewCreator.kt
  91. 29 0
      cs-baselib/src/main/java/com/android/basiclib/engine/dialog/creator/PopupViewCreatorFactory.kt
  92. 110 0
      cs-baselib/src/main/java/com/android/basiclib/engine/image_grid/ImageGridAdapter.kt
  93. 124 0
      cs-baselib/src/main/java/com/android/basiclib/engine/image_grid/ImageGridView.kt
  94. 161 0
      cs-baselib/src/main/java/com/android/basiclib/engine/image_load/ImageLoadExt.kt
  95. 96 0
      cs-baselib/src/main/java/com/android/basiclib/engine/image_select/ImageFileCompressEngine.kt
  96. 125 0
      cs-baselib/src/main/java/com/android/basiclib/engine/image_select/ImageFileCropEngine.kt
  97. 246 0
      cs-baselib/src/main/java/com/android/basiclib/engine/image_select/ImageSelectEngine.kt
  98. 64 0
      cs-baselib/src/main/java/com/android/basiclib/engine/image_select/ImageSelectGlideEngine.kt
  99. 57 0
      cs-baselib/src/main/java/com/android/basiclib/engine/image_select/dialog/ImageSelectDialogEngine.kt
  100. 0 0
      cs-baselib/src/main/java/com/android/basiclib/engine/image_select/result/ImageSelectEntity.kt

+ 18 - 0
.gitignore

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

+ 1 - 0
app/.gitignore

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

+ 20 - 0
app/build.gradle.kts

@@ -0,0 +1,20 @@
+plugins {
+    id("com.android.application")
+}
+
+// 使用自定义插件
+apply<DefaultGradlePlugin>()
+
+android {
+    //需要定义 namespace 和 applicationId 的信息
+    namespace = "vn.hongyegroup.yybusiness"
+    defaultConfig {
+        applicationId = BuildConfig.applicationId
+    }
+
+    //如果要配置 JPush、GooglePlay等配置,直接接下去写即可
+}
+
+dependencies {
+
+}

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 8000 - 0
app/dictionary/proguard-1il.txt


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 8000 - 0
app/dictionary/proguard-o0O.txt


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 8000 - 0
app/dictionary/proguard-socialism.txt


+ 258 - 0
app/proguard-rules.pro

@@ -0,0 +1,258 @@
+
+#####################################################
+#                      基本配置                      #
+#####################################################
+
+# 指定代码的压缩级别 0 - 7
+-optimizationpasses 5
+# 不使用大小写混合类名
+-dontusemixedcaseclassnames
+# 如果应用程序引入的有jar包,并且想混淆jar包里面的class
+-dontskipnonpubliclibraryclasses
+# 混淆时是否做预校验(可去掉加快混淆速度)
+-dontpreverify
+#-dontoptimize
+# 混淆时是否记录日志(混淆后生产映射文件 map 类名 -> 转化后类名的映射
+-verbose
+# 淆采用的算法
+-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
+
+#指定外部模糊字典
+-obfuscationdictionary ./dictionary/proguard-1il.txt
+#指定class模糊字典
+-classobfuscationdictionary ./dictionary/proguard-o0O.txt
+#指定package模糊字典
+-packageobfuscationdictionary ./dictionary/proguard-socialism.txt
+
+
+# 所有 Android 控件的子类不要去混淆
+-keep public class * extends android.app.Activity
+-keep public class * extends android.app.Application
+-keep public class * extends android.app.Service
+-keep public class * extends android.content.BroadcastReceiver
+-keep public class * extends android.content.ContentProvider
+-keep public class * extends android.app.backup.BackupAgentHelper
+-keep public class * extends android.preference.Preference
+# 所有 Android 支持库子类不混淆
+-keep public class * extends com.support.**
+-keep public class * extends androidx.**
+-dontwarn javax.lang.model.element.Element
+
+# 保持 native 的方法不去混淆
+-keepclasseswithmembernames class * {
+    native <methods>;
+}
+
+# 保持自定义控件类不被混淆,指定格式的构造方法不去混淆
+-keepclasseswithmembers class * {
+    public <init>(android.content.Context, android.util.AttributeSet);
+}
+-keepclasseswithmembers class * {
+    public <init>(android.content.Context, android.util.AttributeSet, int);
+}
+# Android layout 布局文件中为控件配置的onClick方法不能混淆
+-keepclassmembers class * extends android.app.Activity {
+    public void *(android.view.View);
+}
+
+ # 保持自定义控件指定规则的方法不被混淆
+-keep public class * extends android.view.View {
+    public <init>(android.content.Context);
+    public <init>(android.content.Context, android.util.AttributeSet);
+    public <init>(android.content.Context, android.util.AttributeSet, int);
+    public void set*(...);
+}
+
+# 保持枚举 enum 不被混淆
+-keepclassmembers enum * {
+    public static **[] values();
+    public static ** valueOf(java.lang.String);
+}
+
+# 保持 Parcelable 不被混淆(aidl文件不能去混淆)
+-keep class * implements android.os.Parcelable {
+    public static final android.os.Parcelable$Creator *;
+}
+
+# 需要序列化和反序列化的类不能被混淆(注:Java反射用到的类也不能被混淆)
+-keepnames class * implements java.io.Serializable
+# 保护实现接口Serializable的类中,指定规则的类成员不被混淆
+-keepclassmembers class * implements java.io.Serializable {
+    static final long serialVersionUID;
+    private static final java.io.ObjectStreamField[] serialPersistentFields;
+    !static !transient <fields>;
+    private void writeObject(java.io.ObjectOutputStream);
+    private void readObject(java.io.ObjectInputStream);
+    java.lang.Object writeReplace();
+    java.lang.Object readResolve();
+}
+
+# 过滤泛型(不写可能会出现类型转换错误,一般情况把这个加上就是了)
+-keepattributes Signature
+
+# 假如项目中有用到注解,应加入这行配置
+-keepattributes *Annotation*
+
+# 保持 R 文件不被混淆,否则,你的反射是获取不到资源id的
+-keep class **.R$* { *; }
+
+# 保持ViewModel和ViewBinding不混淆,否则无法反射自动创建
+-keep class * implements androidx.viewbinding.ViewBinding { *; }
+-keep class * extends androidx.lifecycle.ViewModel { *; }
+
+#####################################################
+#                      自定义配置                    #
+#####################################################
+
+# 保护 WebView 对 HTML 页面的API不被混淆
+-keep class **.WebView2JsInterface { *; }
+
+# 数据 Entity 类
+######
+
+
+# Guarded by a NoClassDefFoundError try/catch and only used when on the classpath.
+-dontwarn kotlin.Unit
+
+-keep class androidx.lifecycle.** { *; }
+-keep class androidx.arch.core.** { *; }
+
+
+# kotlin serialization
+-keepattributes *Annotation*, InnerClasses
+-keepclassmembers class kotlinx.serialization.json.** {
+    *** Companion;
+}
+
+
+#Android X 混淆
+-keep class com.google.android.material.** {*;}
+-keep class androidx.** {*;}
+-keep public class * extends androidx.**
+-keep interface androidx.** {*;}
+-dontwarn com.google.android.material.**
+-dontnote com.google.android.material.**
+-dontwarn androidx.**
+# Compose Rules
+-keep class androidx.compose.** { *; }
+-keep class androidx.documentfile.** { *; }
+-keepclassmembers class androidx.documentfile.** { *; }
+# ...
+
+# Lifecycle
+-keep class * extends androidx.lifecycle.ViewModel { *; }
+-keep class * extends androidx.lifecycle.LifecycleObserver { *; }
+
+# Paging
+-keep class androidx.paging.** { *; }
+
+# Navigation
+-keep class androidx.navigation.** { *; }
+
+# accompanist
+-keep class com.google.accompanist.** { *; }
+
+# Dagger Hilt
+-keep class * extends com.google.dagger.hilt.android.lifecycle.*
+-keepclassmembers class * {
+    javax.inject.* *;
+}
+
+
+# Multidex
+-keep class * extends android.app.Application {
+    void attachBaseContext(...);
+}
+
+#kotlin
+-keep class kotlin.** { *; }
+-keep class kotlin.Metadata { *; }
+-dontwarn kotlin.**
+-keepclassmembers class **$WhenMappings {
+    <fields>;
+}
+-keepclassmembers class kotlin.Metadata {
+    public <methods>;
+}
+-assumenosideeffects class kotlin.jvm.internal.Intrinsics {
+    static void checkParameterIsNotNull(java.lang.Object, java.lang.String);
+}
+
+-keepclasseswithmembernames class * {
+    native <methods>;
+}
+
+-keepclassmembers class * extends android.app.Activity {
+   public void *(android.view.View);
+}
+-keepclassmembers class * implements android.os.Parcelable {
+  public static final android.os.Parcelable$Creator *;
+}
+-keep class **.R$* {*;}
+-keepclassmembers enum * { *;}
+
+#Kotlin Coroutines
+-keepnames class kotlinx.coroutines.internal.MainDispatcherFactory {}
+-keepnames class kotlinx.coroutines.CoroutineExceptionHandler {}
+-keepnames class kotlinx.coroutines.android.AndroidExceptionPreHandler {}
+-keepnames class kotlinx.coroutines.android.AndroidDispatcherFactory {}
+-keepclassmembernames class kotlinx.** {
+    volatile <fields>;
+}
+
+#BRVAH
+-keep class com.chad.library.adapter.** {
+*;
+}
+-keep public class * extends com.chad.library.adapter.base.BaseQuickAdapter
+-keep public class * extends com.chad.library.adapter.base.viewholder.BaseViewHolder
+-keepclassmembers  class **$** extends com.chad.library.adapter.base.viewholder.BaseViewHolder {
+     <init>(...);
+}
+
+# Retrofit
+-dontwarn retrofit2.**
+-keep class retrofit2.** { *; }
+-keepattributes Signature
+-keepattributes Exceptions
+# converters and adapters. 是否要注释
+-keepclassmembernames,allowobfuscation interface * {
+    @retrofit2.http.* <methods>;
+}
+-dontwarn javax.annotation.**
+-dontwarn javax.inject.**
+
+#okhttp
+-dontwarn okhttp3.**
+-keep class okhttp3.**{*;}
+-dontwarn okhttp3.logging.**
+-keep class okhttp3.internal.**{*;}
+
+#okio
+-dontwarn okio.**
+-keep class okio.**{*;}
+
+# Gson
+-keepattributes *Annotation*
+-keepattributes Signature
+-dontwarn sun.misc.**
+-keep class com.google.gson.stream.** { *; }
+-keep class com.google.gson.examples.android.model.** { *; }
+-keep class * implements com.google.gson.TypeAdapterFactory
+-keep class * implements com.google.gson.JsonSerializer
+-keep class * implements com.google.gson.JsonDeserializer
+
+#XPopup
+-dontwarn com.lxj.xpopup.widget.**
+-keep class com.lxj.xpopup.widget.**{*;}
+-keep class com.lxj.xpopupext.bean.** {*;}
+
+
+#XXPermission混淆
+-keep class com.hjq.permissions.** {*;}
+-keep class com.hjq.toast.** {*;}
+
+##腾讯
+-keep class com.tencent.** { *; }
+
+

+ 24 - 0
app/src/androidTest/java/com/newki/template/ExampleInstrumentedTest.kt

@@ -0,0 +1,24 @@
+package com.newki.template
+
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.ext.junit.runners.AndroidJUnit4
+
+import org.junit.Test
+import org.junit.runner.RunWith
+
+import org.junit.Assert.*
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+@RunWith(AndroidJUnit4::class)
+class ExampleInstrumentedTest {
+      @Test
+      fun useAppContext() {
+            // Context of the app under test.
+            val appContext = InstrumentationRegistry.getInstrumentation().targetContext
+            assertEquals("com.newki.template", appContext.packageName)
+      }
+}

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

@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    package="vn.hongyegroup.yybusiness">
+
+    <!--  相机  -->
+    <uses-feature android:name="android.hardware.camera.any" />
+    <uses-feature android:name="android.hardware.camera" />
+    <uses-feature android:name="android.hardware.camera.autofocus" />
+
+    <uses-permission android:name="android.permission.CAMERA" />
+
+    <!--  SD卡  -->
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+
+    <!--  Android 13版本适配,细化存储权限-->
+    <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
+    <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
+    <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
+
+    <!--  拨打电话  -->
+    <uses-feature
+        android:name="android.hardware.telephony"
+        android:required="false" />
+    <uses-permission android:name="android.permission.CALL_PHONE" />
+
+
+    <application
+        android:name=".App"
+        android:allowBackup="true"
+        android:dataExtractionRules="@xml/data_extraction_rules"
+        android:fullBackupContent="@xml/backup_rules"
+        android:icon="@mipmap/ic_launcher"
+        android:label="@string/app_name"
+        android:roundIcon="@mipmap/ic_launcher_round"
+        android:supportsRtl="true"
+        android:theme="@style/AppTheme">
+
+        <activity
+            android:name=".ui.MainActivity"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+
+    </application>
+
+</manifest>

+ 12 - 0
app/src/main/java/vn/hongyegroup/yybusiness/App.kt

@@ -0,0 +1,12 @@
+package vn.hongyegroup.yybusiness
+
+import com.android.basiclib.base.BaseApplication
+import dagger.hilt.android.HiltAndroidApp
+
+@HiltAndroidApp
+class App :BaseApplication(){
+    override fun onCreate() {
+        super.onCreate()
+    }
+
+}

+ 22 - 0
app/src/main/java/vn/hongyegroup/yybusiness/di/ApplicationDIModule.kt

@@ -0,0 +1,22 @@
+package vn.hongyegroup.yybusiness.di
+
+import android.app.Application
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.components.SingletonComponent
+import vn.hongyegroup.yybusiness.App
+
+/**
+ * 全局的DI注入
+ */
+@Module
+@InstallIn(SingletonComponent::class)
+class ApplicationDIModule {
+
+    @Provides
+    fun provideMyApplication(application: Application): App {
+        return application as App
+    }
+
+}

+ 25 - 0
app/src/main/java/vn/hongyegroup/yybusiness/ui/MainActivity.kt

@@ -0,0 +1,25 @@
+package vn.hongyegroup.yybusiness.ui
+
+import android.os.Bundle
+import com.android.basiclib.base.activity.BaseVVDActivity
+import com.android.basiclib.base.vm.EmptyViewModel
+import vn.hongyegroup.yybusiness.databinding.ActivityMainBinding
+
+
+/**
+ * 应用的首页
+ */
+class MainActivity : BaseVVDActivity<EmptyViewModel, ActivityMainBinding>() {
+
+    override fun init(savedInstanceState: Bundle?) {
+        initListener()
+
+    }
+
+    override fun isShatterEnable(): Boolean = true
+
+    private fun initListener() {
+
+    }
+
+}

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

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 30 - 0
app/src/main/res/drawable/ic_launcher_foreground.xml


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

@@ -0,0 +1,194 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@color/white"
+    android:orientation="vertical">
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_margin="@dimen/d_10dp"
+        android:text="测试弹窗" />
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+
+        <Button
+            android:id="@+id/btn_filter_popup"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_margin="@dimen/d_10dp"
+            android:text="筛选"
+            android:textAllCaps="false" />
+
+        <Button
+            android:id="@+id/btn_dropdown_popup"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_margin="@dimen/d_10dp"
+            android:text="下拉选"
+            android:textAllCaps="false" />
+
+        <Button
+            android:id="@+id/btn_keyboard_popup"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_margin="@dimen/d_10dp"
+            android:text="软键盘弹窗"
+            android:textAllCaps="false" />
+
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+
+        <Button
+            android:id="@+id/btn_bottom_popup"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_margin="@dimen/d_10dp"
+            android:text="底部弹窗"
+            android:textAllCaps="false" />
+
+        <Button
+            android:id="@+id/btn_center_popup"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_margin="@dimen/d_10dp"
+            android:text="居中弹窗"
+            android:textAllCaps="false" />
+
+        <Button
+            android:id="@+id/btn_fullscreen_popup"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_margin="@dimen/d_10dp"
+            android:text="全屏弹窗"
+            android:textAllCaps="false" />
+
+    </LinearLayout>
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_margin="@dimen/d_10dp"
+        android:text="首页的其他功能" />
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+
+        <Button
+            android:id="@+id/btn_Login"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_margin="@dimen/d_10dp"
+            android:text="去登录页面" />
+
+        <Button
+            android:id="@+id/btn_push_id"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_margin="@dimen/d_10dp"
+            android:text="获取PushId"
+            android:textAllCaps="false" />
+
+
+        <Button
+            android:id="@+id/btn_news"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_margin="@dimen/d_10dp"
+            android:text="Pager页面"
+            android:textAllCaps="false" />
+
+    </LinearLayout>
+
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+
+        <Button
+            android:id="@+id/btn_permission_camera"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_margin="@dimen/d_10dp"
+            android:text="获取相机权限"
+            android:textAllCaps="false" />
+
+        <Button
+            android:id="@+id/btn_permission_phone"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_margin="@dimen/d_10dp"
+            android:text="获取电话权限"
+            android:textAllCaps="false" />
+
+    </LinearLayout>
+
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+
+        <Button
+            android:id="@+id/btn_choose_camera"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_margin="@dimen/d_10dp"
+            android:text="选择相机"
+            android:textAllCaps="false" />
+
+        <Button
+            android:id="@+id/btn_choose_album"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_margin="@dimen/d_10dp"
+            android:text="选择相册"
+            android:textAllCaps="false" />
+
+    </LinearLayout>
+
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+
+        <Button
+            android:id="@+id/btn_deep_link"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_margin="@dimen/d_10dp"
+            android:text="Navigation Deep Link"
+            android:textAllCaps="false" />
+
+        <Button
+            android:id="@+id/btn_load_shatter"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_margin="@dimen/d_10dp"
+            android:text="加载Shatter"
+            android:textAllCaps="false" />
+
+    </LinearLayout>
+
+    <LinearLayout
+        android:id="@+id/fl_content"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical" >
+
+    </LinearLayout>
+
+</LinearLayout>

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


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


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


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


BIN
app/src/main/res/mipmap-xxhdpi/done.webp


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


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


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


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


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

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <color name="purple_200">#FFBB86FC</color>
+    <color name="purple_500">#FF6200EE</color>
+    <color name="purple_700">#FF3700B3</color>
+    <color name="teal_200">#FF03DAC5</color>
+    <color name="teal_700">#FF018786</color>
+    <color name="black">#FF000000</color>
+    <color name="white">#FFFFFFFF</color>
+</resources>

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

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

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

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

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

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

+ 17 - 0
app/src/test/java/com/newki/template/ExampleUnitTest.kt

@@ -0,0 +1,17 @@
+package com.newki.template
+
+import org.junit.Test
+
+import org.junit.Assert.*
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+class ExampleUnitTest {
+      @Test
+      fun addition_isCorrect() {
+            assertEquals(10, 5 + 5)
+      }
+}

+ 25 - 0
build.gradle.kts

@@ -0,0 +1,25 @@
+buildscript {
+
+    repositories {
+        maven("https://maven.aliyun.com/nexus/content/groups/public/")
+        maven("https://maven.aliyun.com/nexus/content/repositories/jcenter")
+        maven("https://maven.aliyun.com/nexus/content/repositories/google")
+        maven("https://maven.aliyun.com/nexus/content/repositories/gradle-plugin")
+        maven("https://jitpack.io")
+        google()
+        mavenCentral()
+        jcenter()
+    }
+
+    dependencies {
+        classpath("com.android.tools.build:gradle:8.1.3")
+        //其他插件依赖
+        classpath(VersionAndroidX.Hilt.hiltPlugin)
+    }
+
+}
+
+tasks.register("clean", Delete::class) {
+    delete(rootProject.buildDir)
+}
+

+ 34 - 0
buildSrc/build.gradle.kts

@@ -0,0 +1,34 @@
+plugins {
+    `kotlin-dsl`
+}
+
+repositories {
+    google()
+    mavenCentral()
+}
+
+dependencies {
+    implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.22")
+    implementation("com.android.tools.build:gradle:8.1.3")
+}
+
+val compileKotlin: org.jetbrains.kotlin.gradle.tasks.KotlinCompile by tasks
+compileKotlin.kotlinOptions {
+    jvmTarget = "17"
+}
+
+configurations.all {
+    resolutionStrategy.force("androidx.fragment:fragment:1.5.4")
+    resolutionStrategy.force("androidx.fragment:fragment-ktx:1.5.4")
+    resolutionStrategy.force("androidx.transition:transition:1.4.1")
+    resolutionStrategy.force("org.jetbrains.kotlin:kotlin-stdlib:1.8.22")
+    resolutionStrategy.force("org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.22")
+    resolutionStrategy.force("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.22")
+    resolutionStrategy.force("org.jetbrains.kotlin:kotlin-reflect:1.8.22")
+    resolutionStrategy.force("org.jetbrains:annotations:23.0.0")
+    resolutionStrategy.force("org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.7.1")
+    resolutionStrategy.force("com.google.code.gson:gson:2.10.1")
+    resolutionStrategy.force("com.google.dagger:dagger:2.45")
+    resolutionStrategy.force("com.squareup:javapoet:1.13.0")
+    resolutionStrategy.force("com.squareup:javawriter:2.5.0")
+}

+ 264 - 0
buildSrc/src/main/kotlin/DefaultGradlePlugin.kt

@@ -0,0 +1,264 @@
+import com.android.build.gradle.LibraryExtension
+import com.android.build.gradle.internal.dsl.BaseAppModuleExtension
+import org.gradle.api.JavaVersion
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.kotlin.dsl.dependencies
+import org.gradle.kotlin.dsl.project
+import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions
+import org.jetbrains.kotlin.gradle.plugin.KaptExtension
+
+/**
+ * @author Newki
+ *
+ * 默认的配置实现,支持 library 和 application 级别,根据子组件的类型自动判断
+ */
+open class DefaultGradlePlugin : Plugin<Project> {
+
+    override fun apply(project: Project) {
+        setProjectConfig(project)
+        setConfigurations(project)
+    }
+
+    //项目配置
+    private fun setProjectConfig(project: Project) {
+        val isApplicationModule = project.plugins.hasPlugin("com.android.application")
+
+        if (isApplicationModule) {
+            // 处理 com.android.application 模块逻辑
+            println("===> Handle Project Config by [com.android.application] Logic")
+            setProjectConfigByApplication(project)
+        } else {
+            // 处理 com.android.library 模块逻辑
+            println("===> Handle Project Config by [com.android.library] Logic")
+            setProjectConfigByLibrary(project)
+        }
+    }
+
+    //指定依赖版本
+    private fun setConfigurations(project: Project) {
+        //配置ARouter的Kapt配置
+        project.configureKapt()
+
+        project.configurations.all {
+            resolutionStrategy.force("androidx.fragment:fragment:1.5.4")
+            resolutionStrategy.force("androidx.fragment:fragment-ktx:1.5.4")
+            resolutionStrategy.force("androidx.transition:transition:1.4.1")
+            resolutionStrategy.force("org.jetbrains.kotlin:kotlin-stdlib:1.8.22")
+            resolutionStrategy.force("org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.22")
+            resolutionStrategy.force("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.22")
+            resolutionStrategy.force("org.jetbrains.kotlin:kotlin-reflect:1.8.22")
+            resolutionStrategy.force("org.jetbrains:annotations:23.0.0")
+            resolutionStrategy.force("org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.7.1")
+            resolutionStrategy.force("com.google.code.gson:gson:2.10.1")
+            resolutionStrategy.force("com.google.dagger:dagger:2.45")
+            resolutionStrategy.force("com.squareup:javapoet:1.13.0")
+            resolutionStrategy.force("com.squareup:javawriter:2.5.0")
+        }
+    }
+
+    //设置 library 的相关配置
+    private fun setProjectConfigByLibrary(project: Project) {
+        //添加插件
+        project.apply {
+            plugin("kotlin-android")
+            plugin("kotlin-kapt")
+            plugin("org.jetbrains.kotlin.android")
+            plugin("dagger.hilt.android.plugin")
+        }
+
+        project.library().apply {
+
+            compileSdk = BuildConfig.compileSdk
+
+            defaultConfig {
+                minSdk = BuildConfig.minSdk
+                testInstrumentationRunner = BuildConfig.testInstrumentationRunner
+                vectorDrawables {
+                    useSupportLibrary = true
+                }
+                ndk {
+                    //常用构建目标 'x86_64','armeabi-v7a','arm64-v8a'
+                    abiFilters.addAll(arrayListOf("armeabi-v7a", "arm64-v8a"))
+                }
+                multiDexEnabled = true
+            }
+
+            compileOptions {
+                sourceCompatibility = JavaVersion.VERSION_17
+                targetCompatibility = JavaVersion.VERSION_17
+            }
+
+            kotlinOptions {
+                jvmTarget = "17"
+            }
+
+            buildFeatures {
+                buildConfig = true
+                viewBinding = true
+            }
+
+            packaging {
+                resources {
+                    excludes += "/META-INF/{AL2.0,LGPL2.1}"
+                }
+            }
+
+        }
+
+        //默认 library 的依赖
+        project.dependencies {
+            hilt()
+            test()
+            appcompat()
+            lifecycle()
+            kotlin()
+            widgetLayout()
+
+            if (isLibraryNeedService()) {
+                //依赖 Service 服务
+                implementation(project(":cs-service"))
+            }
+        }
+
+    }
+
+    //设置 application 的相关配置
+    private fun setProjectConfigByApplication(project: Project) {
+        //添加插件
+        project.apply {
+            plugin("kotlin-android")
+            plugin("kotlin-kapt")
+            plugin("org.jetbrains.kotlin.android")
+            plugin("dagger.hilt.android.plugin")
+        }
+
+        project.application().apply {
+            compileSdk = BuildConfig.compileSdk
+
+            defaultConfig {
+                minSdk = BuildConfig.minSdk
+                targetSdk = BuildConfig.targetSdk
+                versionCode = BuildConfig.versionCode
+                versionName = BuildConfig.versionName
+                testInstrumentationRunner = BuildConfig.testInstrumentationRunner
+                vectorDrawables {
+                    useSupportLibrary = true
+                }
+                ndk {
+                    //常用构建目标 'x86_64','armeabi-v7a','arm64-v8a'
+                    abiFilters.addAll(arrayListOf("armeabi-v7a", "arm64-v8a"))
+                }
+                multiDexEnabled = true
+            }
+
+            compileOptions {
+                sourceCompatibility = JavaVersion.VERSION_17
+                targetCompatibility = JavaVersion.VERSION_17
+            }
+
+            // 设置 Kotlin JVM 目标版本
+            kotlinOptions {
+                jvmTarget = "17"
+            }
+
+            buildFeatures {
+                buildConfig = true
+                viewBinding = true
+            }
+
+            packaging {
+                resources {
+                    excludes += "/META-INF/{AL2.0,LGPL2.1}"
+                }
+            }
+
+            signingConfigs {
+                create("release") {
+                    keyAlias = SigningConfigs.key_alias
+                    keyPassword = SigningConfigs.key_password
+                    storeFile = project.rootDir.resolve(SigningConfigs.store_file)
+                    storePassword = SigningConfigs.store_password
+                    enableV1Signing = true
+                    enableV2Signing = true
+                    enableV3Signing = true
+                    enableV4Signing = true
+                }
+            }
+
+            buildTypes {
+                release {
+                    isDebuggable = false    //是否可调试
+                    isMinifyEnabled = true  //是否启用混淆
+                    isShrinkResources = true   //是否移除无用的resource文件
+                    isJniDebuggable = false // 是否打开jniDebuggable开关
+
+                    proguardFiles(
+                        getDefaultProguardFile("proguard-android-optimize.txt"),
+                        "proguard-rules.pro"
+                    )
+                    signingConfig = signingConfigs.findByName("release")
+                }
+                debug {
+                    isDebuggable = true
+                    isMinifyEnabled = false
+                    isShrinkResources = false
+                    isJniDebuggable = true
+                }
+            }
+
+        }
+
+        //默认 application 的依赖
+        project.dependencies {
+            hilt()
+            test()
+            appcompat()
+            lifecycle()
+            kotlin()
+            widgetLayout()
+
+            //依赖 Service 服务
+            implementation(project(":cs-service"))
+        }
+
+    }
+
+    //根据组件模块的类型给出不同的对象去配置
+    private fun Project.library(): LibraryExtension {
+        return extensions.getByType(LibraryExtension::class.java)
+    }
+
+    private fun Project.application(): BaseAppModuleExtension {
+        return extensions.getByType(BaseAppModuleExtension::class.java)
+    }
+
+    // Application 级别 - 扩展函数来设置 KotlinOptions
+    private fun BaseAppModuleExtension.kotlinOptions(action: KotlinJvmOptions.() -> Unit) {
+        (this as org.gradle.api.plugins.ExtensionAware).extensions.configure(
+            "kotlinOptions",
+            action
+        )
+    }
+
+    // Library 级别 - 扩展函数来设置 KotlinOptions
+    private fun LibraryExtension.kotlinOptions(action: KotlinJvmOptions.() -> Unit) {
+        (this as org.gradle.api.plugins.ExtensionAware).extensions.configure(
+            "kotlinOptions",
+            action
+        )
+    }
+
+    //配置 Project 的 kapt
+    private fun Project.configureKapt() {
+        this.extensions.findByType(KaptExtension::class.java)?.apply {
+            arguments {
+                arg("AROUTER_MODULE_NAME", name)
+            }
+        }
+    }
+
+    //Library模块是否需要依赖底层 Service 服务,一般子 Module 模块或者 Module-api 模块会依赖到
+    protected open fun isLibraryNeedService(): Boolean = false
+
+}

+ 139 - 0
buildSrc/src/main/kotlin/Dependencies.kt

@@ -0,0 +1,139 @@
+import org.gradle.api.artifacts.dsl.DependencyHandler
+
+/**
+ *  @author Newki
+ *
+ * 通过扩展函数的方式导入功能模块的全部依赖
+ * 可以自行随意添加或更改
+ */
+fun DependencyHandler.appcompat() {
+    api(VersionAndroidX.appcompat)
+    api(VersionAndroidX.supportV4)
+    api(VersionAndroidX.coreKtx)
+    api(VersionAndroidX.activityKtx)
+    api(VersionAndroidX.fragment)
+    api(VersionAndroidX.fragmentKtx)
+    api(VersionAndroidX.multidex)
+    api(VersionAndroidX.documentFile)
+}
+
+//生命周期监听
+fun DependencyHandler.lifecycle() {
+    api(VersionAndroidX.Lifecycle.livedata)
+    api(VersionAndroidX.Lifecycle.liveDataKtx)
+    api(VersionAndroidX.Lifecycle.runtime)
+    api(VersionAndroidX.Lifecycle.runtimeKtx)
+
+    api(VersionAndroidX.Lifecycle.viewModel)
+    api(VersionAndroidX.Lifecycle.viewModelKtx)
+    api(VersionAndroidX.Lifecycle.viewModelSavedState)
+
+    kapt(VersionAndroidX.Lifecycle.compiler)
+}
+
+//Kotlin与协程
+fun DependencyHandler.kotlin() {
+    api(VersionKotlin.stdlib)
+    api(VersionKotlin.reflect)
+    api(VersionKotlin.stdlibJdk7)
+    api(VersionKotlin.stdlibJdk8)
+
+    api(VersionKotlin.Coroutines.android)
+    api(VersionKotlin.Coroutines.core)
+}
+
+//依赖注入
+fun DependencyHandler.hilt() {
+    implementation(VersionAndroidX.Hilt.hiltAndroid)
+    implementation(VersionAndroidX.Hilt.javapoet)
+    implementation(VersionAndroidX.Hilt.javawriter)
+    kapt(VersionAndroidX.Hilt.hiltCompiler)
+}
+
+//测试Test依赖
+fun DependencyHandler.test() {
+    testImplementation(VersionTesting.junit)
+    androidTestImplementation(VersionTesting.androidJunit)
+    androidTestImplementation(VersionTesting.espresso)
+}
+
+//常用的布局控件
+fun DependencyHandler.widgetLayout() {
+    api(VersionAndroidX.constraintlayout)
+    api(VersionAndroidX.cardView)
+    api(VersionAndroidX.recyclerView)
+    api(VersionThirdPart.baseRecycleViewHelper)
+    api(VersionAndroidX.material)
+    api(VersionAndroidX.ViewPager.viewpager)
+    api(VersionAndroidX.ViewPager.viewpager2)
+}
+
+//Work任务
+fun DependencyHandler.work() {
+    api(VersionAndroidX.Work.runtime)
+    api(VersionAndroidX.Work.runtime_ktx)
+}
+
+//KV存储
+fun DependencyHandler.dataStore() {
+    api(VersionAndroidX.DataStore.preferences)
+    api(VersionAndroidX.DataStore.core)
+}
+
+//Navigation路由
+fun DependencyHandler.navigation() {
+    api(VersionAndroidX.Navigation.fragmentKtx)
+    api(VersionAndroidX.Navigation.uiKtx)
+    api(VersionAndroidX.Navigation.dynamic)
+    api(VersionAndroidX.Navigation.dynamicRuntime)
+    androidTestImplementation(VersionAndroidX.Navigation.testing)
+}
+
+//网络请求
+fun DependencyHandler.retrofit() {
+    api(VersionThirdPart.Retrofit.core)
+    implementation(VersionThirdPart.Retrofit.convertGson)
+    api(VersionThirdPart.Retrofit.gson)
+    api(VersionThirdPart.Retrofit.okio)
+    api(VersionThirdPart.gsonFactory)
+}
+
+//图片加载
+fun DependencyHandler.glide() {
+    implementation(VersionThirdPart.Glide.core)
+    implementation(VersionThirdPart.Glide.annotation)
+    implementation(VersionThirdPart.Glide.integration)
+    kapt(VersionThirdPart.Glide.compiler)
+    implementation(VersionThirdPart.gifDrawable)
+}
+
+//多媒体相机相册
+fun DependencyHandler.imageSelector() {
+    implementation(VersionThirdPart.ImageSelector.core)
+    implementation(VersionThirdPart.ImageSelector.compress)
+    implementation(VersionThirdPart.ImageSelector.ucrop)
+}
+
+//弹窗
+fun DependencyHandler.xpopup() {
+    implementation(VersionThirdPart.XPopup.core)
+    implementation(VersionThirdPart.XPopup.picker)
+    api(VersionThirdPart.XPopup.easyAdapter)
+}
+
+//下拉刷新
+fun DependencyHandler.refresh() {
+    api(VersionThirdPart.SmartRefresh.core)
+    api(VersionThirdPart.SmartRefresh.classicsHeader)
+}
+
+
+//fun DependencyHandler.compose() {
+//    implementation(VersionAndroidX.Compose.composeUi)
+//    implementation(VersionAndroidX.Compose.composeMaterial)
+//    implementation(VersionAndroidX.Compose.composeRuntime)
+//    implementation(VersionAndroidX.Compose.composeUiTooling)
+//    implementation(VersionAndroidX.Compose.composeUiGraphics)
+//    implementation(VersionAndroidX.Compose.composeUiToolingPreview)
+//}
+

+ 31 - 0
buildSrc/src/main/kotlin/DependencyHandleExt.kt

@@ -0,0 +1,31 @@
+import org.gradle.api.artifacts.dsl.DependencyHandler
+
+/**
+ * @author Newki
+ *
+ * 给 DependencyHandler 默认添加扩展方法用于代码中添加依赖- 此类无需更改
+ */
+fun DependencyHandler.api(dependency: Any) {
+    add("api", dependency)
+}
+
+fun DependencyHandler.implementation(dependency: Any) {
+    add("implementation", dependency)
+}
+
+fun DependencyHandler.testImplementation(dependency: Any) {
+    add("testImplementation", dependency)
+}
+
+fun DependencyHandler.androidTestImplementation(dependency: Any) {
+    add("androidTestImplementation", dependency)
+}
+
+fun DependencyHandler.debugImplementation(dependency: Any) {
+    add("debugImplementation", dependency)
+}
+
+fun DependencyHandler.kapt(dependency: Any) {
+    add("kapt", dependency)
+}
+

+ 8 - 0
buildSrc/src/main/kotlin/ModuleGradlePlugin.kt

@@ -0,0 +1,8 @@
+// 一般子 Module 模块或者 Module-api 模块会用到此 Gradle 模块
+class ModuleGradlePlugin : DefaultGradlePlugin() {
+
+    //允许 Library 模块依赖到 Service 模块
+    override fun isLibraryNeedService(): Boolean {
+        return true
+    }
+}

+ 46 - 0
buildSrc/src/main/kotlin/ProjectConfig.kt

@@ -0,0 +1,46 @@
+/**
+ * @author Newki
+ *
+ * 项目编译配置与AppId配置
+ */
+object BuildConfig {
+    const val minSdk = 21
+    const val compileSdk = 34
+    const val targetSdk = 33
+
+    const val versionCode = 100
+    const val versionName = "1.0.0"
+
+    const val applicationId = "com.newki.template"
+    const val testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+
+}
+
+//项目的一些配置参数,可以在Service组件中写入到buildConfigField
+object ProjectConfig {
+
+//    const val isReleaseServer = true       //服务器环境, 正式环境设置 true
+    const val isReleaseServer = false      //服务器环境,测试环境设置 false
+
+    // 测试环境的域名
+    const val baseUrl_dev = "http://www.wanandroid.com/"
+
+    // 正式环境的域名
+    const val baseUrl_rel = "https://www.wanandroid.com/"
+}
+
+//签名文件信息配置
+object SigningConfigs {
+    //密钥文件路径
+    const val store_file = "key/yybusiness.jks"
+
+    //密钥密码
+    const val store_password = "123456"
+
+    //密钥别名
+    const val key_alias = "guadou"
+
+    //别名密码
+    const val key_password = "123456"
+}
+

+ 162 - 0
buildSrc/src/main/kotlin/VersionAndroidX.kt

@@ -0,0 +1,162 @@
+/**
+ * @author Newki
+ *
+ * AndroidX Support Google 官方的包
+ */
+object VersionAndroidX {
+
+    //appcompat中默认引入了很多库,比如activity库、fragment库、core库、annotation库、drawerLayout库、appcompat-resources等
+    const val appcompat = "androidx.appcompat:appcompat:1.6.1"
+
+    //support兼容库
+    const val supportV4 = "androidx.legacy:legacy-support-v4:1.0.0"
+
+    //core包+ktx扩展函数
+    const val coreKtx = "androidx.core:core-ktx:1.9.0"
+
+    //activity+ktx扩展函数
+    const val activityKtx = "androidx.activity:activity-ktx:1.8.0"
+
+    //fragment+ktx扩展函数
+    const val fragment = "androidx.fragment:fragment:1.5.4"
+    const val fragmentKtx = "androidx.fragment:fragment-ktx:1.5.4"
+
+    //约束布局
+    const val constraintlayout = "androidx.constraintlayout:constraintlayout:2.1.4"
+
+    //卡片控件
+    const val cardView = "androidx.cardview:cardview:1.0.0"
+
+    //recyclerView
+    const val recyclerView = "androidx.recyclerview:recyclerview:1.2.1"
+
+    //材料设计
+    const val material = "com.google.android.material:material:1.11.0"
+
+    //分包
+    const val multidex = "androidx.multidex:multidex:2.0.1"
+
+    //文档管理
+    const val documentFile = "androidx.documentfile:documentfile:1.0.1"
+
+
+    object ViewPager {
+        //viewpager
+        const val viewpager = "androidx.viewpager:viewpager:1.0.0"
+
+        //viewpager2
+        const val viewpager2 = "androidx.viewpager2:viewpager2:1.1.0-beta01"
+    }
+
+    object Hilt {
+        private const val version = "2.45"
+
+        const val hiltAndroid = "com.google.dagger:hilt-android:$version"
+        const val hiltCompiler = "com.google.dagger:hilt-android-compiler:$version"
+        const val hiltPlugin = "com.google.dagger:hilt-android-gradle-plugin:$version"
+
+        const val javapoet = "com.squareup:javapoet:1.13.0"
+        const val javawriter = "com.squareup:javawriter:2.5.0"
+
+        // compose - Hilt
+//        const val navigation_compose = "androidx.hilt:hilt-navigation-compose:1.1.0-alpha01"
+    }
+
+    object Lifecycle {
+        private const val version = "2.7.0"
+
+        const val livedata = "androidx.lifecycle:lifecycle-livedata:$version"
+        const val liveDataKtx = "androidx.lifecycle:lifecycle-livedata-ktx:$version"
+
+        const val runtime = "androidx.lifecycle:lifecycle-runtime:$version"
+        const val runtimeKtx = "androidx.lifecycle:lifecycle-runtime-ktx:$version"
+
+        //ViewModel处理
+        const val viewModel = "androidx.lifecycle:lifecycle-viewmodel:$version"
+        const val viewModelKtx = "androidx.lifecycle:lifecycle-viewmodel-ktx:$version"
+        const val viewModelSavedState = "androidx.lifecycle:lifecycle-viewmodel-savedstate:$version"
+
+        //编译处理器
+        const val compiler = "androidx.lifecycle:lifecycle-compiler:$version"
+    }
+
+
+    object Navigation {
+        private const val version = "2.5.3"
+
+        const val fragmentKtx = "androidx.navigation:navigation-fragment-ktx:$version"
+        const val uiKtx = "androidx.navigation:navigation-ui-ktx:$version"
+
+        const val dynamic = "androidx.navigation:navigation-dynamic-features-fragment:$version"
+        const val dynamicRuntime = "androidx.navigation:navigation-dynamic-features-runtime:$version"
+
+        const val testing = "androidx.navigation:navigation-testing:$version"
+
+    }
+
+    object DataStore {
+        private const val version = "1.1.0-beta01"
+        const val preferences = "androidx.datastore:datastore-preferences:$version"
+        const val core = "androidx.datastore:datastore-core:$version"
+    }
+
+    object Work {
+        private const val version = "2.8.1"
+        const val runtime = "androidx.work:work-runtime:$version"
+        const val runtime_ktx = "androidx.work:work-runtime-ktx:$version"
+    }
+
+
+    // 可自行添加 Room Paging  Camera  Compose 等
+//     object Compose {
+//         private const val composeVersion = "1.4.3"
+//         private const val composeMaterial3Version = "1.1.1"
+//
+//         const val composeMaterial = "androidx.compose.material3:material3:$composeMaterial3Version"
+//         const val composeUi = "androidx.compose.ui:ui:$composeVersion"
+//         const val composeUiGraphics = "androidx.compose.ui:ui-graphics:$composeVersion"
+//         const val composeUiTooling = "androidx.compose.ui:ui-tooling:$composeVersion"
+//         const val composeUiToolingPreview = "androidx.compose.ui:ui-tooling-preview:$composeVersion"
+//         const val composeRuntime = "androidx.compose.runtime:runtime:$composeVersion"
+//
+//     }
+
+//    object Room {
+//        private const val version = "2.5.1"
+//
+//        const val runtime = "androidx.room:room-runtime:$version"
+//
+//        const val compiler = "androidx.room:room-compiler:$version"
+//
+//        const val ktx = "androidx.room:room-ktx:$version"
+//
+//        const val guava = "androidx.room:room-guava:$version"
+//
+//        const val testing = "androidx.room:room-testing:$version"
+//    }
+
+//    object Camera {
+//        private const val version = "1.0.1"
+//
+//        const val camera2 = "androidx.camera:camera-camera2:$version"
+//
+//        const val core = "androidx.camera:camera-core:$version"
+//
+//        const val lifecycle = "androidx.camera:camera-lifecycle:$version"
+//
+//        const val view = "androidx.camera:camera-view:1.0.0-alpha27"
+//    }
+
+//    object Paging {
+//        private const val version = "3.2.0-alpha05"
+//        const val runtime = "androidx.paging:paging-runtime:$version"
+//        const val runtimeKtx = "androidx.paging:paging-runtime-ktx:$version"
+//
+//        const val guava = "androidx.paging:paging-guava:$version"
+//
+//        const val testingCommon = "androidx.paging:paging-common:$version"
+//        const val testingCommonKtx = "androidx.paging:paging-common-ktx:$version"
+//
+//        const val compose = "androidx.paging:paging-compose:1.0.0-alpha19"
+//    }
+}

+ 21 - 0
buildSrc/src/main/kotlin/VersionKotlin.kt

@@ -0,0 +1,21 @@
+/**
+ *  @author Newki
+ *
+ *  Kotlin 相关依赖以及 Kotlin 的协程相关
+ */
+object VersionKotlin {
+    private var version = "1.8.22"
+
+    var stdlib = "org.jetbrains.kotlin:kotlin-stdlib:$version"
+    var reflect = "org.jetbrains.kotlin:kotlin-reflect:$version"
+    val stdlibJdk7 = "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$version"
+    val stdlibJdk8 = "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$version"
+
+    //协程
+    object Coroutines {
+        private const val version = "1.7.1"
+        const val core = "org.jetbrains.kotlinx:kotlinx-coroutines-core:$version"
+        const val android = "org.jetbrains.kotlinx:kotlinx-coroutines-android:$version"
+    }
+
+}

+ 10 - 0
buildSrc/src/main/kotlin/VersionTesting.kt

@@ -0,0 +1,10 @@
+/**
+ *  @author Newki
+ *
+ *  测试相关的依赖
+ */
+object VersionTesting {
+    const val junit = "junit:junit:4.13.2"
+    const val androidJunit = "androidx.test.ext:junit:1.1.5"
+    const val espresso = "androidx.test.espresso:espresso-core:3.5.1"
+}

+ 97 - 0
buildSrc/src/main/kotlin/VersionThirdPart.kt

@@ -0,0 +1,97 @@
+/**
+ *  @author Newki
+ *
+ *  第三方依赖包
+ */
+object VersionThirdPart {
+
+    //网络请求 retrofit
+    object Retrofit {
+        private const val version = "2.9.0"
+
+        //内置了OkHttp 3.14.9 与 okio 3.1.0
+        const val core = "com.squareup.retrofit2:retrofit:$version"
+
+        //gson转换器
+        const val convertGson = "com.squareup.retrofit2:converter-gson:$version"
+
+        //指定Gson版本
+        const val gson = "com.google.code.gson:gson:2.10.1"
+        const val okio = "com.squareup.okio:okio:3.0.0"
+    }
+
+    //图片加载框架
+    object Glide {
+        private const val version = "4.11.0"
+
+        const val core = "com.github.bumptech.glide:glide:$version"
+        const val annotation = "com.github.bumptech.glide:annotations:$version"
+        const val integration = "com.github.bumptech.glide:okhttp3-integration:$version"
+        const val compiler = "com.github.bumptech.glide:compiler:$version"
+    }
+
+    //多媒体 https://github.com/LuckSiege/PictureSelector
+    object ImageSelector {
+        private const val version = "v3.11.2"
+
+        const val core = "io.github.lucksiege:pictureselector:$version"
+        const val compress = "io.github.lucksiege:compress:$version"
+        const val ucrop = "io.github.lucksiege:ucrop:$version"
+    }
+
+    //第三方 dialog 项目地址:https://github.com/li-xiaojun/XPopup
+    object XPopup {
+        private const val version = "2.10.0"
+        private const val picker_version = "1.0.1"
+
+        //弹窗
+        const val core = "com.github.li-xiaojun:XPopup:$version"
+
+        //弹窗+PickerView封装
+        const val picker = "com.github.li-xiaojun:XPopupExt:$picker_version"
+
+        //简易RV-Adapter
+        const val easyAdapter = "com.github.li-xiaojun:EasyAdapter:1.2.8"
+    }
+
+    //第三方刷新布局 https://github.com/scwang90/SmartRefreshLayout
+    object SmartRefresh {
+        private const val version = "2.1.0"
+
+        //刷新布局
+        const val core = "io.github.scwang90:refresh-layout-kernel:$version"
+
+        //经典刷新头布局
+        const val classicsHeader = "io.github.scwang90:refresh-header-classics:$version"
+    }
+
+    //气泡吐司  https://github.com/getActivity/Toaster
+    const val toaster = "com.github.getActivity:Toaster:12.6"
+
+    //权限申请  https://github.com/getActivity/XXPermissions
+    const val xxPermission = "com.github.getActivity:XXPermissions:18.6"
+
+    //Gson 解析容错  https://github.com/getActivity/GsonFactory
+    const val gsonFactory = "com.github.getActivity:GsonFactory:9.5"
+
+    //第三方轮播图  https://github.com/youth5201314/banner
+    const val banner = "io.github.youth5201314:banner:2.2.3"
+
+    //RecycleView适配器工具  https://github.com/CymChad/BaseRecyclerViewAdapterHelper
+    const val baseRecycleViewHelper = "com.github.CymChad:BaseRecyclerViewAdapterHelper:3.0.4"
+
+    //LiveBus - 消息总线  https://github.com/JeremyLiao/LiveEventBus
+    const val liveEventBus = "io.github.jeremyliao:live-event-bus-x:1.8.0"
+
+    //lottie 动画
+    // 你可以使用转换工具将mp4转换成json【https://isotropic.co/video-to-lottie/】
+    // 去这里下载素材【https://lottiefiles.com/】
+    const val lottie = "com.airbnb.android:lottie:4.1.0"
+
+    //C库加载GIF图片
+    const val gifDrawable = "pl.droidsonroids.gif:android-gif-drawable:1.2.28"
+
+    //内存泄露检测工具
+    const val leakcanary = "com.squareup.leakcanary:leakcanary-android:2.7"
+
+}

+ 1 - 0
cs-baselib/.gitignore

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

+ 27 - 0
cs-baselib/build.gradle.kts

@@ -0,0 +1,27 @@
+plugins {
+    id("com.android.library")
+}
+
+// 使用自定义插件
+apply<DefaultGradlePlugin>()
+
+android {
+    namespace = "com.android.basiclib"
+}
+
+dependencies {
+    //限制范围,使用引擎类封装使用
+    retrofit()
+    dataStore()
+    imageSelector()
+    xpopup()
+    glide()
+    implementation(VersionThirdPart.toaster)
+    implementation(VersionThirdPart.xxPermission)
+
+    //不限制范围,全局使用
+    refresh()
+    work()
+
+    api(VersionThirdPart.liveEventBus)
+}

+ 0 - 0
cs-baselib/consumer-rules.pro


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

+ 24 - 0
cs-baselib/src/androidTest/java/com/base/lib/ExampleInstrumentedTest.kt

@@ -0,0 +1,24 @@
+package com.base.lib
+
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.ext.junit.runners.AndroidJUnit4
+
+import org.junit.Test
+import org.junit.runner.RunWith
+
+import org.junit.Assert.*
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+@RunWith(AndroidJUnit4::class)
+class ExampleInstrumentedTest {
+    @Test
+    fun useAppContext() {
+        // Context of the app under test.
+        val appContext = InstrumentationRegistry.getInstrumentation().targetContext
+        assertEquals("com.newki.profile", appContext.packageName)
+    }
+}

+ 84 - 0
cs-baselib/src/main/AndroidManifest.xml

@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    package="com.android.basiclib">
+
+    <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.CHANGE_WIFI_STATE" />
+
+    <!--http9.0兼容设置-->
+    <application
+        android:allowBackup="true"
+        android:networkSecurityConfig="@xml/network_security_config"
+        android:supportsRtl="true"
+        android:usesCleartextTraffic="true"
+        android:vmSafeMode="true"
+        tools:ignore="UnusedAttribute">
+
+        <!--9.0兼容http-->
+        <uses-library
+            android:name="org.apache.http.legacy"
+            android:required="false" />
+
+        <!--本项目mate配置-->
+        <meta-data
+            android:name="android.max_aspect"
+            android:value="2.4" />
+
+        <meta-data
+            android:name="android.webkit.WebView.EnableSafeBrowing"
+            android:value="true" />
+
+        <!-- 表示XXPermission项目已经适配了分区存储特性 -->
+        <meta-data
+            android:name="ScopedStorage"
+            android:value="true" />
+
+        <!--   7.1系统Uri适配     -->
+        <provider
+            android:name=".engine.permission.PermissionFileProvider"
+            android:authorities="${applicationId}.file.path.share"
+            android:exported="false"
+            android:grantUriPermissions="true">
+            <meta-data
+                android:name="android.support.FILE_PROVIDER_PATHS"
+                android:resource="@xml/permission_file_paths" />
+        </provider>
+
+    </application>
+
+    <!--   PackageManager.queryIntentActivities()兼容  -->
+    <queries>
+        <intent>
+            <action android:name="android.intent.action.SEND" />
+            <data android:mimeType="image/jpeg" />
+        </intent>
+        <intent>
+            <action android:name="android.intent.action.SEND" />
+            <data android:mimeType="text/plain" />
+        </intent>
+    </queries>
+
+    <!--  包的隐藏可见配置  -->
+    <queries>
+        <package android:name="com.facebook.katana" />
+        <package android:name="com.tencent.mm" />
+    </queries>
+
+    <!-- Android 11 使用相机适配  -->
+    <queries package="${applicationId}">
+        <intent>
+            <action android:name="android.media.action.IMAGE_CAPTURE">
+
+            </action>
+        </intent>
+        <intent>
+            <action android:name="android.media.action.ACTION_VIDEO_CAPTURE">
+
+            </action>
+        </intent>
+    </queries>
+
+</manifest>

+ 26 - 0
cs-baselib/src/main/java/com/android/basiclib/adapter/MyPager2Adapter.kt

@@ -0,0 +1,26 @@
+package com.android.basiclib.adapter
+
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.FragmentManager
+import androidx.lifecycle.Lifecycle
+import androidx.viewpager2.adapter.FragmentStateAdapter
+import com.android.basiclib.utils.log.MyLogUtils
+class MyPager2Adapter(
+    fm: FragmentManager,
+    lifecycle: Lifecycle,
+    private val fragments: List<Fragment>
+) : FragmentStateAdapter(fm, lifecycle) {
+
+    override fun getItemCount(): Int = fragments.size
+
+    override fun createFragment(position: Int): Fragment {
+        return fragments[position]
+    }
+
+    override fun getItemId(position: Int): Long {
+       val name = fragments[position].javaClass.simpleName+ position
+        val toLong = name.hashCode().toLong()
+        MyLogUtils.w("getItemId:$toLong")
+        return toLong
+    }
+}

+ 95 - 0
cs-baselib/src/main/java/com/android/basiclib/adapter/ViewPagerFragmentAdapter.kt

@@ -0,0 +1,95 @@
+package com.android.basiclib.adapter
+
+import android.os.Bundle
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.FragmentManager
+import androidx.fragment.app.FragmentStatePagerAdapter
+import java.util.*
+
+
+/**
+ * 默认Fragment->ViewPager的适配器
+ */
+class ViewPagerFragmentAdapter @JvmOverloads constructor(
+    private val fragmentManager: FragmentManager,
+    private val fragments: List<Fragment>,
+    private val pageTitles: List<String>? = null,
+    behavior: Int = 0
+) : FragmentStatePagerAdapter(fragmentManager, behavior) {
+
+    private val fragmentMap = mutableMapOf<Int, Fragment>()
+    private val fragmentPositions = hashMapOf<Int, Int>()
+
+    init {
+        for ((index, fragment) in fragments.withIndex()) {
+            fragmentMap[index] = fragment
+        }
+    }
+
+    override fun getItem(position: Int): Fragment {
+        return fragments[position]
+    }
+
+    override fun getCount(): Int {
+        return if (fragments.isEmpty()) 0 else fragments.size
+    }
+
+    override fun getPageTitle(position: Int): CharSequence {
+        return if (pageTitles == null) "" else pageTitles[position]
+    }
+
+    override fun instantiateItem(container: ViewGroup, position: Int): Any {
+
+        val fragment = super.instantiateItem(container, position) as Fragment
+
+        val id = generateUniqueId()
+        var args = fragment.arguments
+        if (args == null) {
+            args = Bundle()
+        }
+
+        args.putInt("_uuid", id)
+        fragment.arguments = args
+
+        // 存储 Fragment 的位置信息
+        fragmentPositions[id] = position
+
+        return fragment
+    }
+
+    private fun generateUniqueId(): Int {
+        // 生成唯一的 ID
+        return UUID.randomUUID().hashCode()
+    }
+
+    override fun destroyItem(container: ViewGroup, position: Int, obj: Any) {
+        super.destroyItem(container, position, obj)
+    }
+
+    override fun getItemPosition(obj: Any): Int {
+
+        val fragment = obj as Fragment
+
+        // 从 Fragment 中获取唯一的 ID
+        val args = fragment.arguments
+        if (args != null && args.containsKey("_uuid")) {
+            val id = args.getInt("_uuid")
+
+            // 根据 ID 获取 Fragment 在 Adapter 中的位置
+            val position = fragmentPositions[id]
+            return if (position != null && position == fragments.indexOf(fragment)) {
+                // Fragment 未发生变化,返回 POSITION_UNCHANGED
+                POSITION_UNCHANGED
+            } else {
+                // Fragment 发生变化,返回 POSITION_NONE
+                POSITION_NONE
+            }
+        }
+
+        // 如果不是 Fragment,则返回默认值
+        return super.getItemPosition(obj)
+    }
+
+
+}

+ 19 - 0
cs-baselib/src/main/java/com/android/basiclib/annotation/NetWork.java

@@ -0,0 +1,19 @@
+package com.android.basiclib.annotation;
+
+import com.android.basiclib.utils.NetWorkUtil;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 自定义注解,用于方法上面的,需要方法的容器注解和解注册
+ * 可以直接把网络变换通知到具体的某一个方法
+ */
+@Target(ElementType.METHOD) //定义在方法上面的注解 ,和EventBus的方式类似
+@Retention(RetentionPolicy.RUNTIME) //定义为运行时,在jvm运行的过程中通过反射获取到注解
+public @interface NetWork {
+
+    NetWorkUtil.NetworkType netWorkType() default NetWorkUtil.NetworkType.NETWORK_NO;
+}

+ 79 - 0
cs-baselib/src/main/java/com/android/basiclib/base/BaseApplication.kt

@@ -0,0 +1,79 @@
+package com.android.basiclib.base
+
+import android.app.Application
+import android.content.Context
+import android.os.Handler
+import android.os.Looper
+import androidx.multidex.MultiDex
+import com.android.basiclib.BuildConfig
+import com.android.basiclib.utils.CommUtils
+import com.android.basiclib.utils.NetWorkUtil
+import com.android.basiclib.core.BaseLibCore
+import com.android.basiclib.view.web.WebViewPoolManager
+
+
+/**
+ * 基类的Application
+ */
+open class BaseApplication : Application() {
+
+    //全局的静态Gson对象
+    companion object {
+
+        lateinit var networkType: NetWorkUtil.NetworkType   //此变量会在网络监听中被动态赋值
+
+        //检查当前是否有网络
+        fun checkHasNet(): Boolean {
+            return networkType != NetWorkUtil.NetworkType.NETWORK_NO
+        }
+
+        var mCurAppVersion = "" //当前App的版本号 用于网络请求全局添加请求头
+
+        var timeStampOffset = 0L  //设备时间与服务器时间的差值
+
+        var isUserLogin = false   //当前用户是否登录
+
+    }
+
+    override fun onCreate() {
+        super.onCreate()
+
+        //获取到全局的网络状态
+        networkType = NetWorkUtil.getNetworkType(this@BaseApplication.applicationContext)
+
+        //全局的 CommUtil的初始化
+        BaseLibCore.init(this, Handler(Looper.getMainLooper()), android.os.Process.myTid())
+
+        //赋值App全局版本号
+        mCurAppVersion = packageManager.getPackageInfo(CommUtils.getContext().packageName, 0).versionName
+
+        //网络监听
+        BaseLibCore.registerNetworkObserver(this)
+
+        //初始化WebView缓存容器
+        WebViewPoolManager.prepare(this)
+    }
+
+
+    //Dex分包
+    override fun attachBaseContext(base: Context?) {
+        super.attachBaseContext(base)
+        MultiDex.install(this)
+    }
+
+    override fun onTerminate() {
+        super.onTerminate()
+        BaseLibCore.unregisterNetworkObserver(this)
+
+        //销毁WebView缓存容器
+        WebViewPoolManager.destroy()
+    }
+
+    /**
+     * 设置用户是否登录
+     */
+    protected fun setUserIsLogin(isLogin: Boolean) {
+        isUserLogin = isLogin
+    }
+
+}

+ 90 - 0
cs-baselib/src/main/java/com/android/basiclib/base/BaseRetrofitClient.kt

@@ -0,0 +1,90 @@
+package com.android.basiclib.base
+
+import com.android.basiclib.utils.interceptor.HeadInterceptor
+import com.android.basiclib.utils.interceptor.LoggingInterceptor
+import com.hjq.gson.factory.GsonFactory
+import okhttp3.OkHttpClient
+import retrofit2.Retrofit
+import retrofit2.converter.gson.GsonConverterFactory
+import java.util.concurrent.TimeUnit
+
+
+/**
+ * 基类的Retrofit对象
+ */
+abstract class BaseRetrofitClient {
+
+    companion object {
+        //静态常量
+        const val TIME_OUT = 30
+    }
+
+    private val client: OkHttpClient
+        get() {
+            val builder = OkHttpClient.Builder()
+
+            //可以添加日志拦截和参数拦截
+            builder
+                .addInterceptor(HeadInterceptor())
+                .addInterceptor(LoggingInterceptor())
+                .connectTimeout(TIME_OUT.toLong(), TimeUnit.SECONDS)
+                .writeTimeout(TIME_OUT.toLong(), TimeUnit.SECONDS)
+                .readTimeout(TIME_OUT.toLong(), TimeUnit.SECONDS)
+
+//            if (needHttpCache()) {
+//                handleBuilder(builder)
+//            }
+
+            return builder.build()
+        }
+
+//    open protected fun needHttpCache(): Boolean = false
+
+//    /**
+//     * 处理缓存
+//     */
+//    private fun handleBuilder(builder: OkHttpClient.Builder) {
+//
+//        val httpCacheDirectory = File(CommUtils.getContext().cacheDir, "responses") //Http缓存目录
+//        val cacheSize = 10 * 1024 * 1024L // 10 MiB
+//        val cache = Cache(httpCacheDirectory, cacheSize)     //Http缓存对象
+//        builder.cache(cache)
+//            .cookieJar(cookieJar)
+//            .addInterceptor { chain ->
+//                var request = chain.request()
+//
+//                if (!NetWorkUtil.isAvailable(CommUtils.getContext())) {
+//                    request = request.newBuilder()
+//                        .cacheControl(CacheControl.FORCE_CACHE)
+//                        .build()
+//                }
+//
+//                val response = chain.proceed(request)
+//                //有网络读取接口,没有网络展示缓存
+//                if (!NetWorkUtil.isAvailable(CommUtils.getContext())) {
+//                    val maxAge = 60 * 60
+//                    response.newBuilder()
+//                        .removeHeader("Pragma")
+//                        .header("Cache-Control", "public, max-age=$maxAge")
+//                        .build()
+//                } else {
+//                    val maxStale = 60 * 60 * 24 * 28 // tolerate 4-weeks stale
+//                    response.newBuilder()
+//                        .removeHeader("Pragma")
+//                        .header("Cache-Control", "public, only-if-cached, max-stale=$maxStale")
+//                        .build()
+//                }
+//                response
+//            }
+//    }
+
+    fun <S> getService(serviceClass: Class<S>, baseUrl: String): S {
+        return Retrofit.Builder()
+            .client(client)
+            .addConverterFactory(GsonConverterFactory.create(GsonFactory.getSingletonGson()))  //使用框架进行容错
+            .baseUrl(baseUrl)
+            .build()
+            .create(serviceClass)
+    }
+
+}

+ 188 - 0
cs-baselib/src/main/java/com/android/basiclib/base/activity/AbsActivity.kt

@@ -0,0 +1,188 @@
+package com.android.basiclib.base.activity
+
+import android.app.Activity
+import android.content.Context
+import android.content.Intent
+import android.content.res.Configuration
+import android.content.res.Resources
+import android.graphics.Color
+import android.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+import com.android.basiclib.receiver.ConnectivityReceiver
+import com.android.basiclib.utils.ActivityManage
+import com.android.basiclib.utils.StatusBarUtils
+
+
+/**
+ * 最底层的Activity,给其他Activity继承,一般不直接用这个
+ */
+abstract class AbsActivity : AppCompatActivity(), ConnectivityReceiver.ConnectivityReceiverListener {
+
+    /**
+     * 获取Context对象
+     */
+    protected lateinit var mActivity: Activity
+    protected lateinit var mContext: Context
+
+
+    abstract fun setContentView()
+
+    abstract fun initViewModel()
+
+    abstract fun startObserve()
+
+    abstract fun init(savedInstanceState: Bundle?)
+
+    /**
+     * 从intent中解析数据,具体子类来实现
+     */
+    protected open fun getDataFromIntent(intent: Intent) {}
+
+    /**
+     * 设置顶部状态栏的颜色(默认为白色背景-黑色文字)
+     */
+    protected fun setStatusBarColor(): Int {
+        //如果状态栏文字能变黑那么背景设置为白色,否则返回背景灰色文本默认为白色
+        return if (StatusBarUtils.setStatusBarBlackText(this)) {
+            Color.WHITE
+        } else {
+            Color.parseColor("#B0B0B0")
+        }
+    }
+
+    /**
+     * 动态的设置状态栏颜色
+     * 当颜色为白色的时候显示白底黑字
+     * 其他颜色为其他颜色底白色字
+     * 一般由子类重写
+     */
+    fun setStatusBarColor(color: Int) {
+
+        if (color == Color.WHITE) {
+            //变黑色文字成功
+            if (StatusBarUtils.setStatusBarBlackText(this)) {
+                StatusBarUtils.setColor(this, Color.WHITE)
+            } else {
+                StatusBarUtils.setColor(this, Color.parseColor("#B0B0B0"))
+            }
+
+        } else {
+
+            //变为白色文字成功
+            StatusBarUtils.setStatusBarWhiteText(this)
+            StatusBarUtils.setColor(this, color)
+
+        }
+    }
+
+    /**
+     * 跳转页面
+     */
+    protected fun gotoActivity(clazz: Class<*>) {
+        startActivity(Intent(mActivity, clazz))
+    }
+
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContentView()
+        initViewModel()
+        startObserve()
+
+        mActivity = this
+        mContext = this.applicationContext
+
+        //设置当前页面的顶部状态栏背景.
+        StatusBarUtils.setColor(this, setStatusBarColor())
+
+        //获取intent传递的数据
+        if (intent != null) {
+            getDataFromIntent(intent)
+        }
+
+        //设置竖屏展示 (最底层设置是否需要强制默认竖屏展示)
+//        requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
+
+        /** 管理Activity的栈  */
+        ActivityManage.addActivity(this)
+
+        if (needRegisterNetworkChangeObserver()) {
+            ConnectivityReceiver.registerObserver(this)
+            ConnectivityReceiver.registerAnnotationObserver(this)
+        }
+
+        init(savedInstanceState)
+    }
+
+    /**
+     * 是否需要注册监听网络变换
+     */
+    protected open fun needRegisterNetworkChangeObserver(): Boolean {
+        return false
+    }
+
+    //此页面是否启动Shatter
+    protected open fun isShatterEnable(): Boolean {
+        return false
+    }
+
+    override fun onNewIntent(intent: Intent?) {
+        super.onNewIntent(intent)
+    }
+
+    override fun onRestart() {
+        super.onRestart()
+    }
+
+    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+        super.onActivityResult(requestCode, resultCode, data)
+    }
+
+    /**
+     * 只映射了onDestroy方法
+     */
+    override fun onDestroy() {
+        super.onDestroy()
+
+        ActivityManage.removeActivity(this)
+
+        if (needRegisterNetworkChangeObserver()) {
+            ConnectivityReceiver.unregisterObserver(this)
+            ConnectivityReceiver.unregisterAnnotationObserver(this)
+        }
+    }
+
+    /**
+     * 设置应用的字体不随系统的字体大小而改变
+     */
+    override fun onConfigurationChanged(newConfig: Configuration) {
+        if (newConfig.fontScale != 1f) {
+            //非默认值
+            // 当接收系统的字体大小改变的时候。重置为默认
+            resources
+        }
+        super.onConfigurationChanged(newConfig)
+    }
+
+    override fun getResources(): Resources {
+        val res = super.getResources()
+        if (res.configuration.fontScale != 1f) { //非默认值
+            val newConfig = Configuration()
+            newConfig.setToDefaults() //设置默认
+            res.updateConfiguration(newConfig, res.displayMetrics)
+        }
+        return res
+    }
+
+    /**
+     * 设置状态栏的文本颜色
+     */
+    protected fun setStatusBarWhiteText() {
+        StatusBarUtils.setStatusBarWhiteText(this)
+    }
+
+    protected fun setStatusBarBlackText() {
+        StatusBarUtils.setStatusBarBlackText(this)
+    }
+
+}

+ 89 - 0
cs-baselib/src/main/java/com/android/basiclib/base/activity/BaseVMActivity.kt

@@ -0,0 +1,89 @@
+package com.android.basiclib.base.activity
+
+import androidx.activity.viewModels
+import androidx.lifecycle.Observer
+import androidx.lifecycle.ViewModelProvider
+import com.android.basiclib.utils.NetWorkUtil
+import com.android.basiclib.view.dialog.LoadingDialogManager
+import com.android.basiclib.base.vm.BaseViewModel
+import com.android.basiclib.bean.LoadAction
+import com.android.basiclib.ext.getVMCls
+
+/**
+ * 加入ViewModel与LoadState
+ * 默认为Loading弹窗的加载方式
+ */
+abstract class BaseVMActivity<VM : BaseViewModel> : AbsActivity() {
+
+    protected lateinit var mViewModel: VM
+
+    //使用这个方法简化ViewModel的获取
+    protected inline fun <reified VM : BaseViewModel> getViewModel(): VM {
+        val viewModel: VM by viewModels()
+        return viewModel
+    }
+
+    //反射自动获取ViewModel实例
+    protected open fun createViewModel(): VM {
+        return ViewModelProvider(this).get(getVMCls(this))
+    }
+
+    override fun initViewModel() {
+        mViewModel = createViewModel()
+    }
+
+    override fun startObserve() {
+        //观察网络数据状态
+        mViewModel.getActionLiveData().observe(this, stateObserver)
+    }
+
+    override fun setContentView() {
+        setContentView(getLayoutIdRes())
+    }
+
+    abstract fun getLayoutIdRes(): Int
+
+    override fun onNetworkConnectionChanged(isConnected: Boolean, networkType: NetWorkUtil.NetworkType?) {
+    }
+
+    // ================== 网络状态的监听 ======================
+
+    private var stateObserver: Observer<LoadAction> = Observer { loadAction ->
+        when (loadAction.action) {
+            LoadAction.STATE_NORMAL -> showStateNormal()
+            LoadAction.STATE_ERROR -> showStateError(loadAction.message)
+            LoadAction.STATE_SUCCESS -> showStateSuccess()
+            LoadAction.STATE_LOADING -> showStateLoading()
+            LoadAction.STATE_NO_DATA -> showStateNoData()
+            LoadAction.STATE_PROGRESS -> showStateProgress()
+            LoadAction.STATE_HIDE_PROGRESS -> hideStateProgress()
+        }
+    }
+
+    protected open fun showStateNormal() {}
+
+    protected open fun showStateError(message: String?) {
+        LoadingDialogManager.get().dismissLoading()
+    }
+
+    protected open fun showStateSuccess() {
+        LoadingDialogManager.get().dismissLoading()
+    }
+
+    protected open fun showStateLoading() {
+        LoadingDialogManager.get().showLoading(this)
+    }
+
+    protected open fun showStateNoData() {
+        LoadingDialogManager.get().dismissLoading()
+    }
+
+    protected fun showStateProgress() {
+        LoadingDialogManager.get().showLoading(mActivity)
+    }
+
+    protected fun hideStateProgress() {
+        LoadingDialogManager.get().dismissLoading()
+    }
+
+}

+ 105 - 0
cs-baselib/src/main/java/com/android/basiclib/base/activity/BaseVMLoadingActivity.kt

@@ -0,0 +1,105 @@
+package com.android.basiclib.base.activity
+
+import androidx.activity.viewModels
+import androidx.lifecycle.Observer
+import androidx.lifecycle.ViewModelProvider
+import com.android.basiclib.utils.NetWorkUtil
+import com.android.basiclib.view.dialog.LoadingDialogManager
+import com.android.basiclib.view.gloading.Gloading
+import com.android.basiclib.base.vm.BaseViewModel
+import com.android.basiclib.bean.LoadAction
+import com.android.basiclib.ext.getVMCls
+
+/**
+ * 加入ViewModel与LoadState
+ * 默认为布局加载的方式
+ */
+abstract class BaseVMLoadingActivity<VM : BaseViewModel> : AbsActivity() {
+
+    protected lateinit var mViewModel: VM
+
+    protected val mGLoadingHolder by lazy {
+        generateGLoading()
+    }
+
+    //如果要替换GLoading,重写次方法
+    protected open fun generateGLoading(): Gloading.Holder {
+        return Gloading.getDefault().wrap(this).withRetry {
+            onGoadingRetry()
+        }
+    }
+
+    //使用这个方法简化ViewModel的获取
+    protected inline fun <reified VM : BaseViewModel> getViewModel(): VM {
+        val viewModel: VM by viewModels()
+        return viewModel
+    }
+
+    //反射自动获取ViewModel实例
+    protected open fun createViewModel(): VM {
+        return ViewModelProvider(this).get(getVMCls(this))
+    }
+
+    override fun initViewModel() {
+        mViewModel = createViewModel()
+    }
+
+    override fun startObserve() {
+        //观察网络数据状态
+        mViewModel.getActionLiveData().observe(this, stateObserver)
+    }
+
+    override fun setContentView() {
+        setContentView(getLayoutIdRes())
+    }
+
+    abstract fun getLayoutIdRes(): Int
+
+
+    protected open fun onGoadingRetry() {
+    }
+
+    override fun onNetworkConnectionChanged(isConnected: Boolean, networkType: NetWorkUtil.NetworkType?) {
+    }
+
+    // ================== 网络状态的监听 ======================
+
+    private var stateObserver: Observer<LoadAction> = Observer { loadAction ->
+        when (loadAction.action) {
+            LoadAction.STATE_NORMAL -> showStateNormal()
+            LoadAction.STATE_ERROR -> showStateError(loadAction.message)
+            LoadAction.STATE_SUCCESS -> showStateSuccess()
+            LoadAction.STATE_LOADING -> showStateLoading()
+            LoadAction.STATE_NO_DATA -> showStateNoData()
+            LoadAction.STATE_PROGRESS -> showStateProgress()
+            LoadAction.STATE_HIDE_PROGRESS -> hideStateProgress()
+        }
+    }
+
+    protected open fun showStateNormal() {}
+
+    protected open fun showStateLoading() {
+        mGLoadingHolder.showLoading()
+    }
+
+    protected open fun showStateSuccess() {
+        mGLoadingHolder.showLoadSuccess()
+    }
+
+    protected open fun showStateError(message: String?) {
+        mGLoadingHolder.showLoadFailed(message)
+    }
+
+    protected open fun showStateNoData() {
+        mGLoadingHolder.showEmpty()
+    }
+
+    protected fun showStateProgress() {
+        LoadingDialogManager.get().showLoading(mActivity)
+    }
+
+    protected fun hideStateProgress() {
+        LoadingDialogManager.get().dismissLoading()
+    }
+
+}

+ 47 - 0
cs-baselib/src/main/java/com/android/basiclib/base/activity/BaseVVDActivity.kt

@@ -0,0 +1,47 @@
+package com.android.basiclib.base.activity
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import androidx.viewbinding.ViewBinding
+import com.android.basiclib.base.vm.BaseViewModel
+import java.lang.reflect.ParameterizedType
+
+/**
+ * 继承自 BaseVMActivity ,加入ViewBinding
+ * 默认为LoadingDialog的加载方式
+ */
+abstract class BaseVVDActivity<VM : BaseViewModel, VB : ViewBinding> : BaseVMActivity<VM>() {
+
+    private var _binding: VB? = null
+    protected val mBinding: VB
+        get() = requireNotNull(_binding) { "ViewBinding对象为空" }
+
+    // 反射创建ViewBinding
+    protected open fun createViewBinding() {
+
+        try {
+            val clazz: Class<*> = (this.javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[1] as Class<VB>
+            val inflateMethod = clazz.getMethod("inflate", LayoutInflater::class.java)
+            _binding = inflateMethod.invoke(null, layoutInflater) as VB
+        } catch (e: Exception) {
+            e.printStackTrace()
+            throw IllegalArgumentException("无法通过反射创建ViewBinding对象")
+        }
+    }
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        createViewBinding()
+        super.onCreate(savedInstanceState)
+    }
+
+    override fun setContentView() {
+        setContentView(mBinding.root)
+    }
+
+    override fun getLayoutIdRes(): Int = 0
+
+    override fun onDestroy() {
+        super.onDestroy()
+        _binding = null
+    }
+}

+ 47 - 0
cs-baselib/src/main/java/com/android/basiclib/base/activity/BaseVVDLoadingActivity.kt

@@ -0,0 +1,47 @@
+package com.android.basiclib.base.activity
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import androidx.viewbinding.ViewBinding
+import com.android.basiclib.base.vm.BaseViewModel
+import java.lang.reflect.ParameterizedType
+
+/**
+ * 继承自 BaseVMLoadingActivity ,加入ViewBinding
+ * 默认为GLoading的布局Loading加载方式
+ */
+abstract class BaseVVDLoadingActivity<VM : BaseViewModel, VB : ViewBinding> : BaseVMLoadingActivity<VM>() {
+
+    private var _binding: VB? = null
+    protected val mBinding: VB
+        get() = requireNotNull(_binding) { "ViewBinding对象为空" }
+
+    // 反射创建ViewBinding
+    protected open fun createViewBinding() {
+
+        try {
+            val clazz: Class<*> = (this.javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[1] as Class<VB>
+            val inflateMethod = clazz.getMethod("inflate", LayoutInflater::class.java)
+            _binding = inflateMethod.invoke(null, layoutInflater) as VB
+        } catch (e: Exception) {
+            e.printStackTrace()
+            throw IllegalArgumentException("无法通过反射创建ViewBinding对象")
+        }
+    }
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        createViewBinding()
+        super.onCreate(savedInstanceState)
+    }
+
+    override fun setContentView() {
+        setContentView(mBinding.root)
+    }
+
+    override fun getLayoutIdRes(): Int = 0
+
+    override fun onDestroy() {
+        super.onDestroy()
+        _binding = null
+    }
+}

+ 94 - 0
cs-baselib/src/main/java/com/android/basiclib/base/fragment/AbsFragment.kt

@@ -0,0 +1,94 @@
+package com.android.basiclib.base.fragment
+
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.FragmentActivity
+import com.android.basiclib.receiver.ConnectivityReceiver
+import com.android.basiclib.utils.StatusBarUtils
+
+
+
+/**
+ * 普通的Fragment,基类Fragment
+ */
+
+abstract class AbsFragment : Fragment(), ConnectivityReceiver.ConnectivityReceiverListener {
+
+    /**
+     * 获取Context对象
+     */
+    protected lateinit var mActivity: FragmentActivity
+    protected lateinit var mContext: Context
+
+    abstract fun setContentView(container: ViewGroup?): View
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        mActivity = requireActivity()
+        mContext = requireActivity().applicationContext
+
+        if (needRegisterNetworkChangeObserver()) {
+            ConnectivityReceiver.registerObserver(this)
+            ConnectivityReceiver.registerAnnotationObserver(this)
+        }
+    }
+
+    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+        return transformRootView(setContentView(container))
+    }
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+
+        initViews(view)
+    }
+
+    //用于转换根数图View(可以对其做一些别的操作,例如加入GLoading)
+    protected open fun transformRootView(view: View): View {
+        return view
+    }
+
+    protected open fun initViews(view: View) {
+    }
+
+    /**
+     * 只映射了onDestroy方法 取消任务
+     */
+    override fun onDestroy() {
+        super.onDestroy()
+
+        if (needRegisterNetworkChangeObserver()) {
+            ConnectivityReceiver.unregisterObserver(this)
+            ConnectivityReceiver.unregisterAnnotationObserver(this)
+        }
+    }
+
+    /**
+     * 是否需要注册监听网络变换
+     */
+    protected open fun needRegisterNetworkChangeObserver(): Boolean {
+        return false
+    }
+
+    //此页面是否启动Shatter
+    protected open fun isShatterEnable(): Boolean {
+        return false
+    }
+
+    /**
+     * 设置状态栏的文本颜色
+     */
+    protected fun setStatusBarWhiteText() {
+        StatusBarUtils.setStatusBarWhiteText(requireActivity())
+    }
+
+    protected fun setStatusBarBlackText() {
+        StatusBarUtils.setStatusBarBlackText(requireActivity())
+    }
+
+}

+ 109 - 0
cs-baselib/src/main/java/com/android/basiclib/base/fragment/BaseVMFragment.kt

@@ -0,0 +1,109 @@
+package com.android.basiclib.base.fragment
+
+import android.os.Bundle
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.viewModels
+import androidx.lifecycle.Observer
+import androidx.lifecycle.ViewModelProvider
+import com.android.basiclib.utils.NetWorkUtil
+import com.android.basiclib.view.dialog.LoadingDialogManager
+import com.android.basiclib.base.vm.BaseViewModel
+import com.android.basiclib.bean.LoadAction
+import com.android.basiclib.ext.getVMCls
+
+/**
+ * 加入ViewModel与LoadState
+ * 默认为Loading的加载
+ */
+abstract class BaseVMFragment<VM : BaseViewModel> : AbsFragment() {
+
+    protected lateinit var mViewModel: VM
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        initViewModel()
+    }
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+
+        startObserve()
+
+        init(savedInstanceState)
+    }
+
+    protected open fun initViewModel() {
+        mViewModel = createViewModel()
+    }
+
+    protected open fun startObserve() {
+        //观察网络数据状态
+        mViewModel.getActionLiveData().observe(viewLifecycleOwner, stateObserver)
+    }
+
+    //使用这个方法简化ViewModel的初始化
+    protected inline fun <reified VM : BaseViewModel> getViewModel(): VM {
+        val viewModel: VM by viewModels()
+        return viewModel
+    }
+
+    //反射获取ViewModel实例
+    protected open fun createViewModel(): VM {
+        return ViewModelProvider(this).get(getVMCls(this))
+    }
+
+    override fun setContentView(container: ViewGroup?): View {
+        return layoutInflater.inflate(getLayoutIdRes(), container, false)
+    }
+
+    /**
+     * 获取layout的id,具体由子类实现
+     */
+    abstract fun getLayoutIdRes(): Int
+
+    abstract fun init(savedInstanceState: Bundle?)
+
+    override fun onNetworkConnectionChanged(isConnected: Boolean, networkType: NetWorkUtil.NetworkType?) {
+    }
+
+    // ================== 网络状态的监听 ======================
+
+    private var stateObserver: Observer<LoadAction> = Observer { loadAction ->
+        when (loadAction.action) {
+            LoadAction.STATE_NORMAL -> showStateNormal()
+            LoadAction.STATE_ERROR -> showStateError(loadAction.message)
+            LoadAction.STATE_SUCCESS -> showStateSuccess()
+            LoadAction.STATE_LOADING -> showStateLoading()
+            LoadAction.STATE_NO_DATA -> showStateNoData()
+            LoadAction.STATE_PROGRESS -> showStateProgress()
+            LoadAction.STATE_HIDE_PROGRESS -> hideStateProgress()
+        }
+    }
+
+    protected fun showStateNormal() {}
+
+    protected fun showStateError(message: String?) {
+        LoadingDialogManager.get().dismissLoading()
+    }
+
+    protected fun showStateSuccess() {
+        LoadingDialogManager.get().dismissLoading()
+    }
+
+    protected fun showStateLoading() {
+        LoadingDialogManager.get().showLoading(mActivity)
+    }
+
+    protected fun showStateNoData() {
+        LoadingDialogManager.get().dismissLoading()
+    }
+
+    protected fun showStateProgress() {
+        LoadingDialogManager.get().showLoading(mActivity)
+    }
+
+    protected fun hideStateProgress() {
+        LoadingDialogManager.get().dismissLoading()
+    }
+}

+ 191 - 0
cs-baselib/src/main/java/com/android/basiclib/base/fragment/BaseVMLazyLoadingFragment.kt

@@ -0,0 +1,191 @@
+package com.android.basiclib.base.fragment
+
+import android.os.Bundle
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.viewModels
+import androidx.lifecycle.Observer
+import androidx.lifecycle.ViewModelProvider
+import com.android.basiclib.utils.NetWorkUtil
+import com.android.basiclib.view.dialog.LoadingDialogManager
+import com.android.basiclib.view.gloading.Gloading
+import com.android.basiclib.base.vm.BaseViewModel
+import com.android.basiclib.bean.LoadAction
+import com.android.basiclib.ext.getVMCls
+
+/**
+ * 加入ViewModel与LoadState
+ * 默认为Loading布局的懒加载
+ */
+abstract class BaseVMLazyLoadingFragment<VM : BaseViewModel> : AbsFragment() {
+
+    protected lateinit var mViewModel: VM
+    private var isViewCreated = false//布局是否被创建
+    private var isLoadData = false//数据是否加载
+    private var isFirstVisible = true//是否第一次可见
+    protected lateinit var mGLoadingHolder: Gloading.Holder
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        initViewModel()
+    }
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+        isViewCreated = true
+
+        startObserve()
+
+        init(savedInstanceState)
+    }
+
+    protected open fun initViewModel() {
+        mViewModel = createViewModel()
+    }
+
+    protected open fun startObserve() {
+        //观察网络数据状态
+        mViewModel.getActionLiveData().observe(viewLifecycleOwner, stateObserver)
+    }
+
+    override fun onActivityCreated(savedInstanceState: Bundle?) {
+        super.onActivityCreated(savedInstanceState)
+
+        if (isFragmentVisible(this) && this.isAdded) {
+
+            if (parentFragment == null || isFragmentVisible(parentFragment)) {
+                onLazyInitData()
+                isLoadData = true
+                if (isFirstVisible) isFirstVisible = false
+            }
+        }
+    }
+
+    //使用这个方法简化ViewModewl的Hilt依赖注入获取
+    protected inline fun <reified VM : BaseViewModel> getViewModel(): VM {
+        val viewModel: VM by viewModels()
+        return viewModel
+    }
+
+    //反射获取ViewModel实例
+    protected open fun createViewModel(): VM {
+        return ViewModelProvider(this).get(getVMCls(this))
+    }
+
+    override fun setContentView(container: ViewGroup?): View {
+        return layoutInflater.inflate(getLayoutIdRes(), container, false)
+    }
+
+    /**
+     * 获取layout的id,具体由子类实现
+     */
+    abstract fun getLayoutIdRes(): Int
+    abstract fun init(savedInstanceState: Bundle?)
+    abstract fun onLazyInitData()
+
+    //Loading Create Root View
+    override fun transformRootView(view: View): View {
+        mGLoadingHolder = generateGLoading(view)
+        return mGLoadingHolder.wrapper
+    }
+
+    //如果要替换GLoading,重写次方法
+    protected open fun generateGLoading(view: View): Gloading.Holder {
+        return Gloading.getDefault().wrap(view).withRetry {
+            onGoadingRetry()
+        }
+    }
+
+    protected open fun onGoadingRetry() {
+    }
+
+    override fun onNetworkConnectionChanged(
+        isConnected: Boolean,
+        networkType: NetWorkUtil.NetworkType?
+    ) {
+    }
+
+    // ============================  Lazy Load begin ↓  =============================
+
+    override fun setUserVisibleHint(isVisibleToUser: Boolean) {
+        super.setUserVisibleHint(isVisibleToUser)
+        if (isFragmentVisible(this) && !isLoadData && isViewCreated && this.isAdded) {
+            onLazyInitData()
+            isLoadData = true
+        }
+    }
+
+    override fun onHiddenChanged(hidden: Boolean) {
+        super.onHiddenChanged(hidden)
+        //onHiddenChanged调用在Resumed之前,所以此时可能fragment被add, 但还没resumed
+        if (!hidden && !this.isResumed)
+            return
+        //使用hide和show时,fragment的所有生命周期方法都不会调用,除了onHiddenChanged()
+        if (!hidden && isFirstVisible && this.isAdded) {
+            onLazyInitData()
+            isFirstVisible = false
+        }
+    }
+
+    override fun onDestroy() {
+        super.onDestroy()
+
+        isViewCreated = false
+        isLoadData = false
+        isFirstVisible = true
+    }
+
+    /**
+     * 当前Fragment是否对用户是否可见
+     * @param fragment 要判断的fragment
+     * @return true表示对用户可见
+     */
+    private fun isFragmentVisible(fragment: Fragment?): Boolean {
+        return !fragment?.isHidden!! && fragment.userVisibleHint
+    }
+
+    // ================== 网络状态的监听 ======================
+
+    private var stateObserver: Observer<LoadAction> = Observer { loadAction ->
+        if (loadAction != null) {
+
+            when (loadAction.action) {
+                LoadAction.STATE_NORMAL -> showStateNormal()
+                LoadAction.STATE_ERROR -> showStateError(loadAction.message)
+                LoadAction.STATE_SUCCESS -> showStateSuccess()
+                LoadAction.STATE_LOADING -> showStateLoading()
+                LoadAction.STATE_NO_DATA -> showStateNoData()
+                LoadAction.STATE_PROGRESS -> showStateProgress()
+                LoadAction.STATE_HIDE_PROGRESS -> hideStateProgress()
+            }
+
+        }
+    }
+
+    protected open fun showStateNormal() {}
+
+    protected open fun showStateLoading() {
+        mGLoadingHolder.showLoading()
+    }
+
+    protected open fun showStateSuccess() {
+        mGLoadingHolder.showLoadSuccess()
+    }
+
+    protected open fun showStateError(message: String?) {
+        mGLoadingHolder.showLoadFailed(message)
+    }
+
+    protected open fun showStateNoData() {
+        mGLoadingHolder.showEmpty()
+    }
+
+    protected fun showStateProgress() {
+        LoadingDialogManager.get().showLoading(mActivity)
+    }
+
+    protected fun hideStateProgress() {
+        LoadingDialogManager.get().dismissLoading()
+    }
+}

+ 127 - 0
cs-baselib/src/main/java/com/android/basiclib/base/fragment/BaseVMLoadingFragment.kt

@@ -0,0 +1,127 @@
+package com.android.basiclib.base.fragment
+
+import android.os.Bundle
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.viewModels
+import androidx.lifecycle.Observer
+import androidx.lifecycle.ViewModelProvider
+import com.android.basiclib.utils.NetWorkUtil
+import com.android.basiclib.view.dialog.LoadingDialogManager
+import com.android.basiclib.view.gloading.Gloading
+import com.android.basiclib.base.vm.BaseViewModel
+import com.android.basiclib.bean.LoadAction
+import com.android.basiclib.ext.getVMCls
+
+/**
+ * 加入ViewModel与LoadState
+ * 默认为Loading布局的加载方式
+ */
+abstract class BaseVMLoadingFragment<VM : BaseViewModel> : AbsFragment() {
+
+    protected lateinit var mViewModel: VM
+
+    protected lateinit var mGLoadingHolder: Gloading.Holder
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        initViewModel()
+    }
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+
+        startObserve()
+
+        init(savedInstanceState)
+    }
+
+    protected open fun initViewModel() {
+        mViewModel = createViewModel()
+    }
+
+    protected open fun startObserve() {
+        //观察网络数据状态
+        mViewModel.getActionLiveData().observe(viewLifecycleOwner, stateObserver)
+    }
+
+    override fun transformRootView(view: View): View {
+        mGLoadingHolder = generateGLoading(view)
+        return mGLoadingHolder.wrapper
+    }
+
+    //如果要替换GLoading,重写次方法
+    protected open fun generateGLoading(view: View): Gloading.Holder {
+        return Gloading.getDefault().wrap(view).withRetry {
+            onGoadingRetry()
+        }
+    }
+
+    protected open fun onGoadingRetry() {
+    }
+
+    //使用这个方法简化ViewModewl的Hilt依赖注入获取
+    protected inline fun <reified VM : BaseViewModel> getViewModel(): VM {
+        val viewModel: VM by viewModels()
+        return viewModel
+    }
+
+    //反射获取ViewModel实例
+    protected open fun createViewModel(): VM {
+        return ViewModelProvider(this).get(getVMCls(this))
+    }
+
+    override fun setContentView(container: ViewGroup?): View {
+        return layoutInflater.inflate(getLayoutIdRes(), container, false)
+    }
+
+    /**
+     * 获取layout的id,具体由子类实现
+     */
+    abstract fun getLayoutIdRes(): Int
+    abstract fun init(savedInstanceState: Bundle?)
+
+    override fun onNetworkConnectionChanged(isConnected: Boolean, networkType: NetWorkUtil.NetworkType?) {
+    }
+
+    // ================== 网络状态的监听 ======================
+
+    private var stateObserver: Observer<LoadAction> = Observer { loadAction ->
+        when (loadAction.action) {
+            LoadAction.STATE_NORMAL -> showStateNormal()
+            LoadAction.STATE_ERROR -> showStateError(loadAction.message)
+            LoadAction.STATE_SUCCESS -> showStateSuccess()
+            LoadAction.STATE_LOADING -> showStateLoading()
+            LoadAction.STATE_NO_DATA -> showStateNoData()
+            LoadAction.STATE_PROGRESS -> showStateProgress()
+            LoadAction.STATE_HIDE_PROGRESS -> hideStateProgress()
+        }
+    }
+
+    protected open fun showStateNormal() {}
+
+    protected open fun showStateLoading() {
+        mGLoadingHolder.showLoading()
+    }
+
+    protected open fun showStateSuccess() {
+        mGLoadingHolder.showLoadSuccess()
+    }
+
+    protected open fun showStateError(message: String?) {
+        mGLoadingHolder.showLoadFailed(message)
+    }
+
+    protected open fun showStateNoData() {
+        mGLoadingHolder.showEmpty()
+    }
+
+    protected fun showStateProgress() {
+        LoadingDialogManager.get().showLoading(mActivity)
+    }
+
+    protected fun hideStateProgress() {
+        LoadingDialogManager.get().dismissLoading()
+    }
+
+}

+ 46 - 0
cs-baselib/src/main/java/com/android/basiclib/base/fragment/BaseVVDFragment.kt

@@ -0,0 +1,46 @@
+package com.android.basiclib.base.fragment
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.viewbinding.ViewBinding
+import com.android.basiclib.base.vm.BaseViewModel
+import java.lang.reflect.ParameterizedType
+
+abstract class BaseVVDFragment<VM : BaseViewModel, VB : ViewBinding> : BaseVMFragment<VM>() {
+
+    private var _binding: VB? = null
+    protected val mBinding: VB
+        get() = requireNotNull(_binding) { "ViewBinding对象为空" }
+
+    // 反射创建ViewBinding
+    protected open fun createViewBinding() {
+
+        try {
+            val clazz: Class<*> = (this.javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[1] as Class<VB>
+            val inflateMethod = clazz.getMethod("inflate", LayoutInflater::class.java)
+            _binding = inflateMethod.invoke(null, layoutInflater) as VB
+        } catch (e: Exception) {
+            e.printStackTrace()
+            throw IllegalArgumentException("无法通过反射创建ViewBinding对象")
+        }
+    }
+
+    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+        createViewBinding()
+        return super.onCreateView(inflater, container, savedInstanceState)
+    }
+
+    override fun setContentView(container: ViewGroup?): View {
+        return mBinding.root
+    }
+
+    override fun getLayoutIdRes(): Int = 0
+
+    override fun onDestroyView() {
+        super.onDestroyView()
+        _binding = null
+    }
+
+}

+ 46 - 0
cs-baselib/src/main/java/com/android/basiclib/base/fragment/BaseVVDLazyLoadingFragment.kt

@@ -0,0 +1,46 @@
+package com.android.basiclib.base.fragment
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.viewbinding.ViewBinding
+import com.android.basiclib.base.vm.BaseViewModel
+import java.lang.reflect.ParameterizedType
+
+abstract class BaseVVDLazyLoadingFragment<VM : BaseViewModel, VB : ViewBinding> : BaseVMLazyLoadingFragment<VM>() {
+
+    private var _binding: VB? = null
+    protected val mBinding: VB
+        get() = requireNotNull(_binding) { "ViewBinding对象为空" }
+
+    // 反射创建ViewBinding
+    protected open fun createViewBinding() {
+
+        try {
+            val clazz: Class<*> = (this.javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[1] as Class<VB>
+            val inflateMethod = clazz.getMethod("inflate", LayoutInflater::class.java)
+            _binding = inflateMethod.invoke(null, layoutInflater) as VB
+        } catch (e: Exception) {
+            e.printStackTrace()
+            throw IllegalArgumentException("无法通过反射创建ViewBinding对象")
+        }
+    }
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        createViewBinding()
+    }
+
+    override fun setContentView(container: ViewGroup?): View {
+        return mBinding.root
+    }
+
+    override fun getLayoutIdRes(): Int = 0
+
+    override fun onDestroyView() {
+        super.onDestroyView()
+        _binding = null
+    }
+
+}

+ 46 - 0
cs-baselib/src/main/java/com/android/basiclib/base/fragment/BaseVVDLoadingFragment.kt

@@ -0,0 +1,46 @@
+package com.android.basiclib.base.fragment
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.viewbinding.ViewBinding
+import com.android.basiclib.base.vm.BaseViewModel
+import java.lang.reflect.ParameterizedType
+
+abstract class BaseVVDLoadingFragment<VM : BaseViewModel, VB : ViewBinding> : BaseVMLoadingFragment<VM>() {
+
+    private var _binding: VB? = null
+    protected val mBinding: VB
+        get() = requireNotNull(_binding) { "ViewBinding对象为空" }
+
+    // 反射创建ViewBinding
+    protected open fun createViewBinding() {
+
+        try {
+            val clazz: Class<*> = (this.javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[1] as Class<VB>
+            val inflateMethod = clazz.getMethod("inflate", LayoutInflater::class.java)
+            _binding = inflateMethod.invoke(null, layoutInflater) as VB
+        } catch (e: Exception) {
+            e.printStackTrace()
+            throw IllegalArgumentException("无法通过反射创建ViewBinding对象")
+        }
+    }
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        createViewBinding()
+    }
+
+    override fun setContentView(container: ViewGroup?): View {
+        return mBinding.root
+    }
+
+    override fun getLayoutIdRes(): Int = 0
+
+    override fun onDestroyView() {
+        super.onDestroyView()
+        _binding = null
+    }
+
+}

+ 31 - 0
cs-baselib/src/main/java/com/android/basiclib/base/mvi/BaseEISViewModel.kt

@@ -0,0 +1,31 @@
+package com.android.basiclib.base.mvi
+
+import androidx.lifecycle.viewModelScope
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.SharedFlow
+import kotlinx.coroutines.flow.asSharedFlow
+
+import kotlinx.coroutines.launch
+
+/**
+ * @author Newki
+ *
+ * 基于BaseViewModel实现,用于实现 MVI 模式的使用
+ * <p>
+ * 需要指定 Intent(事件) 与 State(状态) 加 Effect(UI效果)三种泛型类型
+ */
+abstract class BaseEISViewModel<E : IUIEffect, I : IUiIntent, S : IUiState> : BaseISViewModel<I, S>() {
+
+    //一次性事件,无需更新
+    private val _effectFlow = MutableSharedFlow<E>()
+    val uiEffectFlow: SharedFlow<E> by lazy { _effectFlow.asSharedFlow() }
+
+    //两种方式发射,在协程外用viewModelScope发射
+    protected fun sendEffect(builder: suspend () -> E?) = viewModelScope.launch {
+        builder()?.let { _effectFlow.emit(it) }
+    }
+
+    //两种方式发射,suspend 协程中直接发射
+    protected suspend fun sendEffect(effect: E) = _effectFlow.emit(effect)
+
+}

+ 61 - 0
cs-baselib/src/main/java/com/android/basiclib/base/mvi/BaseISViewModel.kt

@@ -0,0 +1,61 @@
+package com.android.basiclib.base.mvi
+
+import androidx.lifecycle.viewModelScope
+import com.android.basiclib.base.vm.BaseViewModel
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.consumeAsFlow
+import kotlinx.coroutines.flow.update
+import kotlinx.coroutines.launch
+
+/**
+ * @author Newki
+ *
+ * 基于BaseViewModel实现,用于实现 MVI 模式的使用
+ * <p>
+ * 需要指定 Intent(事件) 与 State(状态) 的泛型类型
+ */
+abstract class BaseISViewModel<I : IUiIntent, S : IUiState> : BaseViewModel() {
+
+    private var _uiStateFlow = MutableStateFlow(initUiState())
+    val uiStateFlow: StateFlow<S> = _uiStateFlow
+
+    //页面事件的 Channel 分发
+    private val _uiIntentFlow = Channel<I>(Channel.UNLIMITED)
+
+    //更新State页面状态 (data class 类型)
+    fun updateUiState(reducer: S.() -> S) {
+        _uiStateFlow.update { reducer(_uiStateFlow.value) }
+    }
+
+    //更新State页面状态 (sealed class 类型)
+    fun sendUiState(s: S) {
+        _uiStateFlow.value = s
+    }
+
+    //发送页面事件
+    fun sendUiIntent(uiIntent: I) {
+        viewModelScope.launch {
+            _uiIntentFlow.send(uiIntent)
+        }
+    }
+
+    init {
+        // 这里是通过Channel的方式自动分发的。
+        viewModelScope.launch {
+            //收集意图 (观察者模式改变之后就自动更新)用于协程通信的,所以需要在协程中调用
+            _uiIntentFlow.consumeAsFlow().collect { intent ->
+                handleIntent(intent)
+            }
+        }
+
+    }
+
+    //每个页面的 UiState 都不相同,必须实自己去创建
+    protected abstract fun initUiState(): S
+
+    //每个页面处理的 UiIntent 都不同,必须实现自己页面对应的状态处理
+    protected abstract fun handleIntent(intent: I)
+
+}

+ 11 - 0
cs-baselib/src/main/java/com/android/basiclib/base/mvi/IUIEffect.kt

@@ -0,0 +1,11 @@
+package com.android.basiclib.base.mvi
+
+import androidx.annotation.Keep
+
+/**
+ * @author Newki
+ */
+//MVI 页面【UI效果】管理的基类
+//在R8代码混淆压缩时必须保留被标记的类或方法,以防止代码出现因混淆而导致的崩溃。
+@Keep
+interface IUIEffect

+ 11 - 0
cs-baselib/src/main/java/com/android/basiclib/base/mvi/IUiIntent.kt

@@ -0,0 +1,11 @@
+package com.android.basiclib.base.mvi
+
+import androidx.annotation.Keep
+
+/**
+ * @author Newki
+ */
+//MVI 页面【事件】管理的基类
+//在R8代码混淆压缩时必须保留被标记的类或方法,以防止代码出现因混淆而导致的崩溃。
+@Keep
+interface IUiIntent

+ 11 - 0
cs-baselib/src/main/java/com/android/basiclib/base/mvi/IUiState.kt

@@ -0,0 +1,11 @@
+package com.android.basiclib.base.mvi
+
+import androidx.annotation.Keep
+
+/**
+ * @author Newki
+ */
+//MVI 页面【状态】管理的基类
+//在R8代码混淆压缩时必须保留被标记的类或方法,以防止代码出现因混淆而导致的崩溃。
+@Keep
+interface IUiState

+ 64 - 0
cs-baselib/src/main/java/com/android/basiclib/base/mvi/LiveDataExt.kt

@@ -0,0 +1,64 @@
+package com.android.basiclib.base.mvi
+
+import androidx.lifecycle.*
+import kotlin.reflect.KProperty1
+
+
+/**
+ * @auther Newki
+ *
+ * @description LiveData的扩展 支持MVI模式 订阅单个LiveData实现监听不同的操作与数据
+ */
+
+//监听一个属性
+fun <T, A> LiveData<T>.observeState(
+    lifecycleOwner: LifecycleOwner,
+    prop1: KProperty1<T, A>,
+    action: (A) -> Unit
+) {
+    this.map {
+        StateTuple1(prop1.get(it))
+    }.distinctUntilChanged().observe(lifecycleOwner) { (a) ->
+        action.invoke(a)
+    }
+}
+
+//监听两个属性
+fun <T, A, B> LiveData<T>.observeState(
+    lifecycleOwner: LifecycleOwner,
+    prop1: KProperty1<T, A>,
+    prop2: KProperty1<T, B>,
+    action: (A, B) -> Unit
+) {
+    this.map {
+        StateTuple2(prop1.get(it), prop2.get(it))
+    }.distinctUntilChanged().observe(lifecycleOwner) { (a, b) ->
+        action.invoke(a, b)
+    }
+}
+
+//监听三个属性
+fun <T, A, B, C> LiveData<T>.observeState(
+    lifecycleOwner: LifecycleOwner,
+    prop1: KProperty1<T, A>,
+    prop2: KProperty1<T, B>,
+    prop3: KProperty1<T, C>,
+    action: (A, B, C) -> Unit
+) {
+    this.map {
+        StateTuple3(prop1.get(it), prop2.get(it), prop3.get(it))
+    }.distinctUntilChanged().observe(lifecycleOwner) { (a, b, c) ->
+        action.invoke(a, b, c)
+    }
+}
+
+
+internal data class StateTuple1<A>(val a: A)
+internal data class StateTuple2<A, B>(val a: A, val b: B)
+internal data class StateTuple3<A, B, C>(val a: A, val b: B, val c: C)
+
+
+//更新State
+fun <T> MutableLiveData<T>.setState(reducer: T.() -> T) {
+    this.value = this.value?.reducer()
+}

+ 81 - 0
cs-baselib/src/main/java/com/android/basiclib/base/vm/BaseRepository.kt

@@ -0,0 +1,81 @@
+package com.android.basiclib.base.vm
+
+import android.text.TextUtils
+import com.android.basiclib.R
+import com.android.basiclib.bean.BaseBean
+import com.android.basiclib.utils.CommUtils
+import com.google.gson.JsonParseException
+import com.android.basiclib.bean.OkResult
+import com.android.basiclib.core.BaseLibCore
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.coroutineScope
+import retrofit2.HttpException
+import java.io.IOException
+import java.net.ConnectException
+import java.net.SocketException
+import java.net.SocketTimeoutException
+import java.net.UnknownHostException
+
+open class BaseRepository {
+
+    //无异常处理 -> 一般不用这个,内部没有捕获异常 一旦报错会导致App崩溃
+    suspend inline fun <T : Any> handleApiCall(call: suspend () -> BaseBean<T>): BaseBean<T> {
+        return call.invoke()
+    }
+
+    //处理Http错误-内部再处理Api错误
+    suspend fun <T : Any> handleErrorApiCall(call: suspend () -> OkResult<T>, errorMessage: String = ""): OkResult<T> {
+        return try {
+            call()
+        } catch (e: Exception) {
+            if (!TextUtils.isEmpty(errorMessage)) {
+                OkResult.Error(IOException(errorMessage))
+            } else {
+                OkResult.Error(handleExceptionMessage(e))
+            }
+        }
+    }
+
+    //处理Api错误,例如403Token过期 ;把BaseBean的数据转换为自定义的Result数据
+    suspend fun <T : Any> handleApiErrorResponse(
+        response: BaseBean<T>,
+        successBlock: (suspend CoroutineScope.() -> Unit)? = null,
+        errorBlock: (suspend CoroutineScope.() -> Unit)? = null
+    ): OkResult<T> {
+
+        return coroutineScope {
+            //执行挂起函数
+            if (response.code == BaseLibCore.successCode) {
+                successBlock?.let { it() }
+
+                if (response.data == null) {
+                    //如果为空,就构造一个空的对象
+                    OkResult.Error(RuntimeException("data为空"))
+                } else {
+                    OkResult.Success(response.data)
+                }
+
+            } else {
+                errorBlock?.let { it() }
+                OkResult.Error(IOException(response.message))
+            }
+        }
+    }
+
+
+    //处理自定义错误消息
+    fun handleExceptionMessage(e: Exception): Exception {
+        return when (e) {
+            is UnknownHostException -> IOException(CommUtils.getString(R.string.error_domain_name))
+            is JsonParseException -> IOException(CommUtils.getString(R.string.error_data_parsing))
+            is ConnectException -> IOException(CommUtils.getString(R.string.error_network_connections))
+            is HttpException -> IOException(CommUtils.getString(R.string.error_server_business))
+            is SocketException -> IOException(CommUtils.getString(R.string.error_server_business))
+            is SocketTimeoutException -> IOException(CommUtils.getString(R.string.error_network_timeout))
+            is RuntimeException -> IOException(CommUtils.getString(R.string.error_running))
+            else -> e
+        }
+
+    }
+
+}

+ 61 - 0
cs-baselib/src/main/java/com/android/basiclib/base/vm/BaseViewModel.kt

@@ -0,0 +1,61 @@
+package com.android.basiclib.base.vm
+
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import com.android.basiclib.bean.LoadAction
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+open class BaseViewModel : ViewModel() {
+
+    var mLoadActionLiveData: MutableLiveData<LoadAction> = MutableLiveData()
+
+    fun launchOnUI(block: suspend CoroutineScope.() -> Unit) {
+        viewModelScope.launch { block() }
+    }
+
+    suspend fun <T> launchOnIO(block: suspend CoroutineScope.() -> T) {
+        withContext(Dispatchers.IO) {
+            block
+        }
+    }
+
+    /**
+     * 设置并发射加载状态
+     */
+    fun setStateLiveData(loadState: LoadAction) {
+        mLoadActionLiveData.postValue(loadState)
+    }
+
+    fun loadStartLoading() {
+        mLoadActionLiveData.postValue(LoadAction(LoadAction.STATE_LOADING))
+    }
+
+    fun loadSuccess() {
+        mLoadActionLiveData.postValue(LoadAction(LoadAction.STATE_SUCCESS))
+    }
+
+    fun loadError(message: String?) {
+        mLoadActionLiveData.postValue(LoadAction(LoadAction.STATE_ERROR, message))
+    }
+
+    fun loadNoData() {
+        mLoadActionLiveData.postValue(LoadAction(LoadAction.STATE_NO_DATA))
+    }
+
+    fun loadStartProgress() {
+        mLoadActionLiveData.postValue(LoadAction(LoadAction.STATE_PROGRESS))
+    }
+
+    fun loadHideProgress() {
+        mLoadActionLiveData.postValue(LoadAction(LoadAction.STATE_HIDE_PROGRESS))
+    }
+
+    fun getActionLiveData(): MutableLiveData<LoadAction> {
+        return mLoadActionLiveData
+    }
+
+}

+ 7 - 0
cs-baselib/src/main/java/com/android/basiclib/base/vm/EmptyViewModel.kt

@@ -0,0 +1,7 @@
+package com.android.basiclib.base.vm
+
+import dagger.hilt.android.lifecycle.HiltViewModel
+import javax.inject.Inject
+
+@HiltViewModel
+class EmptyViewModel @Inject constructor() : BaseViewModel()

+ 49 - 0
cs-baselib/src/main/java/com/android/basiclib/bean/BaseBean.java

@@ -0,0 +1,49 @@
+package com.android.basiclib.bean;
+
+import com.google.gson.annotations.SerializedName;
+
+
+import java.io.Serializable;
+
+/**
+ * 基类数据结构
+ */
+public class BaseBean<T> implements Serializable {
+    @SerializedName(value = "code", alternate = "errorCode")
+    private int code;
+    @SerializedName("data")
+    private T data;
+    @SerializedName(value = "msg", alternate = "errorMsg")
+    private String message;
+
+    public void setData(T data) {
+        this.data = data;
+    }
+
+    public T getData() {
+        return data;
+    }
+
+    public int getCode() {
+        return code;
+    }
+
+    public void setCode(int code) {
+        this.code = code;
+    }
+
+    public String getMessage() {
+        return message;
+    }
+
+    public void setMessage(String message) {
+        this.message = message;
+    }
+
+    /**
+     * 自定义可为空的字段
+     */
+    public Optional<T> transform() {
+        return new Optional<>(data);
+    }
+}

+ 15 - 0
cs-baselib/src/main/java/com/android/basiclib/bean/ErrorBean.java

@@ -0,0 +1,15 @@
+package com.android.basiclib.bean;
+
+public class ErrorBean {
+    public int code;
+    public String message;
+
+    @Override
+    public String toString() {
+        return "ErrorBean{" +
+                "code=" + code +
+                ", message='" + message + '\'' +
+                '}';
+    }
+
+}

+ 18 - 0
cs-baselib/src/main/java/com/android/basiclib/bean/LoadAction.kt

@@ -0,0 +1,18 @@
+package com.android.basiclib.bean
+
+
+/**
+ * 加载状态的bean
+ */
+data class LoadAction(var action: Int, var message: String? = "") {
+
+    companion object {
+        const val STATE_NORMAL = 141
+        const val STATE_LOADING = 142
+        const val STATE_SUCCESS = 143
+        const val STATE_ERROR = 144
+        const val STATE_NO_DATA = 145
+        const val STATE_PROGRESS = 146
+        const val STATE_HIDE_PROGRESS = 147
+    }
+}

+ 44 - 0
cs-baselib/src/main/java/com/android/basiclib/bean/OkResult.kt

@@ -0,0 +1,44 @@
+package com.android.basiclib.bean
+
+import kotlin.coroutines.resume
+import kotlin.coroutines.suspendCoroutine
+
+/**
+ * 自定义网络返回结果
+ * 二次处理之后赋值给ViewModel中展示与判断
+ */
+sealed class OkResult<out T : Any> {
+
+    data class Success<out T : Any>(val data: T) : OkResult<T>()
+    data class Error(val exception: Exception) : OkResult<Nothing>()
+
+    //检测成功与失败
+    fun checkResult(success: (T) -> Unit, error: (String?) -> Unit) {
+        if (this is Success) {
+            success(data)
+        } else if (this is Error) {
+            error(exception.message)
+        }
+    }
+
+    //只是检测成功
+    fun checkSuccess(success: (T) -> Unit) {
+        if (this is Success) {
+            success(data)
+        }
+    }
+
+    //检查并返回是成功还是失败(在协程中使用直接返回铺平回调)
+    suspend fun isSuccess(): Boolean {
+        return suspendCoroutine { continuation ->
+            continuation.resume(this is Success)
+        }
+    }
+
+    override fun toString(): String {
+        return when (this) {
+            is Success<*> -> "Success[data=$data]"
+            is Error -> "Error[exception=$exception]"
+        }
+    }
+}

+ 25 - 0
cs-baselib/src/main/java/com/android/basiclib/bean/Optional.java

@@ -0,0 +1,25 @@
+package com.android.basiclib.bean;
+
+
+/**
+ * 处理data为null的情况
+ */
+public class Optional<M> {
+
+    private final M data; // 接收到的返回结果
+
+    public Optional(M data) {
+        this.data = data;
+    }
+
+    // 判断返回结果是否为null
+    public boolean isEmpty() {
+        return this.data == null;
+    }
+
+    // 获取可以为null的返回结果
+    public M getData() {
+        return data;
+    }
+
+}

+ 17 - 0
cs-baselib/src/main/java/com/android/basiclib/bean/PageInfo.java

@@ -0,0 +1,17 @@
+package com.android.basiclib.bean;
+
+import com.google.gson.annotations.SerializedName;
+
+import java.io.Serializable;
+import java.util.List;
+
+public class PageInfo<T> implements Serializable {
+
+    public String count;
+    public String curPage;
+    public String pageSize;
+    public String countPage;
+    @SerializedName(value="list", alternate="data")
+    public List<T> list;
+
+}

+ 817 - 0
cs-baselib/src/main/java/com/android/basiclib/cache/ACache.java

@@ -0,0 +1,817 @@
+package com.android.basiclib.cache;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.PixelFormat;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+
+import com.android.basiclib.utils.CommUtils;
+
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.RandomAccessFile;
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * 本地缓存
+ */
+
+public class ACache {
+
+    public static final int TIME_HOUR = 60 * 60;
+    public static final int TIME_DAY = TIME_HOUR * 24;
+    private static final int MAX_SIZE = 1000 * 1000 * 50; // 50 mb
+    private static final int MAX_COUNT = Integer.MAX_VALUE; // 不限制存放数据的数量
+    private static Map<String, ACache> mInstanceMap = new HashMap<String, ACache>();
+    private ACacheManager mCache;
+
+    public static ACache get() {
+        return get(CommUtils.getContext(), "ACache");
+    }
+
+    public static ACache get(Context ctx) {
+        return get(ctx, "ACache");
+    }
+
+    public static ACache get(Context ctx, String cacheName) {
+        File f = new File(ctx.getApplicationContext().getCacheDir(), cacheName);
+        return get(f, MAX_SIZE, MAX_COUNT);
+    }
+
+    public static ACache get(File cacheDir) {
+        return get(cacheDir, MAX_SIZE, MAX_COUNT);
+    }
+
+    public static ACache get(Context ctx, long max_zise, int max_count) {
+        File f = new File(ctx.getCacheDir(), "ACache"); //默认在私有目录的缓存目录
+        return get(f, max_zise, max_count);
+    }
+
+    public static ACache get(File cacheDir, long max_zise, int max_count) {
+        ACache manager = mInstanceMap.get(cacheDir.getAbsoluteFile() + myPid());
+        if (manager == null) {
+            manager = new ACache(cacheDir, max_zise, max_count);
+            mInstanceMap.put(cacheDir.getAbsolutePath() + myPid(), manager);
+        }
+        return manager;
+    }
+
+    private static String myPid() {
+        return "_" + android.os.Process.myPid();
+    }
+
+    private ACache(File cacheDir, long max_size, int max_count) {
+        if (!cacheDir.exists() && !cacheDir.mkdirs()) {
+            throw new RuntimeException("can't make dirs in "
+                    + cacheDir.getAbsolutePath());
+        }
+        mCache = new ACacheManager(cacheDir, max_size, max_count);
+    }
+    // =======================================
+    // ============ String数据 读写 ==============
+    // =======================================
+
+    /**
+     * 保存 String数据 到 缓存中
+     *
+     * @param key   保存的key
+     * @param value 保存的String数据
+     */
+    public void put(String key, String value) {
+        File file = mCache.newFile(key);
+        BufferedWriter out = null;
+        try {
+            out = new BufferedWriter(new FileWriter(file), 1024);
+            out.write(value);
+        } catch (IOException e) {
+            e.printStackTrace();
+        } finally {
+            if (out != null) {
+                try {
+                    out.flush();
+                    out.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+            mCache.put(file);
+        }
+    }
+
+    /**
+     * 保存 String数据 到 缓存中
+     *
+     * @param key      保存的key
+     * @param value    保存的String数据
+     * @param saveTime 保存的时间,单位:秒
+     */
+    public void put(String key, String value, int saveTime) {
+        put(key, Utils.newStringWithDateInfo(saveTime, value));
+    }
+
+    /**
+     * 读取 String数据
+     *
+     * @param key
+     * @return String 数据
+     */
+    public String getAsString(String key) {
+        File file = mCache.get(key);
+        if (!file.exists())
+            return null;
+        boolean removeFile = false;
+        BufferedReader in = null;
+        try {
+            in = new BufferedReader(new FileReader(file));
+            String readString = "";
+            String currentLine;
+            while ((currentLine = in.readLine()) != null) {
+                readString += currentLine;
+            }
+            if (!Utils.isDue(readString)) {
+                return Utils.clearDateInfo(readString);
+            } else {
+                removeFile = true;
+                return null;
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+            return null;
+        } finally {
+            if (in != null) {
+                try {
+                    in.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+            if (removeFile)
+                remove(key);
+        }
+    }
+    // =======================================
+    // ============= JSONObject 数据 读写 ==============
+    // =======================================
+
+    /**
+     * 保存 JSONObject数据 到 缓存中
+     *
+     * @param key   保存的key
+     * @param value 保存的JSON数据
+     */
+    public void put(String key, JSONObject value) {
+        put(key, value.toString());
+    }
+
+    /**
+     * 保存 JSONObject数据 到 缓存中
+     *
+     * @param key      保存的key
+     * @param value    保存的JSONObject数据
+     * @param saveTime 保存的时间,单位:秒
+     */
+    public void put(String key, JSONObject value, int saveTime) {
+        put(key, value.toString(), saveTime);
+    }
+
+    /**
+     * 读取JSONObject数据
+     *
+     * @param key
+     * @return JSONObject数据
+     */
+    public JSONObject getAsJSONObject(String key) {
+        String JSONString = getAsString(key);
+        try {
+            JSONObject obj = new JSONObject(JSONString);
+            return obj;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+    // =======================================
+    // ============ JSONArray 数据 读写 =============
+    // =======================================
+
+    /**
+     * 保存 JSONArray数据 到 缓存中
+     *
+     * @param key   保存的key
+     * @param value 保存的JSONArray数据
+     */
+    public void put(String key, JSONArray value) {
+        put(key, value.toString());
+    }
+
+    /**
+     * 保存 JSONArray数据 到 缓存中
+     *
+     * @param key      保存的key
+     * @param value    保存的JSONArray数据
+     * @param saveTime 保存的时间,单位:秒
+     */
+    public void put(String key, JSONArray value, int saveTime) {
+        put(key, value.toString(), saveTime);
+    }
+
+    /**
+     * 读取JSONArray数据
+     *
+     * @param key
+     * @return JSONArray数据
+     */
+    public JSONArray getAsJSONArray(String key) {
+        String JSONString = getAsString(key);
+        try {
+            JSONArray obj = new JSONArray(JSONString);
+            return obj;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+    // =======================================
+    // ============== byte 数据 读写 =============
+    // =======================================
+
+    /**
+     * 保存 byte数据 到 缓存中
+     *
+     * @param key   保存的key
+     * @param value 保存的数据
+     */
+    public void put(String key, byte[] value) {
+        File file = mCache.newFile(key);
+        FileOutputStream out = null;
+        try {
+            out = new FileOutputStream(file);
+            out.write(value);
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            if (out != null) {
+                try {
+                    out.flush();
+                    out.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+            mCache.put(file);
+        }
+    }
+
+    /**
+     * 保存 byte数据 到 缓存中
+     *
+     * @param key      保存的key
+     * @param value    保存的数据
+     * @param saveTime 保存的时间,单位:秒
+     */
+    public void put(String key, byte[] value, int saveTime) {
+        put(key, Utils.newByteArrayWithDateInfo(saveTime, value));
+    }
+
+    /**
+     * 获取 byte 数据
+     *
+     * @param key
+     * @return byte 数据
+     */
+    public byte[] getAsBinary(String key) {
+        RandomAccessFile RAFile = null;
+        boolean removeFile = false;
+        try {
+            File file = mCache.get(key);
+            if (!file.exists())
+                return null;
+            RAFile = new RandomAccessFile(file, "r");
+            byte[] byteArray = new byte[(int) RAFile.length()];
+            RAFile.read(byteArray);
+            if (!Utils.isDue(byteArray)) {
+                return Utils.clearDateInfo(byteArray);
+            } else {
+                removeFile = true;
+                return null;
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        } finally {
+            if (RAFile != null) {
+                try {
+                    RAFile.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+            if (removeFile)
+                remove(key);
+        }
+    }
+    // =======================================
+    // ============= 序列化 数据 读写 ===============
+    // =======================================
+
+    /**
+     * 保存 Serializable数据 到 缓存中
+     *
+     * @param key   保存的key
+     * @param value 保存的value
+     */
+    public void put(String key, Serializable value) {
+        put(key, value, -1);
+    }
+
+    /**
+     * 保存 Serializable数据到 缓存中
+     *
+     * @param key      保存的key
+     * @param value    保存的value
+     * @param saveTime 保存的时间,单位:秒
+     */
+    public void put(String key, Serializable value, int saveTime) {
+        ByteArrayOutputStream baos = null;
+        ObjectOutputStream oos = null;
+        try {
+            baos = new ByteArrayOutputStream();
+            oos = new ObjectOutputStream(baos);
+            oos.writeObject(value);
+            byte[] data = baos.toByteArray();
+            if (saveTime != -1) {
+                put(key, data, saveTime);
+            } else {
+                put(key, data);
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            try {
+                oos.close();
+            } catch (IOException e) {
+            }
+        }
+    }
+
+    /**
+     * 读取 Serializable数据
+     *
+     * @param key
+     * @return Serializable 数据
+     */
+    public Object getAsObject(String key) {
+        byte[] data = getAsBinary(key);
+        if (data != null) {
+            ByteArrayInputStream bais = null;
+            ObjectInputStream ois = null;
+            try {
+                bais = new ByteArrayInputStream(data);
+                ois = new ObjectInputStream(bais);
+                Object reObject = ois.readObject();
+                return reObject;
+            } catch (Exception e) {
+                e.printStackTrace();
+                return null;
+            } finally {
+                try {
+                    if (bais != null)
+                        bais.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+                try {
+                    if (ois != null)
+                        ois.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+        return null;
+    }
+    // =======================================
+    // ============== bitmap 数据 读写 =============
+    // =======================================
+
+    /**
+     * 保存 bitmap 到 缓存中
+     *
+     * @param key   保存的key
+     * @param value 保存的bitmap数据
+     */
+    public void put(String key, Bitmap value) {
+        put(key, Utils.Bitmap2Bytes(value));
+    }
+
+    /**
+     * 保存 bitmap 到 缓存中
+     *
+     * @param key      保存的key
+     * @param value    保存的 bitmap 数据
+     * @param saveTime 保存的时间,单位:秒
+     */
+    public void put(String key, Bitmap value, int saveTime) {
+        put(key, Utils.Bitmap2Bytes(value), saveTime);
+    }
+
+    /**
+     * 读取 bitmap 数据
+     *
+     * @param key
+     * @return bitmap 数据
+     */
+    public Bitmap getAsBitmap(String key) {
+        if (getAsBinary(key) == null) {
+            return null;
+        }
+        return Utils.Bytes2Bimap(getAsBinary(key));
+    }
+    // =======================================
+    // ============= drawable 数据 读写 =============
+    // =======================================
+
+    /**
+     * 保存 drawable 到 缓存中
+     *
+     * @param key   保存的key
+     * @param value 保存的drawable数据
+     */
+    public void put(String key, Drawable value) {
+        put(key, Utils.drawable2Bitmap(value));
+    }
+
+    /**
+     * 保存 drawable 到 缓存中
+     *
+     * @param key      保存的key
+     * @param value    保存的 drawable 数据
+     * @param saveTime 保存的时间,单位:秒
+     */
+    public void put(String key, Drawable value, int saveTime) {
+        put(key, Utils.drawable2Bitmap(value), saveTime);
+    }
+
+    /**
+     * 读取 Drawable 数据
+     *
+     * @param key
+     * @return Drawable 数据
+     */
+    public Drawable getAsDrawable(String key) {
+        if (getAsBinary(key) == null) {
+            return null;
+        }
+        return Utils.bitmap2Drawable(Utils.Bytes2Bimap(getAsBinary(key)));
+    }
+
+    /**
+     * 获取缓存文件
+     *
+     * @param key
+     * @return value 缓存的文件
+     */
+    public File file(String key) {
+        File f = mCache.newFile(key);
+        if (f.exists())
+            return f;
+        return null;
+    }
+
+    /**
+     * 移除某个key
+     *
+     * @param key
+     * @return 是否移除成功
+     */
+    public boolean remove(String key) {
+        return mCache.remove(key);
+    }
+
+    /**
+     * 清除所有数据
+     */
+    public void clear() {
+        mCache.clear();
+    }
+
+    /**
+     * 缓存管理器
+     */
+    private class ACacheManager {
+        private final AtomicLong cacheSize;
+        private final AtomicInteger cacheCount;
+        private final long sizeLimit;
+        private final int countLimit;
+        private final Map<File, Long> lastUsageDates = Collections
+                .synchronizedMap(new HashMap<File, Long>());
+        protected File cacheDir;
+
+        private ACacheManager(File cacheDir, long sizeLimit, int countLimit) {
+            this.cacheDir = cacheDir;
+            this.sizeLimit = sizeLimit;
+            this.countLimit = countLimit;
+            cacheSize = new AtomicLong();
+            cacheCount = new AtomicInteger();
+            calculateCacheSizeAndCacheCount();
+        }
+
+        /**
+         * 计算 cacheSize和cacheCount
+         */
+        private void calculateCacheSizeAndCacheCount() {
+            new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    int size = 0;
+                    int count = 0;
+                    File[] cachedFiles = cacheDir.listFiles();
+                    if (cachedFiles != null) {
+                        for (File cachedFile : cachedFiles) {
+                            size += calculateSize(cachedFile);
+                            count += 1;
+                            lastUsageDates.put(cachedFile,
+                                    cachedFile.lastModified());
+                        }
+                        cacheSize.set(size);
+                        cacheCount.set(count);
+                    }
+                }
+            }).start();
+        }
+
+        private void put(File file) {
+            int curCacheCount = cacheCount.get();
+            while (curCacheCount + 1 > countLimit) {
+                long freedSize = removeNext();
+                cacheSize.addAndGet(-freedSize);
+                curCacheCount = cacheCount.addAndGet(-1);
+            }
+            cacheCount.addAndGet(1);
+            long valueSize = calculateSize(file);
+            long curCacheSize = cacheSize.get();
+            while (curCacheSize + valueSize > sizeLimit) {
+                long freedSize = removeNext();
+                curCacheSize = cacheSize.addAndGet(-freedSize);
+            }
+            cacheSize.addAndGet(valueSize);
+            Long currentTime = System.currentTimeMillis();
+            file.setLastModified(currentTime);
+            lastUsageDates.put(file, currentTime);
+        }
+
+        private File get(String key) {
+            File file = newFile(key);
+            Long currentTime = System.currentTimeMillis();
+            file.setLastModified(currentTime);
+            lastUsageDates.put(file, currentTime);
+            return file;
+        }
+
+        private File newFile(String key) {
+            return new File(cacheDir, key.hashCode() + "");
+        }
+
+        private boolean remove(String key) {
+            File image = get(key);
+            return image.delete();
+        }
+
+        private void clear() {
+            lastUsageDates.clear();
+            cacheSize.set(0);
+            File[] files = cacheDir.listFiles();
+            if (files != null) {
+                for (File f : files) {
+                    f.delete();
+                }
+            }
+        }
+
+        /**
+         * 移除旧的文件
+         *
+         * @return
+         */
+        private long removeNext() {
+            if (lastUsageDates.isEmpty()) {
+                return 0;
+            }
+            Long oldestUsage = null;
+            File mostLongUsedFile = null;
+            Set<Map.Entry<File, Long>> entries = lastUsageDates.entrySet();
+            synchronized (lastUsageDates) {
+                for (Map.Entry<File, Long> entry : entries) {
+                    if (mostLongUsedFile == null) {
+                        mostLongUsedFile = entry.getKey();
+                        oldestUsage = entry.getValue();
+                    } else {
+                        Long lastValueUsage = entry.getValue();
+                        if (lastValueUsage < oldestUsage) {
+                            oldestUsage = lastValueUsage;
+                            mostLongUsedFile = entry.getKey();
+                        }
+                    }
+                }
+            }
+            long fileSize = calculateSize(mostLongUsedFile);
+            if (mostLongUsedFile.delete()) {
+                lastUsageDates.remove(mostLongUsedFile);
+            }
+            return fileSize;
+        }
+
+        private long calculateSize(File file) {
+            return file.length();
+        }
+    }
+
+    /**
+     * 时间计算工具类
+     * 1.0
+     */
+    private static class Utils {
+        /**
+         * 判断缓存的String数据是否到期
+         *
+         * @param str
+         * @return true:到期了 false:还没有到期
+         */
+        private static boolean isDue(String str) {
+            return isDue(str.getBytes());
+        }
+
+        /**
+         * 判断缓存的byte数据是否到期
+         *
+         * @param data
+         * @return true:到期了 false:还没有到期
+         */
+        private static boolean isDue(byte[] data) {
+            String[] strs = getDateInfoFromDate(data);
+            if (strs != null && strs.length == 2) {
+                String saveTimeStr = strs[0];
+                while (saveTimeStr.startsWith("0")) {
+                    saveTimeStr = saveTimeStr
+                            .substring(1, saveTimeStr.length());
+                }
+                long saveTime = Long.valueOf(saveTimeStr);
+                long deleteAfter = Long.valueOf(strs[1]);
+                if (System.currentTimeMillis() > saveTime + deleteAfter * 1000) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        private static String newStringWithDateInfo(int second, String strInfo) {
+            return createDateInfo(second) + strInfo;
+        }
+
+        private static byte[] newByteArrayWithDateInfo(int second, byte[] data2) {
+            byte[] data1 = createDateInfo(second).getBytes();
+            byte[] retdata = new byte[data1.length + data2.length];
+            System.arraycopy(data1, 0, retdata, 0, data1.length);
+            System.arraycopy(data2, 0, retdata, data1.length, data2.length);
+            return retdata;
+        }
+
+        private static String clearDateInfo(String strInfo) {
+            if (strInfo != null && hasDateInfo(strInfo.getBytes())) {
+                strInfo = strInfo.substring(strInfo.indexOf(mSeparator) + 1,
+                        strInfo.length());
+            }
+            return strInfo;
+        }
+
+        private static byte[] clearDateInfo(byte[] data) {
+            if (hasDateInfo(data)) {
+                return copyOfRange(data, indexOf(data, mSeparator) + 1,
+                        data.length);
+            }
+            return data;
+        }
+
+        private static boolean hasDateInfo(byte[] data) {
+            return data != null && data.length > 15 && data[13] == '-'
+                    && indexOf(data, mSeparator) > 14;
+        }
+
+        private static String[] getDateInfoFromDate(byte[] data) {
+            if (hasDateInfo(data)) {
+                String saveDate = new String(copyOfRange(data, 0, 13));
+                String deleteAfter = new String(copyOfRange(data, 14,
+                        indexOf(data, mSeparator)));
+                return new String[]{saveDate, deleteAfter};
+            }
+            return null;
+        }
+
+        private static int indexOf(byte[] data, char c) {
+            for (int i = 0; i < data.length; i++) {
+                if (data[i] == c) {
+                    return i;
+                }
+            }
+            return -1;
+        }
+
+        private static byte[] copyOfRange(byte[] original, int from, int to) {
+            int newLength = to - from;
+            if (newLength < 0)
+                throw new IllegalArgumentException(from + " > " + to);
+            byte[] copy = new byte[newLength];
+            System.arraycopy(original, from, copy, 0,
+                    Math.min(original.length - from, newLength));
+            return copy;
+        }
+
+        private static final char mSeparator = ' ';
+
+        private static String createDateInfo(int second) {
+            String currentTime = System.currentTimeMillis() + "";
+            while (currentTime.length() < 13) {
+                currentTime = "0" + currentTime;
+            }
+            return currentTime + "-" + second + mSeparator;
+        }
+
+        /*
+         * Bitmap → byte[]
+         */
+        private static byte[] Bitmap2Bytes(Bitmap bm) {
+            if (bm == null) {
+                return null;
+            }
+            ByteArrayOutputStream baos = new ByteArrayOutputStream();
+            bm.compress(Bitmap.CompressFormat.PNG, 100, baos);
+            return baos.toByteArray();
+        }
+
+        /*
+         * byte[] → Bitmap
+         */
+        private static Bitmap Bytes2Bimap(byte[] b) {
+            if (b.length == 0) {
+                return null;
+            }
+            return BitmapFactory.decodeByteArray(b, 0, b.length);
+        }
+
+        /*
+         * Drawable → Bitmap
+         */
+        private static Bitmap drawable2Bitmap(Drawable drawable) {
+            if (drawable == null) {
+                return null;
+            }
+            // 取 drawable 的长宽
+            int w = drawable.getIntrinsicWidth();
+            int h = drawable.getIntrinsicHeight();
+            // 取 drawable 的颜色格式
+            Bitmap.Config config = drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888
+                    : Bitmap.Config.RGB_565;
+            // 建立对应 bitmap
+            Bitmap bitmap = Bitmap.createBitmap(w, h, config);
+            // 建立对应 bitmap 的画布
+            Canvas canvas = new Canvas(bitmap);
+            drawable.setBounds(0, 0, w, h);
+            // 把 drawable 内容画到画布中
+            drawable.draw(canvas);
+            return bitmap;
+        }
+
+        /*
+         * Bitmap → Drawable
+         */
+        @SuppressWarnings("deprecation")
+        private static Drawable bitmap2Drawable(Bitmap bm) {
+            if (bm == null) {
+                return null;
+            }
+            return new BitmapDrawable(bm);
+        }
+    }
+
+}

+ 166 - 0
cs-baselib/src/main/java/com/android/basiclib/cache/BaseLockCaches.java

@@ -0,0 +1,166 @@
+package com.android.basiclib.cache;
+
+import android.text.TextUtils;
+
+import java.io.IOException;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+/**
+ * 带读写锁的缓存基类
+ */
+public abstract class BaseLockCaches {
+
+    private final ReadWriteLock mLock = new ReentrantReadWriteLock();
+
+    /**
+     * 读取缓存
+     *
+     * @param key 缓存key
+     */
+    public final <T> T load(String key) {
+        //1.先检查key
+        if (TextUtils.isEmpty(key)) return null;
+
+        //2.判断key是否存在,key不存在去读缓存没意义
+        if (!containsKey(key)) {
+            return null;
+        }
+
+        //3.判断是否过期,过期自动清理
+        if (isExpiry(key)) {
+            remove(key);
+            return null;
+        }
+
+        //4.开始真正的读取缓存
+        mLock.readLock().lock();
+        try {
+            // 读取缓存
+            return doLoad(key);
+        } finally {
+            mLock.readLock().unlock();
+        }
+    }
+
+    /**
+     * 保存缓存
+     *
+     * @param key   缓存key
+     * @param value 缓存内容
+     * @return
+     */
+    public final <T> boolean save(String key, T value, long existTime) {
+        //1.先检查key
+        if (TextUtils.isEmpty(key)) return false;
+
+        //2.如果要保存的值为空,则删除
+        if (value == null) {
+            return remove(key);
+        }
+
+        //3.写入缓存
+        boolean status = false;
+        mLock.writeLock().lock();
+        try {
+            try {
+                status = doSave(key, value, existTime);
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        } finally {
+            mLock.writeLock().unlock();
+        }
+        return status;
+    }
+
+    public final <T> boolean save(String key, T value) {
+        return this.save(key, value,0);
+    }
+
+    /**
+     * 删除缓存
+     */
+    public final boolean remove(String key) {
+        mLock.writeLock().lock();
+        try {
+            return doRemove(key);
+        } finally {
+            mLock.writeLock().unlock();
+        }
+    }
+
+    /**
+     * 获取缓存大小
+     *
+     * @return
+     */
+    public long size() {
+        return getSize();
+    }
+
+    /**
+     * 清空缓存
+     */
+    public final boolean clear() {
+        mLock.writeLock().lock();
+        try {
+            return doClear();
+        } finally {
+            mLock.writeLock().unlock();
+        }
+    }
+
+    /**
+     * 是否包含 加final 是让子类不能被重写,只能使用doContainsKey
+     * 这里加了锁处理,操作安全。<br>
+     *
+     * @param key 缓存key
+     * @return 是否有缓存
+     */
+    public final boolean containsKey(String key) {
+        mLock.readLock().lock();
+        try {
+            return doContainsKey(key);
+        } finally {
+            mLock.readLock().unlock();
+        }
+    }
+
+    /**
+     * 是否包含  采用protected修饰符  被子类修改
+     */
+    protected abstract boolean doContainsKey(String key);
+
+    /**
+     * 是否过期
+     */
+    protected abstract boolean isExpiry(String key);
+
+    /**
+     * 读取缓存
+     */
+    protected abstract <T> T doLoad(String key);
+
+    /**
+     * 保存
+     */
+    protected abstract <T> boolean doSave(String key, T value, long existTime) throws IOException;
+
+    /**
+     * 删除缓存
+     */
+    protected abstract boolean doRemove(String key);
+
+    /**
+     * 清空缓存
+     */
+    protected abstract boolean doClear();
+
+    /**
+     * 获取缓存大小
+     *
+     * @return
+     */
+    protected abstract long getSize();
+}

+ 100 - 0
cs-baselib/src/main/java/com/android/basiclib/core/BaseLibCore.kt

@@ -0,0 +1,100 @@
+package com.android.basiclib.core
+
+import android.app.Application
+import android.graphics.Color
+import android.os.Environment
+import android.os.Handler
+import com.android.basiclib.BuildConfig
+import com.android.basiclib.R
+import com.android.basiclib.engine.preferences.IDataStoreOwner
+import com.android.basiclib.receiver.ConnectivityReceiver
+import com.android.basiclib.utils.CommUtils
+import com.android.basiclib.utils.ThreadPoolUtils
+
+import com.android.basiclib.utils.log.MyLogUtils
+import com.android.basiclib.utils.log.interceptor.Log2FileInterceptor
+import com.android.basiclib.utils.log.interceptor.LogDecorateInterceptor
+import com.android.basiclib.utils.log.interceptor.LogPrintInterceptor
+import com.android.basiclib.view.gloading.Gloading
+import com.android.basiclib.view.gloading.GloadingGlobalAdapter
+import com.android.basiclib.view.gloading.GloadingRoatingAdapter
+import com.android.basiclib.view.titlebar.EasyTitleBar
+
+import com.hjq.toast.Toaster
+import java.io.File
+
+/**
+ * 底层的模组依赖库都在这里初始化
+ * 当前类在Application中初始化
+ */
+object BaseLibCore {
+
+    const val successCode = 0   //指定网络请求成功的 Code,一般为 200 或者 0
+
+    /**
+     * 初始化全局工具类和图片加载引擎
+     */
+    fun init(application: Application, handler: Handler?, mainThread: Int) {
+
+        //CommUtil初始化
+        CommUtils.init(application, handler, mainThread)
+
+        //初始化全局通用的线程池
+        ThreadPoolUtils.init()
+
+        //EaseTitleBar的初始化
+        EasyTitleBar.init()
+            .backIconRes(R.mipmap.back_black)
+            .backgroud(Color.WHITE)
+            .titleSize(18)
+            .showLine(true)
+            .lineHeight(1)
+            .menuImgSize(23)
+            .menuTextSize(16)
+            .lineColor(Color.parseColor("#D2D2D2"))
+            .titleColor(Color.BLACK)
+            .viewPadding(10)
+            .titleBarHeight(48)
+
+        //全局的Loading状态默认配置
+        Gloading.initDefault(GloadingGlobalAdapter())
+
+        //吐司框架初始化
+        Toaster.init(application)
+
+        //配置Log的拦截器,只有Debug下才生效
+        if (BuildConfig.DEBUG) {
+
+            val logPath = if (Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED) {
+                application.applicationContext.getExternalFilesDir("log")?.absolutePath
+                    ?: (application.applicationContext.filesDir.absolutePath + "/log/")
+            } else {
+                application.applicationContext.filesDir.absolutePath + "/log/"
+            }
+
+            val dir = File(logPath)
+            if (!dir.exists()) {
+                dir.mkdirs()
+            }
+
+            MyLogUtils.addInterceptor(LogDecorateInterceptor(true))
+            MyLogUtils.addInterceptor(LogPrintInterceptor(true))
+            MyLogUtils.addInterceptor(Log2FileInterceptor.getInstance(logPath, true))
+        }
+
+        //DataStoreEngine的初始化
+        IDataStoreOwner.application = application
+
+    }
+
+    /**
+     * 注册网络监听
+     */
+    fun registerNetworkObserver(application: Application) {
+        ConnectivityReceiver.registerReceiver(application)
+    }
+
+    fun unregisterNetworkObserver(application: Application) {
+        ConnectivityReceiver.unregisterReceiver(application)
+    }
+}

+ 83 - 0
cs-baselib/src/main/java/com/android/basiclib/engine/dialog/DialogExt.kt

@@ -0,0 +1,83 @@
+package com.android.basiclib.engine.dialog
+
+import android.view.LayoutInflater
+import android.view.View
+import androidx.fragment.app.FragmentActivity
+import androidx.viewbinding.ViewBinding
+import com.android.basiclib.engine.dialog.callback.IPopupController
+import com.android.basiclib.engine.dialog.creator.PopupViewCreatorFactory
+import com.lxj.xpopup.XPopup
+import com.lxj.xpopup.enums.PopupAnimation
+
+/**
+ * 弹窗的弹出引擎,目前使用Xpopup
+ */
+fun <VB : ViewBinding> FragmentActivity.showPopup(
+    popupType: PopupType,  //必须指定类型
+    viewBinding: (LayoutInflater) -> VB,
+    popupAnimationType: PopupAnimationType? = null,  //动画类型
+    targetView: View? = null, //如果是Attach则一定要指定在哪个View的基础上弹窗
+    width: Int = 0,
+    height: Int = 0,   //宽高一般不需要额外设置,自动适配布局的宽高
+    maxWidth: Int = 0,  //如果想要指定宽高,可以自定义宽高
+    maxHeight: Int = 0,
+    offsetX: Int = 0,   //弹框在 X,Y 方向的偏移值
+    offsetY: Int = 0,
+    isCenterHorizontal: Boolean = false,  //布局是否水平居中
+    onCreateListener: ((VB, IPopupController) -> Unit)? = null,   //创建的回调
+    onDismissListener: (() -> Unit)? = null,     //消失的回调
+) {
+
+    //根据类型转换对应的PopupView继承
+    val creator = PopupViewCreatorFactory.getCreator<VB>(popupType, this, width, height, maxWidth, maxHeight, onCreateListener, onDismissListener)
+    val popupView = creator.create(viewBinding)
+
+    val builder = XPopup.Builder(this)
+        .hasShadowBg(popupType == PopupType.BOTTOM || popupType == PopupType.CENTER)  //默认底部弹窗和中间弹窗展示灰色背景遮罩
+        .isDestroyOnDismiss(true)  //消失的时候是否释放资源
+        .offsetX(offsetX)
+        .offsetY(offsetY)
+        .isCenterHorizontal(isCenterHorizontal)
+
+    if (targetView != null) {
+        builder.atView(targetView)
+    }
+
+    if (popupType == PopupType.KEYBOARD) {
+        builder.autoOpenSoftInput(true)
+            .moveUpToKeyboard(true)
+    }
+
+    //转换动画效果,是否需要添加动画
+    if (transformAnimationType(popupAnimationType) != null) {
+        builder.popupAnimation(transformAnimationType(popupAnimationType))
+    }
+
+    builder.asCustom(popupView)
+        .show()
+}
+
+//动画转换
+fun transformAnimationType(animationType: PopupAnimationType?): PopupAnimation? {
+
+    return when (animationType) {
+        PopupAnimationType.ScaleAlphaFromCenter -> PopupAnimation.ScaleAlphaFromCenter
+        PopupAnimationType.ScaleAlphaFromLeftTop -> PopupAnimation.ScaleAlphaFromLeftTop
+        PopupAnimationType.ScaleAlphaFromRightTop -> PopupAnimation.ScaleAlphaFromRightTop
+        PopupAnimationType.ScaleAlphaFromLeftBottom -> PopupAnimation.ScaleAlphaFromLeftBottom
+        PopupAnimationType.ScaleAlphaFromRightBottom -> PopupAnimation.ScaleAlphaFromRightBottom
+        PopupAnimationType.TranslateAlphaFromLeft -> PopupAnimation.TranslateAlphaFromLeft
+        PopupAnimationType.TranslateAlphaFromRight -> PopupAnimation.TranslateAlphaFromRight
+        PopupAnimationType.TranslateAlphaFromTop -> PopupAnimation.TranslateAlphaFromTop
+        PopupAnimationType.TranslateAlphaFromBottom -> PopupAnimation.TranslateAlphaFromBottom
+        PopupAnimationType.TranslateFromLeft -> PopupAnimation.TranslateFromLeft
+        PopupAnimationType.TranslateFromRight -> PopupAnimation.TranslateFromRight
+        PopupAnimationType.TranslateFromTop -> PopupAnimation.TranslateFromTop
+        PopupAnimationType.TranslateFromBottom -> PopupAnimation.TranslateFromBottom
+        PopupAnimationType.NoAnimation -> PopupAnimation.NoAnimation
+        else -> null
+    }
+
+}
+
+

+ 54 - 0
cs-baselib/src/main/java/com/android/basiclib/engine/dialog/DialogType.kt

@@ -0,0 +1,54 @@
+package com.android.basiclib.engine.dialog
+
+//常见的几种类型
+enum class PopupType {
+    BOTTOM, KEYBOARD, CENTER, FULL_SCREEN, ATTACH, PART_SCREEN
+}
+
+//常见的弹窗动画
+enum class PopupAnimationType {
+
+    // 缩放 + 透明渐变
+    ScaleAlphaFromCenter,
+
+    // 从中心进行缩放+透明渐变
+    ScaleAlphaFromLeftTop,
+
+    //从左上角进行缩放+透明渐变
+    ScaleAlphaFromRightTop,
+
+    //从右上角进行缩放+透明渐变
+    ScaleAlphaFromLeftBottom,
+
+    //从左下角进行缩放+透明渐变
+    ScaleAlphaFromRightBottom,
+
+    //从右下角进行缩放+透明渐变
+    // 平移 + 透明渐变
+    TranslateAlphaFromLeft,
+
+    // 从左平移进入
+    TranslateAlphaFromRight,
+
+    // 从右平移进入
+    TranslateAlphaFromTop,
+
+    // 从上方平移进入
+    TranslateAlphaFromBottom,
+
+    // 从下方平移进入
+    // 从左平移进入
+    TranslateFromLeft,
+
+    // 从右平移进入
+    TranslateFromRight,
+
+    // 从上方平移进入
+    TranslateFromTop,
+
+    // 从下方平移进入
+    TranslateFromBottom,
+
+    //禁用动画
+    NoAnimation
+}

+ 5 - 0
cs-baselib/src/main/java/com/android/basiclib/engine/dialog/callback/IPopupController.kt

@@ -0,0 +1,5 @@
+package com.android.basiclib.engine.dialog.callback
+
+fun interface IPopupController {
+    fun dismiss()  //关闭弹窗的操作
+}

+ 89 - 0
cs-baselib/src/main/java/com/android/basiclib/engine/dialog/creator/AttachPopupViewCreator.kt

@@ -0,0 +1,89 @@
+package com.android.basiclib.engine.dialog.creator
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.view.LayoutInflater
+import androidx.viewbinding.ViewBinding
+import com.android.basiclib.engine.dialog.callback.IPopupController
+import com.lxj.xpopup.core.AttachPopupView
+import com.lxj.xpopup.core.BasePopupView
+
+/**
+ * 宽度,高度自适应的依附弹窗,参照性别的下拉选弹窗
+ */
+@SuppressLint("ViewConstructor")
+private open class OpenAttachPopupView<VB : ViewBinding>(
+    context: Context,
+    private val viewBinding: ((LayoutInflater) -> VB)?,
+    private val width: Int,
+    private val height: Int,
+    private val maxWidth: Int,
+    private val maxHeight: Int,
+    private val onCreateListener: ((VB, IPopupController) -> Unit)?,
+    private val onDismissListener: (() -> Unit)?,
+) : AttachPopupView(context) {
+
+    //弹窗控制器-常见的功能
+    private val closeControl = IPopupController { dismiss() }
+    lateinit var mBinding: VB
+
+    override fun addInnerContent() {
+        if (viewBinding != null) {
+            mBinding = viewBinding.invoke(LayoutInflater.from(context))
+            attachPopupContainer.addView(mBinding.root)
+        } else {
+            super.addInnerContent()
+        }
+    }
+
+    override fun getPopupWidth(): Int {
+        return if (width != 0) width else super.getPopupWidth()
+    }
+
+    override fun getPopupHeight(): Int {
+        return if (height != 0) height else super.getPopupHeight()
+    }
+
+    // 如果需要覆写 getMaxWidth 和 getMaxHeight,可以提供自定义的最大宽高
+    override fun getMaxWidth(): Int {
+        return if (maxWidth != 0) maxWidth else super.getMaxWidth()
+    }
+
+    override fun getMaxHeight(): Int {
+        return if (maxHeight != 0) maxHeight else super.getMaxHeight()
+    }
+
+    override fun onCreate() {
+        super.onCreate()
+        onCreateListener?.invoke(mBinding, closeControl)
+    }
+
+    override fun onDismiss() {
+        super.onDismiss()
+        onDismissListener?.invoke()
+    }
+
+}
+
+class AttachPopupViewCreator<VB : ViewBinding>(
+    private val context: Context,
+    private val width: Int,
+    private val height: Int,
+    private val maxWidth: Int,
+    private val maxHeight: Int,
+    private val onCreateListener: ((VB, IPopupController) -> Unit)?,
+    private val onDismissListener: (() -> Unit)?,
+) : IPopupViewCreator<VB> {
+
+    override fun create(viewBinding: ((LayoutInflater) -> VB)?): BasePopupView {
+        return OpenAttachPopupView<VB>(
+            context,
+            viewBinding,
+            width, height,
+            maxWidth, maxHeight,
+            onCreateListener, onDismissListener
+        )
+
+    }
+
+}

+ 83 - 0
cs-baselib/src/main/java/com/android/basiclib/engine/dialog/creator/BottomPopupViewCreator.kt

@@ -0,0 +1,83 @@
+package com.android.basiclib.engine.dialog.creator
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.view.LayoutInflater
+import androidx.viewbinding.ViewBinding
+import com.android.basiclib.engine.dialog.callback.IPopupController
+import com.lxj.xpopup.core.BasePopupView
+import com.lxj.xpopup.core.BottomPopupView
+
+@SuppressLint("ViewConstructor")
+private open class OpenBottomPopupView<VB : ViewBinding>(
+    context: Context,
+    private val viewBinding: ((LayoutInflater) -> VB)?,
+    private val width: Int,
+    private val height: Int,
+    private val maxWidth: Int,
+    private val maxHeight: Int,
+    private val onCreateListener: ((VB, IPopupController) -> Unit)?,
+    private val onDismissListener: (() -> Unit)?,
+) : BottomPopupView(context) {
+
+    //弹窗控制器-常见的功能
+    private val closeControl = IPopupController { dismiss() }
+    lateinit var mBinding: VB
+
+    override fun addInnerContent() {
+        if (viewBinding != null) {
+            mBinding = viewBinding.invoke(LayoutInflater.from(context))
+            bottomPopupContainer.addView(mBinding.root)
+        } else {
+            super.addInnerContent()
+        }
+    }
+
+    override fun getPopupWidth(): Int {
+        return if (width != 0) width else super.getPopupWidth()
+    }
+
+    override fun getPopupHeight(): Int {
+        return if (height != 0) height else super.getPopupHeight()
+    }
+
+    // 如果需要覆写 getMaxWidth 和 getMaxHeight,可以提供自定义的最大宽高
+    override fun getMaxWidth(): Int {
+        return if (maxWidth != 0) maxWidth else super.getMaxWidth()
+    }
+
+    override fun getMaxHeight(): Int {
+        return if (maxHeight != 0) maxHeight else super.getMaxHeight()
+    }
+
+    override fun onCreate() {
+        super.onCreate()
+        onCreateListener?.invoke(mBinding, closeControl)
+    }
+
+    override fun onDismiss() {
+        super.onDismiss()
+        onDismissListener?.invoke()
+    }
+
+}
+
+class BottomPopupViewCreator<VB : ViewBinding>(
+    private val context: Context,
+    private val width: Int,
+    private val height: Int,
+    private val maxWidth: Int,
+    private val maxHeight: Int,
+    private val onCreateListener: ((VB, IPopupController) -> Unit)?,
+    private val onDismissListener: (() -> Unit)?,
+) : IPopupViewCreator<VB> {
+    override fun create(viewBinding: ((LayoutInflater) -> VB)?): BasePopupView {
+        return OpenBottomPopupView(
+            context,
+            viewBinding,
+            width, height,
+            maxWidth, maxHeight,
+            onCreateListener, onDismissListener
+        )
+    }
+}

+ 83 - 0
cs-baselib/src/main/java/com/android/basiclib/engine/dialog/creator/CenterPopupViewCreator.kt

@@ -0,0 +1,83 @@
+package com.android.basiclib.engine.dialog.creator
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.view.LayoutInflater
+import androidx.viewbinding.ViewBinding
+import com.android.basiclib.engine.dialog.callback.IPopupController
+import com.lxj.xpopup.core.BasePopupView
+import com.lxj.xpopup.core.CenterPopupView
+
+@SuppressLint("ViewConstructor")
+private open class OpenCenterPopupView<VB : ViewBinding>(
+    context: Context,
+    private val viewBinding: ((LayoutInflater) -> VB)?,
+    private val width: Int,
+    private val height: Int,
+    private val maxWidth: Int,
+    private val maxHeight: Int,
+    private val onCreateListener: ((VB, IPopupController) -> Unit)?,
+    private val onDismissListener: (() -> Unit)?,
+) : CenterPopupView(context) {
+
+    //弹窗控制器-常见的功能
+    private val closeControl = IPopupController { dismiss() }
+    lateinit var mBinding: VB
+
+    override fun addInnerContent() {
+        if (viewBinding != null) {
+            mBinding = viewBinding.invoke(LayoutInflater.from(context))
+            centerPopupContainer.addView(mBinding.root)
+        } else {
+            super.addInnerContent()
+        }
+    }
+
+    override fun getPopupWidth(): Int {
+        return if (width != 0) width else super.getPopupWidth()
+    }
+
+    override fun getPopupHeight(): Int {
+        return if (height != 0) height else super.getPopupHeight()
+    }
+
+    // 如果需要覆写 getMaxWidth 和 getMaxHeight,可以提供自定义的最大宽高
+    override fun getMaxWidth(): Int {
+        return if (maxWidth != 0) maxWidth else super.getMaxWidth()
+    }
+
+    override fun getMaxHeight(): Int {
+        return if (maxHeight != 0) maxHeight else super.getMaxHeight()
+    }
+
+    override fun onCreate() {
+        super.onCreate()
+        onCreateListener?.invoke(mBinding, closeControl)
+    }
+
+    override fun onDismiss() {
+        super.onDismiss()
+        onDismissListener?.invoke()
+    }
+
+}
+
+class CenterPopupViewCreator<VB : ViewBinding>(
+    private val context: Context,
+    private val width: Int,
+    private val height: Int,
+    private val maxWidth: Int,
+    private val maxHeight: Int,
+    private val onCreateListener: ((VB, IPopupController) -> Unit)?,
+    private val onDismissListener: (() -> Unit)?,
+) : IPopupViewCreator<VB> {
+    override fun create( viewBinding: ((LayoutInflater) -> VB)?): BasePopupView {
+        return OpenCenterPopupView(
+            context,
+            viewBinding,
+            width, height,
+            maxWidth, maxHeight,
+            onCreateListener, onDismissListener
+        )
+    }
+}

+ 57 - 0
cs-baselib/src/main/java/com/android/basiclib/engine/dialog/creator/FullScreenPopupViewCreator.kt

@@ -0,0 +1,57 @@
+package com.android.basiclib.engine.dialog.creator
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.view.LayoutInflater
+import androidx.viewbinding.ViewBinding
+import com.android.basiclib.engine.dialog.callback.IPopupController
+import com.lxj.xpopup.core.BasePopupView
+import com.lxj.xpopup.impl.FullScreenPopupView
+
+@SuppressLint("ViewConstructor")
+private open class OpenFullScreenPopupView<VB : ViewBinding>(
+    context: Context,
+
+    private val viewBinding: ((LayoutInflater) -> VB)?,
+    private val onCreateListener: ((VB, IPopupController) -> Unit)?,
+    private val onDismissListener: (() -> Unit)?,
+) : FullScreenPopupView(context) {
+
+    //弹窗控制器-常见的功能
+    private val closeControl = IPopupController { dismiss() }
+    lateinit var mBinding: VB
+
+    override fun addInnerContent() {
+        if (viewBinding != null) {
+            mBinding = viewBinding.invoke(LayoutInflater.from(context))
+            fullPopupContainer.addView(mBinding.root)
+        } else {
+            super.addInnerContent()
+        }
+    }
+
+    override fun onCreate() {
+        super.onCreate()
+        onCreateListener?.invoke(mBinding, closeControl)
+    }
+
+    override fun onDismiss() {
+        super.onDismiss()
+        onDismissListener?.invoke()
+    }
+
+}
+
+class FullScreenPopupViewCreator<VB : ViewBinding>(
+    private val context: Context,
+    private val onCreateListener: ((VB, IPopupController) -> Unit)?,
+    private val onDismissListener: (() -> Unit)?,
+) : IPopupViewCreator<VB> {
+    override fun create( viewBinding: ((LayoutInflater) -> VB)?): BasePopupView {
+        return OpenFullScreenPopupView(
+            context,
+            viewBinding,
+            onCreateListener, onDismissListener
+        )
+    }
+}

+ 10 - 0
cs-baselib/src/main/java/com/android/basiclib/engine/dialog/creator/IPopupViewCreator.kt

@@ -0,0 +1,10 @@
+package com.android.basiclib.engine.dialog.creator
+
+import android.view.LayoutInflater
+import androidx.viewbinding.ViewBinding
+import com.lxj.xpopup.core.BasePopupView
+
+//根据弹窗的枚举类型,创建对应的弹窗实例,在引擎封装类中处理,外部只需要处理View的逻辑即可
+interface IPopupViewCreator<VB : ViewBinding> {
+    fun create(viewBinding: ((LayoutInflater) -> VB)?): BasePopupView
+}

+ 74 - 0
cs-baselib/src/main/java/com/android/basiclib/engine/dialog/creator/PartPopupViewCreator.kt

@@ -0,0 +1,74 @@
+package com.android.basiclib.engine.dialog.creator
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.view.LayoutInflater
+import androidx.viewbinding.ViewBinding
+import com.android.basiclib.engine.dialog.callback.IPopupController
+import com.lxj.xpopup.core.BasePopupView
+import com.lxj.xpopup.impl.PartShadowPopupView
+
+/**
+ * 宽度自动占满一屏,高度自适应的弹窗,参照京东的筛选弹窗
+ */
+@SuppressLint("ViewConstructor")
+private open class OpenPartPopupView<VB : ViewBinding>(
+    context: Context,
+    private val viewBinding: ((LayoutInflater) -> VB)?,
+    private val height: Int,
+    private val maxHeight: Int,
+    private val onCreateListener: ((VB, IPopupController) -> Unit)?,
+    private val onDismissListener: (() -> Unit)?,
+) : PartShadowPopupView(context) {
+
+    //弹窗控制器-常见的功能
+    private val closeControl = IPopupController { dismiss() }
+
+    lateinit var mBinding: VB
+
+    override fun addInnerContent() {
+        if (viewBinding != null) {
+            mBinding = viewBinding.invoke(LayoutInflater.from(context))
+            attachPopupContainer.addView(mBinding.root)
+        } else {
+            super.addInnerContent()
+        }
+    }
+
+    override fun getPopupHeight(): Int {
+        return if (height != 0) height else super.getPopupHeight()
+    }
+
+    override fun getMaxHeight(): Int {
+        return if (maxHeight != 0) maxHeight else super.getMaxHeight()
+    }
+
+    override fun onCreate() {
+        super.onCreate()
+        onCreateListener?.invoke(mBinding, closeControl)
+    }
+
+    override fun onDismiss() {
+        super.onDismiss()
+        onDismissListener?.invoke()
+    }
+
+}
+
+class PartPopupViewCreator<VB : ViewBinding>(
+    private val context: Context,
+    private val height: Int,
+    private val maxHeight: Int,
+    private val onCreateListener: ((VB, IPopupController) -> Unit)?,
+    private val onDismissListener: (() -> Unit)?,
+) : IPopupViewCreator<VB> {
+    override fun create(viewBinding: ((LayoutInflater) -> VB)?): BasePopupView {
+        return OpenPartPopupView(
+            context,
+            viewBinding,
+            height,
+            maxHeight,
+            onCreateListener, onDismissListener
+        )
+    }
+}

+ 29 - 0
cs-baselib/src/main/java/com/android/basiclib/engine/dialog/creator/PopupViewCreatorFactory.kt

@@ -0,0 +1,29 @@
+package com.android.basiclib.engine.dialog.creator
+
+import android.content.Context
+import androidx.viewbinding.ViewBinding
+import com.android.basiclib.engine.dialog.PopupType
+import com.android.basiclib.engine.dialog.callback.IPopupController
+
+object PopupViewCreatorFactory {
+
+    fun <VB : ViewBinding> getCreator(
+        popupType: PopupType,
+        context: Context,
+        width: Int = 0,
+        height: Int = 0,
+        maxWidth: Int = 0,
+        maxHeight: Int = 0,
+        onCreateListener: ((VB, IPopupController) -> Unit)? = null,
+        onDismissListener: (() -> Unit)? = null,
+    ): IPopupViewCreator<VB> {
+        return when (popupType) {
+            PopupType.BOTTOM -> BottomPopupViewCreator(context, width, height, maxWidth, maxHeight, onCreateListener, onDismissListener)
+            PopupType.KEYBOARD -> BottomPopupViewCreator(context, width, height, maxWidth, maxHeight, onCreateListener, onDismissListener)
+            PopupType.CENTER -> CenterPopupViewCreator(context, width, height, maxWidth, maxHeight, onCreateListener, onDismissListener)
+            PopupType.FULL_SCREEN -> FullScreenPopupViewCreator(context, onCreateListener, onDismissListener)
+            PopupType.PART_SCREEN -> PartPopupViewCreator(context, height, maxHeight, onCreateListener, onDismissListener)
+            PopupType.ATTACH -> AttachPopupViewCreator(context, width, height, maxWidth, maxHeight, onCreateListener, onDismissListener)
+        }
+    }
+}

+ 110 - 0
cs-baselib/src/main/java/com/android/basiclib/engine/image_grid/ImageGridAdapter.kt

@@ -0,0 +1,110 @@
+package com.android.basiclib.engine.image_grid
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageView
+import androidx.recyclerview.widget.RecyclerView
+import com.android.basiclib.R
+import com.android.basiclib.engine.image_load.loadImage
+import com.android.basiclib.engine.preview.ImagePreviewEngine
+import com.android.basiclib.ext.click
+
+/**
+ * 图片选择显示九宫格适配器
+ */
+class ImageGridAdapter(
+    private var imageUrls: MutableList<String>,
+    private val maxSelection: Int,
+    private val addIconResId: Int,
+    private val deleteIconResId: Int,
+    private val placeholderResId: Int,
+    private val addImageAction: () -> Unit,  //添加图片的回调
+    private val delImageAction: (Int) -> Unit,  //删除图片的回调
+) : RecyclerView.Adapter<ImageGridAdapter.ImageViewHolder>() {
+
+    private var isSelectionMode: Boolean = true
+
+    //手动设置展示模式,选择模式还是显示模式
+    fun setSelectionMode(isSelectionMode: Boolean) {
+        this.isSelectionMode = isSelectionMode
+    }
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ImageViewHolder {
+        val view = LayoutInflater.from(parent.context).inflate(R.layout.item_image_grid, parent, false)
+        return ImageViewHolder(view)
+    }
+
+    override fun onBindViewHolder(holder: ImageViewHolder, position: Int) {
+        if (isSelectionMode) {
+            if (imageUrls.size > 0 && position < imageUrls.size) {
+                val item = imageUrls[position]
+                //绑定数据,设置图片
+                holder.ivImage.loadImage(item, placeholderResId)
+                holder.ivDelete.setImageResource(deleteIconResId)
+
+                //设置状态
+                holder.ivDelete.visibility = View.VISIBLE
+
+                //设置点击事件
+                holder.ivDelete.click {
+                    delImageAction(position)
+                }
+
+                //调用图片预览引擎预览多图
+                holder.ivImage.click {
+                    ImagePreviewEngine.multipleImagePreview(it.context, holder.ivImage, imageUrls, position,
+                        updateAction = { controller, index ->
+                            val rv = holder.itemView.parent as RecyclerView
+                            controller.updateSrcView(rv.getChildAt(index).findViewById(R.id.iv_image))
+                        })
+                }
+
+            } else {
+                // 最后一个位置显示 "Add" 图片
+                holder.ivImage.setImageResource(addIconResId)
+                holder.ivDelete.visibility = View.GONE
+                holder.ivImage.click {
+                    //添加图片的选择
+                    addImageAction()
+                }
+            }
+
+        } else {
+            //纯展示模式,没有添加和删除的功能
+            val item = imageUrls[position]
+            //绑定数据,设置图片
+            holder.ivImage.loadImage(item, placeholderResId)
+            holder.ivDelete.setImageResource(deleteIconResId)
+            holder.ivDelete.visibility = View.GONE
+
+            //调用图片预览引擎预览多图
+            holder.ivImage.click {
+                ImagePreviewEngine.multipleImagePreview(it.context, holder.ivImage, imageUrls, position,
+                    updateAction = { controller, index ->
+                        val rv = holder.itemView.parent as RecyclerView
+                        controller.updateSrcView(rv.getChildAt(index).findViewById(R.id.iv_image))
+                    })
+            }
+
+        }
+
+    }
+
+    override fun getItemCount(): Int {
+        return if (!isSelectionMode) {
+            imageUrls.size
+        } else {
+            if (imageUrls.size >= maxSelection) {
+                imageUrls.size
+            } else {
+                imageUrls.size + 1
+            }
+        }
+    }
+
+    inner class ImageViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
+        val ivImage: ImageView = itemView.findViewById(R.id.iv_image)
+        val ivDelete: ImageView = itemView.findViewById(R.id.iv_delete)
+    }
+}

+ 124 - 0
cs-baselib/src/main/java/com/android/basiclib/engine/image_grid/ImageGridView.kt

@@ -0,0 +1,124 @@
+package com.android.basiclib.engine.image_grid
+
+import android.annotation.SuppressLint
+import android.app.Activity
+import android.content.Context
+import android.util.AttributeSet
+import androidx.recyclerview.widget.GridLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import com.android.basiclib.R
+import com.android.basiclib.engine.image_select.dialog.ImageSelectDialogEngine
+import com.luck.picture.lib.decoration.GridSpacingItemDecoration
+
+/**
+ * 图片选择显示九宫格,根据模式可以选择图片,可以显示图片,常用于提交多图片场景,如发布朋友圈,提交意见反馈图片等
+ */
+class ImageGridView(context: Context, attrs: AttributeSet) : RecyclerView(context, attrs) {
+
+    private var maxSelection: Int = 0
+    private var addIconResId: Int = 0
+    private var deleteIconResId: Int = 0
+    private var placeholderResId: Int = 0
+    private var isSelectionMode: Boolean = true
+    private var gridSpacing: Int = 0
+    private val images: MutableList<String> = mutableListOf()
+    private lateinit var imageAdapter: ImageGridAdapter
+
+    init {
+        val a = context.obtainStyledAttributes(attrs, R.styleable.ImageGridView)
+        maxSelection = a.getInt(R.styleable.ImageGridView_maxSelection, 9)
+        addIconResId = a.getResourceId(R.styleable.ImageGridView_addIcon, R.drawable.img_picture_add)
+        deleteIconResId = a.getResourceId(R.styleable.ImageGridView_deleteIcon, R.drawable.img_picture_delete)
+        placeholderResId = a.getResourceId(R.styleable.ImageGridView_placeholder, com.luck.picture.lib.R.drawable.ps_image_placeholder)
+        isSelectionMode = a.getBoolean(R.styleable.ImageGridView_isSelectionMode, true)
+        gridSpacing = a.getDimensionPixelSize(R.styleable.ImageGridView_gridSpacing, 10)
+        a.recycle()
+    }
+
+    //手动设置展示模式,选择模式还是显示模式
+    fun setSelectionMode(isSelectionMode: Boolean) {
+        this.isSelectionMode = isSelectionMode
+    }
+
+    fun setMaxSelection(maxSelection: Int) {
+        this.maxSelection = maxSelection
+    }
+
+    fun setAddIconResId(addIconResId: Int) {
+        this.addIconResId = addIconResId
+    }
+
+    fun setDeleteIconResId(deleteIconResId: Int) {
+        this.deleteIconResId = deleteIconResId
+    }
+
+    fun setPlaceholderResId(placeholderResId: Int) {
+        this.placeholderResId = placeholderResId
+    }
+
+    fun setGridSpacing(gridSpacing: Int) {
+        this.gridSpacing = gridSpacing
+    }
+
+    //获取当前已经选取的图片集合
+    fun getImageList(): List<String> = images
+
+    /**
+     * 设置类型与数据
+     */
+    @SuppressLint("NotifyDataSetChanged")
+    fun setup(isSelectionMode: Boolean, list: List<String>? = null) {
+
+        // 设置布局管理器和间距
+        val layoutManager = GridLayoutManager(context, 3)
+        layoutManager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
+            override fun getSpanSize(position: Int): Int {
+                return 1
+            }
+        }
+        layoutManager.spanCount = 3
+        this.layoutManager = layoutManager
+
+        this.addItemDecoration(GridSpacingItemDecoration(3, gridSpacing, true))
+
+        if (!list.isNullOrEmpty()) {
+            images.clear()
+            images.addAll(list)
+        }
+
+        imageAdapter = ImageGridAdapter(images, maxSelection, addIconResId, deleteIconResId, placeholderResId,
+            addImageAction = {
+                addImagePick()
+            },
+            delImageAction = {
+                deleteImageByPosition(it)
+            }
+        )
+
+        this.isSelectionMode = isSelectionMode
+        imageAdapter.setSelectionMode(this.isSelectionMode)
+
+        this.adapter = imageAdapter
+
+    }
+
+    //删除图片
+    @SuppressLint("NotifyDataSetChanged")
+    private fun deleteImageByPosition(position: Int) {
+        images.removeAt(position)
+        imageAdapter.notifyDataSetChanged()
+    }
+
+    //添加图片
+    @SuppressLint("NotifyDataSetChanged")
+    private fun addImagePick() {
+        val activity = context as? Activity
+        activity?.let {
+            ImageSelectDialogEngine.imageSelect(it, maxNumFromAlbum = maxSelection - images.size) {
+                images.addAll(it)
+                imageAdapter.notifyDataSetChanged()
+            }
+        }
+
+    }
+}

+ 161 - 0
cs-baselib/src/main/java/com/android/basiclib/engine/image_load/ImageLoadExt.kt

@@ -0,0 +1,161 @@
+package com.android.basiclib.engine.image_load
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.graphics.drawable.Drawable
+import android.widget.ImageView
+import com.android.basiclib.glideconfig.GlideApp
+import com.bumptech.glide.load.resource.bitmap.CenterCrop
+import com.bumptech.glide.load.resource.bitmap.CircleCrop
+import com.bumptech.glide.load.resource.bitmap.RoundedCorners
+import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions
+import com.bumptech.glide.request.RequestOptions
+import com.bumptech.glide.request.target.SimpleTarget
+import com.bumptech.glide.request.target.Target
+import com.bumptech.glide.request.transition.Transition
+import pl.droidsonroids.gif.GifDrawable
+import java.io.File
+import java.io.IOException
+
+/**
+ *  引擎类
+ *  ImageView相关的图片加载
+ */
+
+/**
+ * Glide加载图片
+ * @param url 可以是网络,可以是File,可以是资源id等等Glide支持的类型
+ * @param placeholderRes 默认占位图
+ * @param error 失败占位图
+ * @param isCircle 是否是圆形,默认false,注意:isCircle和roundRadius两个只能有一个生效
+ * @param isCenterCrop 是否设置scaleType为CenterCrop,你也可以在布局文件中设置
+ * @param roundRadius 圆角角度,默认为0,不带圆角,注意:isCircle和roundRadius两个只能有一个生效
+ * @param isCrossFade 是否有过渡动画,默认有过渡动画
+ * @param isForceOriginalSize 是否强制使用原图,默认false
+ */
+@SuppressLint("CheckResult")
+fun ImageView.loadImage(
+    url: Any?,
+    placeholderRes: Int = 0,
+    error: Int = 0,
+    isCircle: Boolean = false,
+    isCenterCrop: Boolean = false,
+    roundRadius: Int = 0,
+    placeholder: Drawable? = null,
+    isCrossFade: Boolean = false,
+    isForceOriginalSize: Boolean = false
+) {
+
+    //配置选项
+    val options = RequestOptions()
+
+    if (placeholder != null) {
+        options.placeholder(placeholder)
+    } else {
+        options.placeholder(placeholderRes)
+    }
+
+    options.error(error).apply {
+        if (isCenterCrop && scaleType != ImageView.ScaleType.CENTER_CROP)
+            scaleType = ImageView.ScaleType.CENTER_CROP
+        if (isCircle) {
+            if (scaleType == ImageView.ScaleType.CENTER_CROP) {
+                transforms(CenterCrop(), CircleCrop())
+            } else {
+                transform(CircleCrop())
+            }
+        } else if (roundRadius != 0) {
+            if (scaleType == ImageView.ScaleType.CENTER_CROP) {
+                transforms(CenterCrop(), RoundedCorners(roundRadius))
+            } else {
+                transform(RoundedCorners(roundRadius))
+            }
+        }
+        if (isForceOriginalSize) {
+            override(Target.SIZE_ORIGINAL)
+        }
+    }
+
+    //以配置的方式加载图片
+    GlideApp.with(context).load(url)
+        .apply(options)
+        .apply {
+            if (isCrossFade) transition(DrawableTransitionOptions.withCrossFade())
+        }
+        .into(this)
+}
+
+/**
+ * ImageView加载Gif的图片
+ */
+@SuppressLint("CheckResult")
+fun ImageView.loadGif(path: Any, placeholderRes: Int) {
+
+    val options = RequestOptions()
+    if (placeholderRes > 0) {
+        options.placeholder(placeholderRes)
+    }
+
+    GlideApp.with(context).load(path).apply(options).downloadOnly(object : SimpleTarget<File?>() {
+        override fun onLoadStarted(placeholder: Drawable?) {
+            super.onLoadStarted(placeholder)
+        }
+
+        override fun onLoadFailed(errorDrawable: Drawable?) {
+            super.onLoadFailed(errorDrawable)
+        }
+
+        override fun onResourceReady(resource: File, transition: Transition<in File?>?) {
+            try {
+                val gifDrawable = GifDrawable(resource)
+                gifDrawable.start()
+                setImageDrawable(gifDrawable)
+            } catch (e: IOException) {
+                e.printStackTrace()
+            }
+        }
+
+    })
+}
+
+/**
+ * 下载并返回File对象
+ */
+@SuppressLint("CheckResult")
+fun Any.downloadImage(context: Context, path: Any?, block: (file: File) -> Unit) {
+
+    GlideApp.with(context).load(path).downloadOnly(object : SimpleTarget<File?>() {
+
+        override fun onResourceReady(resource: File, transition: Transition<in File?>?) {
+
+            block(resource)
+        }
+    })
+
+}
+
+/**
+ * 显示Url图片,下载并解析出Drawable对象
+ */
+@SuppressLint("CheckResult")
+fun Any.loadDrawable(context: Context, path: String?, placeholderRes: Int?, block: (drawable: Drawable?) -> Unit) {
+
+    val options = RequestOptions()
+    placeholderRes?.also {
+        if (it > 0) {
+            options.placeholder(placeholderRes)
+        }
+    }
+
+    GlideApp.with(context).load(path).apply(options).into(object : SimpleTarget<Drawable>() {
+        override fun onLoadStarted(placeholder: Drawable?) {
+            super.onLoadStarted(placeholder)
+            block(placeholder)
+        }
+
+        override fun onResourceReady(resource: Drawable, transition: Transition<in Drawable>?) {
+            block(resource)
+        }
+
+    })
+}

+ 96 - 0
cs-baselib/src/main/java/com/android/basiclib/engine/image_select/ImageFileCompressEngine.kt

@@ -0,0 +1,96 @@
+package com.android.basiclib.engine.image_select
+
+import android.content.Context
+import android.net.Uri
+import com.android.basiclib.utils.log.MyLogUtils
+import com.luck.picture.lib.config.PictureMimeType
+import com.luck.picture.lib.engine.CompressFileEngine
+import com.luck.picture.lib.interfaces.OnKeyValueResultCallbackListener
+import com.luck.picture.lib.utils.DateUtils
+import top.zibin.luban.Luban
+import top.zibin.luban.OnNewCompressListener
+import java.io.File
+
+/**
+ * 图片压缩引擎,可用于 ImageSelectEngine 中设置,实现CompressFileEngine可以在 ImageSelectEngine 中自动配置相机和相册后的压缩
+ *
+ * 也可以单独使用,设置本地图片的Uri和目标Path的Uri,即可单独实现裁剪
+ */
+class ImageFileCompressEngine : CompressFileEngine {
+    /**
+     * 自定义方法,可以自己手动压缩图片,传入路径即可
+     */
+    fun onStartSanboxCompress(context: Context?, paths: ArrayList<String>?, call: OnKeyValueResultCallbackListener?) {
+
+        Luban.with(context).load(paths).ignoreBy(100) //最大100K
+            .setRenameListener { filePath ->
+                val indexOf = filePath.lastIndexOf(".")
+                val postfix = if (indexOf != -1) filePath.substring(indexOf) else ".jpg"
+                DateUtils.getCreateFileName("CMP_") + postfix
+            }.filter { path -> //是带图片后缀的图片,并且不含有Http的,不是gif图片
+                if (PictureMimeType.isUrlHasImage(path) &&
+                    !PictureMimeType.isHasHttp(path) &&
+                    !PictureMimeType.isUrlHasGif(path)
+                ) {
+                    MyLogUtils.w("符合条件可以使用Luban压缩")
+                    true
+                } else {
+                    MyLogUtils.w("不符合条件不能使用Luban压缩")
+                    false
+                }
+            }.setCompressListener(object : OnNewCompressListener {
+                override fun onStart() {
+                    MyLogUtils.w("开始Luban压缩")
+                }
+
+                override fun onSuccess(source: String, compressFile: File) {
+                    MyLogUtils.w("Luban压缩成功 source:" + source + " compressFile:" + compressFile.absolutePath)
+                    call?.onCallback(source, compressFile.absolutePath)
+                }
+
+                override fun onError(source: String, e: Throwable) {
+                    MyLogUtils.w("Luban压缩失败 source:$source")
+                    call?.onCallback(source, null)
+                }
+            }).launch()
+    }
+
+    /**
+     * 重写的接口方法,专用于 Image Select 中的图片自动压缩
+     */
+    override fun onStartCompress(context: Context, source: ArrayList<Uri>, call: OnKeyValueResultCallbackListener) {
+        Luban.with(context).load(source).ignoreBy(100) //最大100K
+            .setRenameListener { filePath ->
+                val indexOf = filePath.lastIndexOf(".")
+                val postfix = if (indexOf != -1) filePath.substring(indexOf) else ".jpg"
+                DateUtils.getCreateFileName("CMP_") + postfix
+            }.filter { path -> //是带图片后缀的图片,并且不含有Http的,不是gif图片
+                if (PictureMimeType.isUrlHasImage(path) &&
+                    !PictureMimeType.isHasHttp(path) &&
+                    !PictureMimeType.isUrlHasGif(path)
+                ) {
+                    MyLogUtils.w("符合条件可以使用Luban压缩")
+                    true
+                } else {
+                    MyLogUtils.w("不符合条件不能使用Luban压缩")
+                    false
+                }
+            }.setCompressListener(object : OnNewCompressListener {
+                override fun onStart() {
+                    MyLogUtils.w("开始Luban压缩")
+                }
+
+                override fun onSuccess(source: String, compressFile: File) {
+                    MyLogUtils.w("Luban压缩成功 source:" + source + " compressFile:" + compressFile.absolutePath)
+                    call.onCallback(source, compressFile.absolutePath)
+
+                }
+
+                override fun onError(source: String, e: Throwable) {
+                    MyLogUtils.w("Luban压缩失败 source:$source")
+                    call.onCallback(source, null)
+
+                }
+            }).launch()
+    }
+}

+ 125 - 0
cs-baselib/src/main/java/com/android/basiclib/engine/image_select/ImageFileCropEngine.kt

@@ -0,0 +1,125 @@
+package com.android.basiclib.engine.image_select
+
+import android.app.Activity
+import android.content.Context
+import android.content.ContextWrapper
+import android.graphics.Bitmap
+import android.graphics.drawable.Drawable
+import android.net.Uri
+import android.widget.ImageView
+import androidx.core.content.ContextCompat
+import androidx.fragment.app.Fragment
+import com.android.basiclib.utils.CommUtils
+import com.bumptech.glide.Glide
+import com.bumptech.glide.request.target.CustomTarget
+import com.bumptech.glide.request.transition.Transition
+import com.luck.picture.lib.R
+import com.luck.picture.lib.engine.CropFileEngine
+import com.luck.picture.lib.utils.StyleUtils
+import com.yalantis.ucrop.UCrop
+import com.yalantis.ucrop.UCropImageEngine
+
+/**
+ * 图片裁剪引擎,可用于 ImageSelectEngine 中设置,实现 CropFileEngine 接口可以在 ImageSelectEngine 中自动配置相机和相册后的裁剪
+ *
+ *
+ * 也可以单独使用,设置本地图片的Uri和目标Path的Uri,即可单独实现裁剪
+ */
+class ImageFileCropEngine(private val ratioX: Int, private val ratioY: Int) : CropFileEngine {
+
+    override fun onStartCrop(fragment: Fragment, srcUri: Uri, destinationUri: Uri, dataSource: ArrayList<String>, requestCode: Int) {
+        val options = buildOptions(CommUtils.getContext(), ratioX, ratioY)
+        val uCrop: UCrop = if (dataSource.isEmpty()) {
+            UCrop.of<Any>(srcUri, destinationUri)
+        } else {
+            UCrop.of(srcUri, destinationUri, dataSource)
+        }
+        uCrop.withOptions(options)
+        uCrop.setImageEngine(object : UCropImageEngine {
+            override fun loadImage(context: Context, url: String, imageView: ImageView) {
+                if (!assertValidRequest(context)) {
+                    return
+                }
+                Glide.with(context).load(url).override(180, 180).into(imageView)
+            }
+
+            override fun loadImage(context: Context, url: Uri, maxWidth: Int, maxHeight: Int, call: UCropImageEngine.OnCallbackListener<Bitmap>) {
+                Glide.with(context).asBitmap().load(url).override(maxWidth, maxHeight).into(object : CustomTarget<Bitmap?>() {
+                    override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap?>?) {
+                        call.onCall(resource)
+                    }
+
+                    override fun onLoadCleared(placeholder: Drawable?) {
+                        call.onCall(null)
+                    }
+                })
+            }
+        })
+        uCrop.start(fragment.requireActivity(), fragment, requestCode)
+    }
+
+    /**
+     * 配制UCrop,可根据需求自我扩展
+     */
+    private fun buildOptions(context: Context, ratioX: Int, ratioY: Int): UCrop.Options {
+        val options = UCrop.Options()
+//        options.setHideBottomControls(!cb_hide.isChecked());
+//        options.setFreeStyleCropEnabled(cb_styleCrop.isChecked());
+//        options.setShowCropFrame(cb_showCropFrame.isChecked());
+//        options.setShowCropGrid(cb_showCropGrid.isChecked());
+//        options.setCircleDimmedLayer(cb_crop_circular.isChecked());
+//        options.setCropOutputPathDir(getSandboxPath());
+//        options.setSkipCropMimeType(getNotSupportCrop());
+//        options.isForbidCropGifWebp(cb_not_gif.isChecked());
+        options.withAspectRatio(ratioX.toFloat(), ratioY.toFloat()) //裁剪比例
+        options.isCropDragSmoothToCenter(false)
+        options.isForbidSkipMultipleCrop(true)
+        options.setMaxScaleMultiplier(100f)
+        val selectorStyle = ImageSelectEngine.selectorStyle
+        if (selectorStyle.selectMainStyle.statusBarColor != 0) {
+            val mainStyle = selectorStyle.selectMainStyle
+            val isDarkStatusBarBlack = mainStyle.isDarkStatusBarBlack
+            val statusBarColor = mainStyle.statusBarColor
+            options.isDarkStatusBarBlack(isDarkStatusBarBlack)
+            if (StyleUtils.checkStyleValidity(statusBarColor)) {
+                options.setStatusBarColor(statusBarColor)
+                options.setToolbarColor(statusBarColor)
+            } else {
+                options.setStatusBarColor(ContextCompat.getColor(context, R.color.ps_color_grey))
+                options.setToolbarColor(ContextCompat.getColor(context, R.color.ps_color_grey))
+            }
+            val titleBarStyle = selectorStyle.titleBarStyle
+            if (StyleUtils.checkStyleValidity(titleBarStyle.titleTextColor)) {
+                options.setToolbarWidgetColor(titleBarStyle.titleTextColor)
+            } else {
+                options.setToolbarWidgetColor(ContextCompat.getColor(context, R.color.ps_color_white))
+            }
+        } else {
+            options.setStatusBarColor(ContextCompat.getColor(CommUtils.getContext(), R.color.ps_color_grey))
+            options.setToolbarColor(ContextCompat.getColor(CommUtils.getContext(), R.color.ps_color_grey))
+            options.setToolbarWidgetColor(ContextCompat.getColor(CommUtils.getContext(), R.color.ps_color_white))
+        }
+        return options
+    }
+
+
+
+    fun assertValidRequest(context: Context?): Boolean {
+        if (context is Activity) {
+            return !isDestroy(context)
+        } else if (context is ContextWrapper) {
+            if (context.baseContext is Activity) {
+                val activity = context.baseContext as Activity
+                return !isDestroy(activity)
+            }
+        }
+        return true
+    }
+
+    private fun isDestroy(activity: Activity?): Boolean {
+        return if (activity == null) {
+            true
+        } else activity.isFinishing || activity.isDestroyed
+    }
+
+}

+ 246 - 0
cs-baselib/src/main/java/com/android/basiclib/engine/image_select/ImageSelectEngine.kt

@@ -0,0 +1,246 @@
+package com.android.basiclib.engine.image_select
+
+import android.Manifest
+import android.animation.AnimatorSet
+import android.animation.ObjectAnimator
+import android.app.Activity
+import android.content.Context
+import android.content.pm.ActivityInfo
+import android.view.animation.AnimationUtils
+import androidx.core.content.ContextCompat
+import com.android.basiclib.engine.image_select.result.ImageSelectEntity
+import com.android.basiclib.engine.image_select.result.OnImageSelectResult
+import com.android.basiclib.engine.permission.requestPermission
+import com.android.basiclib.engine.toast.toast
+import com.android.basiclib.utils.CheckUtil
+import com.android.basiclib.utils.CommUtils
+import com.luck.picture.lib.R
+import com.luck.picture.lib.animators.AnimationType
+import com.luck.picture.lib.basic.PictureSelector
+import com.luck.picture.lib.config.PictureMimeType
+import com.luck.picture.lib.config.SelectMimeType
+import com.luck.picture.lib.engine.UriToFileTransformEngine
+import com.luck.picture.lib.entity.LocalMedia
+import com.luck.picture.lib.interfaces.OnKeyValueResultCallbackListener
+import com.luck.picture.lib.interfaces.OnQueryFilterListener
+import com.luck.picture.lib.interfaces.OnResultCallbackListener
+import com.luck.picture.lib.style.BottomNavBarStyle
+import com.luck.picture.lib.style.PictureSelectorStyle
+import com.luck.picture.lib.style.SelectMainStyle
+import com.luck.picture.lib.style.TitleBarStyle
+import com.luck.picture.lib.utils.SandboxTransformUtils
+
+object ImageSelectEngine {
+
+    @JvmField
+    var selectorStyle: PictureSelectorStyle = PictureSelectorStyle()
+
+    init {
+        // 主体风格
+        val numberSelectMainStyle = SelectMainStyle()
+        numberSelectMainStyle.isSelectNumberStyle = true
+        numberSelectMainStyle.isPreviewSelectNumberStyle = false
+        numberSelectMainStyle.isPreviewDisplaySelectGallery = true
+        numberSelectMainStyle.selectBackground = R.drawable.ps_default_num_selector
+        numberSelectMainStyle.previewSelectBackground = R.drawable.ps_preview_checkbox_selector
+        numberSelectMainStyle.selectNormalBackgroundResources = R.drawable.ps_select_complete_normal_bg
+        numberSelectMainStyle.selectNormalTextColor = ContextCompat.getColor(CommUtils.getContext(), R.color.ps_color_53575e)
+        numberSelectMainStyle.setSelectNormalText(R.string.ps_send)
+        numberSelectMainStyle.adapterPreviewGalleryBackgroundResource = R.drawable.ps_preview_gallery_bg
+        numberSelectMainStyle.adapterPreviewGalleryItemSize = CommUtils.dip2px(52)
+        numberSelectMainStyle.setPreviewSelectText(R.string.ps_select)
+        numberSelectMainStyle.previewSelectTextSize = 14
+        numberSelectMainStyle.previewSelectTextColor = ContextCompat.getColor(CommUtils.getContext(), R.color.ps_color_white)
+        numberSelectMainStyle.previewSelectMarginRight = CommUtils.dip2px(6)
+        numberSelectMainStyle.selectBackgroundResources = R.drawable.ps_select_complete_bg
+        numberSelectMainStyle.setSelectText(R.string.ps_send_num)
+        numberSelectMainStyle.selectTextColor = ContextCompat.getColor(CommUtils.getContext(), R.color.ps_color_white)
+        numberSelectMainStyle.mainListBackgroundColor = ContextCompat.getColor(CommUtils.getContext(), R.color.ps_color_black)
+        numberSelectMainStyle.isCompleteSelectRelativeTop = true
+        numberSelectMainStyle.isPreviewSelectRelativeBottom = true
+        numberSelectMainStyle.isAdapterItemIncludeEdge = false
+
+        // 头部TitleBar 风格
+        val numberTitleBarStyle = TitleBarStyle()
+        numberTitleBarStyle.isHideCancelButton = true
+        numberTitleBarStyle.isAlbumTitleRelativeLeft = true
+        numberTitleBarStyle.titleAlbumBackgroundResource = R.drawable.ps_album_bg
+        numberTitleBarStyle.titleDrawableRightResource = R.drawable.ps_ic_grey_arrow
+        numberTitleBarStyle.previewTitleLeftBackResource = R.drawable.ps_ic_normal_back
+
+        // 底部NavBar 风格
+        val numberBottomNavBarStyle = BottomNavBarStyle()
+        numberBottomNavBarStyle.bottomPreviewNarBarBackgroundColor =
+            ContextCompat.getColor(CommUtils.getContext(), R.color.ps_color_half_grey)
+        numberBottomNavBarStyle.setBottomPreviewNormalText(R.string.ps_preview)
+        numberBottomNavBarStyle.bottomPreviewNormalTextColor = ContextCompat.getColor(CommUtils.getContext(), R.color.ps_color_9b)
+        numberBottomNavBarStyle.bottomPreviewNormalTextSize = 16
+        numberBottomNavBarStyle.isCompleteCountTips = false
+        numberBottomNavBarStyle.setBottomPreviewSelectText(R.string.ps_preview_num)
+        numberBottomNavBarStyle.bottomPreviewSelectTextColor = ContextCompat.getColor(CommUtils.getContext(), R.color.ps_color_white)
+        selectorStyle.titleBarStyle = numberTitleBarStyle
+        selectorStyle.bottomBarStyle = numberBottomNavBarStyle
+        selectorStyle.selectMainStyle = numberSelectMainStyle
+    }
+
+    /**
+     * 自定义沙盒文件处理
+     */
+    private class MeSandboxFileEngine : UriToFileTransformEngine {
+        override fun onUriToFileAsyncTransform(
+            context: Context,
+            srcPath: String,
+            mineType: String,
+            call: OnKeyValueResultCallbackListener
+        ) {
+            call.onCallback(srcPath, SandboxTransformUtils.copyPathToSandbox(context, srcPath, mineType))
+        }
+    }
+
+    private val notSupportCrop: Array<String>
+        //不支持裁剪的格式
+        get() = arrayOf(PictureMimeType.ofGIF(), PictureMimeType.ofWEBP())
+
+    /**
+     * 打开相机
+     */
+    fun openCamera(
+        activity: Activity,
+        selectedList: List<LocalMedia>?,
+        canCrop: Boolean = false,
+        canCompress: Boolean = true,
+        ratioX: Int = 1,
+        ratioY: Int = 1,
+        resultListener: OnImageSelectResult?
+    ) {
+
+        activity.requestPermission(
+            Manifest.permission.CAMERA,
+            Manifest.permission.READ_MEDIA_IMAGES,  //Target33之后Android7的手机也需要申请此权限
+            Manifest.permission.WRITE_EXTERNAL_STORAGE
+        ) {
+            PictureSelector.create(activity)
+                .openCamera(SelectMimeType.ofImage())
+                .setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) // 设置相册Activity方向,不设置默认使用系统
+                .setCropEngine(if (canCrop) ImageFileCropEngine(ratioX, ratioY) else null)
+                .setCompressEngine(if (canCompress) ImageFileCompressEngine() else null)
+                .setSandboxFileEngine(MeSandboxFileEngine())
+                .setSelectedData(selectedList)
+                .forResult(ImageSelectResultListener(resultListener))
+        }
+    }
+
+    //自定义的数据回调
+    class ImageSelectResultListener(private val listener: OnImageSelectResult?) :
+        OnResultCallbackListener<LocalMedia?> {
+        override fun onResult(result: java.util.ArrayList<LocalMedia?>?) {
+            val dataList = ArrayList<ImageSelectEntity>()
+
+            result?.let {
+                for (item in it) {
+                    item?.let { localMedia ->
+                        val compressPath = localMedia.compressPath
+                        val realPath = localMedia.realPath
+                        val path = if (CheckUtil.isEmpty(compressPath)) realPath else compressPath
+
+                        dataList.add(
+                            ImageSelectEntity(
+                                localMedia.mimeType,
+                                path,
+                                localMedia.videoThumbnailPath,
+                                localMedia.duration
+                            )
+                        )
+                    }
+                }
+            }
+
+            listener?.onResult(dataList)
+        }
+
+        override fun onCancel() {
+            toast(R.string.ps_cancel)
+        }
+    }
+
+
+    /**
+     * 选择相册
+     */
+    fun openAlbum(
+        activity: Activity,
+        selectedList: List<LocalMedia>?,
+        selectNum: Int,
+        canTackPhoto: Boolean = false,
+        canVideo: Boolean = false,
+        canCrop: Boolean = false,
+        canCompress: Boolean = true,
+        ratioX: Int = 1,
+        ratioY: Int = 1,
+        resultListener: OnImageSelectResult?
+    ) {
+        val permissionList: MutableList<String> = ArrayList()
+        permissionList.add(Manifest.permission.READ_MEDIA_IMAGES)
+        permissionList.add(Manifest.permission.WRITE_EXTERNAL_STORAGE)
+        if (canVideo) {
+            permissionList.add(Manifest.permission.READ_MEDIA_VIDEO)
+        }
+        if (canTackPhoto) {
+            permissionList.add(Manifest.permission.CAMERA)
+        }
+        val permissionArray = permissionList.toTypedArray<String>()
+
+        activity.requestPermission(*permissionArray) {
+            // 正式开启相册
+            PictureSelector.create(activity)
+                .openGallery(if (canVideo) SelectMimeType.ofAll() else SelectMimeType.ofImage())
+                .setSelectorUIStyle(selectorStyle)
+                .setImageEngine(ImageSelectGlideEngine) // 外部传入图片加载引擎,必传项
+                .setCropEngine(if (canCrop) ImageFileCropEngine(ratioX, ratioY) else null)
+                .setCompressEngine(if (canCompress) ImageFileCompressEngine() else null)
+                .setSandboxFileEngine(MeSandboxFileEngine())
+                .setSelectedData(selectedList)
+                .isLoopAutoVideoPlay(true)
+                .isUseSystemVideoPlayer(true)
+                .isPageSyncAlbumCount(true)
+                .setQueryFilterListener(OnQueryFilterListener { return@OnQueryFilterListener false })
+                .setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) // 设置相册Activity方向,不设置默认使用系统
+                .isOriginalControl(false)
+                .isDisplayCamera(canTackPhoto)
+                .isOpenClickSound(false)
+                .setSkipCropMimeType(*notSupportCrop)
+                .isFastSlidingSelect(false)
+                .isWithSelectVideoImage(true)
+                .isPreviewFullScreenMode(false)
+                .isVideoPauseResumePlay(true)
+                .isPreviewZoomEffect(true)
+                .isPreviewImage(true)
+                .isPreviewVideo(true)
+                .isPreviewAudio(true)
+                .setGridItemSelectAnimListener { view, isSelected ->
+                    val set = AnimatorSet()
+                    set.playTogether(
+                        ObjectAnimator.ofFloat(view, "scaleX", if (isSelected) 1f else 1.12f, if (isSelected) 1.12f else 1.0f),
+                        ObjectAnimator.ofFloat(view, "scaleY", if (isSelected) 1f else 1.12f, if (isSelected) 1.12f else 1.0f)
+                    )
+                    set.setDuration(350)
+                    set.start()
+                }
+                .setSelectAnimListener { view ->
+                    val animation = AnimationUtils.loadAnimation(CommUtils.getContext(), R.anim.ps_anim_modal_in)
+                    view.startAnimation(animation)
+                    return@setSelectAnimListener animation.duration
+                }
+                .isMaxSelectEnabledMask(true)
+                .isDirectReturnSingle(true)
+                .setMaxSelectNum(selectNum)
+                .setRecyclerAnimationMode(AnimationType.DEFAULT_ANIMATION)
+                .isGif(true)
+                .forResult(ImageSelectResultListener(resultListener))
+        }
+
+    }
+
+    private fun cleanAllCache() {}
+
+}

+ 64 - 0
cs-baselib/src/main/java/com/android/basiclib/engine/image_select/ImageSelectGlideEngine.kt

@@ -0,0 +1,64 @@
+package com.android.basiclib.engine.image_select
+
+import android.content.Context
+import android.widget.ImageView
+import com.android.basiclib.glideconfig.GlideApp
+import com.bumptech.glide.Glide
+import com.bumptech.glide.load.resource.bitmap.CenterCrop
+import com.bumptech.glide.load.resource.bitmap.RoundedCorners
+import com.bumptech.glide.request.RequestOptions
+import com.luck.picture.lib.R
+import com.luck.picture.lib.engine.ImageEngine
+import com.luck.picture.lib.utils.ActivityCompatHelper
+
+/**
+ * ImageSelect 的图片加载引擎
+ */
+object ImageSelectGlideEngine : ImageEngine {
+
+    /**
+     * 图片选择的框架加载图片
+     */
+    override fun loadImage(context: Context, url: String, imageView: ImageView) {
+        GlideApp.with(context).load(url).into(imageView)
+    }
+
+    override fun loadImage(context: Context, imageView: ImageView, url: String, maxWidth: Int, maxHeight: Int) {
+        if (!ActivityCompatHelper.assertValidRequest(context)) {
+            return
+        }
+        Glide.with(context)
+            .load(url)
+            .override(maxWidth, maxHeight)
+            .into(imageView)
+    }
+
+    override fun loadAlbumCover(context: Context, url: String, imageView: ImageView) {
+        if (!ActivityCompatHelper.assertValidRequest(context)) {
+            return
+        }
+        Glide.with(context)
+            .asBitmap()
+            .load(url)
+            .override(180, 180)
+            .sizeMultiplier(0.5f)
+            .transform(CenterCrop(), RoundedCorners(8))
+            .placeholder(R.drawable.ps_image_placeholder)
+            .into(imageView)
+    }
+
+    override fun loadGridImage(context: Context, url: String, imageView: ImageView) {
+        if (!ActivityCompatHelper.assertValidRequest(context)) {
+            return
+        }
+        GlideApp.with(context)
+            .load(url)
+            .override(200, 200)
+            .centerCrop()
+            .apply(RequestOptions().placeholder(R.drawable.ps_image_placeholder))
+            .into(imageView)
+    }
+
+    override fun pauseRequests(context: Context) {}
+    override fun resumeRequests(context: Context) {}
+}

+ 57 - 0
cs-baselib/src/main/java/com/android/basiclib/engine/image_select/dialog/ImageSelectDialogEngine.kt

@@ -0,0 +1,57 @@
+package com.android.basiclib.engine.image_select.dialog
+
+import android.app.Activity
+import com.android.basiclib.engine.image_select.ImageSelectEngine
+import com.android.basiclib.view.dialog.PickPhotoDialog
+
+/**
+ * 带弹窗的相机相册的选择
+ */
+object ImageSelectDialogEngine {
+
+    /**
+     * 弹出选择相机相册的弹窗
+     */
+    fun imageSelect(
+        activity: Activity,
+        maxNumFromAlbum: Int = 1,
+        allowAlbum: Boolean = true,
+        needCrop: Boolean = false,
+        needCompress: Boolean = true,
+        cropRatioX: Int = 1,
+        cropRatioY: Int = 1,
+        resultAction: (List<String>) -> Unit
+    ) {
+
+        PickPhotoDialog(activity, allowAlbum).apply {
+            SetOnChooseClickListener(object : PickPhotoDialog.OnChooseClickListener {
+                override fun chooseCamera() {
+                    ImageSelectEngine.openCamera(activity, null, needCrop, needCompress, cropRatioX, cropRatioY) {
+                        resultAction(it.map {
+                            it.finalPath
+                        })
+                    }
+                }
+
+                override fun chooseAlbum() {
+                    ImageSelectEngine.openAlbum(
+                        activity, null, maxNumFromAlbum,
+                        canTackPhoto = false,
+                        canVideo = false,
+                        canCrop = needCrop,
+                        canCompress = needCompress,
+                        ratioX = cropRatioX,
+                        ratioY = cropRatioY
+                    ) {
+                        resultAction(it.map {
+                            it.finalPath
+                        })
+                    }
+                }
+            })
+            show()
+        }
+
+    }
+
+}

+ 0 - 0
cs-baselib/src/main/java/com/android/basiclib/engine/image_select/result/ImageSelectEntity.kt


Vissa filer visades inte eftersom för många filer har ändrats