master 1c3abb817160 cached
139 files
181.2 KB
51.5k tokens
1 requests
Download .txt
Showing preview only (228K chars total). Download the full file or copy to clipboard to get everything.
Repository: iceCola7/AndroidModuleSamples
Branch: master
Commit: 1c3abb817160
Files: 139
Total size: 181.2 KB

Directory structure:
gitextract_u32wwojs/

├── .gitignore
├── README.md
├── app/
│   ├── .gitignore
│   ├── build.gradle
│   ├── proguard-rules.pro
│   └── src/
│       ├── androidTest/
│       │   └── java/
│       │       └── com/
│       │           └── cxz/
│       │               └── module/
│       │                   └── samples/
│       │                       └── ExampleInstrumentedTest.kt
│       ├── main/
│       │   ├── AndroidManifest.xml
│       │   ├── java/
│       │   │   └── com/
│       │   │       └── cxz/
│       │   │           └── module/
│       │   │               └── samples/
│       │   │                   ├── MainActivity.kt
│       │   │                   ├── OtherActivity.kt
│       │   │                   └── app/
│       │   │                       └── App.kt
│       │   └── res/
│       │       ├── drawable/
│       │       │   └── ic_launcher_background.xml
│       │       ├── drawable-v24/
│       │       │   └── ic_launcher_foreground.xml
│       │       ├── layout/
│       │       │   ├── app_activity_main.xml
│       │       │   └── app_activity_other.xml
│       │       ├── mipmap-anydpi-v26/
│       │       │   ├── ic_launcher.xml
│       │       │   └── ic_launcher_round.xml
│       │       └── values/
│       │           ├── colors.xml
│       │           ├── strings.xml
│       │           └── styles.xml
│       └── test/
│           └── java/
│               └── com/
│                   └── cxz/
│                       └── module/
│                           └── samples/
│                               └── ExampleUnitTest.kt
├── baselibs/
│   ├── .gitignore
│   ├── build.gradle
│   ├── proguard-rules.pro
│   └── src/
│       └── main/
│           ├── AndroidManifest.xml
│           ├── java/
│           │   └── com/
│           │       └── cxz/
│           │           └── kotlin/
│           │               └── baselibs/
│           │                   ├── base/
│           │                   │   ├── BaseActivity.kt
│           │                   │   ├── BaseFragment.kt
│           │                   │   ├── BaseMvpActivity.kt
│           │                   │   ├── BaseMvpFragment.kt
│           │                   │   └── BaseMvpTitleActivity.kt
│           │                   ├── bean/
│           │                   │   └── BaseBean.kt
│           │                   ├── config/
│           │                   │   └── AppConfig.kt
│           │                   ├── ext/
│           │                   │   ├── CommonExt.kt
│           │                   │   └── RxExt.kt
│           │                   ├── http/
│           │                   │   ├── HttpStatus.kt
│           │                   │   ├── RetrofitFactory.kt
│           │                   │   ├── constant/
│           │                   │   │   └── HttpConstant.kt
│           │                   │   ├── cookies/
│           │                   │   │   ├── CookieManager.kt
│           │                   │   │   ├── OkHttpCookies.kt
│           │                   │   │   └── PersistentCookieStore.kt
│           │                   │   ├── exception/
│           │                   │   │   ├── ApiException.kt
│           │                   │   │   └── ExceptionHandle.kt
│           │                   │   ├── function/
│           │                   │   │   └── RetryWithDelay.kt
│           │                   │   └── interceptor/
│           │                   │       ├── CacheInterceptor.kt
│           │                   │       ├── CookieInterceptor.kt
│           │                   │       ├── HeaderInterceptor.kt
│           │                   │       └── QueryParameterInterceptor.kt
│           │                   ├── mvp/
│           │                   │   ├── BaseModel.kt
│           │                   │   ├── BasePresenter.kt
│           │                   │   ├── IModel.kt
│           │                   │   ├── IPresenter.kt
│           │                   │   └── IView.kt
│           │                   ├── provider/
│           │                   │   └── NewsService.kt
│           │                   ├── rx/
│           │                   │   ├── BaseObserver.kt
│           │                   │   ├── BaseSubscriber.kt
│           │                   │   ├── SchedulerUtils.kt
│           │                   │   └── scheduler/
│           │                   │       ├── BaseScheduler.kt
│           │                   │       ├── ComputationMainScheduler.kt
│           │                   │       ├── IoMainScheduler.kt
│           │                   │       ├── NewThreadMainScheduler.kt
│           │                   │       ├── SingleMainScheduler.kt
│           │                   │       └── TrampolineMainScheduler.kt
│           │                   ├── utils/
│           │                   │   ├── AnimatorUtil.kt
│           │                   │   ├── AppUtils.kt
│           │                   │   ├── CommonUtil.kt
│           │                   │   ├── FileProvider7.kt
│           │                   │   ├── KeyBoardUtil.kt
│           │                   │   ├── NLog.kt
│           │                   │   ├── NetWorkUtil.kt
│           │                   │   ├── Preference.kt
│           │                   │   ├── RomUtil.kt
│           │                   │   ├── RxTimerUtil.kt
│           │                   │   └── StatusBarUtil.kt
│           │                   └── widget/
│           │                       ├── CustomToast.kt
│           │                       ├── LoadingDialog.kt
│           │                       └── OnNoDoubleClickListener.kt
│           └── res/
│               ├── drawable/
│               │   ├── bg_loading_dialog.xml
│               │   └── bg_toast_custom.xml
│               ├── layout/
│               │   ├── activity_base_title.xml
│               │   ├── base_toolbar.xml
│               │   ├── layout_loading_dialog.xml
│               │   └── toast_custom.xml
│               ├── values/
│               │   ├── colors.xml
│               │   ├── dimen.xml
│               │   ├── ids.xml
│               │   ├── strings.xml
│               │   └── styles.xml
│               └── xml/
│                   ├── file_paths.xml
│                   └── network_security_config.xml
├── build.gradle
├── config.gradle
├── gradle/
│   └── wrapper/
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── module_me/
│   ├── .gitignore
│   ├── build.gradle
│   ├── proguard-rules.pro
│   └── src/
│       └── main/
│           ├── AndroidManifest.xml
│           ├── java/
│           │   ├── com/
│           │   │   └── cxz/
│           │   │       └── module/
│           │   │           └── me/
│           │   │               ├── MeMainActivity.kt
│           │   │               └── mvp/
│           │   │                   ├── contract/
│           │   │                   │   └── MeMainContract.kt
│           │   │                   ├── model/
│           │   │                   │   └── MeMainModel.kt
│           │   │                   └── persenter/
│           │   │                       └── MeMainPresenter.kt
│           │   └── debug/
│           │       └── MeApplication.kt
│           ├── module/
│           │   └── AndroidManifest.xml
│           └── res/
│               ├── layout/
│               │   └── me_activity_me_main.xml
│               └── values/
│                   ├── colors.xml
│                   ├── strings.xml
│                   └── styles.xml
├── module_news/
│   ├── .gitignore
│   ├── build.gradle
│   ├── proguard-rules.pro
│   └── src/
│       └── main/
│           ├── AndroidManifest.xml
│           ├── java/
│           │   ├── com/
│           │   │   └── cxz/
│           │   │       └── module/
│           │   │           └── news/
│           │   │               ├── NewsMainActivity.kt
│           │   │               ├── NewsServiceImpl.kt
│           │   │               └── mvp/
│           │   │                   ├── contract/
│           │   │                   │   └── NewsMainContract.kt
│           │   │                   ├── model/
│           │   │                   │   └── NewsMainModel.kt
│           │   │                   └── persenter/
│           │   │                       └── NewsMainPresenter.kt
│           │   └── debug/
│           │       └── NewsApplication.kt
│           ├── module/
│           │   └── AndroidManifest.xml
│           └── res/
│               ├── layout/
│               │   └── news_activity_news_main.xml
│               └── values/
│                   ├── colors.xml
│                   ├── strings.xml
│                   └── styles.xml
├── module_video/
│   ├── .gitignore
│   ├── build.gradle
│   ├── proguard-rules.pro
│   └── src/
│       └── main/
│           ├── AndroidManifest.xml
│           ├── java/
│           │   ├── com/
│           │   │   └── cxz/
│           │   │       └── module/
│           │   │           └── video/
│           │   │               ├── VideoMainActivity.kt
│           │   │               └── mvp/
│           │   │                   ├── contract/
│           │   │                   │   └── VideoMainContract.kt
│           │   │                   ├── model/
│           │   │                   │   └── VideoMainModel.kt
│           │   │                   └── persenter/
│           │   │                       └── VideoMainPresenter.kt
│           │   └── debug/
│           │       └── VideoApplication.kt
│           ├── module/
│           │   └── AndroidManifest.xml
│           └── res/
│               ├── layout/
│               │   └── video_activity_video_main.xml
│               └── values/
│                   ├── colors.xml
│                   ├── strings.xml
│                   └── styles.xml
└── settings.gradle

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
*.iml
.gradle
/local.properties
/.idea
.DS_Store
/build
/captures
.externalNativeBuild


================================================
FILE: README.md
================================================
# 基于 MVP 的 Android 组件化开发框架实践

## 一、背景

当我们的项目变得越来越大,代码变得越来越臃肿,耦合会越来越多,编译速度越来越慢,开发效率也会变得越来越低,怎么办?这个时候我们就需要对旧项目进行重构,即是模块的拆分,官方的说法就是组件化。

## 二、简介

那什么是组件化呢?其基本理念是:把常用的功能、控件、基础类、第三方库、权限等公共部分抽离封装,我们称之为基础组件(`baselibs`);把业务分成 N 个模块进行独立的管理,每一个模块我们称之为业务组件;而所有的业务组件都需要依赖于封装的基础组件,业务组件之间不做依赖,这样的目的是为了让每一个业务模块都能单独运行。而在 APP 层对整个项目的模块进行封装。

> 业务模块之间的跳转可以通过路由(`Arouter`)实现;业务模块之间的通信可以通过消息(`EventBus`)来实现。

## 三、基础搭建 

#### 1、组件框架图

![](/art/00.png)

#### 2、根据组件框架图搭建的项目结构图

![项目结构图](/art/01.png)

#### 3、接下来介绍每个模块

项目中总共有五个 `module` ,包括 3 个业务模块、一个基础模块和一个 `APP` 壳模块。

在建好项目之后我们需要给 3 个 `module` 配置 “集成开发模式” 和 “组件开发模式” 的切换开关,可以在 `gradle.properties` 文件中定义变量 `isModel` ,`isModel=false` 代表是 “集成开发模式” , `isModel=true` 代表是 “组件开发模式” (**注:每次修改isModel的值后一定要Sysn才会生效**)。

![](/art/02.png)

**1)APP 壳模块**

主要就是集成每一个模块,最终打包成一个完整的 `apk` ,其中 `gradle` 做了如下配置,根据配置文件中的 `isModel` 字段来依赖不同的业务组件;

![](/art/03.png)

**2)baselibs 模块**

主要负责封装公共部分,如 `MVP` 架构、 `BaseView` 的封装、网络请求库、图片加载库、工具类以及自定义控件等;
> 为了防止重复依赖,所有的第三方库都放在这个模块,业务模块不做任何第三方依赖,只依赖于 `baselibs` 模块。

`baselibs` 模块的结构如下:

![](/art/04.png)

在 `baselibs` 模块的 `gradle` 中引入的库

```
dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    configurations {
        all*.exclude group: 'com.android.support', module: 'support-v13'
    }
    testImplementation rootProject.ext.testDeps["junit"]
    androidTestImplementation rootProject.ext.testDeps["runner"]
    androidTestImplementation rootProject.ext.testDeps["espresso-core"]
    //leakCanary
    debugApi rootProject.ext.testDeps["leakcanary-debug"]
    releaseApi rootProject.ext.testDeps["leakcanary-release"]
    // Support库
    api rootProject.ext.supportLibs
    // 网络请求库
    api rootProject.ext.networkLibs
    // RxJava2
    api rootProject.ext.rxJavaLibs
    // commonLibs
    api rootProject.ext.commonLibs
    kapt rootProject.ext.otherDeps["arouter-compiler"]
}
```

**3)业务模块(module_news、module_video、module_me)**

每一个业务模块在 “集成开发模式” 下以 `library` 的形式存在;在 “组件开发模式” 下以 `application` 的形式存在,可以单独运行。

由于每个业务模块的配置文件都差不多,下面就以 `module_news` 模块为例;

以下是 `module_news` 模块的 `gradle` 配置文件:

```
if (isModule.toBoolean()) {
    apply plugin: 'com.android.application'
} else {
    apply plugin: 'com.android.library'
}
android {
    if (isModule.toBoolean()) {
        applicationId "com.cxz.module.me"
    }
    compileSdkVersion rootProject.ext.android.compileSdkVersion
    defaultConfig {
        minSdkVersion rootProject.ext.android.minSdkVersion
        targetSdkVersion rootProject.ext.android.targetSdkVersion
        versionCode 1
        versionName "1.0"
    }
}
dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    testImplementation rootProject.ext.testDeps["junit"]
    androidTestImplementation rootProject.ext.testDeps["runner"]
    androidTestImplementation rootProject.ext.testDeps["espresso-core"]
    implementation project(':baselibs')
    kapt rootProject.ext.otherDeps["arouter-compiler"]
}
```

**4)配置文件 `config.gradle` ,对项目中的第三库、 `app` 的版本等配置**

```
ext {
    android = [
            compileSdkVersion: 28,
            buildToolsVersion: "28.0.3",
            minSdkVersion    : 16,
            targetSdkVersion : 27,
            versionCode      : 1,
            versionName      : "1.0.0"
    ]
    dependVersion = [
            androidSupportSdkVersion: "28.0.0",
            espressoSdkVersion      : "3.0.2",
            retrofitSdkVersion      : "2.4.0",
            glideSdkVersion         : "4.8.0",
            rxJava                  : "2.2.2",
            rxAndroid               : "2.1.0",
            rxKotlin                : "2.3.0",
            anko                    : "0.10.7"
    ]
    supportDeps = [
            "supportv4"        : "com.android.support:support-v4:${dependVersion.androidSupportSdkVersion}",
            "appcompatv7"      : "com.android.support:appcompat-v7:${dependVersion.androidSupportSdkVersion}",
            "cardview"         : "com.android.support:cardview-v7:${dependVersion.androidSupportSdkVersion}",
            "design"           : "com.android.support:design:${dependVersion.androidSupportSdkVersion}",
            "constraint-layout": "com.android.support.constraint:constraint-layout:1.1.3",
            "annotations"      : "com.android.support:support-annotations:${dependVersion.androidSupportSdkVersion}"
    ]
    retrofit = [
            "retrofit"                : "com.squareup.retrofit2:retrofit:${dependVersion.retrofitSdkVersion}",
            "retrofitConverterGson"   : "com.squareup.retrofit2:converter-gson:${dependVersion.retrofitSdkVersion}",
            "retrofitAdapterRxjava2"  : "com.squareup.retrofit2:adapter-rxjava2:${dependVersion.retrofitSdkVersion}",
            "okhttp3LoggerInterceptor": 'com.squareup.okhttp3:logging-interceptor:3.11.0',
            "retrofitConverterMoshi"  : 'com.squareup.retrofit2:converter-moshi:2.4.0',
            "retrofitKotlinMoshi"     : "com.squareup.moshi:moshi-kotlin:1.7.0"
    ]
    rxJava = [
            "rxJava"   : "io.reactivex.rxjava2:rxjava:${dependVersion.rxJava}",
            "rxAndroid": "io.reactivex.rxjava2:rxandroid:${dependVersion.rxAndroid}",
            "rxKotlin" : "io.reactivex.rxjava2:rxkotlin:${dependVersion.rxKotlin}",
            "anko"     : "org.jetbrains.anko:anko:${dependVersion.anko}"
    ]
    testDeps = [
            "junit"                    : 'junit:junit:4.12',
            "runner"                   : 'com.android.support.test:runner:1.0.2',
            "espresso-core"            : "com.android.support.test.espresso:espresso-core:${dependVersion.espressoSdkVersion}",
            "espresso-contrib"         : "com.android.support.test.espresso:espresso-contrib:${dependVersion.espressoSdkVersion}",
            "espresso-intents"         : "com.android.support.test.espresso:espresso-intents:${dependVersion.espressoSdkVersion}",
            "leakcanary-debug"         : 'com.squareup.leakcanary:leakcanary-android:1.6.1',
            "leakcanary-release"       : 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.1',
            "leakcanary-debug-fragment": 'com.squareup.leakcanary:leakcanary-support-fragment:1.6.1',
            "debug-db"                 : 'com.amitshekhar.android:debug-db:1.0.4'
    ]
    commonDeps = [
            "multidex": 'com.android.support:multidex:1.0.3',
            "logger"  : 'com.orhanobut:logger:2.2.0',
            "glide"   : 'com.github.bumptech.glide:glide:4.8.0',
            "eventbus": 'org.greenrobot:eventbus:3.1.1',
            "spinkit" : 'com.github.ybq:Android-SpinKit:1.2.0',
            "arouter" : 'com.alibaba:arouter-api:1.4.0'
    ]
    otherDeps = [
            "arouter-compiler": 'com.alibaba:arouter-compiler:1.2.1'
    ]
    supportLibs = supportDeps.values()
    networkLibs = retrofit.values()
    rxJavaLibs = rxJava.values()
    commonLibs = commonDeps.values()
}

```

**最后别忘记在工程的中 `build.gradle` 引入该配置文件**

```
apply from: "config.gradle"
```

## 四、业务模块之间交互

> 业务模块之间的跳转可以通过路由(`Arouter`)实现;业务模块之间的通信可以通过消息(`EventBus`)来实现。

#### 1、Arouter 实现业务模块之间的跳转

我们在之前已经依赖了 `Arouter` (详细用法参照:[https://github.com/alibaba/ARouter](https://github.com/alibaba/ARouter)),用它来实现跳转只需要以下两步:

**第一步**

- `gradle` 配置

```
kapt {
    arguments {
        arg("AROUTER_MODULE_NAME", project.getName())
    }
    generateStubs = true
}
dependencies {
...
    kapt rootProject.ext.otherDeps["arouter-compiler"]
}
```

**第二步**

- 需要指明目标页面以及要带的参数,然后在调用 `navigation()` 方法;

![](/art/05.png)

**第三步**

- 首先在 `onCreate` 方法调用 `ARouter.getInstance().inject(this)` 注入;
- 然后要用 `@Route` 注解标注页面,并在 `path` 变量中给页面定义一个路径;
- 最后对于传送过来的变量我们直接定义一个同名的字段用 `@Autowired` 变量标注,`Arouter` 会对该字段自动赋值

![](/art/06.png)

#### 2、EventBus 实现业务模块之间的通讯

利用第三方如 `EventBus` 对消息进行管理。在 `baselibs` 组件中的 `BaseActivity` 、 `BaseFragment` 类做了对消息的简单封装,子类只需要重写 `useEventBus()` 返回 `true` 即可对事件的注册。

## 五、搭建过程中遇到的问题

#### 1、AndroidManifest 

我们知道 `APP` 在打包的时候最后会把所有的 `AndroidManifest` 进行合并,所以每个业务组件的 `Activity` 只需要在各自的模块中注册即可。

如果业务组件要单独运行,则需要单独的一个 `AndroidManifest` ,在 `gradle` 的 `sourceSets` 加载不同的 `AndroidManifest` 即可。

![](/art/07.png) 

**gradle 配置**

```
android {
...
    sourceSets {
        main {
            if (isModule.toBoolean()) {
                manifest.srcFile 'src/main/module/AndroidManifest.xml'
            } else {
                manifest.srcFile 'src/main/AndroidManifest.xml'
                //集成开发模式下排除debug文件夹中的所有Java文件
                java {
                    exclude 'debug/**'
                }
                kotlin {
                    exclude 'debug/**'
                }
            }
        }
    }
...
}
```

**注意:集成模式下的 AndroidManifest 不需要配置 Application ,组件模式下的 AndroidManifest 需要单独配置 Application 。**

#### 2、资源文件冲突的问题

不同业务组件里的资源文件的名称可能相同,所以就可能出现资源文件冲突的问题,我们可以通过设置资源的前缀来防止资源文件的冲突。

![](/art/08.png)

**gradle 配置,以 module_news 模块为例**

```
android {
...
    resourcePrefix "news_"
...
}
```

这样配置以后,如果我们在命名资源文件没有加前缀的时候,编译器就会提示我们没加前缀。

**至此, `Android` 基本组件化框架已经搭建完成,如有错误之处还请指正。**

## 五、最后

**完整的项目地址:[https://github.com/iceCola7/AndroidModuleSamples](https://github.com/iceCola7/AndroidModuleSamples)**



================================================
FILE: app/.gitignore
================================================
/build


================================================
FILE: app/build.gradle
================================================
apply plugin: 'com.android.application'

apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'

kapt {
    arguments {
        arg("AROUTER_MODULE_NAME", project.getName())
    }
    generateStubs = true
}

android {
    compileSdkVersion rootProject.ext.android.compileSdkVersion
    defaultConfig {
        applicationId "com.cxz.module.samples"
        minSdkVersion rootProject.ext.android.minSdkVersion
        targetSdkVersion rootProject.ext.android.targetSdkVersion
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'

        multiDexEnabled true

    }

    resourcePrefix "app_"

    buildTypes {
        debug {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }

        release {
            minifyEnabled true
            shrinkResources true
            zipAlignEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"

    testImplementation rootProject.ext.testDeps["junit"]
    androidTestImplementation rootProject.ext.testDeps["runner"]
    androidTestImplementation rootProject.ext.testDeps["espresso-core"]

    // leakCanary
    debugImplementation rootProject.ext.testDeps["leakcanary-debug"]
    releaseImplementation rootProject.ext.testDeps["leakcanary-release"]

    kapt rootProject.ext.annotationProcessorDeps["arouter-compiler"]

    // implementation project(':baselibs')
//    if (!isModule.toBoolean()) {
//        implementation project(':module_news')
//        implementation project(':module_video')
//        implementation project(':module_me')
//    }
    if (!isMeModule.toBoolean()){
        implementation project(':module_me')
    }
    if (!isNewsModule.toBoolean()){
        implementation project(':module_news')
    }
    if (!isVideoModule.toBoolean()){
        implementation project(':module_video')
    }
}


================================================
FILE: app/proguard-rules.pro
================================================
# 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


================================================
FILE: app/src/androidTest/java/com/cxz/module/samples/ExampleInstrumentedTest.kt
================================================
package com.cxz.module.samples

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.getTargetContext()
        assertEquals("com.cxz.module.samples", appContext.packageName)
    }
}


================================================
FILE: app/src/main/AndroidManifest.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.cxz.module.samples">

    <application
        android:name=".app.App"
        android:allowBackup="true"
        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=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />

                <action android:name="android.intent.action.VIEW" />
            </intent-filter>
        </activity>
        <activity android:name=".OtherActivity" />
    </application>

</manifest>

================================================
FILE: app/src/main/java/com/cxz/module/samples/MainActivity.kt
================================================
package com.cxz.module.samples

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.alibaba.android.arouter.facade.annotation.Autowired
import com.alibaba.android.arouter.facade.annotation.Route
import com.alibaba.android.arouter.launcher.ARouter
import com.cxz.kotlin.baselibs.ext.showToast
import com.cxz.kotlin.baselibs.provider.NewsService
import kotlinx.android.synthetic.main.app_activity_main.*

@Route(path = "/app/main")
class MainActivity : AppCompatActivity() {

    @Autowired(name = "/news/service")
    @JvmField
    var newsService: NewsService? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        ARouter.getInstance().inject(this)
        super.onCreate(savedInstanceState)
        setContentView(R.layout.app_activity_main)

        btn_news.setOnClickListener {
            ARouter.getInstance().build("/news/main") // 目标页面
                .withString("key1", "test_key1")  // 参数
                .withString("key2", "test_key2")  // 参数
                .navigation()
        }
        btn_video.setOnClickListener {
            val bundle = Bundle()
            bundle.putString("key1", "test_key1")
            bundle.putString("key2", "test_key2")
            ARouter.getInstance().build("/video/main")
                .with(bundle)
                .navigation()
        }
        btn_me.setOnClickListener {
            ARouter.getInstance().build("/me/main")
                .navigation()
        }
        btn_news_service.setOnClickListener {
            showToast(newsService?.getNewsName().toString())
        }

    }
}


================================================
FILE: app/src/main/java/com/cxz/module/samples/OtherActivity.kt
================================================
package com.cxz.module.samples

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.alibaba.android.arouter.facade.annotation.Route

@Route(path = "/app/other")
class OtherActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.app_activity_other)
    }
}


================================================
FILE: app/src/main/java/com/cxz/module/samples/app/App.kt
================================================
package com.cxz.module.samples.app

import android.app.Activity
import android.app.Application
import android.content.Context
import android.os.Bundle
import androidx.multidex.MultiDex
import com.alibaba.android.arouter.launcher.ARouter
import com.cxz.kotlin.baselibs.BuildConfig
import com.cxz.kotlin.baselibs.config.AppConfig
import com.cxz.kotlin.baselibs.utils.NLog
import com.squareup.leakcanary.LeakCanary
import com.squareup.leakcanary.RefWatcher

/**
 * @author chenxz
 * @date 2018/12/22
 * @desc
 */
class App : Application() {

    private val TAG = "App"

    private var refWatcher: RefWatcher? = null

    companion object {
        fun getRefWatcher(context: Context): RefWatcher? {
            val app = context.applicationContext as App
            return app.refWatcher
        }
    }

    override fun onCreate() {
        super.onCreate()
        AppConfig.init(this)
        initLeakCanary()
        initRouter()
    }

    private fun initLeakCanary() {
        refWatcher = if (LeakCanary.isInAnalyzerProcess(this))
            RefWatcher.DISABLED
        else LeakCanary.install(this)

        registerActivityLifecycleCallbacks(mActivityLifecycleCallbacks)
    }

    private fun initRouter() {
        if (BuildConfig.DEBUG) {
            ARouter.openLog()
            ARouter.openDebug()
        }
        ARouter.init(this)
    }

    override fun attachBaseContext(base: Context?) {
        super.attachBaseContext(base)
        MultiDex.install(this)
    }

    private val mActivityLifecycleCallbacks = object : ActivityLifecycleCallbacks {
        override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
            NLog.d(TAG, "onCreated: " + activity.componentName.className)
        }

        override fun onActivityStarted(activity: Activity) {
            NLog.d(TAG, "onStart: " + activity.componentName.className)
        }

        override fun onActivityResumed(activity: Activity) {
            NLog.d(TAG, "onResume: " + activity.componentName.className)
        }

        override fun onActivityPaused(activity: Activity) {
            NLog.d(TAG, "onPause: " + activity.componentName.className)
        }

        override fun onActivityStopped(activity: Activity) {
            NLog.d(TAG, "onStop: " + activity.componentName.className)
        }

        override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {
            NLog.d(TAG, "onSaveInstanceState: " + activity.componentName.className)
        }

        override fun onActivityDestroyed(activity: Activity) {
            NLog.d(TAG, "onDestroy: " + activity.componentName.className)
        }
    }

}

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


================================================
FILE: app/src/main/res/drawable-v24/ic_launcher_foreground.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:aapt="http://schemas.android.com/aapt"
        android:width="108dp"
        android:height="108dp"
        android:viewportHeight="108"
        android:viewportWidth="108">
    <path
            android:fillType="evenOdd"
            android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
            android:strokeColor="#00000000"
            android:strokeWidth="1">
        <aapt:attr name="android:fillColor">
            <gradient
                    android:endX="78.5885"
                    android:endY="90.9159"
                    android:startX="48.7653"
                    android:startY="61.0927"
                    android:type="linear">
                <item
                        android:color="#44000000"
                        android:offset="0.0"/>
                <item
                        android:color="#00000000"
                        android:offset="1.0"/>
            </gradient>
        </aapt:attr>
    </path>
    <path
            android:fillColor="#FFFFFF"
            android:fillType="nonZero"
            android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
            android:strokeColor="#00000000"
            android:strokeWidth="1"/>
</vector>


================================================
FILE: app/src/main/res/layout/app_activity_main.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/btn_news"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="News" />

    <Button
        android:id="@+id/btn_video"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Video" />

    <Button
        android:id="@+id/btn_me"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Me" />

    <Button
        android:id="@+id/btn_news_service"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="测试NewsService" />

</LinearLayout>

================================================
FILE: app/src/main/res/layout/app_activity_other.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".OtherActivity">

</androidx.constraintlayout.widget.ConstraintLayout>

================================================
FILE: app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
    <background android:drawable="@drawable/ic_launcher_background"/>
    <foreground android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon>

================================================
FILE: app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
    <background android:drawable="@drawable/ic_launcher_background"/>
    <foreground android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon>

================================================
FILE: app/src/main/res/values/colors.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorPrimary">#008577</color>
    <color name="colorPrimaryDark">#00574B</color>
    <color name="colorAccent">#D81B60</color>
</resources>


================================================
FILE: app/src/main/res/values/strings.xml
================================================
<resources>
    <string name="app_name">AndroidModuleSamples</string>
</resources>


================================================
FILE: app/src/main/res/values/styles.xml
================================================
<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>

</resources>


================================================
FILE: app/src/test/java/com/cxz/module/samples/ExampleUnitTest.kt
================================================
package com.cxz.module.samples

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(4, 2 + 2)
    }
}


================================================
FILE: baselibs/.gitignore
================================================
/build


================================================
FILE: baselibs/build.gradle
================================================
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'

kapt {
    arguments {
        arg("AROUTER_MODULE_NAME", project.getName())
    }
}
android {
    compileSdkVersion rootProject.ext.android.compileSdkVersion
    defaultConfig {
        minSdkVersion rootProject.ext.android.minSdkVersion
        targetSdkVersion rootProject.ext.android.targetSdkVersion
        versionCode 1
        versionName "1.0.0"

        multiDexEnabled true

    }

    // resourcePrefix "base_"

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

    lintOptions {
        abortOnError false
    }

}

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    api "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"

    implementation rootProject.ext.androisxLibs
    // Support库
    // api rootProject.ext.supportLibs
    // 网络请求库
    api rootProject.ext.networkLibs
    // RxJava2
    api rootProject.ext.rxJavaLibs
    // commonLibs
    api rootProject.ext.commonLibs

    kapt rootProject.ext.annotationProcessorLibs

}



================================================
FILE: baselibs/proguard-rules.pro
================================================
# 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


================================================
FILE: baselibs/src/main/AndroidManifest.xml
================================================
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.cxz.kotlin.baselibs">

    <!-- 获取GSM网络信息状态,如当前的网络连接是否有效 -->
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <!-- 允许访问Wi-Fi网络状态信息 -->
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
    <!-- 允许获取Wi-Fi网络状态改变信息 -->
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
    <!-- 访问网络 -->
    <uses-permission android:name="android.permission.INTERNET"/>
    <!-- 读取手机状态 -->
    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
    <!-- SD 卡读写权限 -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

</manifest>


================================================
FILE: baselibs/src/main/java/com/cxz/kotlin/baselibs/base/BaseActivity.kt
================================================
package com.cxz.kotlin.baselibs.base

import android.content.pm.ActivityInfo
import android.os.Bundle
import android.view.MenuItem
import android.view.MotionEvent
import android.view.WindowManager
import androidx.annotation.ColorInt
import androidx.annotation.LayoutRes
import androidx.appcompat.app.AppCompatActivity
import com.cxz.kotlin.baselibs.utils.CommonUtil
import com.cxz.kotlin.baselibs.utils.KeyBoardUtil
import com.cxz.kotlin.baselibs.utils.StatusBarUtil
import com.tbruyelle.rxpermissions2.RxPermissions
import org.greenrobot.eventbus.EventBus

/**
 * @author chenxz
 * @date 2018/11/19
 * @desc BaseActivity
 */
abstract class BaseActivity : AppCompatActivity() {

    /**
     * 布局文件id
     */
    @LayoutRes
    protected abstract fun attachLayoutRes(): Int

    /**
     * 初始化数据
     */
    open fun initData() {}

    /**
     * 初始化 View
     */
    abstract fun initView()

    /**
     * 开始请求
     */
    abstract fun start()

    /**
     * 是否使用 EventBus
     */
    open fun useEventBus(): Boolean = false

    /**
     * 获取权限处理类
     */
    protected val rxPermissions: RxPermissions by lazy {
        RxPermissions(this)
    }

    /**
     * 设置状态栏的背景颜色
     */
    fun setStatusBarColor(@ColorInt color: Int) {
        StatusBarUtil.setColor(this, color, 0)
    }

    /**
     * 设置状态栏图标的颜色
     *
     * @param dark true: 黑色  false: 白色
     */
    fun setStatusBarIcon(dark: Boolean) {
        if (dark) {
            StatusBarUtil.setLightMode(this)
        } else {
            StatusBarUtil.setDarkMode(this)
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN)
        super.onCreate(savedInstanceState)
        requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT // 强制竖屏
        setContentView(attachLayoutRes())
        if (useEventBus()) {
            EventBus.getDefault().register(this)
        }
        initView()
        initData()
        start()
    }

    override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
        if (ev?.action == MotionEvent.ACTION_UP) {
            val v = currentFocus
            // 如果不是落在EditText区域,则需要关闭输入法
            if (KeyBoardUtil.isHideKeyboard(v, ev)) {
                KeyBoardUtil.hideKeyBoard(this, v)
            }
        }
        return super.dispatchTouchEvent(ev)
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        when (item.itemId) {
            android.R.id.home -> {
                onBackPressed()
            }
        }
        return super.onOptionsItemSelected(item)
    }

    override fun onDestroy() {
        super.onDestroy()
        if (useEventBus()) {
            EventBus.getDefault().unregister(this)
        }
        CommonUtil.fixInputMethodManagerLeak(this)
    }
}

================================================
FILE: baselibs/src/main/java/com/cxz/kotlin/baselibs/base/BaseFragment.kt
================================================
package com.cxz.kotlin.baselibs.base

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.annotation.LayoutRes
import com.tbruyelle.rxpermissions2.RxPermissions
import org.greenrobot.eventbus.EventBus

/**
 * @author chenxz
 * @date 2018/11/19
 * @desc BaseFragment
 */
abstract class BaseFragment : androidx.fragment.app.Fragment() {

    /**
     * 视图是否加载完毕
     */
    private var isViewPrepare = false

    /**
     * 数据是否加载过了
     */
    private var hasLoadData = false

    /**
     * 加载布局
     */
    @LayoutRes
    abstract fun attachLayoutRes(): Int

    /**
     * 初始化数据
     */
    open fun initData() {}

    /**
     * 初始化 View
     */
    abstract fun initView(view: View)

    /**
     * 懒加载
     */
    abstract fun lazyLoad()

    /**
     * 是否使用 EventBus
     */
    open fun useEventBus(): Boolean = false

    /**
     * 获取权限处理类
     */
    protected val rxPermissions: RxPermissions by lazy {
        RxPermissions(this)
    }

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        return inflater.inflate(attachLayoutRes(), null)
    }

    override fun setUserVisibleHint(isVisibleToUser: Boolean) {
        super.setUserVisibleHint(isVisibleToUser)
        if (isVisibleToUser) {
            lazyLoadDataIfPrepared()
        }
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        if (useEventBus()) EventBus.getDefault().register(this)
        isViewPrepare = true
        initView(view)
        initData()
        lazyLoadDataIfPrepared()
    }

    private fun lazyLoadDataIfPrepared() {
        if (userVisibleHint && isViewPrepare && !hasLoadData) {
            lazyLoad()
            hasLoadData = true
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        if (useEventBus()) EventBus.getDefault().unregister(this)
    }
}

================================================
FILE: baselibs/src/main/java/com/cxz/kotlin/baselibs/base/BaseMvpActivity.kt
================================================
package com.cxz.kotlin.baselibs.base

import com.cxz.kotlin.baselibs.ext.showToast
import com.cxz.kotlin.baselibs.mvp.IPresenter
import com.cxz.kotlin.baselibs.mvp.IView

/**
 * @author chenxz
 * @date 2018/11/19
 * @desc BaseMvpActivity
 */
abstract class BaseMvpActivity<in V : IView, P : IPresenter<V>> : BaseActivity(), IView {

    /**
     * Presenter
     */
    protected var mPresenter: P? = null

    protected abstract fun createPresenter(): P

    override fun initView() {
        mPresenter = createPresenter()
        mPresenter?.attachView(this as V)
    }

    override fun onDestroy() {
        super.onDestroy()
        mPresenter?.detachView()
        this.mPresenter = null
    }

    override fun showLoading() {
    }

    override fun hideLoading() {
    }

    override fun showError(errorMsg: String) {
        showToast(errorMsg)
    }

    override fun showDefaultMsg(msg: String) {
        showToast(msg)
    }

    override fun showMsg(msg: String) {
        showToast(msg)
    }

}

================================================
FILE: baselibs/src/main/java/com/cxz/kotlin/baselibs/base/BaseMvpFragment.kt
================================================
package com.cxz.kotlin.baselibs.base

import android.view.View
import com.cxz.kotlin.baselibs.ext.showToast
import com.cxz.kotlin.baselibs.mvp.IPresenter
import com.cxz.kotlin.baselibs.mvp.IView

/**
 * @author chenxz
 * @date 2018/11/19
 * @desc BaseMvpFragment
 */
abstract class BaseMvpFragment<in V : IView, P : IPresenter<V>> : BaseFragment(), IView {

    /**
     * Presenter
     */
    protected var mPresenter: P? = null

    protected abstract fun createPresenter(): P

    override fun initView(view: View) {
        mPresenter = createPresenter()
        mPresenter?.attachView(this as V)
    }

    override fun onDestroyView() {
        super.onDestroyView()
        mPresenter?.detachView()
        this.mPresenter = null
    }

    override fun showLoading() {
    }

    override fun hideLoading() {
    }

    override fun showError(errorMsg: String) {
        showToast(errorMsg)
    }

    override fun showDefaultMsg(msg: String) {
        showToast(msg)
    }

    override fun showMsg(msg: String) {
        showToast(msg)
    }

}

================================================
FILE: baselibs/src/main/java/com/cxz/kotlin/baselibs/base/BaseMvpTitleActivity.kt
================================================
package com.cxz.kotlin.baselibs.base

import androidx.annotation.ColorRes
import androidx.annotation.StringRes
import com.cxz.kotlin.baselibs.R
import com.cxz.kotlin.baselibs.mvp.IPresenter
import com.cxz.kotlin.baselibs.mvp.IView
import kotlinx.android.synthetic.main.activity_base_title.*
import kotlinx.android.synthetic.main.base_toolbar.*

/**
 * @author chenxz
 * @date 2018/11/20
 * @desc BaseMvpTitleActivity
 */
abstract class BaseMvpTitleActivity<in V : IView, P : IPresenter<V>> : BaseMvpActivity<V, P>() {

    /**
     * 子布局文件id
     */
    protected abstract fun attachChildLayoutRes(): Int

    /**
     * 是否启用返回键
     */
    open fun hasBackIcon(): Boolean = true

    override fun attachLayoutRes(): Int = R.layout.activity_base_title

    override fun initView() {
        super.initView()
        base_container.addView(layoutInflater.inflate(attachChildLayoutRes(), null))
        base_toolbar.title = ""
        setSupportActionBar(base_toolbar)
        supportActionBar?.setDisplayHomeAsUpEnabled(hasBackIcon())
    }

    /**
     * 设置 Title
     */
    fun setBaseTitle(title: String) {
        base_title_tv.text = title
    }

    /**
     * 设置 Title
     */
    fun setBaseTitleText(@StringRes resId: Int) {
        base_title_tv.setText(resId)
    }

    /**
     * 设置 Title 颜色
     */
    fun setBaseTitleColor(@ColorRes color: Int) {
        base_title_tv.setTextColor(resources.getColor(color))
    }

}

================================================
FILE: baselibs/src/main/java/com/cxz/kotlin/baselibs/bean/BaseBean.kt
================================================
package com.cxz.kotlin.baselibs.bean

/**
 * @author admin
 * @date 2018/11/21
 * @desc
 */
open class BaseBean {
    var errorCode: Int = 0
    var errorMsg: String = ""
}

================================================
FILE: baselibs/src/main/java/com/cxz/kotlin/baselibs/config/AppConfig.kt
================================================
package com.cxz.kotlin.baselibs.config

import android.app.Application

/**
 * @author chenxz
 * @date 2019/11/1
 * @desc 用来初始化项目所需要的配置
 */
object AppConfig {

    const val TAG = "KotlinMVP"

    var debug = false

    private var application: Application? = null

    /**
     * Init, it must be call before used .
     */
    fun init(application: Application) {
        this.application = application
    }

    fun getApplication(): Application {
        if (application == null) {
            throw RuntimeException("Please init in Application#onCreate first.")
        }
        return application!!
    }

    fun openDebug() {
        debug = true
    }

}

================================================
FILE: baselibs/src/main/java/com/cxz/kotlin/baselibs/ext/CommonExt.kt
================================================
package com.cxz.kotlin.baselibs.ext

import android.app.Activity
import android.content.Context
import android.content.Intent
import android.content.res.Resources
import android.net.Uri
import android.view.View
import android.widget.Checkable
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import com.cxz.kotlin.baselibs.R
import com.cxz.kotlin.baselibs.config.AppConfig
import com.cxz.kotlin.baselibs.utils.NLog
import com.cxz.kotlin.baselibs.widget.CustomToast
import com.google.android.material.snackbar.Snackbar

/**
 * @author chenxz
 * @date 2018/11/20
 * @desc
 */

fun Any.loge(content: String?) {
    val tag = this.javaClass.simpleName ?: AppConfig.TAG
    NLog.e(tag, content ?: "")
}

fun dp2px(dpValue: Float): Int {
    return (0.5f + dpValue * Resources.getSystem().displayMetrics.density).toInt()
}

fun Fragment.showToast(content: String) {
    CustomToast(this.activity?.applicationContext, content).show()
}

fun Context.showToast(content: String) {
    CustomToast(this, content).show()
}

fun Activity.showSnackMsg(msg: String) {
    val snackbar = Snackbar.make(this.window.decorView, msg, Snackbar.LENGTH_SHORT)
    val view = snackbar.view
    view.findViewById<TextView>(R.id.snackbar_text)
        .setTextColor(ContextCompat.getColor(this, R.color.white))
    snackbar.show()
}

fun Fragment.showSnackMsg(msg: String) {
    this.activity ?: return
    val snackbar =
        Snackbar.make(this.activity!!.window.decorView, msg, com.google.android.material.snackbar.Snackbar.LENGTH_SHORT)
    val view = snackbar.view
    view.findViewById<TextView>(R.id.snackbar_text)
        .setTextColor(ContextCompat.getColor(this.activity!!, R.color.white))
    snackbar.show()
}

fun Context.openBrowser(url: String) {
    Intent(Intent.ACTION_VIEW, Uri.parse(url)).run { startActivity(this) }
}

// 扩展点击事件属性(重复点击时长)
var <T : View> T.lastClickTime: Long
    set(value) = setTag(1766613352, value)
    get() = getTag(1766613352) as? Long ?: 0

// 重复点击事件绑定
inline fun <T : View> T.setSingleClickListener(time: Long = 1000, crossinline block: (T) -> Unit) {
    setOnClickListener {
        val currentTimeMillis = System.currentTimeMillis()
        if (currentTimeMillis - lastClickTime > time || this is Checkable) {
            lastClickTime = currentTimeMillis
            block(this)
        }
    }
}


================================================
FILE: baselibs/src/main/java/com/cxz/kotlin/baselibs/ext/RxExt.kt
================================================
package com.cxz.kotlin.baselibs.ext

import com.cxz.kotlin.baselibs.bean.BaseBean
import com.cxz.kotlin.baselibs.http.HttpStatus
import com.cxz.kotlin.baselibs.http.exception.ExceptionHandle
import com.cxz.kotlin.baselibs.http.function.RetryWithDelay
import com.cxz.kotlin.baselibs.mvp.IModel
import com.cxz.kotlin.baselibs.mvp.IView
import com.cxz.kotlin.baselibs.rx.SchedulerUtils
import com.cxz.kotlin.baselibs.utils.NetWorkUtil
import io.reactivex.Observable
import io.reactivex.Observer
import io.reactivex.disposables.Disposable

/**
 * @author chenxz
 * @date 2018/11/20
 * @desc
 */

fun <T : BaseBean> Observable<T>.ss(
    model: IModel?,
    view: IView?,
    isShowLoading: Boolean = true,
    onSuccess: (T) -> Unit,
    onError: ((T) -> Unit)? = null
) {
    this.compose(SchedulerUtils.ioToMain())
        .retryWhen(RetryWithDelay())
        .subscribe(object : Observer<T> {
            override fun onComplete() {
                view?.hideLoading()
            }

            override fun onSubscribe(d: Disposable) {
                if (isShowLoading) view?.showLoading()
                model?.addDisposable(d)
                if (!NetWorkUtil.isConnected()) {
                    view?.showDefaultMsg("当前网络不可用,请检查网络设置")
                    d.dispose()
                    onComplete()
                }
            }

            override fun onNext(t: T) {
                view?.hideLoading()
                when {
                    t.errorCode == HttpStatus.SUCCESS -> onSuccess.invoke(t)
                    t.errorCode == HttpStatus.TOKEN_INVALID -> {
                        // Token 过期,重新登录
                    }
                    else -> {
                        if (onError != null) {
                            onError.invoke(t)
                        } else {
                            if (t.errorMsg.isNotEmpty())
                                view?.showDefaultMsg(t.errorMsg)
                        }
                    }
                }
            }

            override fun onError(t: Throwable) {
                view?.hideLoading()
                view?.showError(ExceptionHandle.handleException(t))
            }
        })
}

fun <T : BaseBean> Observable<T>.sss(
    view: IView?,
    isShowLoading: Boolean = true,
    onSuccess: (T) -> Unit,
    onError: ((T) -> Unit)? = null
): Disposable {
    if (isShowLoading) view?.showLoading()
    return this.compose(SchedulerUtils.ioToMain())
        .retryWhen(RetryWithDelay())
        .subscribe({
            if (isShowLoading) view?.showLoading()
            when {
                it.errorCode == HttpStatus.SUCCESS -> onSuccess.invoke(it)
                it.errorCode == HttpStatus.TOKEN_INVALID -> {
                    // Token 过期,重新登录
                }
                else -> {
                    if (onError != null) {
                        onError.invoke(it)
                    } else {
                        if (it.errorMsg.isNotEmpty())
                            view?.showDefaultMsg(it.errorMsg)
                    }
                }
            }
        }, {
            view?.hideLoading()
            view?.showError(ExceptionHandle.handleException(it))
        })
}


================================================
FILE: baselibs/src/main/java/com/cxz/kotlin/baselibs/http/HttpStatus.kt
================================================
package com.cxz.kotlin.baselibs.http

/**
 * Created by chenxz on 2018/4/21.
 */
object HttpStatus {
    /**
     * 响应成功
     */
    const val SUCCESS = 0

    /**
     * Token 过期
     */
    const val TOKEN_INVALID = 401

    /**
     * 未知错误
     */
    const val UNKNOWN_ERROR = 1002

    /**
     * 服务器内部错误
     */
    const val SERVER_ERROR = 1003

    /**
     * 网络连接超时
     */
    const val NETWORK_ERROR = 1004

    /**
     * API解析异常(或者第三方数据结构更改)等其他异常
     */
    const val API_ERROR = 1005
}

================================================
FILE: baselibs/src/main/java/com/cxz/kotlin/baselibs/http/RetrofitFactory.kt
================================================
package com.cxz.kotlin.baselibs.http

import com.cxz.kotlin.baselibs.config.AppConfig
import com.cxz.kotlin.baselibs.http.constant.HttpConstant
import com.cxz.kotlin.baselibs.http.interceptor.CacheInterceptor
import com.cxz.kotlin.baselibs.http.interceptor.CookieInterceptor
import com.cxz.kotlin.baselibs.http.interceptor.HeaderInterceptor
import okhttp3.Cache
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
import retrofit2.converter.moshi.MoshiConverterFactory
import java.io.File
import java.util.concurrent.TimeUnit

/**
 * @author admin
 * @date 2018/11/21
 * @desc RetrofitFactory
 */
abstract class RetrofitFactory<T> {

    var service: T
    private var mBaseUrl = ""
    private var retrofit: Retrofit? = null

    abstract fun baseUrl(): String

    abstract fun getService(): Class<T>

    init {
        mBaseUrl = this.baseUrl()
        if (mBaseUrl.isEmpty()) {
            throw RuntimeException("base url can not be empty!")
        }
        service = getRetrofit()!!.create(this.getService())
    }

    /**
     * 获取 Retrofit 实例对象
     */
    private fun getRetrofit(): Retrofit? {
        if (retrofit == null) {
            synchronized(RetrofitFactory::class.java) {
                if (retrofit == null) {
                    retrofit = Retrofit.Builder()
                        .baseUrl(mBaseUrl)  // baseUrl
                        .client(attachOkHttpClient())
                        //.addConverterFactory(GsonConverterFactory.create())
                        .addConverterFactory(MoshiConverterFactory.create())
                        .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                        .build()
                }
            }
        }
        return retrofit
    }

    /**
     * 获取 OkHttpClient 实例对象
     * 子类可重写,自定义 OkHttpClient
     */
    open fun attachOkHttpClient(): OkHttpClient {
        val builder = OkHttpClient().newBuilder()
        val httpLoggingInterceptor = HttpLoggingInterceptor()
        if (AppConfig.debug) {
            httpLoggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
        } else {
            httpLoggingInterceptor.level = HttpLoggingInterceptor.Level.NONE
        }

        //设置 请求的缓存的大小跟位置
        val cacheFile = File(AppConfig.getApplication().cacheDir, "cache")
        val cache = Cache(cacheFile, HttpConstant.MAX_CACHE_SIZE)

        builder.run {
            addInterceptor(httpLoggingInterceptor)
            addInterceptor(HeaderInterceptor())
            addInterceptor(CookieInterceptor())
            addInterceptor(CacheInterceptor())
            cache(cache)  //添加缓存
            connectTimeout(HttpConstant.DEFAULT_TIMEOUT, TimeUnit.SECONDS)
            readTimeout(HttpConstant.DEFAULT_TIMEOUT, TimeUnit.SECONDS)
            writeTimeout(HttpConstant.DEFAULT_TIMEOUT, TimeUnit.SECONDS)
            retryOnConnectionFailure(true) // 错误重连
            // cookieJar(CookieManager())
        }
        return builder.build()
    }

}

================================================
FILE: baselibs/src/main/java/com/cxz/kotlin/baselibs/http/constant/HttpConstant.kt
================================================
package com.cxz.kotlin.baselibs.http.constant

/**
 * @author chenxz
 * @date 2018/11/21
 * @desc HttpConstant
 */
object HttpConstant {

    const val DEFAULT_TIMEOUT: Long = 15

    const val MAX_CACHE_SIZE: Long = 1024 * 1024 * 50 // 50M 的缓存大小

    const val TOKEN_KEY = "token"
    const val SET_COOKIE_KEY = "set-cookie"
    const val COOKIE_NAME = "Cookie"

}

================================================
FILE: baselibs/src/main/java/com/cxz/kotlin/baselibs/http/cookies/CookieManager.kt
================================================
package com.cxz.kotlin.baselibs.http.cookies

import okhttp3.Cookie
import okhttp3.CookieJar
import okhttp3.HttpUrl

/**
 * Created by chenxz on 2018/6/6.
 */
class CookieManager : CookieJar {

    private val COOKIE_STORE = PersistentCookieStore()

    override fun saveFromResponse(url: HttpUrl?, cookies: MutableList<Cookie>?) {
        cookies ?: return
        url ?: return
        if (cookies.size > 0) {
            for (cookie in cookies) {
                COOKIE_STORE.add(url, cookie)
            }
        }
    }

    override fun loadForRequest(url: HttpUrl): List<Cookie> = COOKIE_STORE.get(url)

    /**
     * 清除所有cookie
     */
    fun clearAllCookies() {
        COOKIE_STORE.removeAll()
    }

    /**
     * 清除指定cookie
     *
     * @param url HttpUrl
     * @param cookie Cookie
     * @return if clear cookies
     */
    fun clearCookies(url: HttpUrl, cookie: Cookie): Boolean {
        return COOKIE_STORE.remove(url, cookie)
    }

    /**
     * 获取cookies
     *
     * @return List<Cookie>
    </Cookie> */
    fun getCookies(): List<Cookie> {
        return COOKIE_STORE.getCookies()
    }
}

================================================
FILE: baselibs/src/main/java/com/cxz/kotlin/baselibs/http/cookies/OkHttpCookies.kt
================================================
package com.cxz.kotlin.baselibs.http.cookies

import okhttp3.Cookie
import java.io.IOException
import java.io.ObjectInputStream
import java.io.ObjectOutputStream
import java.io.Serializable

/**
 * Created by chenxz on 2018/6/6.
 */
class OkHttpCookies : Serializable {

    private lateinit var cookies: Cookie
    private var clientCookies: Cookie? = null

    private constructor()

    constructor(cookies: Cookie?) {
        this.cookies = cookies!!
    }

    fun getCookies(): Cookie? {
        var bestCookies: Cookie = cookies
        if (clientCookies != null) {
            bestCookies = clientCookies as Cookie
        }
        return bestCookies
    }

    @Throws(IOException::class)
    private fun writeObject(out: ObjectOutputStream) {
        out.writeObject(cookies.name())
        out.writeObject(cookies.value())
        out.writeLong(cookies.expiresAt())
        out.writeObject(cookies.domain())
        out.writeObject(cookies.path())
        out.writeBoolean(cookies.secure())
        out.writeBoolean(cookies.httpOnly())
        out.writeBoolean(cookies.hostOnly())
        out.writeBoolean(cookies.persistent())
    }

    @Throws(IOException::class, ClassNotFoundException::class)
    private fun readObject(`in`: ObjectInputStream) {
        val name = `in`.readObject() as String
        val value = `in`.readObject() as String
        val expiresAt = `in`.readLong()
        val domain = `in`.readObject() as String
        val path = `in`.readObject() as String
        val secure = `in`.readBoolean()
        val httpOnly = `in`.readBoolean()
        val hostOnly = `in`.readBoolean()
        var builder = Cookie.Builder()
        builder = builder.name(name)
        builder = builder.value(value)
        builder = builder.expiresAt(expiresAt)
        builder = if (hostOnly) builder.hostOnlyDomain(domain) else builder.domain(domain)
        builder = builder.path(path)
        builder = if (secure) builder.secure() else builder
        builder = if (httpOnly) builder.httpOnly() else builder
        clientCookies = builder.build()
    }
}

================================================
FILE: baselibs/src/main/java/com/cxz/kotlin/baselibs/http/cookies/PersistentCookieStore.kt
================================================
package com.cxz.kotlin.baselibs.http.cookies

import android.content.Context
import android.content.SharedPreferences
import android.text.TextUtils
import com.cxz.kotlin.baselibs.config.AppConfig
import com.cxz.kotlin.baselibs.utils.NLog
import okhttp3.Cookie
import okhttp3.HttpUrl
import java.io.*
import java.util.*
import java.util.concurrent.ConcurrentHashMap

/**
 * Created by chenxz on 2018/6/6.
 */
class PersistentCookieStore {

    private val LOG_TAG: String = "PersistentCookieStore"
    private val COOKIE_PREFS: String = "Cookies_Prefs"

    private val cookies: HashMap<String, ConcurrentHashMap<String, Cookie>> = HashMap()

    private val cookiePrefs: SharedPreferences

    init {
        cookiePrefs = AppConfig.getApplication().getSharedPreferences(COOKIE_PREFS, Context.MODE_PRIVATE)
        val prefsMap = cookiePrefs.all

        for (entry in prefsMap) {
            val cookieNames = TextUtils.split(entry.value as String, ",")
            for (name in cookieNames) {
                val encodedCookie = cookiePrefs.getString(name, null)
                if (encodedCookie != null) {
                    val decodedCookie = decodeCookie(encodedCookie)
                    if (decodedCookie != null) {
                        if (!cookies.containsKey(entry.key)) {
                            cookies.put(entry.key, ConcurrentHashMap())
                        }
                        cookies[entry.key]?.put(name, decodedCookie)
                    }
                }
            }
        }

    }

    private fun getCookieToken(cookie: Cookie): String = cookie.name() + "@" + cookie.domain()

    fun add(url: HttpUrl, cookie: Cookie) {
        val name = getCookieToken(cookie)
        //将cookies缓存到内存中 如果缓存过期 就重置此cookie
        if (!cookie.persistent()) {
            if (!cookies.containsKey(url.host())) {
                cookies.put(url.host(), ConcurrentHashMap(10))
            }
            cookies[url.host()]?.put(name, cookie)
        } else {
            if (cookies.containsKey(url.host())) {
                cookies[url.host()]?.remove(name)
            }
        }
        // 将Cookie持久化到本地
        val prefsWriter: SharedPreferences.Editor = cookiePrefs.edit()
        cookies[url.host()]?.entries?.let {
            prefsWriter.putString(url.host(), TextUtils.join(",", it))
        }
        prefsWriter.putString(name, encodeCookie(OkHttpCookies(cookie)))
        prefsWriter.apply()
    }

    fun get(url: HttpUrl): List<Cookie> {
        val list: ArrayList<Cookie> = ArrayList()
        if (cookies.containsKey(url.host())) {
            list.addAll(cookies[url.host()]?.values!!)
        }
        return list
    }

    fun removeAll() {
        val prefsWriter = cookiePrefs.edit()
        prefsWriter.clear()
        prefsWriter.apply()
        for (key in cookies.keys) {
            cookies.remove(key)
        }
    }

    fun remove(url: HttpUrl, cookie: Cookie): Boolean {
        val name = getCookieToken(cookie)
        return if (cookies.containsKey(url.host()) && cookies[url.host()]?.containsKey(name)!!) {
            cookies[url.host()]?.remove(name)

            val prefsWriter = cookiePrefs.edit()
            if (cookiePrefs.contains(name)) {
                prefsWriter.remove(name)
            }
            cookies[url.host()]?.keys?.let {
                prefsWriter.putString(url.host(), TextUtils.join(",", it))
            }
            prefsWriter.apply()

            true
        } else {
            false
        }
    }

    fun getCookies(): List<Cookie> {
        val ret = ArrayList<Cookie>()
        for (key in cookies.keys) {
            ret.addAll(cookies[key]?.values!!)
        }
        return ret
    }

    /**
     * cookies 序列化成 string
     *
     * @param cookie 要序列化的cookie
     * @return 序列化之后的string
     */
    private fun encodeCookie(cookie: OkHttpCookies?): String? {
        if (cookie == null) {
            return null
        }
        val os = ByteArrayOutputStream()
        try {
            val outputStream = ObjectOutputStream(os)
            outputStream.writeObject(cookie)
        } catch (e: IOException) {
            NLog.d(LOG_TAG, "IOException in encodeCookie $e")
            return null
        }

        return byteArrayToHexString(os.toByteArray())
    }

    /**
     * 将字符串反序列化成cookies
     *
     * @param cookieString cookies string
     * @return cookie object
     */
    private fun decodeCookie(cookieString: String): Cookie? {
        val bytes = hexStringToByteArray(cookieString)
        val byteArrayInputStream = ByteArrayInputStream(bytes)
        var cookie: Cookie? = null
        try {
            val objectInputStream = ObjectInputStream(byteArrayInputStream)
            cookie = (objectInputStream.readObject() as OkHttpCookies).getCookies()
        } catch (e: IOException) {
            NLog.d(LOG_TAG, "IOException in decodeCookie $e")
        } catch (e: ClassNotFoundException) {
            NLog.d(LOG_TAG, "ClassNotFoundException in decodeCookie $e")
        }

        return cookie
    }

    /**
     * 二进制数组转十六进制字符串
     *
     * @param bytes byte array to be converted
     * @return string containing hex values
     */
    private fun byteArrayToHexString(bytes: ByteArray): String {
        val sb = StringBuilder(bytes.size * 2)
        for (element in bytes) {
            val v = element.toInt() and 0xff
            if (v < 16) {
                sb.append('0')
            }
            sb.append(Integer.toHexString(v))
        }
        return sb.toString().toUpperCase(Locale.US)
    }

    /**
     * 十六进制字符串转二进制数组
     *
     * @param hexString string of hex-encoded values
     * @return decoded byte array
     */
    private fun hexStringToByteArray(hexString: String): ByteArray {
        val len = hexString.length
        val data = ByteArray(len / 2)
        var i = 0
        while (i < len) {
            data[i / 2] = ((Character.digit(hexString[i], 16) shl 4) + Character.digit(hexString[i + 1], 16)).toByte()
            i += 2
        }
        return data
    }

}

================================================
FILE: baselibs/src/main/java/com/cxz/kotlin/baselibs/http/exception/ApiException.kt
================================================
package com.cxz.kotlin.baselibs.http.exception

/**
 * Created by chenxz on 2018/4/21.
 */
class ApiException : RuntimeException {

    private var code: Int? = null

    constructor(throwable: Throwable, code: Int) : super(throwable) {
        this.code = code
    }

    constructor(message: String) : super(Throwable(message))
}

================================================
FILE: baselibs/src/main/java/com/cxz/kotlin/baselibs/http/exception/ExceptionHandle.kt
================================================
package com.cxz.kotlin.baselibs.http.exception

import com.cxz.kotlin.baselibs.http.HttpStatus
import com.google.gson.JsonParseException
import com.orhanobut.logger.Logger
import org.json.JSONException
import retrofit2.HttpException
import java.net.ConnectException
import java.net.SocketTimeoutException
import java.net.UnknownHostException
import java.text.ParseException

/**
 * Created by chenxz on 2018/4/21.
 */
class ExceptionHandle {
    companion object {
        private const val TAG = "ExceptionHandle"
        var errorCode = HttpStatus.UNKNOWN_ERROR
        var errorMsg = "请求失败,请稍后重试"

        fun handleException(e: Throwable): String {
            e.printStackTrace()
            if (e is SocketTimeoutException
                || e is ConnectException
                || e is HttpException
            ) { //均视为网络错误
                Logger.e(TAG, "网络连接异常: " + e.message)
                errorMsg = "网络连接异常"
                errorCode = HttpStatus.NETWORK_ERROR
            } else if (e is JsonParseException
                || e is JSONException
                || e is ParseException
            ) {   //均视为解析错误
                Logger.e(TAG, "数据解析异常: " + e.message)
                errorMsg = "数据解析异常"
                errorCode = HttpStatus.SERVER_ERROR
            } else if (e is ApiException) {//服务器返回的错误信息
                errorMsg = e.message.toString()
                errorCode = HttpStatus.SERVER_ERROR
            } else if (e is UnknownHostException) {
                Logger.e(TAG, "网络连接异常: " + e.message)
                errorMsg = "网络连接异常"
                errorCode = HttpStatus.NETWORK_ERROR
            } else if (e is IllegalArgumentException) {
                errorMsg = "参数错误"
                errorCode = HttpStatus.SERVER_ERROR
            } else {//未知错误
                try {
                    Logger.e(TAG, "错误: " + e.message)
                } catch (e1: Exception) {
                    Logger.e(TAG, "未知错误Debug调试 ")
                }
                errorMsg = "未知错误,可能抛锚了吧~"
                errorCode = HttpStatus.UNKNOWN_ERROR
            }
            return errorMsg
        }

    }
}

================================================
FILE: baselibs/src/main/java/com/cxz/kotlin/baselibs/http/function/RetryWithDelay.kt
================================================
package com.cxz.kotlin.baselibs.http.function

import io.reactivex.Observable
import io.reactivex.functions.BiFunction
import io.reactivex.functions.Function
import retrofit2.HttpException
import java.net.ConnectException
import java.net.SocketTimeoutException
import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeoutException

/**
 * @author chenxz
 * @date 2018/8/21
 * @desc 请求重连
 */
class RetryWithDelay : Function<Observable<out Throwable>, Observable<*>> {

    private var maxRetryCount = 3 // 可重试次数
    private var retryDelayMillis: Long = 3000 // 重试等待时间

    constructor() {}

    constructor(retryDelayMillis: Long) {
        this.retryDelayMillis = retryDelayMillis
    }

    constructor(maxRetryCount: Int, retryDelayMillis: Long) {
        this.maxRetryCount = maxRetryCount
        this.retryDelayMillis = retryDelayMillis
    }

    @Throws(Exception::class)
    override fun apply(observable: Observable<out Throwable>): Observable<*> {
        return observable
            .zipWith(Observable.range(1, maxRetryCount + 1),
                BiFunction<Throwable, Int, Wrapper> { t1, t2 -> Wrapper(t2, t1) })
            .flatMap { wrapper ->
                val t = wrapper.throwable
                if ((t is ConnectException
                            || t is SocketTimeoutException
                            || t is TimeoutException
                            || t is HttpException)
                    && wrapper.index < maxRetryCount + 1
                ) {
                    Observable.timer(retryDelayMillis * wrapper.index, TimeUnit.MILLISECONDS)
                } else Observable.error<Any>(wrapper.throwable)
            }
    }

    private inner class Wrapper(val index: Int, val throwable: Throwable)

}


================================================
FILE: baselibs/src/main/java/com/cxz/kotlin/baselibs/http/interceptor/CacheInterceptor.kt
================================================
package com.cxz.kotlin.baselibs.http.interceptor

import com.cxz.kotlin.baselibs.config.AppConfig
import com.cxz.kotlin.baselibs.utils.NetWorkUtil
import okhttp3.CacheControl
import okhttp3.Interceptor
import okhttp3.Response

/**
 * @author chenxz
 * @date 2018/9/26
 * @desc CacheInterceptor: 设置缓存
 */
class CacheInterceptor : Interceptor {

    override fun intercept(chain: Interceptor.Chain): Response {
        var request = chain.request()
        if (!NetWorkUtil.isConnected(AppConfig.getApplication())) {
            request = request.newBuilder()
                    .cacheControl(CacheControl.FORCE_CACHE)
                    .build()
        }
        val response = chain.proceed(request)
        if (NetWorkUtil.isConnected(AppConfig.getApplication())) {
            val maxAge = 0
            // 有网络时 设置缓存超时时间0个小时 ,意思就是不读取缓存数据,只对get有用,post没有缓冲
            response.newBuilder()
                    .header("Cache-Control", "public, max-age=$maxAge")
                    .removeHeader("Retrofit")// 清除头信息,因为服务器如果不支持,会返回一些干扰信息,不清除下面无法生效
                    .build()
        } else {
            // 无网络时,设置超时为4周  只对get有用,post没有缓冲
            val maxStale = 60 * 60 * 24 * 28
            response.newBuilder()
                    .header("Cache-Control", "public, only-if-cached, max-stale=$maxStale")
                    .removeHeader("nyn")
                    .build()
        }
        return response
    }
}

================================================
FILE: baselibs/src/main/java/com/cxz/kotlin/baselibs/http/interceptor/CookieInterceptor.kt
================================================
package com.cxz.kotlin.baselibs.http.interceptor

import com.cxz.kotlin.baselibs.http.constant.HttpConstant
import com.cxz.kotlin.baselibs.utils.Preference
import okhttp3.Interceptor
import okhttp3.Response

/**
 * @author chenxz
 * @date 2018/9/26
 * @desc CookieInterceptor: 保存 Cookie
 */
class CookieInterceptor : Interceptor {

    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request()
        val response = chain.proceed(request)
        val requestUrl = request.url().toString()
        val domain = request.url().host()

        // set-cookie maybe has multi, login to save cookie
        if (
        //(requestUrl.contains(HttpConstant.SAVE_USER_LOGIN_KEY) || requestUrl.contains(HttpConstant.SAVE_USER_REGISTER_KEY)) &&
            response.headers(HttpConstant.SET_COOKIE_KEY).isNotEmpty()
        ) {
            val cookies = response.headers(HttpConstant.SET_COOKIE_KEY)
            val cookie = encodeCookie(cookies)
            saveCookie(requestUrl, domain, cookie)
        }
        // else if (requestUrl.contains(HttpConstant.REMOVE_USER_LOGOUT_KEY) && domain.isNotEmpty()) {
        // remove cookie
        // Preference.clearPreference(domain)
        // }
        return response
    }

    private fun encodeCookie(cookies: List<String>): String {
        val sb = StringBuilder()
        val set = HashSet<String>()
        cookies.map { cookie ->
            cookie.split(";".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
        }.forEach {
            it.filterNot { set.contains(it) }.forEach { set.add(it) }
        }
        val ite = set.iterator()
        while (ite.hasNext()) {
            val cookie = ite.next()
            sb.append(cookie).append(";")
        }
        val last = sb.lastIndexOf(";")
        if (sb.length - 1 == last) {
            sb.deleteCharAt(last)
        }
        return sb.toString()
    }

    private fun saveCookie(url: String?, domain: String?, cookies: String) {
        url ?: return
        var spUrl: String by Preference(url, cookies)
        @Suppress("UNUSED_VALUE")
        spUrl = cookies
        domain ?: return
        var spDomain: String by Preference(domain, cookies)
        @Suppress("UNUSED_VALUE")
        spDomain = cookies
    }

}

================================================
FILE: baselibs/src/main/java/com/cxz/kotlin/baselibs/http/interceptor/HeaderInterceptor.kt
================================================
package com.cxz.kotlin.baselibs.http.interceptor

import com.cxz.kotlin.baselibs.http.constant.HttpConstant
import com.cxz.kotlin.baselibs.utils.Preference
import okhttp3.Interceptor
import okhttp3.Response

/**
 * @author chenxz
 * @date 2018/9/26
 * @desc HeaderInterceptor: 设置请求头
 */
class HeaderInterceptor : Interceptor {

    /**
     * token
     */
    private var token: String by Preference(HttpConstant.TOKEN_KEY, "")

    override fun intercept(chain: Interceptor.Chain): Response {

        val request = chain.request()
        val builder = request.newBuilder()

        builder.addHeader("Content-type", "application/json; charset=utf-8")
        // .header("token", token)
        // .method(request.method(), request.body())

        val domain = request.url().host()
        val url = request.url().toString()
        if (domain.isNotEmpty()) {
            val spDomain: String by Preference(domain, "")
            val cookie: String = if (spDomain.isNotEmpty()) spDomain else ""
            if (cookie.isNotEmpty()) {
                // 将 Cookie 添加到请求头
                builder.addHeader(HttpConstant.COOKIE_NAME, cookie)
            }
        }

        return chain.proceed(builder.build())
    }

}

================================================
FILE: baselibs/src/main/java/com/cxz/kotlin/baselibs/http/interceptor/QueryParameterInterceptor.kt
================================================
package com.cxz.kotlin.baselibs.http.interceptor

import okhttp3.Interceptor
import okhttp3.Request
import okhttp3.Response

/**
 * @author chenxz
 * @date 2018/9/26
 * @desc QueryParameterInterceptor 设置公共参数
 */
class QueryParameterInterceptor : Interceptor {

    override fun intercept(chain: Interceptor.Chain): Response {
        val originalRequest = chain.request()
        val request: Request
        val modifiedUrl = originalRequest.url().newBuilder()
                // Provide your custom parameter here
                .addQueryParameter("phoneSystem", "")
                .addQueryParameter("phoneModel", "")
                .build()
        request = originalRequest.newBuilder().url(modifiedUrl).build()
        return chain.proceed(request)
    }
}

================================================
FILE: baselibs/src/main/java/com/cxz/kotlin/baselibs/mvp/BaseModel.kt
================================================
package com.cxz.kotlin.baselibs.mvp

import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.OnLifecycleEvent
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.disposables.Disposable

/**
 * @author chenxz
 * @date 2018/11/18
 * @desc BaseModel
 */
abstract class BaseModel : IModel, LifecycleObserver {

    private var mCompositeDisposable: CompositeDisposable? = null

    override fun addDisposable(disposable: Disposable?) {
        if (mCompositeDisposable == null) {
            mCompositeDisposable = CompositeDisposable()
        }
        disposable?.let { mCompositeDisposable?.add(it) }
    }

    override fun onDetach() {
        unDispose()
    }

    private fun unDispose() {
        mCompositeDisposable?.clear()  // 保证Activity结束时取消
        mCompositeDisposable = null
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    fun onDestroy(owner: LifecycleOwner) {
        owner.lifecycle.removeObserver(this)
    }

}

================================================
FILE: baselibs/src/main/java/com/cxz/kotlin/baselibs/mvp/BasePresenter.kt
================================================
package com.cxz.kotlin.baselibs.mvp

import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.OnLifecycleEvent
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.disposables.Disposable
import org.greenrobot.eventbus.EventBus

/**
 * @author chenxz
 * @date 2018/11/18
 * @desc BasePresenter
 */
abstract class BasePresenter<M : IModel, V : IView> : IPresenter<V>, LifecycleObserver {

    protected var mModel: M? = null
    protected var mView: V? = null

    private val isViewAttached: Boolean
        get() = mView != null

    private var mCompositeDisposable: CompositeDisposable? = null

    /**
     * 创建 Model
     */
    open fun createModel(): M? = null

    /**
     * 是否使用 EventBus
     */
    open fun useEventBus(): Boolean = false

    override fun attachView(mView: V) {
        this.mView = mView
        mModel = createModel()
        if (mView is LifecycleOwner) {
            (mView as LifecycleOwner).lifecycle.addObserver(this)
            if (mModel != null && mModel is LifecycleObserver) {
                (mView as LifecycleOwner).lifecycle.addObserver(mModel as LifecycleObserver)
            }
        }
        if (useEventBus()) {
            EventBus.getDefault().register(this)
        }
    }

    override fun detachView() {
        if (useEventBus()) {
            EventBus.getDefault().unregister(this)
        }
        // 保证activity结束时取消所有正在执行的订阅
        unDispose()
        mModel?.onDetach()
        this.mModel = null
        this.mView = null
        this.mCompositeDisposable = null
    }

    open fun addDisposable(disposable: Disposable?) {
        if (mCompositeDisposable == null) {
            mCompositeDisposable = CompositeDisposable()
        }
        disposable?.let { mCompositeDisposable?.add(it) }
    }

    private fun unDispose() {
        mCompositeDisposable?.clear()  // 保证Activity结束时取消
        mCompositeDisposable = null
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    fun onDestroy(owner: LifecycleOwner) {
        // detachView()
        owner.lifecycle.removeObserver(this)
    }

    open fun checkViewAttached() {
        if (!isViewAttached) throw MvpViewNotAttachedException()
    }

    private class MvpViewNotAttachedException internal constructor() :
        RuntimeException("Please call IPresenter.attachView(IBaseView) before" + " requesting data to the IPresenter")


}

================================================
FILE: baselibs/src/main/java/com/cxz/kotlin/baselibs/mvp/IModel.kt
================================================
package com.cxz.kotlin.baselibs.mvp

import io.reactivex.disposables.Disposable

/**
 * @author chenxz
 * @date 2018/11/18
 * @desc IModel
 */
interface IModel {

    fun addDisposable(disposable: Disposable?)

    fun onDetach()

}

================================================
FILE: baselibs/src/main/java/com/cxz/kotlin/baselibs/mvp/IPresenter.kt
================================================
package com.cxz.kotlin.baselibs.mvp

/**
 * @author chenxz
 * @date 2018/11/18
 * @desc IPresenter
 */
interface IPresenter<in V : IView> {

    /**
     * 绑定 View
     */
    fun attachView(mView: V)

    /**
     * 解绑 View
     */
    fun detachView()

}

================================================
FILE: baselibs/src/main/java/com/cxz/kotlin/baselibs/mvp/IView.kt
================================================
package com.cxz.kotlin.baselibs.mvp

/**
 * @author chenxz
 * @date 2018/11/18
 * @desc IView
 */
interface IView {
    /**
     * 显示加载
     */
    fun showLoading()

    /**
     * 隐藏加载
     */
    fun hideLoading()

    /**
     * 使用默认的样式显示信息: CustomToast
     */
    fun showDefaultMsg(msg: String)

    /**
     * 显示信息
     */
    fun showMsg(msg: String)

    /**
     * 显示错误信息
     */
    fun showError(errorMsg: String)


}

================================================
FILE: baselibs/src/main/java/com/cxz/kotlin/baselibs/provider/NewsService.kt
================================================
package com.cxz.kotlin.baselibs.provider

import com.alibaba.android.arouter.facade.template.IProvider

/**
 * @author chenxz
 * @date 2018/12/22
 * @desc
 */
interface NewsService : IProvider {

    fun getNewsName(): String

}

================================================
FILE: baselibs/src/main/java/com/cxz/kotlin/baselibs/rx/BaseObserver.kt
================================================
package com.cxz.kotlin.baselibs.rx

import com.cxz.kotlin.baselibs.bean.BaseBean
import com.cxz.kotlin.baselibs.http.HttpStatus
import com.cxz.kotlin.baselibs.http.exception.ExceptionHandle
import com.cxz.kotlin.baselibs.mvp.IView
import com.cxz.kotlin.baselibs.utils.NetWorkUtil
import io.reactivex.observers.ResourceObserver

/**
 * Created by chenxz on 2018/6/11.
 */
abstract class BaseObserver<T : BaseBean> : ResourceObserver<T> {

    private var mView: IView? = null
    private var mErrorMsg = ""
    private var bShowLoading = true

    constructor(view: IView) {
        this.mView = view
    }

    constructor(view: IView, bShowLoading: Boolean) {
        this.mView = view
        this.bShowLoading = bShowLoading
    }

    /**
     * 成功的回调
     */
    protected abstract fun onSuccess(t: T)

    /**
     * 错误的回调
     */
    protected fun onError(t: T) {}

    override fun onStart() {
        super.onStart()
        if (bShowLoading) mView?.showLoading()
        if (!NetWorkUtil.isConnected()) {
            mView?.showDefaultMsg("当前网络不可用,请检查网络设置")
            onComplete()
        }
    }

    override fun onNext(t: T) {
        mView?.hideLoading()
        when {
            t.errorCode == HttpStatus.SUCCESS -> onSuccess(t)
            t.errorCode == HttpStatus.TOKEN_INVALID -> {
                // TODO Token 过期,重新登录
            }
            else -> {
                onError(t)
                if (t.errorMsg.isNotEmpty())
                    mView?.showDefaultMsg(t.errorMsg)
            }
        }
    }

    override fun onError(e: Throwable) {
        mView?.hideLoading()
        if (mView == null) {
            throw RuntimeException("mView can not be null")
        }
        if (mErrorMsg.isEmpty()) {
            mErrorMsg = ExceptionHandle.handleException(e)
        }
        mView?.showDefaultMsg(mErrorMsg)
    }

    override fun onComplete() {
        mView?.hideLoading()
    }
}



================================================
FILE: baselibs/src/main/java/com/cxz/kotlin/baselibs/rx/BaseSubscriber.kt
================================================
package com.cxz.kotlin.baselibs.rx

import com.cxz.kotlin.baselibs.bean.BaseBean
import com.cxz.kotlin.baselibs.http.HttpStatus
import com.cxz.kotlin.baselibs.http.exception.ExceptionHandle
import com.cxz.kotlin.baselibs.mvp.IView
import com.cxz.kotlin.baselibs.utils.NetWorkUtil
import io.reactivex.subscribers.ResourceSubscriber

/**
 * Created by chenxz on 2018/6/11.
 */
abstract class BaseSubscriber<T : BaseBean> : ResourceSubscriber<T> {

    private var mView: IView? = null
    private var mErrorMsg = ""
    private var bShowLoading = true

    constructor(view: IView) {
        this.mView = view
    }

    constructor(view: IView, bShowLoading: Boolean) {
        this.mView = view
        this.bShowLoading = bShowLoading
    }

    /**
     * 成功的回调
     */
    protected abstract fun onSuccess(t: T)

    /**
     * 错误的回调
     */
    protected fun onError(t: T) {}

    override fun onStart() {
        super.onStart()
        if (bShowLoading) mView?.showLoading()
        if (!NetWorkUtil.isConnected()) {
            mView?.showDefaultMsg("当前网络不可用,请检查网络设置")
            onComplete()
        }
    }

    override fun onNext(t: T) {
        mView?.hideLoading()
        when {
            t.errorCode == HttpStatus.SUCCESS -> onSuccess(t)
            t.errorCode == HttpStatus.TOKEN_INVALID -> {
                // TODO Token 过期,重新登录
            }
            else -> {
                onError(t)
                if (t.errorMsg.isNotEmpty())
                    mView?.showDefaultMsg(t.errorMsg)
            }
        }
    }

    override fun onError(e: Throwable) {
        mView?.hideLoading()
        if (mView == null) {
            throw RuntimeException("mView can not be null")
        }
        if (mErrorMsg.isEmpty()) {
            mErrorMsg = ExceptionHandle.handleException(e)
        }
        mView?.showDefaultMsg(mErrorMsg)
    }

    override fun onComplete() {
        mView?.hideLoading()
    }

}

================================================
FILE: baselibs/src/main/java/com/cxz/kotlin/baselibs/rx/SchedulerUtils.kt
================================================
package com.cxz.kotlin.baselibs.rx

import com.cxz.kotlin.baselibs.rx.scheduler.IoMainScheduler

/**
 * Created by chenxz on 2018/4/21.
 */
object SchedulerUtils {

    fun <T> ioToMain(): IoMainScheduler<T> = IoMainScheduler()

}

================================================
FILE: baselibs/src/main/java/com/cxz/kotlin/baselibs/rx/scheduler/BaseScheduler.kt
================================================
package com.cxz.kotlin.baselibs.rx.scheduler

import io.reactivex.*
import org.reactivestreams.Publisher

/**
 * Created by chenxz on 2018/4/21.
 */
abstract class BaseScheduler<T> protected constructor(
    private val subscribeOnScheduler: Scheduler,
    private val observeOnScheduler: Scheduler
) : ObservableTransformer<T, T>,
    SingleTransformer<T, T>,
    MaybeTransformer<T, T>,
    CompletableTransformer,
    FlowableTransformer<T, T> {

    override fun apply(upstream: Completable): CompletableSource {
        return upstream.subscribeOn(subscribeOnScheduler)
            .observeOn(observeOnScheduler)
    }

    override fun apply(upstream: Flowable<T>): Publisher<T> {
        return upstream.subscribeOn(subscribeOnScheduler)
            .observeOn(observeOnScheduler)
    }

    override fun apply(upstream: Maybe<T>): MaybeSource<T> {
        return upstream.subscribeOn(subscribeOnScheduler)
            .observeOn(observeOnScheduler)
    }

    override fun apply(upstream: Observable<T>): ObservableSource<T> {
        return upstream.subscribeOn(subscribeOnScheduler)
            .observeOn(observeOnScheduler)
    }

    override fun apply(upstream: Single<T>): SingleSource<T> {
        return upstream.subscribeOn(subscribeOnScheduler)
            .observeOn(observeOnScheduler)
    }
}


================================================
FILE: baselibs/src/main/java/com/cxz/kotlin/baselibs/rx/scheduler/ComputationMainScheduler.kt
================================================
package com.cxz.kotlin.baselibs.rx.scheduler

import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers

/**
 * Created by chenxz on 2018/4/21.
 */
class ComputationMainScheduler<T> private constructor() :
    BaseScheduler<T>(Schedulers.computation(), AndroidSchedulers.mainThread())


================================================
FILE: baselibs/src/main/java/com/cxz/kotlin/baselibs/rx/scheduler/IoMainScheduler.kt
================================================
package com.cxz.kotlin.baselibs.rx.scheduler

import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers

/**
 * Created by chenxz on 2018/4/21.
 */
class IoMainScheduler<T> : BaseScheduler<T>(Schedulers.io(), AndroidSchedulers.mainThread())


================================================
FILE: baselibs/src/main/java/com/cxz/kotlin/baselibs/rx/scheduler/NewThreadMainScheduler.kt
================================================
package com.cxz.kotlin.baselibs.rx.scheduler

import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers

/**
 * Created by chenxz on 2018/4/21.
 */
class NewThreadMainScheduler<T> private constructor() :
    BaseScheduler<T>(Schedulers.newThread(), AndroidSchedulers.mainThread())


================================================
FILE: baselibs/src/main/java/com/cxz/kotlin/baselibs/rx/scheduler/SingleMainScheduler.kt
================================================
package com.cxz.kotlin.baselibs.rx.scheduler

import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers

/**
 * Created by chenxz on 2018/4/21.
 */
class SingleMainScheduler<T> private constructor() :
    BaseScheduler<T>(Schedulers.single(), AndroidSchedulers.mainThread())


================================================
FILE: baselibs/src/main/java/com/cxz/kotlin/baselibs/rx/scheduler/TrampolineMainScheduler.kt
================================================
package com.cxz.kotlin.baselibs.rx.scheduler

import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers

/**
 * Created by chenxz on 2018/4/21.
 */
class TrampolineMainScheduler<T> private constructor() :
    BaseScheduler<T>(Schedulers.trampoline(), AndroidSchedulers.mainThread())


================================================
FILE: baselibs/src/main/java/com/cxz/kotlin/baselibs/utils/AnimatorUtil.kt
================================================
package com.cxz.kotlin.baselibs.utils

import android.view.View
import android.view.animation.AccelerateInterpolator
import androidx.core.view.ViewCompat
import androidx.core.view.ViewPropertyAnimatorListener
import androidx.interpolator.view.animation.LinearOutSlowInInterpolator

/**
 * Created by chenxz on 2018/5/13.
 */
object AnimatorUtil {

    private val FAST_OUT_SLOW_IN_INTERPOLATOR: LinearOutSlowInInterpolator by lazy {
        LinearOutSlowInInterpolator()
    }

    private val LINER_INTERPOLATOR: AccelerateInterpolator by lazy {
        AccelerateInterpolator()
    }

    /**
     * 显示View
     * @param view View
     * @param listener ViewPropertyAnimatorListener
     */
    fun scaleShow(view: View, listener: ViewPropertyAnimatorListener) {
        view.visibility = View.VISIBLE
        ViewCompat.animate(view)
            .scaleX(1.0f)
            .scaleY(1.0f)
            .alpha(1.0f)
            .setDuration(800)
            .setListener(listener)
            .setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR)
            .start()
    }

    /**
     * 隐藏View
     * @param view View
     * @param listener ViewPropertyAnimatorListener
     */
    fun scaleHide(view: View, listener: ViewPropertyAnimatorListener) {
        ViewCompat.animate(view)
            .scaleX(0.0f)
            .scaleY(0.0f)
            .alpha(0.0f)
            .setDuration(800)
            .setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR)
            .setListener(listener)
            .start()
    }

    /**
     * 显示view
     *
     * @param view View
     * @param listener ViewPropertyAnimatorListener
     */
    fun translateShow(view: View, listener: ViewPropertyAnimatorListener) {
        view.visibility = View.VISIBLE
        ViewCompat.animate(view)
            .translationY(0f)
            .setDuration(400)
            .setListener(listener)
            .setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR)
            .start()
    }

    /**
     * 隐藏view
     *
     * @param view View
     * @param listener ViewPropertyAnimatorListener
     */
    fun translateHide(view: View, listener: ViewPropertyAnimatorListener) {
        view.visibility = View.VISIBLE
        ViewCompat.animate(view)
            .translationY(350f)
            .setDuration(400)
            .setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR)
            .setListener(listener)
            .start()
    }

}

================================================
FILE: baselibs/src/main/java/com/cxz/kotlin/baselibs/utils/AppUtils.kt
================================================
package com.cxz.kotlin.baselibs.utils

import android.annotation.SuppressLint
import android.app.ActivityManager
import android.content.Context
import android.content.pm.PackageManager
import java.security.MessageDigest

/**
 * Created by chenxz on 2018/4/21.
 */
class AppUtils private constructor() {
    init {
        throw Error("Do not need instantiate!")
    }

    companion object {

        private val DEBUG = true
        private val TAG = "AppUtils"


        /**
         * 得到软件版本号
         *
         * @param context 上下文
         * @return 当前版本Code
         */
        fun getVerCode(context: Context): Int {
            var verCode = -1
            try {
                val packageName = context.packageName
                verCode = context.packageManager
                    .getPackageInfo(packageName, 0).versionCode
            } catch (e: PackageManager.NameNotFoundException) {
                e.printStackTrace()
            }

            return verCode
        }


        /**
         * 获取应用运行的最大内存
         *
         * @return 最大内存
         */
        val maxMemory: Long
            get() = Runtime.getRuntime().maxMemory() / 1024

        /**
         * 得到软件显示版本信息
         *
         * @param context 上下文
         * @return 当前版本信息
         */
        fun getVerName(context: Context): String {
            var verName = ""
            try {
                val packageName = context.packageName
                verName = context.packageManager
                    .getPackageInfo(packageName, 0).versionName
            } catch (e: PackageManager.NameNotFoundException) {
                e.printStackTrace()
            }

            return verName
        }


        /**
         * 获取应用签名
         *
         * @param context 上下文
         * @param pkgName 包名
         * @return 返回应用的签名
         */
        fun getSign(context: Context, pkgName: String): String? {
            return try {
                @SuppressLint("PackageManagerGetSignatures") val pis = context.packageManager
                    .getPackageInfo(
                        pkgName,
                        PackageManager.GET_SIGNATURES
                    )
                hexDigest(pis.signatures[0].toByteArray())
            } catch (e: PackageManager.NameNotFoundException) {
                e.printStackTrace()
                null
            }

        }

        /**
         * 将签名字符串转换成需要的32位签名
         *
         * @param paramArrayOfByte 签名byte数组
         * @return 32位签名字符串
         */
        private fun hexDigest(paramArrayOfByte: ByteArray): String {
            val hexDigits = charArrayOf(
                48.toChar(),
                49.toChar(),
                50.toChar(),
                51.toChar(),
                52.toChar(),
                53.toChar(),
                54.toChar(),
                55.toChar(),
                56.toChar(),
                57.toChar(),
                97.toChar(),
                98.toChar(),
                99.toChar(),
                100.toChar(),
                101.toChar(),
                102.toChar()
            )
            try {
                val localMessageDigest = MessageDigest.getInstance("MD5")
                localMessageDigest.update(paramArrayOfByte)
                val arrayOfByte = localMessageDigest.digest()
                val arrayOfChar = CharArray(32)
                var i = 0
                var j = 0
                while (true) {
                    if (i >= 16) {
                        return String(arrayOfChar)
                    }
                    val k = arrayOfByte[i].toInt()
                    arrayOfChar[j] = hexDigits[0xF and k.ushr(4)]
                    arrayOfChar[++j] = hexDigits[k and 0xF]
                    i++
                    j++
                }
            } catch (e: Exception) {
                e.printStackTrace()
            }

            return ""
        }


        /**
         * 获取设备的可用内存大小
         *
         * @param context 应用上下文对象context
         * @return 当前内存大小
         */
        fun getDeviceUsableMemory(context: Context): Int {
            val am = context.getSystemService(
                Context.ACTIVITY_SERVICE
            ) as ActivityManager
            val mi = ActivityManager.MemoryInfo()
            am.getMemoryInfo(mi)
            // 返回当前系统的可用内存
            return (mi.availMem / (1024 * 1024)).toInt()
        }

        /**
         * 获取手机系统SDK版本
         *
         * @return 如API 17 则返回 17
         */
        val sdkVersion: Int
            get() = android.os.Build.VERSION.SDK_INT
    }

}

================================================
FILE: baselibs/src/main/java/com/cxz/kotlin/baselibs/utils/CommonUtil.kt
================================================
package com.cxz.kotlin.baselibs.utils

import android.content.Context
import android.graphics.Color
import android.text.TextUtils
import android.util.TypedValue
import android.view.View
import android.view.inputmethod.InputMethodManager
import java.io.BufferedReader
import java.io.FileReader
import java.io.IOException
import java.lang.reflect.Field
import java.util.*


/**
 * Created by chenxz on 2018/5/14.
 */
object CommonUtil {

    init {
    }

    fun dp2px(context: Context, dpValue: Float): Int {
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpValue, context.resources.displayMetrics).toInt()
    }

    fun sp2px(context: Context, spValue: Float): Int {
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, spValue, context.resources.displayMetrics).toInt()
    }

    /**
     * 获取状态栏高度
     *
     * @param context context
     * @return 状态栏高度
     */
    fun getStatusBarHeight(context: Context): Int {
        // 获得状态栏高度
        val resourceId = context.resources.getIdentifier("status_bar_height", "dimen", "android")
        return context.resources.getDimensionPixelSize(resourceId)
    }

    /**
     * 获取随机rgb颜色值
     */
    fun randomColor(): Int {
        val random = Random()
        //0-190, 如果颜色值过大,就越接近白色,就看不清了,所以需要限定范围
        var red = random.nextInt(190)
        var green = random.nextInt(190)
        var blue = random.nextInt(190)
//        if (SettingUtil.getIsNightMode()) {
//            //150-255
//            red = random.nextInt(105) + 150
//            green = random.nextInt(105) + 150
//            blue = random.nextInt(105) + 150
//        }
        //使用rgb混合生成一种新的颜色,Color.rgb生成的是一个int数
        return Color.rgb(red, green, blue)
    }

    /**
     * 解决InputMethodManager引起的内存泄漏
     * 在Activity的onDestroy方法里调用
     */
    fun fixInputMethodManagerLeak(context: Context) {

        val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
        val arr = arrayOf("mCurRootView", "mServedView", "mNextServedView")
        var field: Field? = null
        var objGet: Any? = null
        for (i in arr.indices) {
            val param = arr[i]
            try {
                field = imm.javaClass.getDeclaredField(param)
                if (field.isAccessible === false) {
                    field.isAccessible = true
                }
                objGet = field.get(imm)
                if (objGet != null && objGet is View) {
                    val view = objGet
                    if (view.context === context) {
                        // 被InputMethodManager持有引用的context是想要目标销毁的
                        field.set(imm, null) // 置空,破坏掉path to gc节点
                    } else {
                        // 不是想要目标销毁的,即为又进了另一层界面了,不要处理,避免影响原逻辑,也就不用继续for循环了
                        break
                    }
                }
            } catch (t: Throwable) {
                t.printStackTrace()
            }

        }

    }

    /**
     * 获取当前进程名
     */
    fun getProcessName(pid: Int): String {
        var reader: BufferedReader? = null
        try {
            reader = BufferedReader(FileReader("/proc/$pid/cmdline"))
            var processName = reader!!.readLine()
            if (!TextUtils.isEmpty(processName)) {
                processName = processName.trim({ it <= ' ' })
            }
            return processName
        } catch (throwable: Throwable) {
            throwable.printStackTrace()
        } finally {
            try {
                if (reader != null) {
                    reader!!.close()
                }
            } catch (exception: IOException) {
                exception.printStackTrace()
            }
        }
        return ""
    }

}

================================================
FILE: baselibs/src/main/java/com/cxz/kotlin/baselibs/utils/FileProvider7.kt
================================================
package com.cxz.kotlin.baselibs.utils

import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import androidx.core.content.FileProvider
import java.io.File

/**
 * Created by chenxz on 2018/7/16.
 */
object FileProvider7 {

    fun getUriForFile(context: Context, file: File): Uri? {
        var fileUri: Uri? = null
        if (Build.VERSION.SDK_INT >= 24) {
            fileUri = getUriForFile24(context, file)
        } else {
            fileUri = Uri.fromFile(file)
        }
        return fileUri
    }


    fun getUriForFile24(context: Context, file: File): Uri {
        val fileUri = FileProvider.getUriForFile(context, context.packageName + ".fileprovider", file)
        return fileUri
    }


    fun setIntentDataAndType(
        context: Context,
        intent: Intent,
        type: String,
        file: File,
        writeAble: Boolean
    ) {
        if (Build.VERSION.SDK_INT >= 24) {
            intent.setDataAndType(getUriForFile(context, file), type)
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
            if (writeAble) {
                intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
            }
        } else {
            intent.setDataAndType(Uri.fromFile(file), type)
        }
    }

    fun setIntentData(
        context: Context,
        intent: Intent,
        file: File,
        writeAble: Boolean
    ) {
        if (Build.VERSION.SDK_INT >= 24) {
            intent.data = getUriForFile(context, file)
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
            if (writeAble) {
                intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
            }
        } else {
            intent.data = Uri.fromFile(file)
        }
    }

    fun grantPermissions(context: Context, intent: Intent, uri: Uri, writeAble: Boolean) {
        var flag = Intent.FLAG_GRANT_READ_URI_PERMISSION
        if (writeAble) {
            flag = flag or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
        }
        intent.addFlags(flag)
        val resInfoList = context.packageManager
            .queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY)
        for (resolveInfo in resInfoList) {
            val packageName = resolveInfo.activityInfo.packageName
            context.grantUriPermission(packageName, uri, flag)
        }
    }

}

================================================
FILE: baselibs/src/main/java/com/cxz/kotlin/baselibs/utils/KeyBoardUtil.kt
================================================
package com.cxz.kotlin.baselibs.utils

import android.content.Context
import android.view.MotionEvent
import android.view.View
import android.view.inputmethod.InputMethodManager
import android.widget.EditText

/**
 * @author chenxz
 * @date 2018/11/19
 * @desc KeyBoardUtil
 */
object KeyBoardUtil {

    /**
     * 是否落在 EditText 区域
     */
    fun isHideKeyboard(view: View?, event: MotionEvent): Boolean {
        if (view != null && view is EditText) {
            val location = intArrayOf(0, 0)
            view.getLocationInWindow(location)
            //获取现在拥有焦点的控件view的位置,即EditText
            val left = location[0]
            val top = location[1]
            val bottom = top + view.height
            val right = left + view.width
            //判断我们手指点击的区域是否落在EditText上面,如果不是,则返回true,否则返回false
            val isInEt = (event.x > left && event.x < right && event.y > top && event.y < bottom)
            return !isInEt
        }
        return false
    }

    /**
     * 关闭软键盘
     */
    fun hideKeyBoard(context: Context, view: View?) {
        val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
        imm.hideSoftInputFromWindow(view!!.windowToken, InputMethodManager.HIDE_NOT_ALWAYS)
    }

    /**
     * 打卡软键盘
     */
    fun openKeyBord(mEditText: EditText, mContext: Context) {
        val imm = mContext.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
        imm.showSoftInput(mEditText, InputMethodManager.RESULT_SHOWN)
        imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, InputMethodManager.HIDE_IMPLICIT_ONLY)
    }

    /**
     * 关闭软键盘
     */
    fun closeKeyBord(mEditText: EditText, mContext: Context) {
        val imm = mContext.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
        imm.hideSoftInputFromWindow(mEditText.windowToken, 0)
    }

}

================================================
FILE: baselibs/src/main/java/com/cxz/kotlin/baselibs/utils/NLog.kt
================================================
package com.cxz.kotlin.baselibs.utils

import android.util.Log

/**
 * @author chenxz
 * @date 2019/11/9
 * @desc 日志打印类
 */
object NLog {

    private const val TAG = "KotlinMVP"

    private var debug: Boolean = false

    fun openLog() {
        debug = true
    }

    fun i(tag: String, content: String) {
        if (debug) {
            Log.i(tag, content)
        }
    }

    fun i(content: String) {
        i(TAG, content)
    }

    fun v(tag: String, content: String) {
        if (debug) {
            Log.v(tag, content)
        }
    }

    fun v(content: String) {
        v(TAG, content)
    }

    fun d(tag: String, content: String) {
        if (debug) {
            Log.d(tag, content)
        }
    }

    fun d(content: String) {
        d(TAG, content)
    }

    fun w(tag: String, content: String) {
        if (debug) {
            Log.w(tag, content)
        }
    }

    fun w(content: String) {
        w(TAG, content)
    }

    fun e(tag: String, content: String) {
        if (debug) {
            Log.e(tag, content)
        }
    }

    fun e(content: String) {
        e(TAG, content)
    }

}

================================================
FILE: baselibs/src/main/java/com/cxz/kotlin/baselibs/utils/NetWorkUtil.kt
================================================
package com.cxz.kotlin.baselibs.utils

import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.net.ConnectivityManager
import android.net.NetworkInfo
import android.net.wifi.WifiManager
import android.provider.Settings
import android.telephony.TelephonyManager
import com.cxz.kotlin.baselibs.config.AppConfig
import java.net.NetworkInterface
import java.net.SocketException

/**
 * @author admin
 * @date 2018/11/21
 * @desc
 */
object NetWorkUtil {

    val NETWORK_WIFI = 1 // wifi network
    val NETWORK_4G = 4 // "4G" networks
    val NETWORK_3G = 3 // "3G" networks
    val NETWORK_2G = 2 // "2G" networks
    val NETWORK_UNKNOWN = 5 // unknown network
    val NETWORK_NO = -1 // no network

    private val NETWORK_TYPE_GSM = 16
    private val NETWORK_TYPE_TD_SCDMA = 17
    private val NETWORK_TYPE_IWLAN = 18

    /**
     * 打开网络设置界面
     *
     * 3.0以下打开设置界面
     *
     * @param context 上下文
     */
    fun openWirelessSettings(context: Context) {
        if (android.os.Build.VERSION.SDK_INT > 10) {
            context.startActivity(Intent(Settings.ACTION_WIRELESS_SETTINGS))
        } else {
            context.startActivity(Intent(Settings.ACTION_SETTINGS))
        }
    }

    /**
     * 获取活动网络信息
     *
     * 需添加权限
     * `<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>`
     *
     * @param context 上下文
     *
     * @return NetworkInfo
     */
    @SuppressLint("MissingPermission")
    private fun getActiveNetworkInfo(context: Context): NetworkInfo? {
        val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
        return cm.activeNetworkInfo
    }

    /**
     * 判断网络是否连接
     *
     * 需添加权限
     * `<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>`
     *
     * @return `true`: 是 `false`: 否
     */
    fun isConnected(): Boolean {
        return isConnected(AppConfig.getApplication())
    }

    /**
     * 判断网络是否连接
     *
     * 需添加权限
     * `<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>`
     *
     * @return `true`: 是 `false`: 否
     */
    fun isConnected(context: Context): Boolean {
        val info = getActiveNetworkInfo(context)
        return info != null && info.isConnected
    }

    /**
     * 打开或关闭移动数据
     *
     * 需系统应用 需添加权限
     * `<uses-permission android:name="android.permission.MODIFY_PHONE_STATE"/>`
     *
     * @param context 上下文
     * @param enabled `true`: 打开 `false`: 关闭
     */
    fun setDataEnabled(context: Context, enabled: Boolean) {
        try {
            val tm = context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
            val setMobileDataEnabledMethod =
                tm.javaClass.getDeclaredMethod("setDataEnabled", Boolean::class.javaPrimitiveType)
            setMobileDataEnabledMethod?.invoke(tm, enabled)
        } catch (e: Exception) {
            e.printStackTrace()
        }

    }

    /**
     * 判断网络是否是4G
     *
     * 需添加权限
     * `<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>`
     *
     * @param context 上下文
     *
     * @return `true`: 是 `false`: 否
     */
    fun is4G(context: Context): Boolean {
        val info = getActiveNetworkInfo(context)
        return info != null && info.isAvailable && info.subtype == TelephonyManager.NETWORK_TYPE_LTE
    }

    /**
     * 判断wifi是否打开
     *
     * 需添加权限
     * `<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>`
     *
     * @param context 上下文
     *
     * @return `true`: 是 `false`: 否
     */
    fun getWifiEnabled(context: Context): Boolean {
        val wifiManager = context.getSystemService(Context.WIFI_SERVICE) as WifiManager
        return wifiManager.isWifiEnabled
    }

    /**
     * 打开或关闭wifi
     *
     * 需添加权限
     * `<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>`
     *
     * @param context 上下文
     *
     * @param enabled true`: 打开 `false`: 关闭
     */
    fun setWifiEnabled(context: Context, enabled: Boolean) {
        val wifiManager = context.getSystemService(Context.WIFI_SERVICE) as WifiManager
        if (enabled) {
            if (!wifiManager.isWifiEnabled) {
                //				wifiManager.setWifiEnabled(true);
            }
        } else {
            if (wifiManager.isWifiEnabled) {
                //				wifiManager.setWifiEnabled(false);
            }
        }
    }

    /**
     * 判断wifi是否连接状态
     *
     * 需添加权限
     * `<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>`
     *
     * @param context 上下文
     *
     * @return `true`: 连接 `false`: 未连接
     */
    fun isWifiConnected(context: Context): Boolean {
        val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
        return cm.activeNetworkInfo?.type == ConnectivityManager.TYPE_WIFI
    }

    /**
     * 获取网络运营商名称
     *
     * 中国移动、如中国联通、中国电信
     *
     * @param context 上下文
     *
     * @return 运营商名称
     */
    fun getNetworkOperatorName(context: Context): String? {
        val tm = context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
        return tm?.networkOperatorName
    }

    /**
     * 获取当前的网络类型(WIFI,2G,3G,4G)
     *
     * 需添加权限
     * `<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>`
     *
     * @param context 上下文
     *
     * @return 网络类型
     *
     *  * [.NETWORK_WIFI] = 1;
     *  * [.NETWORK_4G] = 4;
     *  * [.NETWORK_3G] = 3;
     *  * [.NETWORK_2G] = 2;
     *  * [.NETWORK_UNKNOWN] = 5;
     *  * [.NETWORK_NO] = -1;
     *
     */
    fun getNetworkType(context: Context): Int {
        var netType = NETWORK_NO
        val info = getActiveNetworkInfo(context)
        if (info != null && info.isAvailable) {

            if (info.type == ConnectivityManager.TYPE_WIFI) {
                netType = NETWORK_WIFI
            } else if (info.type == ConnectivityManager.TYPE_MOBILE) {
                when (info.subtype) {

                    NETWORK_TYPE_GSM, TelephonyManager.NETWORK_TYPE_GPRS, TelephonyManager.NETWORK_TYPE_CDMA, TelephonyManager.NETWORK_TYPE_EDGE, TelephonyManager.NETWORK_TYPE_1xRTT, TelephonyManager.NETWORK_TYPE_IDEN -> netType =
                        NETWORK_2G

                    NETWORK_TYPE_TD_SCDMA, TelephonyManager.NETWORK_TYPE_EVDO_A, TelephonyManager.NETWORK_TYPE_UMTS, TelephonyManager.NETWORK_TYPE_EVDO_0, TelephonyManager.NETWORK_TYPE_HSDPA, TelephonyManager.NETWORK_TYPE_HSUPA, TelephonyManager.NETWORK_TYPE_HSPA, TelephonyManager.NETWORK_TYPE_EVDO_B, TelephonyManager.NETWORK_TYPE_EHRPD, TelephonyManager.NETWORK_TYPE_HSPAP -> netType =
                        NETWORK_3G

                    NETWORK_TYPE_IWLAN, TelephonyManager.NETWORK_TYPE_LTE -> netType = NETWORK_4G
                    else -> {

                        val subtypeName = info.subtypeName
                        if ("TD-SCDMA".equals(subtypeName, ignoreCase = true) || "WCDMA".equals(
                                subtypeName,
                                ignoreCase = true
                            )
                            || "CDMA2000".equals(subtypeName, ignoreCase = true)
                        ) {
                            netType = NETWORK_3G
                        } else {
                            netType = NETWORK_UNKNOWN
                        }
                    }
                }
            } else {
                netType = NETWORK_UNKNOWN
            }
        }
        return netType
    }

    /**
     * 获取当前的网络类型(WIFI,2G,3G,4G)
     *
     * 依赖上面的方法
     *
     * @param context 上下文
     *
     * @return 网络类型名称
     *
     *  * NETWORK_WIFI
     *  * NETWORK_4G
     *  * NETWORK_3G
     *  * NETWORK_2G
     *  * NETWORK_UNKNOWN
     *  * NETWORK_NO
     *
     */
    fun getNetworkTypeName(context: Context): String {
        return when (getNetworkType(context)) {
            NETWORK_WIFI -> "NETWORK_WIFI"
            NETWORK_4G -> "NETWORK_4G"
            NETWORK_3G -> "NETWORK_3G"
            NETWORK_2G -> "NETWORK_2G"
            NETWORK_NO -> "NETWORK_NO"
            else -> "NETWORK_UNKNOWN"
        }
    }

    /**
     * 获取IP地址
     *
     * 需添加权限
     * `<uses-permission android:name="android.permission.INTERNET"/>`
     *
     * @param useIPv4 是否用IPv4
     *
     * @return IP地址
     */
    fun getIPAddress(useIPv4: Boolean): String? {
        try {
            val nis = NetworkInterface.getNetworkInterfaces()
            while (nis.hasMoreElements()) {
                val ni = nis.nextElement()
                // 防止小米手机返回10.0.2.15
                if (!ni.isUp) {
                    continue
                }

                val addresses = ni.inetAddresses
                while (addresses.hasMoreElements()) {
                    val inetAddress = addresses.nextElement()
                    if (!inetAddress.isLoopbackAddress) {
                        val hostAddress = inetAddress.hostAddress
                        val isIPv4 = hostAddress.indexOf(':') < 0
                        if (useIPv4) {
                            if (isIPv4) {
                                return hostAddress
                            }

                        } else {
                            if (!isIPv4) {
                                val index = hostAddress.indexOf('%')
                                return if (index < 0)
                                    hostAddress.toUpperCase()
                                else
                                    hostAddress.substring(0, index).toUpperCase()
                            }
                        }
                    }
                }
            }
        } catch (e: SocketException) {
            e.printStackTrace()
        }

        return null
    }

}

================================================
FILE: baselibs/src/main/java/com/cxz/kotlin/baselibs/utils/Preference.kt
================================================
package com.cxz.kotlin.baselibs.utils

import android.annotation.SuppressLint
import android.content.Context
import android.content.SharedPreferences
import com.cxz.kotlin.baselibs.config.AppConfig
import java.io.*
import kotlin.reflect.KProperty

/**
 * Created by chenxz on 2018/4/21.
 * kotlin委托属性+SharedPreference实例
 */
class Preference<T>(val name: String, private val default: T) {

    companion object {
        private val file_name = "wan_android_file"

        private val prefs: SharedPreferences by lazy {
            AppConfig.getApplication().getSharedPreferences(file_name, Context.MODE_PRIVATE)
        }

        /**
         * 删除全部数据
         */
        fun clearPreference() {
            prefs.edit().clear().apply()
        }

        /**
         * 根据key删除存储数据
         */
        fun clearPreference(key: String) {
            prefs.edit().remove(key).apply()
        }

        /**
         * 查询某个key是否已经存在
         *
         * @param key
         * @return
         */
        fun contains(key: String): Boolean {
            return prefs.contains(key)
        }

        /**
         * 返回所有的键值对
         *
         * @param context
         * @return
         */
        fun getAll(): Map<String, *> {
            return prefs.all
        }
    }

    operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
        return getSharedPreferences(name, default)
    }

    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
        putSharedPreferences(name, value)
    }

    @SuppressLint("CommitPrefEdits")
    private fun putSharedPreferences(name: String, value: T) = with(prefs.edit()) {
        when (value) {
            is Long -> putLong(name, value)
            is String -> putString(name, value)
            is Int -> putInt(name, value)
            is Boolean -> putBoolean(name, value)
            is Float -> putFloat(name, value)
            else -> putString(name, serialize(value))
        }.apply()
    }

    @Suppress("UNCHECKED_CAST")
    private fun getSharedPreferences(name: String, default: T): T = with(prefs) {
        val res: Any = when (default) {
            is Long -> getLong(name, default)
            is String -> getString(name, default) ?: ""
            is Int -> getInt(name, default)
            is Boolean -> getBoolean(name, default)
            is Float -> getFloat(name, default)
            else -> deSerialization(getString(name, serialize(default)) ?: "")
        }
        return res as T
    }

    /**
     * 序列化对象

     * @param person
     * *
     * @return
     * *
     * @throws IOException
     */
    @Throws(IOException::class)
    private fun <A> serialize(obj: A): String {
        val byteArrayOutputStream = ByteArrayOutputStream()
        val objectOutputStream = ObjectOutputStream(
            byteArrayOutputStream
        )
        objectOutputStream.writeObject(obj)
        var serStr = byteArrayOutputStream.toString("ISO-8859-1")
        serStr = java.net.URLEncoder.encode(serStr, "UTF-8")
        objectOutputStream.close()
        byteArrayOutputStream.close()
        return serStr
    }

    /**
     * 反序列化对象

     * @param str
     * *
     * @return
     * *
     * @throws IOException
     * *
     * @throws ClassNotFoundException
     */
    @Suppress("UNCHECKED_CAST")
    @Throws(IOException::class, ClassNotFoundException::class)
    private fun <A> deSerialization(str: String): A {
        val redStr = java.net.URLDecoder.decode(str, "UTF-8")
        val byteArrayInputStream = ByteArrayInputStream(
            redStr.toByteArray(charset("ISO-8859-1"))
        )
        val objectInputStream = ObjectInputStream(
            byteArrayInputStream
        )
        val obj = objectInputStream.readObject() as A
        objectInputStream.close()
        byteArrayInputStream.close()
        return obj
    }

}


================================================
FILE: baselibs/src/main/java/com/cxz/kotlin/baselibs/utils/RomUtil.kt
================================================
package com.cxz.kotlin.baselibs.utils

import android.os.Build
import android.text.TextUtils
import java.io.BufferedReader
import java.io.IOException
import java.io.InputStreamReader

/**
 * Created by chenxz on 2018/4/21.
 */
object RomUtil {

    internal object AvailableRomType {
        val MIUI = 1
        val FLYME = 2
        val ANDROID_NATIVE = 3
        val NA = 4
    }

    fun isLightStatusBarAvailable(): Boolean {
        return isMIUIV6OrAbove() || isFlymeV4OrAbove() || isAndroidMOrAbove()
    }

    fun getLightStatausBarAvailableRomType(): Int {
        if (isMIUIV6OrAbove()) {
            return AvailableRomType.MIUI
        }

        if (isFlymeV4OrAbove()) {
            return AvailableRomType.FLYME
        }

        return if (isAndroidMOrAbove()) {
            AvailableRomType.ANDROID_NATIVE
        } else AvailableRomType.NA

    }

    //Flyme V4的displayId格式为 [Flyme OS 4.x.x.xA]
    //Flyme V5的displayId格式为 [Flyme 5.x.x.x beta]
    private fun isFlymeV4OrAbove(): Boolean {
        val displayId = Build.DISPLAY
        if (!TextUtils.isEmpty(displayId) && displayId.contains("Flyme")) {
            val displayIdArray = displayId.split(" ".toRegex()).dropLastWhile({ it.isEmpty() }).toTypedArray()
            for (temp in displayIdArray) {
                //版本号4以上,形如4.x.
                if (temp.matches("^[4-9]\\.(\\d+\\.)+\\S*".toRegex())) {
                    return true
                }
            }
        }
        return false
    }

    //MIUI V6对应的versionCode是4
    //MIUI V7对应的versionCode是5
    private fun isMIUIV6OrAbove(): Boolean {
        val miuiVersionCodeStr = getSystemProperty("ro.miui.ui.version.code")
        if (!TextUtils.isEmpty(miuiVersionCodeStr)) {
            try {
                val miuiVersionCode = Integer.parseInt(miuiVersionCodeStr)
                if (miuiVersionCode >= 4) {
                    return true
                }
            } catch (e: Exception) {
            }

        }
        return false
    }

    //Android Api 23以上
    private fun isAndroidMOrAbove(): Boolean {
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
    }

    private fun getSystemProperty(propName: String): String? {
        val line: String
        var input: BufferedReader? = null
        try {
            val p = Runtime.getRuntime().exec("getprop " + propName)
            input = BufferedReader(InputStreamReader(p.inputStream), 1024)
            line = input!!.readLine()
            input!!.close()
        } catch (ex: IOException) {
            return null
        } finally {
            if (input != null) {
                try {
                    input!!.close()
                } catch (e: IOException) {
                }

            }
        }
        return line
    }

}

================================================
FILE: baselibs/src/main/java/com/cxz/kotlin/baselibs/utils/RxTimerUtil.kt
================================================
package com.cxz.kotlin.baselibs.utils

import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import java.util.concurrent.TimeUnit

/**
 * @author chenxz
 * @date 2019/1/10
 * @desc
 */
object RxTimerUtil {

    private var mDisposable: Disposable? = null

    /**
     * 延迟 milliseconds 毫秒后执行 task
     */
    fun postDelay(milliseconds: Long, task: ((Long) -> Unit)? = null) {
        mDisposable = Observable.timer(milliseconds, TimeUnit.MILLISECONDS)
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe { number ->
                task?.invoke(number)
                cancel()
            }
    }

    /**
     * 每隔 milliseconds 毫秒循环执行 task
     */
    fun interval(milliseconds: Long, task: ((Long) -> Unit)? = null) {
        mDisposable = Observable.interval(milliseconds, TimeUnit.MILLISECONDS)
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe { number ->
                task?.invoke(number)
            }
    }

    /**
     * 取消任务
     */
    fun cancel() {
        mDisposable?.let {
            if (!it.isDisposed) {
                it.dispose()
            }
        }
    }


}

================================================
FILE: baselibs/src/main/java/com/cxz/kotlin/baselibs/utils/StatusBarUtil.kt
================================================
package com.cxz.kotlin.baselibs.utils

import android.annotation.TargetApi
import android.app.Activity
import android.content.Context
import android.graphics.Color
import android.os.Build
import androidx.annotation.ColorInt
import androidx.annotation.IntRange
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.drawerlayout.widget.DrawerLayout
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import android.widget.LinearLayout
import com.cxz.kotlin.baselibs.R

/**
 * Created by chenxz on 2018/4/21.
 * 沉浸式状态栏工具类
 */
object StatusBarUtil {

    val DEFAULT_STATUS_BAR_ALPHA = 112
    private val FAKE_STATUS_BAR_VIEW_ID = R.id.statusbarutil_fake_status_bar_view
    private val FAKE_TRANSLUCENT_VIEW_ID = R.id.statusbarutil_translucent_view
    private val TAG_KEY_HAVE_SET_OFFSET = -123

    /**
     * 设置状态栏颜色
     *
     * @param activity 需要设置的 activity
     * @param color    状态栏颜色值
     */
    fun setColor(activity: Activity, @ColorInt color: Int) {
        setColor(activity, color, DEFAULT_STATUS_BAR_ALPHA)
    }

    /**
     * 设置状态栏颜色
     *
     * @param activity       需要设置的activity
     * @param color          状态栏颜色值
     * @param statusBarAlpha 状态栏透明度
     */

    fun setColor(activity: Activity, @ColorInt color: Int, @IntRange(from = 0, to = 255) statusBarAlpha: Int) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            activity.window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
            activity.window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
            activity.window.statusBarColor = calculateStatusColor(color, statusBarAlpha)
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            activity.window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
            val decorView = activity.window.decorView as ViewGroup
            val fakeStatusBarView = decorView.findViewById<View>(FAKE_STATUS_BAR_VIEW_ID)
            if (fakeStatusBarView != null) {
                if (fakeStatusBarView!!.visibility === View.GONE) {
                    fakeStatusBarView!!.visibility = View.VISIBLE
                }
                fakeStatusBarView!!.setBackgroundColor(calculateStatusColor(color, statusBarAlpha))
            } else {
                decorView.addView(createStatusBarView(activity, color, statusBarAlpha))
            }
            setRootView(activity)
        }
    }

    /**
     * 为滑动返回界面设置状态栏颜色
     *
     * @param activity 需要设置的activity
     * @param color    状态栏颜色值
     */
    fun setColorForSwipeBack(activity: Activity, color: Int) {
        setColorForSwipeBack(activity, color, DEFAULT_STATUS_BAR_ALPHA)
    }

    /**
     * 为滑动返回界面设置状态栏颜色
     *
     * @param activity       需要设置的activity
     * @param color          状态栏颜色值
     * @param statusBarAlpha 状态栏透明度 @IntRange(from = 0, to = 255)
     */
    fun setColorForSwipeBack(activity: Activity, @ColorInt color: Int, statusBarAlpha: Int) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {

            val contentView = activity.findViewById<View>(android.R.id.content) as ViewGroup
            val rootView = contentView.getChildAt(0)
            val statusBarHeight = getStatusBarHeight(activity)
            if (rootView != null && rootView is androidx.coordinatorlayout.widget.CoordinatorLayout) {
                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
                    rootView.fitsSystemWindows = false
                    contentView.setBackgroundColor(calculateStatusColor(color, statusBarAlpha))
                    val isNeedRequestLayout = contentView.paddingTop < statusBarHeight
                    if (isNeedRequestLayout) {
                        contentView.setPadding(0, statusBarHeight, 0, 0)
                        rootView.post { rootView.requestLayout() }
                    }
                } else {
                    rootView.setStatusBarBackgroundColor(calculateStatusColor(color, statusBarAlpha))
                }
            } else {
                contentView.setPadding(0, statusBarHeight, 0, 0)
                contentView.setBackgroundColor(calculateStatusColor(color, statusBarAlpha))
            }
            setTransparentForWindow(activity)
        }
    }

    /**
     * 设置状态栏纯色 不加半透明效果
     *
     * @param activity 需要设置的 activity
     * @param color    状态栏颜色值
     */
    fun setColorNoTranslucent(activity: Activity, @ColorInt color: Int) {
        setColor(activity, color, 0)
    }

    /**
     * 设置状态栏颜色(5.0以下无半透明效果,不建议使用)
     *
     * @param activity 需要设置的 activity
     * @param color    状态栏颜色值
     */
    @Deprecated("")
    fun setColorDiff(activity: Activity, @ColorInt color: Int) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
            return
        }
        transparentStatusBar(activity)
        val contentView = activity.findViewById<View>(android.R.id.content) as ViewGroup
        // 移除半透明矩形,以免叠加
        val fakeStatusBarView = contentView.findViewById<View>(FAKE_STATUS_BAR_VIEW_ID)
        if (fakeStatusBarView != null) {
            if (fakeStatusBarView!!.visibility === View.GONE) {
                fakeStatusBarView!!.visibility = View.VISIBLE
            }
            fakeStatusBarView!!.setBackgroundColor(color)
        } else {
            contentView.addView(createStatusBarView(activity, color))
        }
        setRootView(activity)
    }

    /**
     * 使状态栏半透明
     *
     *
     * 适用于图片作为背景的界面,此时需要图片填充到状态栏
     *
     * @param activity 需要设置的activity
     */
    fun setTranslucent(activity: Activity) {
        setTranslucent(activity, DEFAULT_STATUS_BAR_ALPHA)
    }

    /**
     * 使状态栏半透明
     *
     *
     * 适用于图片作为背景的界面,此时需要图片填充到状态栏
     *
     * @param activity       需要设置的activity
     * @param statusBarAlpha 状态栏透明度 @IntRange(from = 0, to = 255)
     */
    fun setTranslucent(activity: Activity, statusBarAlpha: Int) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
            return
        }
        setTransparent(activity)
        addTranslucentView(activity, statusBarAlpha)
    }

    /**
     * 针对根布局是 CoordinatorLayout, 使状态栏半透明
     *
     *
     * 适用于图片作为背景的界面,此时需要图片填充到状态栏
     *
     * @param activity       需要设置的activity
     * @param statusBarAlpha 状态栏透明度 @IntRange(from = 0, to = 255)
     */
    fun setTranslucentForCoordinatorLayout(activity: Activity, statusBarAlpha: Int) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
            return
        }
        transparentStatusBar(activity)
        addTranslucentView(activity, statusBarAlpha)
    }

    /**
     * 设置状态栏全透明
     *
     * @param activity 需要设置的activity
     */
    fun setTransparent(activity: Activity) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
            return
        }
        transparentStatusBar(activity)
        setRootView(activity)
    }

    /**
     * 使状态栏透明(5.0以上半透明效果,不建议使用)
     *
     *
     * 适用于图片作为背景的界面,此时需要图片填充到状态栏
     *
     * @param activity 需要设置的activity
     */
    @Deprecated("")
    fun setTranslucentDiff(activity: Activity) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            // 设置状态栏透明
            activity.window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
            setRootView(activity)
        }
    }

    /**
     * 为DrawerLayout 布局设置状态栏变色
     *
     * @param activity     需要设置的activity
     * @param drawerLayout DrawerLayout
     * @param color        状态栏颜色值
     */
    fun setColorForDrawerLayout(activity: Activity, drawerLayout: androidx.drawerlayout.widget.DrawerLayout, @ColorInt color: Int) {
        setColorForDrawerLayout(activity, drawerLayout, color, DEFAULT_STATUS_BAR_ALPHA)
    }

    /**
     * 为DrawerLayout 布局设置状态栏颜色,纯色
     *
     * @param activity     需要设置的activity
     * @param drawerLayout DrawerLayout
     * @param color        状态栏颜色值
     */
    fun setColorNoTranslucentForDrawerLayout(activity: Activity, drawerLayout: androidx.drawerlayout.widget.DrawerLayout, @ColorInt color: Int) {
        setColorForDrawerLayout(activity, drawerLayout, color, 0)
    }

    /**
     * 为DrawerLayout 布局设置状态栏变色
     *
     * @param activity       需要设置的activity
     * @param drawerLayout   DrawerLayout
     * @param color          状态栏颜色值
     * @param statusBarAlpha 状态栏透明度 @IntRange(from = 0, to = 255)
     */
    fun setColorForDrawerLayout(activity: Activity, drawerLayout: androidx.drawerlayout.widget.DrawerLayout, @ColorInt color: Int, statusBarAlpha: Int) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
            return
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            activity.window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
            activity.window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
            activity.window.statusBarColor = Color.TRANSPARENT
        } else {
            activity.window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
        }
        // 生成一个状态栏大小的矩形
        // 添加 statusBarView 到布局中
        val contentLayout = drawerLayout.getChildAt(0) as ViewGroup
        val fakeStatusBarView = contentLayout.findViewById<View>(FAKE_STATUS_BAR_VIEW_ID)
        if (fakeStatusBarView != null) {
            if (fakeStatusBarView!!.visibility === View.GONE) {
                fakeStatusBarView!!.visibility = View.VISIBLE
            }
            fakeStatusBarView!!.setBackgroundColor(color)
        } else {
            contentLayout.addView(createStatusBarView(activity, color), 0)
        }
        // 内容布局不是 LinearLayout 时,设置padding top
        if (contentLayout !is LinearLayout && contentLayout.getChildAt(1) != null) {
            contentLayout.getChildAt(1)
                .setPadding(contentLayout.paddingLeft, getStatusBarHeight(activity) + contentLayout.paddingTop,
                    contentLayout.paddingRight, contentLayout.paddingBottom)
        }
        // 设置属性
        setDrawerLayoutProperty(drawerLayout, contentLayout)
        addTranslucentView(activity, statusBarAlpha)
    }

    /**
     * 设置 DrawerLayout 属性
     *
     * @param drawerLayout              DrawerLayout
     * @param drawerLayoutContentLayout DrawerLayout 的内容布局
     */
    private fun setDrawerLayoutProperty(drawerLayout: androidx.drawerlayout.widget.DrawerLayout, drawerLayoutContentLayout: ViewGroup) {
        val drawer = drawerLayout.getChildAt(1) as ViewGroup
        drawerLayout.fitsSystemWindows = false
        drawerLayoutContentLayout.fitsSystemWindows = false
        drawerLayoutContentLayout.clipToPadding = true
        drawer.fitsSystemWindows = false
    }

    /**
     * 为DrawerLayout 布局设置状态栏变色(5.0以下无半透明效果,不建议使用)
     *
     * @param activity     需要设置的activity
     * @param drawerLayout DrawerLayout
     * @param color        状态栏颜色值
     */
    @Deprecated("")
    fun setColorForDrawerLayoutDiff(activity: Activity, drawerLayout: androidx.drawerlayout.widget.DrawerLayout, @ColorInt color: Int) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            activity.window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
            // 生成一个状态栏大小的矩形
            val contentLayout = drawerLayout.getChildAt(0) as ViewGroup
            val fakeStatusBarView = contentLayout.findViewById<View>(FAKE_STATUS_BAR_VIEW_ID)
            if (fakeStatusBarView != null) {
                if (fakeStatusBarView.visibility === View.GONE) {
                    fakeStatusBarView.visibility = View.VISIBLE
                }
                fakeStatusBarView!!.setBackgroundColor(calculateStatusColor(color, DEFAULT_STATUS_BAR_ALPHA))
            } else {
                // 添加 statusBarView 到布局中
                contentLayout.addView(createStatusBarView(activity, color), 0)
            }
            // 内容布局不是 LinearLayout 时,设置padding top
            if (contentLayout !is LinearLayout && contentLayout.getChildAt(1) != null) {
                contentLayout.getChildAt(1).setPadding(0, getStatusBarHeight(activity), 0, 0)
            }
            // 设置属性
            setDrawerLayoutProperty(drawerLayout, contentLayout)
        }
    }

    /**
     * 为 DrawerLayout 布局设置状态栏透明
     *
     * @param activity     需要设置的activity
     * @param drawerLayout DrawerLayout
     */
    fun setTranslucentForDrawerLayout(activity: Activity, drawerLayout: androidx.drawerlayout.widget.DrawerLayout) {
        setTranslucentForDrawerLayout(activity, drawerLayout, DEFAULT_STATUS_BAR_ALPHA)
    }

    /**
     * 为 DrawerLayout 布局设置状态栏透明
     *
     * @param activity     需要设置的activity
     * @param drawerLayout DrawerLayout
     */
    fun setTranslucentForDrawerLayout(activity: Activity, drawerLayout: androidx.drawerlayout.widget.DrawerLayout, statusBarAlpha: Int) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
            return
        }
        setTransparentForDrawerLayout(activity, drawerLayout)
        addTranslucentView(activity, statusBarAlpha)
    }

    /**
     * 为 DrawerLayout 布局设置状态栏透明
     *
     * @param activity     需要设置的activity
     * @param drawerLayout DrawerLayout
     */
    fun setTransparentForDrawerLayout(activity: Activity, drawerLayout: androidx.drawerlayout.widget.DrawerLayout) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
            return
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            activity.window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
            activity.window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
            activity.window.statusBarColor = Color.TRANSPARENT
        } else {
            activity.window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
        }

        val contentLayout = drawerLayout.getChildAt(0) as ViewGroup
        // 内容布局不是 LinearLayout 时,设置padding top
        if (contentLayout !is LinearLayout && contentLayout.getChildAt(1) != null) {
            contentLayout.getChildAt(1).setPadding(0, getStatusBarHeight(activity), 0, 0)
        }

        // 设置属性
        setDrawerLayoutProperty(drawerLayout, contentLayout)
    }

    /**
     * 为 DrawerLayout 布局设置状态栏透明(5.0以上半透明效果,不建议使用)
     *
     * @param activity     需要设置的activity
     * @param drawerLayout DrawerLayout
     */
    @Deprecated("")
    fun setTranslucentForDrawerLayoutDiff(activity: Activity, drawerLayout: androidx.drawerlayout.widget.DrawerLayout) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            // 设置状态栏透明
            activity.window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
            // 设置内容布局属性
            val contentLayout = drawerLayout.getChildAt(0) as ViewGroup
            contentLayout.fitsSystemWindows = true
            contentLayout.clipToPadding = true
            // 设置抽屉布局属性
            val vg = drawerLayout.getChildAt(1) as ViewGroup
            vg.fitsSystemWindows = false
            // 设置 DrawerLayout 属性
            drawerLayout.fitsSystemWindows = false
        }
    }

    /**
     * 为头部是 ImageView 的界面设置状态栏全透明
     *
     * @param activity       需要设置的activity
     * @param needOffsetView 需要向下偏移的 View
     */
    fun setTransparentForImageView(activity: Activity, needOffsetView: View) {
        setTranslucentForImageView(activity, 0, needOffsetView)
    }

    /**
     * 为头部是 ImageView 的界面设置状态栏透明(使用默认透明度)
     *
     * @param activity       需要设置的activity
     * @param needOffsetView 需要向下偏移的 View
     */
    fun setTranslucentForImageView(activity: Activity, needOffsetView: View) {
        setTranslucentForImageView(activity, DEFAULT_STATUS_BAR_ALPHA, needOffsetView)
    }

    /**
     * 为头部是 ImageView 的界面设置状态栏透明
     *
     * @param activity       需要设置的activity
     * @param statusBarAlpha 状态栏透明度
     * @param needOffsetView 需要向下偏移的 View
     */
    fun setTranslucentForImageView(
        activity: Activity, statusBarAlpha: Int,
        needOffsetView: View?
    ) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
            return
        }
        setTransparentForWindow(activity)
        addTranslucentView(activity, statusBarAlpha)
        if (needOffsetView != null) {
            val haveSetOffset = needOffsetView!!.getTag(TAG_KEY_HAVE_SET_OFFSET)
            if (haveSetOffset != null && haveSetOffset as Boolean) {
                return
            }
            val layoutParams = needOffsetView!!.layoutParams as ViewGroup.MarginLayoutParams
            layoutParams.setMargins(
                layoutParams.leftMargin, layoutParams.topMargin + getStatusBarHeight(activity),
                layoutParams.rightMargin, layoutParams.bottomMargin
            )
            needOffsetView!!.setTag(TAG_KEY_HAVE_SET_OFFSET, true)
        }
    }

    /**
     * 为 fragment 头部是 ImageView 的设置状态栏透明
     *
     * @param activity       fragment 对应的 activity
     * @param needOffsetView 需要向下偏移的 View
     */
    fun setTranslucentForImageViewInFragment(activity: Activity, needOffsetView: View) {
        setTranslucentForImageViewInFragment(activity, DEFAULT_STATUS_BAR_ALPHA, needOffsetView)
    }

    /**
     * 为 fragment 头部是 ImageView 的设置状态栏透明
     *
     * @param activity       fragment 对应的 activity
     * @param needOffsetView 需要向下偏移的 View
     */
    fun setTransparentForImageViewInFragment(activity: Activity, needOffsetView: View) {
        setTranslucentForImageViewInFragment(activity, 0, needOffsetView)
    }

    /**
     * 为 fragment 头部是 ImageView 的设置状态栏透明
     *
     * @param activity       fragment 对应的 activity
     * @param statusBarAlpha 状态栏透明度
     * @param needOffsetView 需要向下偏移的 View
     */
    fun setTranslucentForImageViewInFragment(
        activity: Activity, statusBarAlpha: Int,
        needOffsetView: View
    ) {
        setTranslucentForImageView(activity, statusBarAlpha, needOffsetView)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
            clearPreviousSetting(activity)
        }
    }

    /**
     * 隐藏伪状态栏 View
     *
     * @param activity 调用的 Activity
     */
    fun hideFakeStatusBarView(activity: Activity) {
        val decorView = activity.window.decorView as ViewGroup
        val fakeStatusBarView = decorView.findViewById<View>(FAKE_STATUS_BAR_VIEW_ID)
        if (fakeStatusBarView != null) {
            fakeStatusBarView!!.visibility = View.GONE
        }
        val fakeTranslucentView = decorView.findViewById<View>(FAKE_TRANSLUCENT_VIEW_ID)
        if (fakeTranslucentView != null) {
            fakeTranslucentView!!.visibility = View.GONE
        }
    }

    @TargetApi(Build.VERSION_CODES.M)
    fun setLightMode(activity: Activity) {
        setMIUIStatusBarDarkIcon(activity, true)
        setMeizuStatusBarDarkIcon(activity, true)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            activity.window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR //or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
        }
    }

    @TargetApi(Build.VERSION_CODES.M)
    fun setDarkMode(activity: Activity) {
        setMIUIStatusBarDarkIcon(activity, false)
        setMeizuStatusBarDarkIcon(activity, false)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            //activity.window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
        }
    }

    /**
     * 修改 MIUI V6  以上状态栏颜色
     */
    private fun setMIUIStatusBarDarkIcon(activity: Activity, darkIcon: Boolean) {
        val clazz = activity.window.javaClass
        try {
            val layoutParams = Class.forName("android.view.MiuiWindowManager\$LayoutParams")
            val field = layoutParams.getField("EXTRA_FLAG_STATUS_BAR_DARK_MODE")
            val darkModeFlag = field.getInt(layoutParams)
            val extraFlagField =
                clazz.getMethod("setExtraFlags", Int::class.javaPrimitiveType, Int::class.javaPrimitiveType)
            extraFlagField.invoke(activity.window, if (darkIcon) darkModeFlag else 0, darkModeFlag)
        } catch (e: Exception) {
            e.printStackTrace()
        }

    }

    /**
     * 修改魅族状态栏字体颜色 Flyme 4.0
     */
    private fun setMeizuStatusBarDarkIcon(activity: Activity, darkIcon: Boolean) {
        try {
            val lp = activity.window.attributes
            val darkFlag = WindowManager.LayoutParams::class.java.getDeclaredField("MEIZU_FLAG_DARK_STATUS_BAR_ICON")
            val meizuFlags = WindowManager.LayoutParams::class.java.getDeclaredField("meizuFlags")
            darkFlag.isAccessible = true
            meizuFlags.isAccessible = true
            val bit = darkFlag.getInt(null)
            var value = meizuFlags.getInt(lp)
            if (darkIcon) {
                value = value or bit
            } else {
                value = value and bit.inv()
            }
            meizuFlags.setInt(lp, value)
            activity.window.attributes = lp
        } catch (e: Exception) {
            //e.printStackTrace();
        }

    }

    ///////////////////////////////////////////////////////////////////////////////////

    @TargetApi(Build.VERSION_CODES.KITKAT)
    private fun clearPreviousSetting(activity: Activity) {
        val decorView = activity.window.decorView as ViewGroup
        val fakeStatusBarView = decorView.findViewById<View>(FAKE_STATUS_BAR_VIEW_ID)
        if (fakeStatusBarView != null) {
            decorView.removeView(fakeStatusBarView)
            val rootView = (activity.findViewById<View>(android.R.id.content) as ViewGroup).getChildAt(0) as ViewGroup
            rootView.setPadding(0, 0, 0, 0)
        }
    }

    /**
     * 添加半透明矩形条
     *
     * @param activity       需要设置的 activity
     * @param statusBarAlpha 透明值
     */
    private fun addTranslucentView(activity: Activity, statusBarAlpha: Int) {
        val contentView = activity.findViewById<View>(android.R.id.content) as ViewGroup
        val fakeTranslucentView = contentView.findViewById<View>(FAKE_TRANSLUCENT_VIEW_ID)
        if (fakeTranslucentView != null) {
            if (fakeTranslucentView!!.visibility === View.GONE) {
                fakeTranslucentView!!.visibility = View.VISIBLE
            }
            fakeTranslucentView!!.setBackgroundColor(Color.argb(statusBarAlpha, 0, 0, 0))
        } else {
            contentView.addView(createTranslucentStatusBarView(activity, statusBarAlpha))
        }
    }

    /**
     * 生成一个和状态栏大小相同的彩色矩形条
     *
     * @param activity 需要设置的 activity
     * @param color    状态栏颜色值
     * @return 状态栏矩形条
     */
    private fun createStatusBarView(activity: Activity, @ColorInt color: Int): View {
        return createStatusBarView(activity, color, 0)
    }

    /**
     * 生成一个和状态栏大小相同的半透明矩形条
     *
     * @param activity 需要设置的activity
     * @param color    状态栏颜色值
     * @param alpha    透明值
     * @return 状态栏矩形条
     */
    private fun createStatusBarView(activity: Activity, @ColorInt color: Int, @IntRange(from = 0, to = 255) alpha: Int): View {
        // 绘制一个和状态栏一样高的矩形
        val statusBarView = View(activity)
        val params = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getStatusBarHeight(activity))
        statusBarView.layoutParams = params
        statusBarView.setBackgroundColor(calculateStatusColor(color, alpha))
        statusBarView.id = FAKE_STATUS_BAR_VIEW_ID
        return statusBarView
    }

    /**
     * 设置根布局参数
     */
    private fun setRootView(activity: Activity) {
        val parent = activity.findViewById<View>(android.R.id.content) as ViewGroup
        var i = 0
        val count = parent.childCount
        while (i < count) {
            val childView = parent.getChildAt(i)
            if (childView is ViewGroup) {
                childView.setFitsSystemWindows(true)
                childView.clipToPadding = true
            }
            i++
        }
    }

    /**
     * 设置透明
     */
    private fun setTransparentForWindow(activity: Activity) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            activity.window.statusBarColor = Color.TRANSPARENT
            activity.window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
                    View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            activity.window.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS,
                WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
        }
    }

    /**
     * 使状态栏透明
     */
    @TargetApi(Build.VERSION_CODES.KITKAT)
    private fun transparentStatusBar(activity: Activity) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            activity.window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
            activity.window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
            activity.window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION)
            activity.window.statusBarColor = Color.TRANSPARENT
        } else {
            activity.window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
        }
    }

    /**
     * 创建半透明矩形 View
     *
     * @param alpha 透明值
     * @return 半透明 View
     */
    private fun createTranslucentStatusBarView(activity: Activity, @IntRange(from = 0, to = 255) alpha: Int): View {
        // 绘制一个和状态栏一样高的矩形
        val statusBarView = View(activity)
        val params = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getStatusBarHeight(activity))
        statusBarView.layoutParams = params
        statusBarView.setBackgroundColor(Color.argb(alpha, 0, 0, 0))
        statusBarView.id = FAKE_TRANSLUCENT_VIEW_ID
        return statusBarView
    }

    /**
     * 获取状态栏高度
     *
     * @param context context
     * @return 状态栏高度
     */
    private fun getStatusBarHeight(context: Context): Int {
        // 获得状态栏高度
        val resourceId = context.resources.getIdentifier("status_bar_height", "dimen", "android")
        return context.resources.getDimensionPixelSize(resourceId)
    }

    /**
     * 计算状态栏颜色
     *
     * @param color color值
     * @param alpha alpha值
     * @return 最终的状态栏颜色
     */
    private fun calculateStatusColor(@ColorInt color: Int, @IntRange(from = 0, to = 255) alpha: Int): Int {
        if (alpha == 0) {
            return color
        }
        val a = 1 - alpha / 255f
        var red = color shr 16 and 0xff
        var green = color shr 8 and 0xff
        var blue = color and 0xff
        red = (red * a + 0.5).toInt()
        green = (green * a + 0.5).toInt()
        blue = (blue * a + 0.5).toInt()
        return 0xff shl 24 or (red shl 16) or (green shl 8) or blue
    }

}

================================================
FILE: baselibs/src/main/java/com/cxz/kotlin/baselibs/widget/CustomToast.kt
================================================
package com.cxz.kotlin.baselibs.widget

import android.content.Context
import android.view.Gravity
import android.view.View
import android.widget.TextView
import android.widget.Toast
import com.cxz.kotlin.baselibs.R

/**
 * Created by chenxz on 2018/6/9.
 * 自定义Toast
 */
class CustomToast {

    private var toast: Toast
    private var textView: TextView

    constructor(context: Context?, message: String) : this(context, message, Toast.LENGTH_SHORT)

    constructor(context: Context?, message: String, duration: Int) {
        toast = Toast(context)
        toast.duration = duration
        val view = View.inflate(context, R.layout.toast_custom, null)
        textView = view.findViewById(R.id.tv_prompt)
        textView.text = message
        toast.view = view
        toast.setGravity(Gravity.CENTER, 0, 0)
    }

    fun show() {
        toast.show()
    }

}

================================================
FILE: baselibs/src/main/java/com/cxz/kotlin/baselibs/widget/LoadingDialog.kt
================================================
package com.cxz.kotlin.baselibs.widget

import android.app.Dialog
import android.content.Context
import android.view.Gravity
import android.view.View
import com.cxz.kotlin.baselibs.R
import com.github.ybq.android.spinkit.style.Wave
import kotlinx.android.synthetic.main.layout_loading_dialog.*

/**
 * @author chenxz
 * @date 2018/11/27
 * @desc
 */

class LoadingDialog : Dialog {

    constructor(context: Context) : super(context)

    constructor(context: Context, theme: Int) : super(context, theme)

    override fun onWindowFocusChanged(hasFocus: Boolean) {
        loadingBar.setIndeterminateDrawable(Wave())
        super.onWindowFocusChanged(hasFocus)
    }

    companion object {

        fun showDialog(context: Context, cancelable: Boolean): LoadingDialog? {
            return showDialog(context, null, cancelable)
        }

        fun showDialog(context: Context, message: CharSequence?, cancelable: Boolean): LoadingDialog? {
            val dialog = LoadingDialog(context, R.style.LoadingDialog)
            dialog.setContentView(R.layout.layout_loading_dialog)
            if (message.isNullOrBlank()) {
                dialog.tv_load_msg?.visibility = View.GONE
            } else {
                dialog.tv_load_msg?.visibility = View.VISIBLE
                dialog.tv_load_msg?.text = message
            }
            dialog.setCanceledOnTouchOutside(false)
            dialog.setCancelable(cancelable)
            dialog.window?.attributes?.gravity = Gravity.CENTER
            val lp = dialog.window?.attributes
            lp?.dimAmount = 0.2f
            dialog.window?.attributes = lp
            dialog.show()
            return dialog
        }
    }

}

================================================
FILE: baselibs/src/main/java/com/cxz/kotlin/baselibs/widget/OnNoDoubleClickListener.kt
================================================
package com.cxz.kotlin.baselibs.widget

import android.view.View

/**
 * @author admin
 * @date 2018/11/23
 * @desc 防止连续点击
 */
abstract class OnNoDoubleClickListener : View.OnClickListener {

    private var mThrottleFirstTime: Long = 1000
    private var mLastClickTime: Long = 0

    constructor()
    constructor(throttleFirstTime: Long) {
        mThrottleFirstTime = throttleFirstTime
    }

    override fun onClick(v: View?) {
        val currentTime = System.currentTimeMillis()
        if (currentTime - mLastClickTime > mThrottleFirstTime) {
            mLastClickTime = currentTime
            onNoDoubleClick(v)
        }
    }

    abstract fun onNoDoubleClick(v: View?)

}

================================================
FILE: baselibs/src/main/res/drawable/bg_loading_dialog.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">

    <solid android:color="@color/transparent_75" />
    <corners android:radius="6dp" />

</shape>

================================================
FILE: baselibs/src/main/res/drawable/bg_toast_custom.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">

    <solid android:color="@color/transparent_75" />
    <corners android:radius="6dp" />

</shape>

================================================
FILE: baselibs/src/main/res/layout/activity_base_title.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <include layout="@layout/base_toolbar" />

    <FrameLayout
        android:id="@+id/base_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>

================================================
FILE: baselibs/src/main/res/layout/base_toolbar.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.appbar.AppBarLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/app_bar_layout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:fitsSystemWindows="true"
    android:theme="@style/AppTheme.AppBarOverlay"
    app:elevation="0dp">

    <androidx.appcompat.widget.Toolbar
        android:id="@+id/base_toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        app:layout_scrollFlags="scroll|enterAlways"
        app:popupTheme="@style/AppTheme.PopupOverlay">

        <!--中间部分,除标题外可能会含有其他view-->
        <FrameLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:layout_gravity="center">

            <TextView
                android:id="@+id/base_title_tv"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:layout_gravity="center"
                android:gravity="center"
                android:textColor="@android:color/black"
                android:textSize="18sp" />

        </FrameLayout>

    </androidx.appcompat.widget.Toolbar>

</com.google.android.material.appbar.AppBarLayout>

================================================
FILE: baselibs/src/main/res/layout/layout_loading_dialog.xml
================================================
<?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="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:background="@drawable/bg_loading_dialog"
    android:gravity="center_horizontal"
    android:orientation="vertical"
    android:paddingLeft="30dp"
    android:paddingTop="20dp"
    android:paddingRight="30dp"
    android:paddingBottom="20dp">

    <com.github.ybq.android.spinkit.SpinKitView
        android:id="@+id/loadingBar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:SpinKit_Color="@color/white" />

    <TextView
        android:id="@+id/tv_load_msg"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:textColor="@color/white"
        android:textSize="@dimen/sp_14" />

</LinearLayout>

================================================
FILE: baselibs/src/main/res/layout/toast_custom.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical">

    <TextView
        android:id="@+id/tv_prompt"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@drawable/bg_toast_custom"
        android:paddingBottom="@dimen/dp_24"
        android:paddingLeft="@dimen/dp_20"
        android:paddingRight="@dimen/dp_20"
        android:paddingTop="@dimen/dp_24"
        android:textColor="@color/white"
        android:textSize="@dimen/sp_14" />

</LinearLayout>

================================================
FILE: baselibs/src/main/res/values/colors.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorPrimary">#008577</color>
    <color name="colorPrimaryDark">#00574B</color>
    <color name="colorAccent">#D81B60</color>

    <color name="white">#ffffff</color>
    <color name="transparent_75">#ba000000</color>

</resources>

================================================
FILE: baselibs/src/main/res/values/dimen.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>

    <dimen name="dp_20">20dp</dimen>
    <dimen name="dp_24">24dp</dimen>

    <dimen name="sp_14">14sp</dimen>

</resources>

================================================
FILE: baselibs/src/main/res/values/ids.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<resources>
    <item name="statusbarutil_fake_status_bar_view" type="id" />
    <item name="statusbarutil_translucent_view" type="id" />
</resources>


================================================
FILE: baselibs/src/main/res/values/strings.xml
================================================
<resources>
    <string name="app_name">baselibs</string>
</resources>


================================================
FILE: baselibs/src/main/res/values/styles.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>

    <style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />

    <style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />

    <style name="LoadingDialog" parent="@android:style/Theme.Dialog">
        <item name="android:windowFrame">@null</item>
        <item name="android:windowIsFloating">true</item>
        <item name="android:windowContentOverlay">@null</item>
        <item name="android:windowAnimationStyle">@android:style/Animation.Dialog</item>
        <item name="android:windowBackground">@android:color/transparent</item>
        <item name="android:windowNoTitle">true</item>
    </style>

</resources>

================================================
FILE: baselibs/src/main/res/xml/file_paths.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<paths>

    <root-path
        name="root"
        path="" />

    <files-path
        name="files"
        path="." />

    <cache-path
        name="cache"
        path="." />

    <external-path
        name="external"
        path="." />

    <external-files-path
        name="external_file_path"
        path="." />

    <external-cache-path
        name="external_cache_path"
        path="." />

</paths>

================================================
FILE: baselibs/src/main/res/xml/network_security_config.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config cleartextTrafficPermitted="true" />
</network-security-config>

================================================
FILE: build.gradle
================================================
// Top-level build file where you can add configuration options common to all sub-projects/modules.
apply from :"config.gradle"

buildscript {
    ext.kotlin_version = '1.3.50'
    repositories {
        google()
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.5.2'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        google()
        jcenter()
        maven { url 'https://jitpack.io' }
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}


================================================
FILE: config.gradle
================================================
ext {
    android = [
            compileSdkVersion: 30,
            buildToolsVersion: "30.0.3",
            minSdkVersion    : 16,
            targetSdkVersion : 30,
            versionCode      : 1,
            versionName      : "1.0.0"
    ]

    dependVersion = [
            supportSdkVersion: "28.0.0",
            retrofit         : "2.4.0",
            glide            : "4.12.0",
            arouter          : "1.5.1",
            rxJava           : "2.2.2",
            rxAndroid        : "2.1.0",
            rxKotlin         : "2.3.0",
            anko             : "0.10.7"
    ]

    androidxDeps = [
            "appcompat"       : 'androidx.appcompat:appcompat:1.2.0',
            "material"        : 'com.google.android.material:material:1.2.1',
            "constraintlayout": 'androidx.constraintlayout:constraintlayout:2.0.4',
            "androidx-core"   : 'androidx.core:core-ktx:1.3.2',
            "recyclerview"    : "androidx.recyclerview:recyclerview:1.2.0",
    ]

//    supportDeps = [
//            "supportv4"        : "com.android.support:support-v4:${dependVersion.supportSdkVersion}",
//            "appcompatv7"      : "com.android.support:appcompat-v7:${dependVersion.supportSdkVersion}",
//            "cardview"         : "com.android.support:cardview-v7:${dependVersion.supportSdkVersion}",
//            "design"           : "com.android.support:design:${dependVersion.supportSdkVersion}",
//            "constraint-layout": "com.android.support.constraint:constraint-layout:1.1.3",
//            "annotations"      : "com.android.support:support-annotations:${dependVersion.supportSdkVersion}"
//    ]

    retrofit = [
            "retrofit"                : "com.squareup.retrofit2:retrofit:${dependVersion.retrofit}",
            "retrofitConverterGson"   : "com.squareup.retrofit2:converter-gson:${dependVersion.retrofit}",
            "retrofitAdapterRxjava2"  : "com.squareup.retrofit2:adapter-rxjava2:${dependVersion.retrofit}",
            "okhttp3LoggerInterceptor": 'com.squareup.okhttp3:logging-interceptor:3.11.0',
            "retrofitConverterMoshi"  : 'com.squareup.retrofit2:converter-moshi:2.4.0',
            "retrofitKotlinMoshi"     : "com.squareup.moshi:moshi-kotlin:1.7.0"
    ]

    rxJava = [
            "rxJava"   : "io.reactivex.rxjava2:rxjava:${dependVersion.rxJava}",
            "rxAndroid": "io.reactivex.rxjava2:rxandroid:${dependVersion.rxAndroid}",
            "rxKotlin" : "io.reactivex.rxjava2:rxkotlin:${dependVersion.rxKotlin}",
            "anko"     : "org.jetbrains.anko:anko:${dependVersion.anko}"
    ]

    testDeps = [
            "junit"                    : 'junit:junit:4.12',
            "runner"                   : 'androidx.test.ext:junit:1.1.1',
            "espresso-core"            : 'androidx.test.espresso:espresso-core:3.3.0',
            "leakcanary-debug"         : 'com.squareup.leakcanary:leakcanary-android:1.6.1',
            "leakcanary-release"       : 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.1',
            "leakcanary-debug-fragment": 'com.squareup.leakcanary:leakcanary-support-fragment:1.6.1',
            "debug-db"                 : 'com.amitshekhar.android:debug-db:1.0.4'
    ]

    commonDeps = [
            "multidex"     : 'com.android.support:multidex:2.0.1',
            "logger"       : 'com.orhanobut:logger:2.2.0',
            "glide"        : "com.github.bumptech.glide:glide:${dependVersion.glide}",
            "eventbus"     : 'org.greenrobot:eventbus:3.1.1',
            "spinkit"      : 'com.github.ybq:Android-SpinKit:1.2.0',
            "arouter"      : "com.alibaba:arouter-api:${dependVersion.arouter}",
            "rxpermissions": 'com.github.tbruyelle:rxpermissions:0.10.2'
    ]

    annotationProcessorDeps = [
            "arouter-compiler": "com.alibaba:arouter-compiler:${dependVersion.arouter}",
            "glide-compiler"  : "com.github.bumptech.glide:compiler:${dependVersion.glide}"
    ]

    // supportLibs = supportDeps.values()
    androisxLibs = androidxDeps.values()
    networkLibs = retrofit.values()
    rxJavaLibs = rxJava.values()
    commonLibs = commonDeps.values()
    annotationProcessorLibs = annotationProcessorDeps.values()

}

================================================
FILE: gradle/wrapper/gradle-wrapper.properties
================================================
#Sun Oct 27 19:16:55 CST 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip


================================================
FILE: gradle.properties
================================================
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official

# Disables R8 for Android Library modules only.
android.enableR8.libraries = false
# Disables R8 for all modules.
android.enableR8 = false

# trueǡɿģʽ  falseģʽл
isModule=false
isMeModule=false
isNewsModule=false
isVideoModule=false
android.useAndroidX=true
android.enableJetifier=true


================================================
FILE: gradlew
================================================
#!/usr/bin/env sh

##############################################################################
##
##  Gradle start up script for UN*X
##
##############################################################################

# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
    ls=`ls -ld "$PRG"`
    link=`expr "$ls" : '.*-> \(.*\)$'`
    if expr "$link" : '/.*' > /dev/null; then
        PRG="$link"
    else
        PRG=`dirname "$PRG"`"/$link"
    fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null

APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`

# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""

# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"

warn () {
    echo "$*"
}

die () {
    echo
    echo "$*"
    echo
    exit 1
}

# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
  CYGWIN* )
    cygwin=true
    ;;
  Darwin* )
    darwin=true
    ;;
  MINGW* )
    msys=true
    ;;
  NONSTOP* )
    nonstop=true
    ;;
esac

CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar

# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
        # IBM's JDK on AIX uses strange locations for the executables
        JAVACMD="$JAVA_HOME/jre/sh/java"
    else
        JAVACMD="$JAVA_HOME/bin/java"
    fi
    if [ ! -x "$JAVACMD" ] ; then
        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME

Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
    fi
else
    JAVACMD="java"
    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.

Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi

# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
    MAX_FD_LIMIT=`ulimit -H -n`
    if [ $? -eq 0 ] ; then
        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
            MAX_FD="$MAX_FD_LIMIT"
        fi
        ulimit -n $MAX_FD
        if [ $? -ne 0 ] ; then
            warn "Could not set maximum file descriptor limit: $MAX_FD"
        fi
    else
        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
    fi
fi

# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi

# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
    JAVACMD=`cygpath --unix "$JAVACMD"`

    # We build the pattern for arguments to be converted via cygpath
    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
    SEP=""
    for dir in $ROOTDIRSRAW ; do
        ROOTDIRS="$ROOTDIRS$SEP$dir"
        SEP="|"
    done
    OURCYGPATTERN="(^($ROOTDIRS))"
    # Add a user-defined pattern to the cygpath arguments
    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
    fi
    # Now convert the arguments - kludge to limit ourselves to /bin/sh
    i=0
    for arg in "$@" ; do
        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option

        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
        else
            eval `echo args$i`="\"$arg\""
        fi
        i=$((i+1))
    done
    case $i in
        (0) set -- ;;
        (1) set -- "$args0" ;;
        (2) set -- "$args0" "$args1" ;;
        (3) set -- "$args0" "$args1" "$args2" ;;
        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
    esac
fi

# Escape application args
save () {
    for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
    echo " "
}
APP_ARGS=$(save "$@")

# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"

# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
  cd "$(dirname "$0")"
fi

exec "$JAVACMD" "$@"


================================================
FILE: gradlew.bat
================================================
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem  Gradle startup script for Windows
@rem
@rem ##########################################################################

@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal

set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%

@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=

@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome

set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init

echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.

goto fail

:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe

if exist "%JAVA_EXE%" goto init

echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.

goto fail

:init
@rem Get command-line arguments, handling Windows variants

if not "%OS%" == "Windows_NT" goto win9xME_args

:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2

:win9xME_args_slurp
if "x%~1" == "x" goto execute

set CMD_LINE_ARGS=%*

:execute
@rem Setup the command line

set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar

@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%

:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd

:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1

:mainEnd
if "%OS%"=="Windows_NT" endlocal

:omega


================================================
FILE: module_me/.gitignore
================================================
/build


================================================
FILE: module_me/build.gradle
================================================
if (isMeModule.toBoolean()) {
    apply plugin: 'com.android.application'
} else {
    apply plugin: 'com.android.library'
}

apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'

kapt {
    arguments {
        arg("AROUTER_MODULE_NAME", project.getName())
    }
    generateStubs = true
}

android {
    compileSdkVersion rootProject.ext.android.compileSdkVersion
    defaultConfig {
        if (isMeModule.toBoolean()) {
            applicationId "com.cxz.module.me"
        }
        minSdkVersion rootProject.ext.android.minSdkVersion
        targetSdkVersion rootProject.ext.android.targetSdkVersion
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'

    }

    resourcePrefix "me_"

    sourceSets {
        main {
            if (isMeModule.toBoolean()) {
                manifest.srcFile 'src/main/module/AndroidManifest.xml'
            } else {
                manifest.srcFile 'src/main/AndroidManifest.xml'
                //集成开发模式下排除debug文件夹中的所有Java文件
                java {
                    exclude 'debug/**'
                }
                kotlin {
                    exclude 'debug/**'
                }
            }
        }
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

}

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    testImplementation rootProject.ext.testDeps["junit"]
    androidTestImplementation rootProject.ext.testDeps["runner"]
    androidTestImplementation rootProject.ext.testDeps["espresso-core"]
    api project(':baselibs')
    kapt rootProject.ext.annotationProcessorDeps["arouter-compiler"]
}


================================================
FILE: module_me/proguard-rules.pro
================================================
# 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


================================================
FILE: module_me/src/main/AndroidManifest.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.cxz.module.me">

    <application>
        <activity android:name=".MeMainActivity"></activity>
    </application>

</manifest>

================================================
FILE: module_me/src/main/java/com/cxz/module/me/MeMainActivity.kt
================================================
package com.cxz.module.me

import com.alibaba.android.arouter.facade.annotation.Route
import com.cxz.kotlin.baselibs.base.BaseMvpActivity
import com.cxz.module.me.mvp.contract.MeMainContract
import com.cxz.module.news.mvp.persenter.MeMainPresenter

@Route(path = "/me/main")
class MeMainActivity : BaseMvpActivity<MeMainContract.View, MeMainContract.Presenter>(), MeMainContract.View {

    override fun initView() {
        super.initView()
    }

    override fun start() {
    }

    override fun createPresenter(): MeMainContract.Presenter = MeMainPresenter()

    override fun attachLayoutRes(): Int = R.layout.me_activity_me_main

}


================================================
FILE: module_me/src/main/java/com/cxz/module/me/mvp/contract/MeMainContract.kt
================================================
package com.cxz.module.me.mvp.contract

import com.cxz.kotlin.baselibs.mvp.IModel
import com.cxz.kotlin.baselibs.mvp.IPresenter
import com.cxz.kotlin.baselibs.mvp.IView

/**
 * @author chenxz
 * @date 2018/12/25
 * @desc
 */
interface MeMainContract {

    interface View : IView {

    }

    interface Presenter : IPresenter<View> {

    }

    interface Model : IModel {

    }

}

================================================
FILE: module_me/src/main/java/com/cxz/module/me/mvp/model/MeMainModel.kt
================================================
package com.cxz.module.me.mvp.model

import com.cxz.kotlin.baselibs.mvp.BaseModel
import com.cxz.module.me.mvp.contract.MeMainContract

/**
 * @author chenxz
 * @date 2018/12/25
 * @desc
 */
class MeMainModel : BaseModel(), MeMainContract.Model {
}

================================================
FILE: module_me/src/main/java/com/cxz/module/me/mvp/persenter/MeMainPresenter.kt
================================================
package com.cxz.module.news.mvp.persenter

import com.cxz.kotlin.baselibs.mvp.BasePresenter
import com.cxz.module.me.mvp.contract.MeMainContract
import com.cxz.module.me.mvp.model.MeMainModel

/**
 * @author chenxz
 * @date 2018/12/25
 * @desc
 */
class MeMainPresenter : BasePresenter<MeMainContract.Model, MeMainContract.View>(), MeMainContract.Presenter {

    override fun createModel(): MeMainContract.Model? = MeMainModel()

}

================================================
FILE: module_me/src/main/java/debug/MeApplication.kt
================================================
package debug

import android.app.Application
import com.cxz.kotlin.baselibs.config.AppConfig

/**
 * @author chenxz
 * @date 2018/12/22
 * @desc
 */
class MeApplication : Application() {

    override fun onCreate() {
        super.onCreate()
        AppConfig.init(this)
    }

}

================================================
FILE: module_me/src/main/module/AndroidManifest.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.cxz.module.me">

    <application
        android:name="debug.MeApplication"
        android:allowBackup="true"
        android:label="@string/me_app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MeMainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
                <action android:name="android.intent.action.VIEW" />
            </intent-filter>
        </activity>
    </application>

</manifest>

================================================
FILE: module_me/src/main/res/layout/me_activity_me_main.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MeMainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="MeMain"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

================================================
FILE: module_me/src/main/res/values/colors.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
</resources>


================================================
FILE: module_me/src/main/res/values/strings.xml
================================================
<resources>
    <string name="me_app_name">module_me</string>
</resources>


================================================
FILE: module_me/src/main/res/values/styles.xml
================================================
<resources>
</resources>


================================================
FILE: module_news/.gitignore
================================================
/build


================================================
FILE: module_news/build.gradle
================================================
if (isNewsModule.toBoolean()) {
    apply plugin: 'com.android.application'
} else {
    apply plugin: 'com.android.library'
}

apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'

kapt {
    arguments {
        arg("AROUTER_MODULE_NAME", project.getName())
    }
    generateStubs = true
}

android {
    compileSdkVersion rootProject.ext.android.compileSdkVersion
    defaultConfig {
        if (isNewsModule.toBoolean()) {
            applicationId "com.cxz.module.news"
        }
        minSdkVersion rootProject.ext.android.minSdkVersion
        targetSdkVersion rootProject.ext.android.targetSdkVersion
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'

    }

    resourcePrefix "news_"

    sourceSets {
        main {
            if (isNewsModule.toBoolean()) {
                manifest.srcFile 'src/main/module/AndroidManifest.xml'
            } else {
                manifest.srcFile 'src/main/AndroidManifest.xml'
                //集成开发模式下排除debug文件夹中的所有Java文件
                java {
                    exclude 'debug/**'
                }
                kotlin {
                    exclude 'debug/**'
                }
            }
        }
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

}

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    testImplementation rootProject.ext.testDeps["junit"]
    androidTestImplementation rootProject.ext.testDeps["runner"]
    androidTestImplementation rootProject.ext.testDeps["espresso-core"]
    api project(':baselibs')
    kapt rootProject.ext.annotationProcessorDeps["arouter-compiler"]
}


================================================
FILE: module_news/proguard-rules.pro
================================================
# 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


================================================
FILE: module_news/src/main/AndroidManifest.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.cxz.module.news">

    <application>
        <activity android:name=".NewsMainActivity">
        </activity>
    </application>

</manifest>

================================================
FILE: module_news/src/main/java/com/cxz/module/news/NewsMainActivity.kt
================================================
package com.cxz.module.news

import com.alibaba.android.arouter.facade.annotation.Autowired
import com.alibaba.android.arouter.facade.annotation.Route
import com.alibaba.android.arouter.launcher.ARouter
import com.cxz.kotlin.baselibs.base.BaseMvpActivity
import com.cxz.kotlin.baselibs.ext.showToast
import com.cxz.module.news.mvp.contract.NewsMainContract
import com.cxz.module.news.mvp.persenter.NewsMainPresenter

@Route(path = "/news/main")
class NewsMainActivity : BaseMvpActivity<NewsMainContract.View, NewsMainContract.Presenter>(), NewsMainContract.View {

    @Autowired
    @JvmField
    var key1: String? = null
    @Autowired
    @JvmField
    var key2: String? = null

    override fun initView() {
        ARouter.getInstance().inject(this)
        super.initView()
        showToast("key1: $key1, key2: $key2")
    }

    override fun start() {
    }

    override fun createPresenter(): NewsMainContract.Presenter = NewsMainPresenter()

    override fun attachLayoutRes(): Int = R.layout.news_activity_news_main

}


================================================
FILE: module_news/src/main/java/com/cxz/module/news/NewsServiceImpl.kt
================================================
package com.cxz.module.news

import android.content.Context
import com.alibaba.android.arouter.facade.annotation.Route
import com.cxz.kotlin.baselibs.provider.NewsService

/**
 * @author chenxz
 * @date 2018/12/22
 * @desc
 */
@Route(path = "/news/service", name = "News")
class NewsServiceImpl : NewsService {

    override fun getNewsName(): String = "这是一条新闻信息"

    override fun init(context: Context?) {
    }
}

================================================
FILE: module_news/src/main/java/com/cxz/module/news/mvp/contract/NewsMainContract.kt
================================================
package com.cxz.module.news.mvp.contract

import com.cxz.kotlin.baselibs.mvp.IModel
import com.cxz.kotlin.baselibs.mvp.IPresenter
import com.cxz.kotlin.baselibs.mvp.IView

/**
 * @author chenxz
 * @date 2018/12/25
 * @desc
 */
interface NewsMainContract {

    interface View : IView {

    }

    interface Presenter : IPresenter<View> {

    }

    interface Model : IModel {

    }

}

================================================
FILE: module_news/src/main/java/com/cxz/module/news/mvp/model/NewsMainModel.kt
================================================
package com.cxz.module.news.mvp.model

import com.cxz.kotlin.baselibs.mvp.BaseModel
import com.cxz.module.news.mvp.contract.NewsMainContract

/**
 * @author chenxz
 * @date 2018/12/25
 * @desc
 */
class NewsMainModel : BaseModel(), NewsMainContract.Model {
}

================================================
FILE: module_news/src/main/java/com/cxz/module/news/mvp/persenter/NewsMainPresenter.kt
================================================
package com.cxz.module.news.mvp.persenter

import com.cxz.kotlin.baselibs.mvp.BasePresenter
import com.cxz.module.news.mvp.contract.NewsMainContract
import com.cxz.module.news.mvp.model.NewsMainModel

/**
 * @author chenxz
 * @date 2018/12/25
 * @desc
 */
class NewsMainPresenter : BasePresenter<NewsMainContract.Model, NewsMainContract.View>(), NewsMainContract.Presenter {

    override fun createModel(): NewsMainContract.Model? = NewsMainModel()

}

================================================
FILE: module_news/src/main/java/debug/NewsApplication.kt
================================================
package debug

import android.app.Application
import com.cxz.kotlin.baselibs.config.AppConfig

/**
 * @author chenxz
 * @date 2018/12/22
 * @desc
 */
class NewsApplication : Application() {

    override fun onCreate() {
        super.onCreate()
        AppConfig.init(this)
    }

}

================================================
FILE: module_news/src/main/module/AndroidManifest.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.cxz.module.news">

    <application
        android:name="debug.NewsApplication"
        android:allowBackup="true"
        android:label="@string/news_app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".NewsMainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
                <action android:name="android.intent.action.VIEW" />
            </intent-filter>
        </activity>
    </application>

</manifest>

================================================
FILE: module_news/src/main/res/layout/news_activity_news_main.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".NewsMainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="NewsMain"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

================================================
FILE: module_news/src/main/res/values/colors.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
</resources>


================================================
FILE: module_news/src/main/res/values/strings.xml
================================================
<resources>
    <string name="news_app_name">module_news</string>
</resources>


================================================
FILE: module_news/src/main/res/values/styles.xml
================================================
<resources>
</resources>


================================================
FILE: module_video/.gitignore
================================================
/build


================================================
FILE: module_video/build.gradle
================================================
if (isVideoModule.toBoolean()) {
    apply plugin: 'com.android.application'
} else {
    apply plugin: 'com.android.library'
}

apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'

kapt {
    arguments {
        arg("AROUTER_MODULE_NAME", project.getName())
    }
    generateStubs = true
}

android {
    compileSdkVersion rootProject.ext.android.compileSdkVersion
    defaultConfig {
        if (isVideoModule.toBoolean()) {
            applicationId "com.cxz.module.video"
        }
        minSdkVersion rootProject.ext.android.minSdkVersion
        targetSdkVersion rootProject.ext.android.targetSdkVersion
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'

    }

    resourcePrefix "video_"

    sourceSets {
        main {
            if (isVideoModule.toBoolean()) {
                manifest.srcFile 'src/main/module/AndroidManifest.xml'
            } else {
                manifest.srcFile 'src/main/AndroidManifest.xml'
                //集成开发模式下排除debug文件夹中的所有Java文件
                java {
                    exclude 'debug/**'
                }
                kotlin {
                    exclude 'debug/**'
                }
            }
        }
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

}

dependencies {
  
Download .txt
gitextract_u32wwojs/

├── .gitignore
├── README.md
├── app/
│   ├── .gitignore
│   ├── build.gradle
│   ├── proguard-rules.pro
│   └── src/
│       ├── androidTest/
│       │   └── java/
│       │       └── com/
│       │           └── cxz/
│       │               └── module/
│       │                   └── samples/
│       │                       └── ExampleInstrumentedTest.kt
│       ├── main/
│       │   ├── AndroidManifest.xml
│       │   ├── java/
│       │   │   └── com/
│       │   │       └── cxz/
│       │   │           └── module/
│       │   │               └── samples/
│       │   │                   ├── MainActivity.kt
│       │   │                   ├── OtherActivity.kt
│       │   │                   └── app/
│       │   │                       └── App.kt
│       │   └── res/
│       │       ├── drawable/
│       │       │   └── ic_launcher_background.xml
│       │       ├── drawable-v24/
│       │       │   └── ic_launcher_foreground.xml
│       │       ├── layout/
│       │       │   ├── app_activity_main.xml
│       │       │   └── app_activity_other.xml
│       │       ├── mipmap-anydpi-v26/
│       │       │   ├── ic_launcher.xml
│       │       │   └── ic_launcher_round.xml
│       │       └── values/
│       │           ├── colors.xml
│       │           ├── strings.xml
│       │           └── styles.xml
│       └── test/
│           └── java/
│               └── com/
│                   └── cxz/
│                       └── module/
│                           └── samples/
│                               └── ExampleUnitTest.kt
├── baselibs/
│   ├── .gitignore
│   ├── build.gradle
│   ├── proguard-rules.pro
│   └── src/
│       └── main/
│           ├── AndroidManifest.xml
│           ├── java/
│           │   └── com/
│           │       └── cxz/
│           │           └── kotlin/
│           │               └── baselibs/
│           │                   ├── base/
│           │                   │   ├── BaseActivity.kt
│           │                   │   ├── BaseFragment.kt
│           │                   │   ├── BaseMvpActivity.kt
│           │                   │   ├── BaseMvpFragment.kt
│           │                   │   └── BaseMvpTitleActivity.kt
│           │                   ├── bean/
│           │                   │   └── BaseBean.kt
│           │                   ├── config/
│           │                   │   └── AppConfig.kt
│           │                   ├── ext/
│           │                   │   ├── CommonExt.kt
│           │                   │   └── RxExt.kt
│           │                   ├── http/
│           │                   │   ├── HttpStatus.kt
│           │                   │   ├── RetrofitFactory.kt
│           │                   │   ├── constant/
│           │                   │   │   └── HttpConstant.kt
│           │                   │   ├── cookies/
│           │                   │   │   ├── CookieManager.kt
│           │                   │   │   ├── OkHttpCookies.kt
│           │                   │   │   └── PersistentCookieStore.kt
│           │                   │   ├── exception/
│           │                   │   │   ├── ApiException.kt
│           │                   │   │   └── ExceptionHandle.kt
│           │                   │   ├── function/
│           │                   │   │   └── RetryWithDelay.kt
│           │                   │   └── interceptor/
│           │                   │       ├── CacheInterceptor.kt
│           │                   │       ├── CookieInterceptor.kt
│           │                   │       ├── HeaderInterceptor.kt
│           │                   │       └── QueryParameterInterceptor.kt
│           │                   ├── mvp/
│           │                   │   ├── BaseModel.kt
│           │                   │   ├── BasePresenter.kt
│           │                   │   ├── IModel.kt
│           │                   │   ├── IPresenter.kt
│           │                   │   └── IView.kt
│           │                   ├── provider/
│           │                   │   └── NewsService.kt
│           │                   ├── rx/
│           │                   │   ├── BaseObserver.kt
│           │                   │   ├── BaseSubscriber.kt
│           │                   │   ├── SchedulerUtils.kt
│           │                   │   └── scheduler/
│           │                   │       ├── BaseScheduler.kt
│           │                   │       ├── ComputationMainScheduler.kt
│           │                   │       ├── IoMainScheduler.kt
│           │                   │       ├── NewThreadMainScheduler.kt
│           │                   │       ├── SingleMainScheduler.kt
│           │                   │       └── TrampolineMainScheduler.kt
│           │                   ├── utils/
│           │                   │   ├── AnimatorUtil.kt
│           │                   │   ├── AppUtils.kt
│           │                   │   ├── CommonUtil.kt
│           │                   │   ├── FileProvider7.kt
│           │                   │   ├── KeyBoardUtil.kt
│           │                   │   ├── NLog.kt
│           │                   │   ├── NetWorkUtil.kt
│           │                   │   ├── Preference.kt
│           │                   │   ├── RomUtil.kt
│           │                   │   ├── RxTimerUtil.kt
│           │                   │   └── StatusBarUtil.kt
│           │                   └── widget/
│           │                       ├── CustomToast.kt
│           │                       ├── LoadingDialog.kt
│           │                       └── OnNoDoubleClickListener.kt
│           └── res/
│               ├── drawable/
│               │   ├── bg_loading_dialog.xml
│               │   └── bg_toast_custom.xml
│               ├── layout/
│               │   ├── activity_base_title.xml
│               │   ├── base_toolbar.xml
│               │   ├── layout_loading_dialog.xml
│               │   └── toast_custom.xml
│               ├── values/
│               │   ├── colors.xml
│               │   ├── dimen.xml
│               │   ├── ids.xml
│               │   ├── strings.xml
│               │   └── styles.xml
│               └── xml/
│                   ├── file_paths.xml
│                   └── network_security_config.xml
├── build.gradle
├── config.gradle
├── gradle/
│   └── wrapper/
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── module_me/
│   ├── .gitignore
│   ├── build.gradle
│   ├── proguard-rules.pro
│   └── src/
│       └── main/
│           ├── AndroidManifest.xml
│           ├── java/
│           │   ├── com/
│           │   │   └── cxz/
│           │   │       └── module/
│           │   │           └── me/
│           │   │               ├── MeMainActivity.kt
│           │   │               └── mvp/
│           │   │                   ├── contract/
│           │   │                   │   └── MeMainContract.kt
│           │   │                   ├── model/
│           │   │                   │   └── MeMainModel.kt
│           │   │                   └── persenter/
│           │   │                       └── MeMainPresenter.kt
│           │   └── debug/
│           │       └── MeApplication.kt
│           ├── module/
│           │   └── AndroidManifest.xml
│           └── res/
│               ├── layout/
│               │   └── me_activity_me_main.xml
│               └── values/
│                   ├── colors.xml
│                   ├── strings.xml
│                   └── styles.xml
├── module_news/
│   ├── .gitignore
│   ├── build.gradle
│   ├── proguard-rules.pro
│   └── src/
│       └── main/
│           ├── AndroidManifest.xml
│           ├── java/
│           │   ├── com/
│           │   │   └── cxz/
│           │   │       └── module/
│           │   │           └── news/
│           │   │               ├── NewsMainActivity.kt
│           │   │               ├── NewsServiceImpl.kt
│           │   │               └── mvp/
│           │   │                   ├── contract/
│           │   │                   │   └── NewsMainContract.kt
│           │   │                   ├── model/
│           │   │                   │   └── NewsMainModel.kt
│           │   │                   └── persenter/
│           │   │                       └── NewsMainPresenter.kt
│           │   └── debug/
│           │       └── NewsApplication.kt
│           ├── module/
│           │   └── AndroidManifest.xml
│           └── res/
│               ├── layout/
│               │   └── news_activity_news_main.xml
│               └── values/
│                   ├── colors.xml
│                   ├── strings.xml
│                   └── styles.xml
├── module_video/
│   ├── .gitignore
│   ├── build.gradle
│   ├── proguard-rules.pro
│   └── src/
│       └── main/
│           ├── AndroidManifest.xml
│           ├── java/
│           │   ├── com/
│           │   │   └── cxz/
│           │   │       └── module/
│           │   │           └── video/
│           │   │               ├── VideoMainActivity.kt
│           │   │               └── mvp/
│           │   │                   ├── contract/
│           │   │                   │   └── VideoMainContract.kt
│           │   │                   ├── model/
│           │   │                   │   └── VideoMainModel.kt
│           │   │                   └── persenter/
│           │   │                       └── VideoMainPresenter.kt
│           │   └── debug/
│           │       └── VideoApplication.kt
│           ├── module/
│           │   └── AndroidManifest.xml
│           └── res/
│               ├── layout/
│               │   └── video_activity_video_main.xml
│               └── values/
│                   ├── colors.xml
│                   ├── strings.xml
│                   └── styles.xml
└── settings.gradle
Condensed preview — 139 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (213K chars).
[
  {
    "path": ".gitignore",
    "chars": 87,
    "preview": "*.iml\n.gradle\n/local.properties\n/.idea\n.DS_Store\n/build\n/captures\n.externalNativeBuild\n"
  },
  {
    "path": "README.md",
    "chars": 9050,
    "preview": "# 基于 MVP 的 Android 组件化开发框架实践\n\n## 一、背景\n\n当我们的项目变得越来越大,代码变得越来越臃肿,耦合会越来越多,编译速度越来越慢,开发效率也会变得越来越低,怎么办?这个时候我们就需要对旧项目进行重构,即是模块的拆"
  },
  {
    "path": "app/.gitignore",
    "chars": 7,
    "preview": "/build\n"
  },
  {
    "path": "app/build.gradle",
    "chars": 2189,
    "preview": "apply plugin: 'com.android.application'\n\napply plugin: 'kotlin-android'\napply plugin: 'kotlin-android-extensions'\napply "
  },
  {
    "path": "app/proguard-rules.pro",
    "chars": 751,
    "preview": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguar"
  },
  {
    "path": "app/src/androidTest/java/com/cxz/module/samples/ExampleInstrumentedTest.kt",
    "chars": 656,
    "preview": "package com.cxz.module.samples\n\nimport androidx.test.platform.app.InstrumentationRegistry\nimport androidx.test.ext.junit"
  },
  {
    "path": "app/src/main/AndroidManifest.xml",
    "chars": 869,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package="
  },
  {
    "path": "app/src/main/java/com/cxz/module/samples/MainActivity.kt",
    "chars": 1600,
    "preview": "package com.cxz.module.samples\n\nimport android.os.Bundle\nimport androidx.appcompat.app.AppCompatActivity\nimport com.alib"
  },
  {
    "path": "app/src/main/java/com/cxz/module/samples/OtherActivity.kt",
    "chars": 398,
    "preview": "package com.cxz.module.samples\n\nimport androidx.appcompat.app.AppCompatActivity\nimport android.os.Bundle\nimport com.alib"
  },
  {
    "path": "app/src/main/java/com/cxz/module/samples/app/App.kt",
    "chars": 2662,
    "preview": "package com.cxz.module.samples.app\n\nimport android.app.Activity\nimport android.app.Application\nimport android.content.Co"
  },
  {
    "path": "app/src/main/res/drawable/ic_launcher_background.xml",
    "chars": 4887,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector\n        xmlns:android=\"http://schemas.android.com/apk/res/android\"\n      "
  },
  {
    "path": "app/src/main/res/drawable-v24/ic_launcher_foreground.xml",
    "chars": 1969,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n        xmlns:aapt=\"http://schemas.android.com/aapt\"\n"
  },
  {
    "path": "app/src/main/res/layout/app_activity_main.xml",
    "chars": 1017,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmln"
  },
  {
    "path": "app/src/main/res/layout/app_activity_other.xml",
    "chars": 370,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas."
  },
  {
    "path": "app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "chars": 270,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <b"
  },
  {
    "path": "app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
    "chars": 270,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <b"
  },
  {
    "path": "app/src/main/res/values/colors.xml",
    "chars": 208,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"colorPrimary\">#008577</color>\n    <color name=\"color"
  },
  {
    "path": "app/src/main/res/values/strings.xml",
    "chars": 83,
    "preview": "<resources>\n    <string name=\"app_name\">AndroidModuleSamples</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/styles.xml",
    "chars": 383,
    "preview": "<resources>\n\n    <!-- Base application theme. -->\n    <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.DarkActionBar"
  },
  {
    "path": "app/src/test/java/com/cxz/module/samples/ExampleUnitTest.kt",
    "chars": 347,
    "preview": "package com.cxz.module.samples\n\nimport org.junit.Test\n\nimport org.junit.Assert.*\n\n/**\n * Example local unit test, which "
  },
  {
    "path": "baselibs/.gitignore",
    "chars": 7,
    "preview": "/build\n"
  },
  {
    "path": "baselibs/build.gradle",
    "chars": 1248,
    "preview": "apply plugin: 'com.android.library'\napply plugin: 'kotlin-android'\napply plugin: 'kotlin-android-extensions'\napply plugi"
  },
  {
    "path": "baselibs/proguard-rules.pro",
    "chars": 751,
    "preview": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguar"
  },
  {
    "path": "baselibs/src/main/AndroidManifest.xml",
    "chars": 813,
    "preview": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n          package=\"com.cxz.kotlin.baselibs\">\n\n    <"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/base/BaseActivity.kt",
    "chars": 2825,
    "preview": "package com.cxz.kotlin.baselibs.base\n\nimport android.content.pm.ActivityInfo\nimport android.os.Bundle\nimport android.vie"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/base/BaseFragment.kt",
    "chars": 2019,
    "preview": "package com.cxz.kotlin.baselibs.base\n\nimport android.os.Bundle\nimport android.view.LayoutInflater\nimport android.view.Vi"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/base/BaseMvpActivity.kt",
    "chars": 1012,
    "preview": "package com.cxz.kotlin.baselibs.base\n\nimport com.cxz.kotlin.baselibs.ext.showToast\nimport com.cxz.kotlin.baselibs.mvp.IP"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/base/BaseMvpFragment.kt",
    "chars": 1055,
    "preview": "package com.cxz.kotlin.baselibs.base\n\nimport android.view.View\nimport com.cxz.kotlin.baselibs.ext.showToast\nimport com.c"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/base/BaseMvpTitleActivity.kt",
    "chars": 1434,
    "preview": "package com.cxz.kotlin.baselibs.base\n\nimport androidx.annotation.ColorRes\nimport androidx.annotation.StringRes\nimport co"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/bean/BaseBean.kt",
    "chars": 172,
    "preview": "package com.cxz.kotlin.baselibs.bean\n\n/**\n * @author admin\n * @date 2018/11/21\n * @desc\n */\nopen class BaseBean {\n    va"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/config/AppConfig.kt",
    "chars": 665,
    "preview": "package com.cxz.kotlin.baselibs.config\n\nimport android.app.Application\n\n/**\n * @author chenxz\n * @date 2019/11/1\n * @des"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/ext/CommonExt.kt",
    "chars": 2386,
    "preview": "package com.cxz.kotlin.baselibs.ext\n\nimport android.app.Activity\nimport android.content.Context\nimport android.content.I"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/ext/RxExt.kt",
    "chars": 3202,
    "preview": "package com.cxz.kotlin.baselibs.ext\n\nimport com.cxz.kotlin.baselibs.bean.BaseBean\nimport com.cxz.kotlin.baselibs.http.Ht"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/http/HttpStatus.kt",
    "chars": 500,
    "preview": "package com.cxz.kotlin.baselibs.http\n\n/**\n * Created by chenxz on 2018/4/21.\n */\nobject HttpStatus {\n    /**\n     * 响应成功"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/http/RetrofitFactory.kt",
    "chars": 3073,
    "preview": "package com.cxz.kotlin.baselibs.http\n\nimport com.cxz.kotlin.baselibs.config.AppConfig\nimport com.cxz.kotlin.baselibs.htt"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/http/constant/HttpConstant.kt",
    "chars": 365,
    "preview": "package com.cxz.kotlin.baselibs.http.constant\n\n/**\n * @author chenxz\n * @date 2018/11/21\n * @desc HttpConstant\n */\nobjec"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/http/cookies/CookieManager.kt",
    "chars": 1120,
    "preview": "package com.cxz.kotlin.baselibs.http.cookies\n\nimport okhttp3.Cookie\nimport okhttp3.CookieJar\nimport okhttp3.HttpUrl\n\n/**"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/http/cookies/OkHttpCookies.kt",
    "chars": 2080,
    "preview": "package com.cxz.kotlin.baselibs.http.cookies\n\nimport okhttp3.Cookie\nimport java.io.IOException\nimport java.io.ObjectInpu"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/http/cookies/PersistentCookieStore.kt",
    "chars": 6056,
    "preview": "package com.cxz.kotlin.baselibs.http.cookies\n\nimport android.content.Context\nimport android.content.SharedPreferences\nim"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/http/exception/ApiException.kt",
    "chars": 331,
    "preview": "package com.cxz.kotlin.baselibs.http.exception\n\n/**\n * Created by chenxz on 2018/4/21.\n */\nclass ApiException : RuntimeE"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/http/exception/ExceptionHandle.kt",
    "chars": 2132,
    "preview": "package com.cxz.kotlin.baselibs.http.exception\n\nimport com.cxz.kotlin.baselibs.http.HttpStatus\nimport com.google.gson.Js"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/http/function/RetryWithDelay.kt",
    "chars": 1755,
    "preview": "package com.cxz.kotlin.baselibs.http.function\n\nimport io.reactivex.Observable\nimport io.reactivex.functions.BiFunction\ni"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/http/interceptor/CacheInterceptor.kt",
    "chars": 1425,
    "preview": "package com.cxz.kotlin.baselibs.http.interceptor\n\nimport com.cxz.kotlin.baselibs.config.AppConfig\nimport com.cxz.kotlin."
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/http/interceptor/CookieInterceptor.kt",
    "chars": 2280,
    "preview": "package com.cxz.kotlin.baselibs.http.interceptor\n\nimport com.cxz.kotlin.baselibs.http.constant.HttpConstant\nimport com.c"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/http/interceptor/HeaderInterceptor.kt",
    "chars": 1221,
    "preview": "package com.cxz.kotlin.baselibs.http.interceptor\n\nimport com.cxz.kotlin.baselibs.http.constant.HttpConstant\nimport com.c"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/http/interceptor/QueryParameterInterceptor.kt",
    "chars": 765,
    "preview": "package com.cxz.kotlin.baselibs.http.interceptor\n\nimport okhttp3.Interceptor\nimport okhttp3.Request\nimport okhttp3.Respo"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/mvp/BaseModel.kt",
    "chars": 1053,
    "preview": "package com.cxz.kotlin.baselibs.mvp\n\nimport androidx.lifecycle.Lifecycle\nimport androidx.lifecycle.LifecycleObserver\nimp"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/mvp/BasePresenter.kt",
    "chars": 2475,
    "preview": "package com.cxz.kotlin.baselibs.mvp\n\nimport androidx.lifecycle.Lifecycle\nimport androidx.lifecycle.LifecycleObserver\nimp"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/mvp/IModel.kt",
    "chars": 232,
    "preview": "package com.cxz.kotlin.baselibs.mvp\n\nimport io.reactivex.disposables.Disposable\n\n/**\n * @author chenxz\n * @date 2018/11/"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/mvp/IPresenter.kt",
    "chars": 256,
    "preview": "package com.cxz.kotlin.baselibs.mvp\n\n/**\n * @author chenxz\n * @date 2018/11/18\n * @desc IPresenter\n */\ninterface IPresen"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/mvp/IView.kt",
    "chars": 430,
    "preview": "package com.cxz.kotlin.baselibs.mvp\n\n/**\n * @author chenxz\n * @date 2018/11/18\n * @desc IView\n */\ninterface IView {\n    "
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/provider/NewsService.kt",
    "chars": 228,
    "preview": "package com.cxz.kotlin.baselibs.provider\n\nimport com.alibaba.android.arouter.facade.template.IProvider\n\n/**\n * @author c"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/rx/BaseObserver.kt",
    "chars": 1927,
    "preview": "package com.cxz.kotlin.baselibs.rx\n\nimport com.cxz.kotlin.baselibs.bean.BaseBean\nimport com.cxz.kotlin.baselibs.http.Htt"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/rx/BaseSubscriber.kt",
    "chars": 1934,
    "preview": "package com.cxz.kotlin.baselibs.rx\n\nimport com.cxz.kotlin.baselibs.bean.BaseBean\nimport com.cxz.kotlin.baselibs.http.Htt"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/rx/SchedulerUtils.kt",
    "chars": 230,
    "preview": "package com.cxz.kotlin.baselibs.rx\n\nimport com.cxz.kotlin.baselibs.rx.scheduler.IoMainScheduler\n\n/**\n * Created by chenx"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/rx/scheduler/BaseScheduler.kt",
    "chars": 1315,
    "preview": "package com.cxz.kotlin.baselibs.rx.scheduler\n\nimport io.reactivex.*\nimport org.reactivestreams.Publisher\n\n/**\n * Created"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/rx/scheduler/ComputationMainScheduler.kt",
    "chars": 326,
    "preview": "package com.cxz.kotlin.baselibs.rx.scheduler\n\nimport io.reactivex.android.schedulers.AndroidSchedulers\nimport io.reactiv"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/rx/scheduler/IoMainScheduler.kt",
    "chars": 282,
    "preview": "package com.cxz.kotlin.baselibs.rx.scheduler\n\nimport io.reactivex.android.schedulers.AndroidSchedulers\nimport io.reactiv"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/rx/scheduler/NewThreadMainScheduler.kt",
    "chars": 322,
    "preview": "package com.cxz.kotlin.baselibs.rx.scheduler\n\nimport io.reactivex.android.schedulers.AndroidSchedulers\nimport io.reactiv"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/rx/scheduler/SingleMainScheduler.kt",
    "chars": 316,
    "preview": "package com.cxz.kotlin.baselibs.rx.scheduler\n\nimport io.reactivex.android.schedulers.AndroidSchedulers\nimport io.reactiv"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/rx/scheduler/TrampolineMainScheduler.kt",
    "chars": 324,
    "preview": "package com.cxz.kotlin.baselibs.rx.scheduler\n\nimport io.reactivex.android.schedulers.AndroidSchedulers\nimport io.reactiv"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/utils/AnimatorUtil.kt",
    "chars": 2399,
    "preview": "package com.cxz.kotlin.baselibs.utils\n\nimport android.view.View\nimport android.view.animation.AccelerateInterpolator\nimp"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/utils/AppUtils.kt",
    "chars": 4580,
    "preview": "package com.cxz.kotlin.baselibs.utils\n\nimport android.annotation.SuppressLint\nimport android.app.ActivityManager\nimport "
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/utils/CommonUtil.kt",
    "chars": 3723,
    "preview": "package com.cxz.kotlin.baselibs.utils\n\nimport android.content.Context\nimport android.graphics.Color\nimport android.text."
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/utils/FileProvider7.kt",
    "chars": 2431,
    "preview": "package com.cxz.kotlin.baselibs.utils\n\nimport android.content.Context\nimport android.content.Intent\nimport android.conte"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/utils/KeyBoardUtil.kt",
    "chars": 1868,
    "preview": "package com.cxz.kotlin.baselibs.utils\n\nimport android.content.Context\nimport android.view.MotionEvent\nimport android.vie"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/utils/NLog.kt",
    "chars": 1129,
    "preview": "package com.cxz.kotlin.baselibs.utils\n\nimport android.util.Log\n\n/**\n * @author chenxz\n * @date 2019/11/9\n * @desc 日志打印类\n"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/utils/NetWorkUtil.kt",
    "chars": 9805,
    "preview": "package com.cxz.kotlin.baselibs.utils\n\nimport android.annotation.SuppressLint\nimport android.content.Context\nimport andr"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/utils/Preference.kt",
    "chars": 3859,
    "preview": "package com.cxz.kotlin.baselibs.utils\n\nimport android.annotation.SuppressLint\nimport android.content.Context\nimport andr"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/utils/RomUtil.kt",
    "chars": 2779,
    "preview": "package com.cxz.kotlin.baselibs.utils\n\nimport android.os.Build\nimport android.text.TextUtils\nimport java.io.BufferedRead"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/utils/RxTimerUtil.kt",
    "chars": 1223,
    "preview": "package com.cxz.kotlin.baselibs.utils\n\nimport io.reactivex.Observable\nimport io.reactivex.android.schedulers.AndroidSche"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/utils/StatusBarUtil.kt",
    "chars": 26827,
    "preview": "package com.cxz.kotlin.baselibs.utils\n\nimport android.annotation.TargetApi\nimport android.app.Activity\nimport android.co"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/widget/CustomToast.kt",
    "chars": 870,
    "preview": "package com.cxz.kotlin.baselibs.widget\n\nimport android.content.Context\nimport android.view.Gravity\nimport android.view.V"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/widget/LoadingDialog.kt",
    "chars": 1686,
    "preview": "package com.cxz.kotlin.baselibs.widget\n\nimport android.app.Dialog\nimport android.content.Context\nimport android.view.Gra"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/widget/OnNoDoubleClickListener.kt",
    "chars": 686,
    "preview": "package com.cxz.kotlin.baselibs.widget\n\nimport android.view.View\n\n/**\n * @author admin\n * @date 2018/11/23\n * @desc 防止连续"
  },
  {
    "path": "baselibs/src/main/res/drawable/bg_loading_dialog.xml",
    "chars": 235,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:sha"
  },
  {
    "path": "baselibs/src/main/res/drawable/bg_toast_custom.xml",
    "chars": 235,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:sha"
  },
  {
    "path": "baselibs/src/main/res/layout/activity_base_title.xml",
    "chars": 443,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    andr"
  },
  {
    "path": "baselibs/src/main/res/layout/base_toolbar.xml",
    "chars": 1446,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.google.android.material.appbar.AppBarLayout xmlns:android=\"http://schemas.an"
  },
  {
    "path": "baselibs/src/main/res/layout/layout_loading_dialog.xml",
    "chars": 1041,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmln"
  },
  {
    "path": "baselibs/src/main/res/layout/toast_custom.xml",
    "chars": 727,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    andr"
  },
  {
    "path": "baselibs/src/main/res/values/colors.xml",
    "chars": 300,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"colorPrimary\">#008577</color>\n    <color name=\"color"
  },
  {
    "path": "baselibs/src/main/res/values/dimen.xml",
    "chars": 177,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <dimen name=\"dp_20\">20dp</dimen>\n    <dimen name=\"dp_24\">24dp</d"
  },
  {
    "path": "baselibs/src/main/res/values/ids.xml",
    "chars": 190,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<resources>\n    <item name=\"statusbarutil_fake_status_bar_view\" type=\"id\" />\n    "
  },
  {
    "path": "baselibs/src/main/res/values/strings.xml",
    "chars": 71,
    "preview": "<resources>\n    <string name=\"app_name\">baselibs</string>\n</resources>\n"
  },
  {
    "path": "baselibs/src/main/res/values/styles.xml",
    "chars": 1078,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <!-- Base application theme. -->\n    <style name=\"AppTheme\" pare"
  },
  {
    "path": "baselibs/src/main/res/xml/file_paths.xml",
    "chars": 452,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<paths>\n\n    <root-path\n        name=\"root\"\n        path=\"\" />\n\n    <files-path\n "
  },
  {
    "path": "baselibs/src/main/res/xml/network_security_config.xml",
    "chars": 144,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<network-security-config>\n    <base-config cleartextTrafficPermitted=\"true\" />\n</"
  },
  {
    "path": "build.gradle",
    "chars": 715,
    "preview": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\napply from :\"config."
  },
  {
    "path": "config.gradle",
    "chars": 4214,
    "preview": "ext {\n    android = [\n            compileSdkVersion: 30,\n            buildToolsVersion: \"30.0.3\",\n            minSdkVers"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "chars": 232,
    "preview": "#Sun Oct 27 19:16:55 CST 2019\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_"
  },
  {
    "path": "gradle.properties",
    "chars": 1103,
    "preview": "# Project-wide Gradle settings.\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will ov"
  },
  {
    "path": "gradlew",
    "chars": 5296,
    "preview": "#!/usr/bin/env sh\n\n##############################################################################\n##\n##  Gradle start up"
  },
  {
    "path": "gradlew.bat",
    "chars": 2176,
    "preview": "@if \"%DEBUG%\" == \"\" @echo off\n@rem ##########################################################################\n@rem\n@rem "
  },
  {
    "path": "module_me/.gitignore",
    "chars": 7,
    "preview": "/build\n"
  },
  {
    "path": "module_me/build.gradle",
    "chars": 1832,
    "preview": "if (isMeModule.toBoolean()) {\n    apply plugin: 'com.android.application'\n} else {\n    apply plugin: 'com.android.librar"
  },
  {
    "path": "module_me/proguard-rules.pro",
    "chars": 751,
    "preview": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguar"
  },
  {
    "path": "module_me/src/main/AndroidManifest.xml",
    "chars": 252,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package="
  },
  {
    "path": "module_me/src/main/java/com/cxz/module/me/MeMainActivity.kt",
    "chars": 639,
    "preview": "package com.cxz.module.me\n\nimport com.alibaba.android.arouter.facade.annotation.Route\nimport com.cxz.kotlin.baselibs.bas"
  },
  {
    "path": "module_me/src/main/java/com/cxz/module/me/mvp/contract/MeMainContract.kt",
    "chars": 383,
    "preview": "package com.cxz.module.me.mvp.contract\n\nimport com.cxz.kotlin.baselibs.mvp.IModel\nimport com.cxz.kotlin.baselibs.mvp.IPr"
  },
  {
    "path": "module_me/src/main/java/com/cxz/module/me/mvp/model/MeMainModel.kt",
    "chars": 248,
    "preview": "package com.cxz.module.me.mvp.model\n\nimport com.cxz.kotlin.baselibs.mvp.BaseModel\nimport com.cxz.module.me.mvp.contract."
  },
  {
    "path": "module_me/src/main/java/com/cxz/module/me/mvp/persenter/MeMainPresenter.kt",
    "chars": 432,
    "preview": "package com.cxz.module.news.mvp.persenter\n\nimport com.cxz.kotlin.baselibs.mvp.BasePresenter\nimport com.cxz.module.me.mvp"
  },
  {
    "path": "module_me/src/main/java/debug/MeApplication.kt",
    "chars": 281,
    "preview": "package debug\n\nimport android.app.Application\nimport com.cxz.kotlin.baselibs.config.AppConfig\n\n/**\n * @author chenxz\n * "
  },
  {
    "path": "module_me/src/main/module/AndroidManifest.xml",
    "chars": 731,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package="
  },
  {
    "path": "module_me/src/main/res/layout/me_activity_me_main.xml",
    "chars": 776,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas."
  },
  {
    "path": "module_me/src/main/res/values/colors.xml",
    "chars": 64,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n</resources>\n"
  },
  {
    "path": "module_me/src/main/res/values/strings.xml",
    "chars": 75,
    "preview": "<resources>\n    <string name=\"me_app_name\">module_me</string>\n</resources>\n"
  },
  {
    "path": "module_me/src/main/res/values/styles.xml",
    "chars": 25,
    "preview": "<resources>\n</resources>\n"
  },
  {
    "path": "module_news/.gitignore",
    "chars": 7,
    "preview": "/build\n"
  },
  {
    "path": "module_news/build.gradle",
    "chars": 1842,
    "preview": "if (isNewsModule.toBoolean()) {\n    apply plugin: 'com.android.application'\n} else {\n    apply plugin: 'com.android.libr"
  },
  {
    "path": "module_news/proguard-rules.pro",
    "chars": 751,
    "preview": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguar"
  },
  {
    "path": "module_news/src/main/AndroidManifest.xml",
    "chars": 271,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n          pa"
  },
  {
    "path": "module_news/src/main/java/com/cxz/module/news/NewsMainActivity.kt",
    "chars": 1031,
    "preview": "package com.cxz.module.news\n\nimport com.alibaba.android.arouter.facade.annotation.Autowired\nimport com.alibaba.android.a"
  },
  {
    "path": "module_news/src/main/java/com/cxz/module/news/NewsServiceImpl.kt",
    "chars": 415,
    "preview": "package com.cxz.module.news\n\nimport android.content.Context\nimport com.alibaba.android.arouter.facade.annotation.Route\ni"
  },
  {
    "path": "module_news/src/main/java/com/cxz/module/news/mvp/contract/NewsMainContract.kt",
    "chars": 387,
    "preview": "package com.cxz.module.news.mvp.contract\n\nimport com.cxz.kotlin.baselibs.mvp.IModel\nimport com.cxz.kotlin.baselibs.mvp.I"
  },
  {
    "path": "module_news/src/main/java/com/cxz/module/news/mvp/model/NewsMainModel.kt",
    "chars": 258,
    "preview": "package com.cxz.module.news.mvp.model\n\nimport com.cxz.kotlin.baselibs.mvp.BaseModel\nimport com.cxz.module.news.mvp.contr"
  },
  {
    "path": "module_news/src/main/java/com/cxz/module/news/mvp/persenter/NewsMainPresenter.kt",
    "chars": 452,
    "preview": "package com.cxz.module.news.mvp.persenter\n\nimport com.cxz.kotlin.baselibs.mvp.BasePresenter\nimport com.cxz.module.news.m"
  },
  {
    "path": "module_news/src/main/java/debug/NewsApplication.kt",
    "chars": 283,
    "preview": "package debug\n\nimport android.app.Application\nimport com.cxz.kotlin.baselibs.config.AppConfig\n\n/**\n * @author chenxz\n * "
  },
  {
    "path": "module_news/src/main/module/AndroidManifest.xml",
    "chars": 739,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package="
  },
  {
    "path": "module_news/src/main/res/layout/news_activity_news_main.xml",
    "chars": 780,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas."
  },
  {
    "path": "module_news/src/main/res/values/colors.xml",
    "chars": 64,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n</resources>\n"
  },
  {
    "path": "module_news/src/main/res/values/strings.xml",
    "chars": 79,
    "preview": "<resources>\n    <string name=\"news_app_name\">module_news</string>\n</resources>\n"
  },
  {
    "path": "module_news/src/main/res/values/styles.xml",
    "chars": 25,
    "preview": "<resources>\n</resources>\n"
  },
  {
    "path": "module_video/.gitignore",
    "chars": 7,
    "preview": "/build\n"
  },
  {
    "path": "module_video/build.gradle",
    "chars": 1847,
    "preview": "if (isVideoModule.toBoolean()) {\n    apply plugin: 'com.android.application'\n} else {\n    apply plugin: 'com.android.lib"
  },
  {
    "path": "module_video/proguard-rules.pro",
    "chars": 751,
    "preview": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguar"
  },
  {
    "path": "module_video/src/main/AndroidManifest.xml",
    "chars": 273,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n          pa"
  },
  {
    "path": "module_video/src/main/java/com/cxz/module/video/VideoMainActivity.kt",
    "chars": 1045,
    "preview": "package com.cxz.module.video\n\nimport com.alibaba.android.arouter.facade.annotation.Autowired\nimport com.alibaba.android."
  },
  {
    "path": "module_video/src/main/java/com/cxz/module/video/mvp/contract/VideoMainContract.kt",
    "chars": 388,
    "preview": "package com.cxz.module.news.mvp.contract\n\nimport com.cxz.kotlin.baselibs.mvp.IModel\nimport com.cxz.kotlin.baselibs.mvp.I"
  },
  {
    "path": "module_video/src/main/java/com/cxz/module/video/mvp/model/VideoMainModel.kt",
    "chars": 261,
    "preview": "package com.cxz.module.news.mvp.model\n\nimport com.cxz.kotlin.baselibs.mvp.BaseModel\nimport com.cxz.module.news.mvp.contr"
  },
  {
    "path": "module_video/src/main/java/com/cxz/module/video/mvp/persenter/VideoMainPresenter.kt",
    "chars": 460,
    "preview": "package com.cxz.module.news.mvp.persenter\n\nimport com.cxz.kotlin.baselibs.mvp.BasePresenter\nimport com.cxz.module.news.m"
  },
  {
    "path": "module_video/src/main/java/debug/VideoApplication.kt",
    "chars": 285,
    "preview": "package debug\n\nimport android.app.Application\nimport com.cxz.kotlin.baselibs.config.AppConfig\n\n\n/**\n * @author chenxz\n *"
  },
  {
    "path": "module_video/src/main/module/AndroidManifest.xml",
    "chars": 743,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package="
  },
  {
    "path": "module_video/src/main/res/layout/video_activity_video_main.xml",
    "chars": 785,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout\n    xmlns:android=\"http://sche"
  },
  {
    "path": "module_video/src/main/res/values/colors.xml",
    "chars": 64,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n</resources>\n"
  },
  {
    "path": "module_video/src/main/res/values/strings.xml",
    "chars": 81,
    "preview": "<resources>\n    <string name=\"video_app_name\">module_video</string>\n</resources>\n"
  },
  {
    "path": "module_video/src/main/res/values/styles.xml",
    "chars": 26,
    "preview": "<resources>\n\n</resources>\n"
  },
  {
    "path": "settings.gradle",
    "chars": 75,
    "preview": "include ':app', ':baselibs', ':module_news', ':module_me', ':module_video'\n"
  }
]

// ... and 1 more files (download for full content)

About this extraction

This page contains the full source code of the iceCola7/AndroidModuleSamples GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 139 files (181.2 KB), approximately 51.5k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!